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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
// 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/>.
//! Device identity kit: the **public-key side** of a device identity, plus the
//! **private-key side** that drives the platform-native key store.
//!
//! # Scope
//!
//! | Module | Responsibility | Holds private keys |
//! |------|--------|----------|
//! | [`device`] | Public-key representation, fingerprint ([`DeviceId`]), signature verification | No |
//! | [`keystore`] | Key generation, public-key retrieval, message signing, master key storage | Yes (in platform hardware) |
//!
//! Both halves share the single error type [`Error`]: the caller sees the failure of one
//! device-identity operation, not the failure of two subsystems.
//!
//! This crate is the only place in the project where `unsafe` is allowed, and the only
//! place that calls platform-native APIs (Windows CNG, Android Keystore, iOS Security
//! framework). Every `unsafe` operation must be written explicitly inside an `unsafe {}`
//! block; `#![deny(unsafe_op_in_unsafe_fn)]` enforces this.
//!
//! Representation and verification live in the same crate as generation and signing so that
//! a device can verify **another** device's signature even if it holds no hardware key of
//! its own — a desktop machine without a TPM, for example.
//!
//! # A standalone library
//!
//! Any project that needs a P-256 identity key pair held in platform hardware can use this
//! crate without accepting the Qualia2FA vault format, the sync protocol, or anything else.
//! Downstream crates depend on this one, never the reverse: protocol logic, cryptographic
//! primitives, and anything else that can be written in pure Rust and is unrelated to device
//! identity belongs downstream.
//!
//! # Both halves of the platform key store
//!
//! The platform key store holds two things, and this crate exposes one contract for each:
//!
//! | What | Contract | Where the key material lives |
//! |---|---|---|
//! | The **device identity** | [`DeviceKeyStore`] / [`DeviceSigner`] | in the platform, and never leaves it |
//! | The **application's master key** | [`DirectMasterKeyStore`] / [`WrappedMasterKeyStore`] | see below |
//!
//! The master key is [`MASTER_KEY_LEN`] bytes of opaque material in [`MasterKey`], and there
//! are **two ways to hold it**, chosen by the caller through the type it reaches for:
//!
//! - **Direct** ([`DirectMasterKeyStore`]): the platform store keeps the material itself and
//! hands it back on request. The caller persists nothing.
//! - **Wrapped** ([`WrappedMasterKeyStore`]): the platform store creates a **non-exportable**
//! key-encryption key and wraps the material with it; only the wrapped bytes
//! ([`WrappedMasterKey`]) are persisted, and the caller keeps them next to the data they
//! open. The key-encryption key and its operations are **not** part of the interface.
//!
//! Either way the store reports what actually answered through `backend()`, with the same
//! rule as everywhere else in this crate: software is reported as
//! [`keystore::Backend::Software`], never dressed up as hardware. A platform that implements
//! neither form returns [`Error::UnsupportedPlatform`].
//!
//! # Signature algorithm: ECDSA P-256
//!
//! The algorithm is a hard constraint rather than a preference, because the private key has
//! to live in platform hardware:
//!
//! - **Android**: KeyMint HAL's `Algorithm.aidl` lists only `RSA=1, EC=3, AES=32,
//! TRIPLE_DES=33, HMAC=128` — there is no EdDSA, and `KeyProperties.java` defines no
//! Ed25519 constant. `CURVE_25519` exists, but it is an **XDH key-agreement curve, not a
//! signature algorithm**.
//! - **iOS**: Apple documents the Secure Enclave as NIST P-256 only.
//!
//! P-256/ECDSA is therefore the only signature algorithm that can be held in hardware on
//! both mobile platforms; any other choice restricts mobile identity keys to software
//! storage.
//!
//! # Platform backends
//!
//! | Platform | Backend | Status |
//! |---|---|---|
//! | Windows | CNG `NCrypt*`, TPM 2.0 (`MS_PLATFORM_CRYPTO_PROVIDER`) | Implemented |
//! | Windows (no TPM) | CNG software KSP (`MS_KEY_STORAGE_PROVIDER`) | Implemented (fallback) |
//! | macOS | Secure Enclave | Not implemented (placeholder backend) |
//! | Linux | TPM 2.0 | Not implemented (placeholder backend) |
//! | Android | Keystore / StrongBox | Implemented |
//! | iOS | Secure Enclave | Not implemented (placeholder backend) |
//!
//! Unimplemented platforms **never degrade silently**: calling
//! [`keystore::DeviceKeyStore::backend`] yields [`keystore::Backend::Unavailable`], and key
//! creation returns [`Error::UnsupportedPlatform`].
//!
//! "Not implemented" and "no hardware present" are two different things and have two
//! different values: [`keystore::Backend::Unavailable`] means this platform has no backend
//! yet, while [`keystore::Backend::Software`] means a backend exists but this machine has no
//! hardware. Merging them would make "switch platform" and "switch machine"
//! indistinguishable to callers.
//!
//! The two master key forms follow the same rule, with one platform specific detail worth
//! knowing before choosing between them:
//!
//! | Platform | Direct | Wrapped |
//! |---|---|---|
//! | Windows | software provider (the TPM provider will not hand material back) | TPM where there is one, software otherwise |
//! | Android | implemented, reports what the keystore says about the entry | implemented, StrongBox preferred |
//! | Others | [`Error::UnsupportedPlatform`] | [`Error::UnsupportedPlatform`] |
//!
//! # I/O, clock, and environment
//!
//! The crate performs no I/O, reads no clock, and reads no environment variables; the only
//! exceptions are `cfg` and the platform APIs themselves. Hardware handles are owned by the
//! key store object and released when it is dropped.
//!
//! # Example
//!
//! Generate → read public key → sign → verify, using the in-memory backend:
//!
//! ```
//! # #[cfg(feature = "mock")]
//! # fn demo() -> Result<(), qualia_dik::Error> {
//! use qualia_dik::{DeviceKeyStore, device};
//! # use qualia_dik::MockKeyStore;
//!
//! let store = MockKeyStore::new();
//! let signer = store.create("my-device")?;
//!
//! println!("device id: {}", signer.public_key().device_id());
//!
//! let message = b"a message to sign";
//! let signature = signer.sign(message)?;
//!
//! device::verify(signer.public_key(), message, &signature)?;
//! # Ok(())
//! # }
//! ```
//!
//! Holding the master key, in either form:
//!
//! ```
//! # #[cfg(feature = "mock")]
//! # fn demo() -> Result<(), qualia_dik::Error> {
//! use qualia_dik::{
//! DirectMasterKeyStore, MasterKey, MockDirectMasterKeyStore, MockWrappedMasterKeyStore,
//! WrappedMasterKeyStore,
//! };
//!
//! let key = MasterKey::from_bytes([0x2A; qualia_dik::MASTER_KEY_LEN]);
//!
//! // Direct: the store keeps the material, the caller persists nothing.
//! let direct = MockDirectMasterKeyStore::new();
//! direct.store("account-1", &key)?;
//! let loaded = direct.load("account-1")?;
//!
//! // Wrapped: the store keeps a key-encryption key, the caller persists these bytes.
//! let wrapped_store = MockWrappedMasterKeyStore::new();
//! let wrapped = wrapped_store.store("account-1", &key)?;
//! let loaded_again = wrapped_store.load("account-1", &wrapped)?;
//!
//! assert_eq!(loaded.as_bytes(), loaded_again.as_bytes());
//! # Ok(())
//! # }
//! ```
//!
//! The public key is not a secret and may be propagated freely. Verification needs neither
//! the key store nor the private key, which is what makes the public-key side usable on its
//! own.
//!
//! `MockKeyStore` exists only under the `mock` feature: it is a path with no hardware
//! protection and is therefore off by default in production builds. To run this example
//! locally:
//!
//! ```console
//! cargo test --doc --features mock
//! ```
//!
//! Without that feature `cargo test --doc` still passes, because the whole example is
//! compiled out by the `cfg`.
//!
//! To obtain the real backend of the current platform **without** enabling `mock`, use
//! [`keystore::PlatformKeyStore`].
pub use ;
pub use ;
pub use ;
pub use ;
/// Whether this platform has an implemented hardware key store backend.
///
/// Callers (UI in particular) can use this to tell the user that without hardware
/// protection the device identity key is held in software, which lowers its security.
/// **This function does not probe whether hardware is actually usable** — that would require
/// opening a key store; it answers "does this platform have an implementation".
///
/// For the runtime situation, use [`DeviceKeyStore::backend`].
///
/// Windows and Android return `true` today: those are the platforms
/// [`keystore::PlatformKeyStore`] has a backend for. On every other platform the function
/// returns `false`, and every operation of [`keystore::PlatformKeyStore`] returns
/// [`Error::UnsupportedPlatform`] — there is **no** fallback to a software key.
pub const