Passki
A simple, secure, and easy-to-use WebAuthn/Passkey implementation for Rust.
Features
- ✨ Simple API - Easy-to-use interface for passkey registration and authentication
- 🔐 Multiple Algorithms - Support for EdDSA (Ed25519), ES256/ES384 (P-256/P-384), and RS256/RS384 (RSA)
- 🛡️ Security First - Built-in replay attack protection via signature counters
- 📦 Framework Agnostic - No web framework lock-in, works with any HTTP server
- 🔑 Extensions - Support for
credProps(discoverable credential reporting) and PRF (key derivation / E2E encryption) - 🦀 Pure Rust - Memory-safe implementation with no unsafe code
Installation
Add this to your Cargo.toml:
[]
= "0.2"
Quick Start
use ;
// Initialize Passki with your relying party information
let passki = new;
// Registration flow
// Step 1: Start registration and send challenge to client
let user_id = b"unique_user_identifier_12345"; // At least 16 bytes
let = passki.start_passkey_registration.expect;
// Send registration_challenge to client (as JSON)
// Client uses WebAuthn API to create credential
// Step 2: Receive credential from client and complete registration
let mut stored_passkey = passki.finish_passkey_registration?;
// Save stored_passkey to your database associated with the user
// Authentication flow
// Step 1: Start authentication and send challenge to client
let = passki.start_passkey_authentication;
// Send authentication_challenge to client (as JSON)
// Client uses WebAuthn API to sign the challenge
// Step 2: Receive credential from client and verify authentication
let result = passki.finish_passkey_authentication?;
// Update the counter in your database to prevent replay attacks
stored_passkey.counter = result.counter;
Supported Algorithms
Passki supports the following COSE algorithms:
- EdDSA (Ed25519) - Algorithm ID: -8
- ES256 (ECDSA with P-256 and SHA-256) - Algorithm ID: -7
- ES384 (ECDSA with P-384 and SHA-384) - Algorithm ID: -35
- RS256 (RSASSA-PKCS1-v1_5 with SHA-256) - Algorithm ID: -257
- RS384 (RSASSA-PKCS1-v1_5 with SHA-384) - Algorithm ID: -258
Extensions
credProps
The credProps extension reports whether the authenticator created a discoverable (resident) credential - one stored on the device and usable in passwordless flows. Request it during registration; the result is stored in StoredPasskey::rk.
use RegistrationExtensions;
// Request credProps during registration
let = passki.start_passkey_registration?;
let passkey = passki.finish_passkey_registration?;
// passkey.rk == Some(true) → discoverable credential created
// passkey.rk == Some(false) → non-discoverable credential created
// passkey.rk == None → authenticator did not report
PRF
The WebAuthn PRF extension lets a passkey derive deterministic secret bytes from the authenticator's internal HMAC-secret. This is useful for end-to-end encryption, per-user key derivation, and other scenarios where you need a stable secret tied to a specific passkey.
The server passes input salts; the browser computes HMAC-SHA256("WebAuthn PRF" || 0x00 || input) and feeds the result into the authenticator. Passki passes the outputs through without processing them.
use ;
// During registration, probe for PRF support
let = passki.start_passkey_registration?;
// Check client_extension_results.prf.enabled in the credential before calling finish
// to know whether the authenticator supports PRF
// During authentication, request a PRF derivation for a given context
let = passki.start_passkey_authentication;
// result.prf_first contains the derived key bytes (32 bytes)
// The same passkey + same context always yields the same bytes
Security Considerations
- 🔒 Always use HTTPS in production to prevent man-in-the-middle attacks
- 🔄 Update signature counters after successful authentication to detect cloned authenticators
- 🎯 Verify origin matches your expected domain (Passki does this automatically)
- 💾 Store passkeys securely in your database with proper access controls
- ⏱️ Set appropriate timeouts for registration and authentication ceremonies
- 🔐 Use user verification when handling sensitive operations
Architecture
Passki follows a simple two-step pattern for both registration and authentication:
- Start: Generate a challenge and return it to the client
- Finish: Verify the response from the client
This design keeps state management simple and allows you to store session data however you prefer (in-memory, Redis, database, etc.).
Requirements
- Rust 1.85 or later (Edition 2024)
- A web server to handle HTTP requests
- HTTPS in production (required by WebAuthn specification)
Examples
The examples/ directory has complete registration and authentication flows for several web frameworks:
Actix-web | Axum | Poem | Rocket | Warp
All examples request both credProps and PRF during registration. Registration reports whether a resident key was created; authentication accepts an optional key context string (prf_salt) to derive a 32-byte key.
Then visit http://localhost:3000 in your browser.
WebAuthn Specification Levels
WebAuthn has three specification levels published by the W3C. Checkboxes mark features currently implemented in passki.
Level 1 (2019)
The initial recommendation. Defined the core protocol:
- Registration ceremony (
create) and authentication ceremony (get) - Challenge generation and binding
- Client data JSON origin verification
- Authenticator data parsing
- COSE public key extraction
- Signature verification (EdDSA/Ed25519, ES256/P-256, ES384/P-384, RS256, RS384)
- Signature counter tracking and replay detection
- Credential exclusion (
excludeCredentials) -
AttestationConveyancePreference(none/indirect/direct) - Attestation object CBOR parsing
- Attestation statement verification (
packed,tpm,android-key,fido-u2f) - verifies the statement signature and certificate requirements - rpId hash verification in authenticator data - the hash in bytes 0-31 is compared against
sha256(rp_id) - UP (user present) flag enforcement
- UV (user verified) flag enforcement
Level 2 (2021)
A substantial expansion, still the most widely implemented level today:
- Discoverable credentials / usernameless flows (empty
allowCredentials) -
ResidentKeyRequirement(discouraged/preferred/required) -
enterpriseattestation conveyance preference - Zero-counter authenticator support (explicitly allowed per spec)
-
credPropsextension - reports whether a discoverable credential was created -
largeBlobextension - store small blobs on the authenticator (e.g. SSH keys) -
minPinLengthextension - query or enforce minimum PIN length -
credProtectextension - control UV requirement for credential access -
uvmextension - user verification method details -
userHandlein authentication response - needed to identify the user in usernameless flows
Level 3 (Candidate Recommendation, not yet a W3C Recommendation)
Still under active development:
- PRF extension (
prf) - deterministic key derivation via HMAC-Secret -
paymentextension - Secure Payment Confirmation (SPC) integration - Related origin requests - use credentials across subdomains / related origins
- JSON serialization helpers (
parseCreationOptionsFromJSON,toJSON, etc.) - Signal API - lets RPs notify the browser that a credential was deleted or changed
- Hybrid transport / cross-device auth (caBLE) - QR/BLE cross-device flows
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the Apache License, Version 2.0 (LICENSE).
Acknowledgments
Passki is built on top of aws-lc-rs for cryptographic operations.