1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// Copyright (C) 2026 Qualia2FA contributors
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option) any
// later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, see <https://www.gnu.org/licenses/>.
//! Platform key store: create a P-256 key in hardware, read its public key and
//! sign a message with it.
//!
//! Three operations live here, and nothing else:
//!
//! 1. create an ECDSA P-256 key whose private half never leaves the platform;
//! 2. return the matching public key;
//! 3. sign a **message** with it.
//!
//! Protocol logic, cryptographic primitives and anything expressible in pure Rust
//! belong to the core crate: code in this module cannot be fuzzed, cannot run on
//! CI across platforms, and lives outside the protection of
//! `forbid(unsafe_code)`.
//!
//! # Signing takes a message, not a digest
//!
//! Platform APIs accept digests only (the Windows CNG parameter is named
//! `pbHashValue`), so a SHA-256 happens in between. This module performs that
//! hash itself: the caller passes the raw message. Verification
//! ([`crate::device::verify`]) follows the same convention, so both sides always
//! hash the same bytes.
//!
//! # The application's master key: two contracts, two forms
//!
//! Besides the device identity key, the platform key store holds the
//! application's **master key** — the key a vault file is encrypted with. It is
//! opaque material of [`MASTER_KEY_LEN`] bytes, and this crate only moves it
//! between the caller and the platform; it never derives anything from it.
//!
//! There are two ways to hold it, and **the caller picks by reaching for one of
//! two types**, not by passing a flag:
//!
//! | Form | Trait | What the platform store holds | What the caller persists |
//! |---|---|---|---|
//! | Direct | [`DirectMasterKeyStore`] | the material itself | nothing |
//! | Wrapped | [`WrappedMasterKeyStore`] | a non-exportable key-encryption key | the wrapped bytes |
//!
//! The wrapped form's key-encryption key is **not part of the interface**: there is
//! no method that wraps, unwraps, encrypts or returns it. That is the point of the
//! form — the material is never persisted, and the key that would decrypt the
//! persisted bytes cannot leave the platform.
//!
//! Both contracts report what actually happened through
//! [`DirectMasterKeyStore::backend`] / [`WrappedMasterKeyStore::backend`], with the
//! same rule as the device identity side: a software-backed answer is reported as
//! [`Backend::Software`], never dressed up as hardware.
//!
//! # A standalone crate, but not a raw-crypto one
//!
//! The wrapping of the wrapped form happens **inside the platform**; this crate
//! chooses no algorithm, derives no key from the master key and exposes no
//! primitive. A caller that needs a cipher wants a different crate.
use crate::;
/// Conversions a platform's answers go through.
///
/// Compiled where a backend needs it — Android — and under `test`, so that they are checked
/// on the host as well. On a platform whose backend converts nothing, they would be dead
/// code, which `-D warnings` treats as an error.
pub use ;
/// The key store implementation of the current platform, selected at compile time.
///
/// It exists so that callers write `PlatformKeyStore::open_best_available()` once
/// instead of carrying `#[cfg(target_os = "windows")] use ...windows::WindowsKeyStore`
/// themselves.
///
/// On platforms without a backend this is the stub: every operation returns
/// [`Error::UnsupportedPlatform`], and it never falls back to a software key.
///
/// # The backends have different constructor signatures
///
/// | backend | signature |
/// |---|---|
/// | Windows, Android | `fn open_best_available() -> Result<Self>` |
/// | others | `const fn open_best_available() -> Self` |
///
/// Opening the best available provider can fail where there is one to open: Windows may
/// have no TPM, or a key store held by another process, and Android may be reached without
/// the Java VM this library needs. A caller has to be told, so the call returns a `Result`.
/// The stub backend cannot fail, and a `Result` around it would only add an error branch
/// that is never taken. Callers therefore write the call once per platform with `#[cfg]`,
/// which keeps the difference between the two failure models visible in the code.
pub use PlatformKeyStore;
pub use ;
/// Error type of the key store.
///
/// An alias of [`Error`]: device identity and the key store are two halves of one
/// operation, so the whole crate shares a single error enum. The name stays so
/// that a signature shows which failures originate here.
pub type KeyStoreError = Error;
/// Result type of key store operations.
///
/// The same type as [`crate::Result`], under a name that makes "this function
/// only fails for key store reasons" visible in a signature.
pub type KeyStoreResult<T> = ;