qualia_bioauth/lib.rs
1// Copyright (C) 2026 Qualia2FA contributors
2//
3// This library is free software; you can redistribute it and/or modify it under
4// the terms of the GNU Lesser General Public License as published by the Free
5// Software Foundation; either version 3 of the License, or (at your option) any
6// later version.
7//
8// This library is distributed in the hope that it will be useful, but WITHOUT
9// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
10// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
11// details.
12//
13// You should have received a copy of the GNU Lesser General Public License
14// along with this library; if not, see <https://www.gnu.org/licenses/>.
15
16//! Auth-bound key storage: wrapping a 32-byte key so that using it requires the
17//! platform's user authentication.
18//!
19//! # What it is for
20//!
21//! An application can protect its master key with a password
22//! ([`qualia2fa-core`](https://crates.io/crates/qualia2fa-core)'s lock wrap), but a
23//! password is something the user types and an attacker can guess. This crate adds
24//! the other way in: a **platform-authenticated** wrap. On Android that is an AES key
25//! in the Keystore whose use requires a `BiometricPrompt` `CryptoObject`; on Windows
26//! it is a Windows Hello credential whose `hmac-secret` output is released only by a
27//! Hello gesture.
28//!
29//! The two wraps are independent: either one opens the same master key, and an
30//! application keeps both so that a forgotten password or a re-enrolled fingerprint
31//! does not lock the user out.
32//!
33//! # What this crate is not
34//!
35//! - It does not know the vault format, the password wrap or any Qualia2FA protocol.
36//! It has no dependency on `qualia2fa-core`; its key type is local.
37//! - It does no I/O. The generated bytes are returned to the caller, which decides
38//! where they live.
39//! - It is **not** a substitute for a password. The platform can be unavailable, the
40//! user can remove the PIN or the fingerprint, and a credential can be invalidated
41//! by a re-enrolment; the password path must stay in place.
42//!
43//! # How the guarantee is drawn
44//!
45//! The wrapped bytes are encrypted with a key that lives in the platform's
46//! authentication boundary: the Keystore key on Android, the Hello-bound
47//! `hmac-secret` on Windows. A popup whose success callback is merely checked in
48//! user-mode is **not** enough — the same process can ignore the callback. The test
49//! is "can this process decrypt the wrapped bytes without the platform releasing the
50//! key?" If the answer is yes, the wrap is not auth-bound.
51//!
52//! # File format
53//!
54//! The outer blob is platform-neutral and self-describing:
55//!
56//! ```text
57//! magic `Q2FABIO1` (8) | version 1 (1) | backend (1) | wrapped length (4, BE)
58//! | wrapped bytes
59//! ```
60//!
61//! The backend identifies which platform wrote the blob, and the wrapped bytes are
62//! private to that backend. See [`crate::format`] for the limits and the rejection rules.
63//!
64//! # Status
65//!
66//! - Implemented: the outer format, the key type, the trait, the mock backend, the
67//! unsupported fallback, and the Windows backend (Windows Hello through WebAuthn
68//! `hmac-secret`).
69//! - The Windows backend does not trust the attestation's `bPrfEnabled` flag; the first
70//! `GetAssertion` is the test. On Windows 11 23H2 (WebAuthn API version 7) the credential
71//! is created with the flag at zero and the assertion returns without the secret, so the
72//! store reports [`Availability::Unavailable`]; the password path is unaffected. A machine
73//! where the assertion succeeds works even when the flag is zero.
74//! - Planned: the Android Keystore backend. Until it is present, Android reports
75//! [`platform::stub::UnsupportedKeyStore`] and the password path is the only way in.
76
77mod error;
78
79pub mod format;
80pub mod host;
81pub mod key;
82#[cfg(any(test, feature = "mock"))]
83pub mod mock;
84pub mod platform;
85pub mod traits;
86
87pub use error::{Error, Result};
88pub use host::{DelegatingKeyStore, PlatformSealer};
89pub use key::{MASTER_KEY_LEN, MasterKey};
90pub use traits::{AuthBoundKeyStore, Availability, Backend};