Keypass
Passkey-derived encryption secrets for Dart applications, without Flutter.
Keypass is being built to enroll passkeys through OS credential providers or physical FIDO2 security keys and obtain repeatable WebAuthn PRF output. Applications such as Keybay can use that output to protect encryption keys. The provider handles the passkey and verification UI; Keypass does not read fingerprints, face data, or a passkey's private signing key.
Status: experimental OS-provider, desktop USB and phone hardware adapters. Swift macOS/iOS, Kotlin/JNI Android and Windows WebAuthn adapters feed the same Dart verifier and binary-secret transport. The signed macOS FFI demo passed enrollment, fresh-process decryption and cancellation/retry through the normal public client. A physical iPhone passed those core checks with a diagnostic backend wrapper; the undecorated iOS path and broader lifecycle coverage remain to be verified. Android has emulator host smoke evidence; real-provider verification is deferred. A shared Flutter test app exercises packaged native FFI consumers without adding Flutter to the SDK. Windows has cross-compiled. See consumer setup and the scoped validation receipts.
The new direct USB adapter uses libfido2 and the public
Keypass.hardware API. It requires verified
hmac-secret output, a PIN or configured on-key verification, and touch. Provider
bindings keep their existing encoding; hardware bindings explicitly carry their
route. The iPhone NFC adapter now implements
the same contract with standard FIDO CTAP through pinned YubiKit Swift. Physical
hardware support is capability-based, without vendor allowlists. Native packaging
is still manual. The Android USB/NFC adapter
is implemented with generic Android connections and the YubiKit FIDO protocol
library. A physical Pixel 6a recovered the Mac-created marker over NFC with the
same key; Android USB and broader qualification remain pending. Wired iPhone USB and Windows hardware
remain unfinished. A physical YubiKey passed macOS USB
enrollment, matching secret evaluations and fresh-process authenticated
decryption through the standalone Dart CLI. The same key then decrypted that
Mac-created marker on the iPhone and Android over NFC. Other hardware paths and broader
failure/lifecycle checks remain unqualified; exact proof is recorded separately.
The separate browser probe recovered Google Password Manager's PRF output in a
fresh AOT CLI process and decrypted its earlier saved test marker. Browser-assisted
CLI access is now deferred. Configuration is explicit in the Dart constructors.
Native clients require the corresponding host library to be packaged; absent libraries
return backendUnavailable, and missing windows return hostUnavailable.
The package is unpublished; its API and binding encoding are not frozen.
Consumer API
Configure a client once and reuse it. Each operation owns its native resources; the client itself needs no disposal.
import 'package:keypass/keypass.dart';
final passkeys = Keypass.system(rpId: 'vault.example.com');
final readiness = await passkeys.check(); // Optional, no prompt.
if (!readiness.canAttempt) {
// Present readiness.reason or an independently configured access method.
return;
}
final result = await passkeys.create(label: 'Personal vault');
try {
// Use result.secret with your purpose-bound KDF and key-wrapping scheme.
// Atomically save result.record.toJson() with the authenticated envelope.
} finally {
result.dispose();
}
For direct physical keys, construct the same interface with
Keypass.hardware(rpId: 'vault.example.com', requestPin:, selectConnection:, onEvent:).
Supply your application's PIN/connection UI where required. The stable RP ID
scopes credentials; direct hardware access needs no hosted website.
| Operation | Result |
|---|---|
check() |
Prompt-free readiness to attempt an operation; not proof of PRF support |
create(label:, cancellation:) |
A new credential, two verified matching PRFs, and an owned PasskeyResult |
unlock(record, cancellation:) |
The existing credential's verified secret and updated record; no enrollment or fallback |
Both secret operations return the same PasskeyResult. Its secret is a
read-only byte view; record is opaque, nonsecret metadata with toJson() and
PasskeyRecord.fromJson(). Dispose the result in finally, even if wrapping or
persistence fails. Keypass clears its owned buffer; caller copies and derived
keys remain the caller's responsibility. Integrity-protect records and persist
advanced verification state transactionally.
Always await secret-bearing operations. Abandoning their Future or using
Future.timeout alone does not cancel native work or dispose a late result.
Cancel through PasskeyCancellation, await settlement, and dispose any result
that succeeded. check() is optional: it reserves nothing, may throw busy,
and cannot guarantee a credential's PRF support.
See the consumer SDK guide for both routes, hardware callbacks, cancellation, ownership and complete persistence responsibilities. Applications own encryption, storage, synchronization and recovery policy.
System-provider RP IDs are app-associated domains, not API endpoints. Apple and Android still require domain association, signing and native host integration. Native packaging is currently manual; adding the Dart dependency does not configure an app host. Keypass requires no Keypass-operated runtime website, API, relay or account service; see the runtime independence constraint.
Desktop CLIs target direct physical-key access. USB and phone NFC are in scope; no browser helper or extension is required by the accepted product plan. The standalone browser probe remains separate research and is not wired into the public API.
| Target | Planned integration | Current implementation |
|---|---|---|
| Signed macOS apps / iOS | OS providers plus physical keys | Apple provider adapter; macOS normal-client and iPhone diagnostic-host enrollment/restart/cancel-retry verified; macOS USB enrollment/restart also verified with a physical YubiKey; iPhone NFC recovered the Mac-created marker with the same key |
| Android | OS providers plus USB/NFC physical keys | Credential Manager AAR/JNI and emulator smoke; generic USB/NFC hardware module; Pixel 6a NFC decrypted the Mac-created marker with the same key in the debug demo; real provider, USB and broader qualification pending |
| Windows | Native providers and physical keys | WebAuthn adapter cross-compiled; runtime and physical-key access unqualified |
| Linux / desktop CLIs | Direct physical FIDO2 keys | libfido2 USB adapter and standalone Dart CLI implemented on macOS/Linux; other hardware adapters pending |
These are targets, not supported-platform claims. Provider PRF support and cross-device behavior must be tested for each supported path.
Try the interactive platform demos, or see the implementation plan, native backend contract, consumer SDK, platform setup, and Keybay integration.
Development
dart pub get
dart format --output=none --set-exit-if-changed lib test example tool demo/provider_app/lib/store.dart
dart analyze --fatal-infos
dart test
node --test test/browser/ceremony.test.mjs test/browser/failure.test.mjs test/browser/gesture.test.mjs test/browser/deadline.test.mjs
dart run example/keypass_example.dart
Development tests require Node 22+ for WebCrypto interoperability. The SDK has no Node or Flutter runtime dependency. Tests cover synthetic core lifecycle, signed synthetic browser assertions and encrypted bridge failures. They do not establish real provider, native UI, device, sync, offline or vault qualification.
Linux validation can also run in an isolated container:
docker build -f tool/validation/Dockerfile -t keypass-validation .
docker run --rm --network none keypass-validation
The build runs analysis, tests and AOT compilation. The final command only checks CLI startup/help; it does not exercise a Linux desktop passkey provider. Docker context exclusions keep local bindings and build/cache directories out of the image.
Desktop hardware consumers use Dart build hooks for automatic native compilation and loading. The public SDK API is unchanged.
Libraries
- keypass
- Verified passkey-derived encryption material, without Flutter.
- keypass_backend
- Trusted adapter/test integration. Each factory invocation owns a fresh backend. Backends are security components, not application authentication callbacks.