README.md
13.0 KB · 284 lines · markdown Raw
1 # PQC Secure Enclave SDK
2
3 ![PQC Native](https://img.shields.io/badge/PQC-Native-blue)
4 ![ML-KEM-768](https://img.shields.io/badge/ML--KEM--768-FIPS%20203-green)
5 ![AES-256-GCM](https://img.shields.io/badge/AES--256--GCM-FIPS%20197-green)
6 ![iOS + Android Ready](https://img.shields.io/badge/iOS%20%2B%20Android-Ready-black)
7 ![License](https://img.shields.io/badge/License-Apache%202.0-orange)
8 ![Version](https://img.shields.io/badge/version-0.1.0-lightgrey)
9
10 **Quantum-safe on-device AI.** A clean Python SDK for storing AI model weights, LoRA adapters, tokenizers, and API credentials in **device secure enclaves** using **ML-KEM-768** key encapsulation + **AES-256-GCM** encryption. Pluggable backends for Apple Secure Enclave, Android StrongBox, and Qualcomm QSEE let you ship quantum-resistant on-device AI today - without waiting for the platform vendors to finish their PQC rollouts.
11
12 ## The Problem
13
14 Your phone runs AI inference constantly: autocomplete, voice recognition, image classification, on-device LLMs. The model weights and API credentials those features rely on sit in device storage for **years** - Apple Neural Engine, Qualcomm AI Engine, and MediaTek APU models typically persist across OS upgrades. Today they are protected by classical cryptography baked into the secure element.
15
16 This is the **HNDL threat model** (Harvest Now, Decrypt Later) applied to on-device AI:
17
18 - An attacker who exfiltrates encrypted weight files today - from backups, compromised cloud sync, supply-chain tooling, or forensic device imaging - can store them indefinitely.
19 - When a cryptographically relevant quantum computer arrives, every RSA/ECDSA-wrapped symmetric key is retroactively broken and the plaintext weights fall out.
20 - For proprietary fine-tunes, biometric templates, and long-lived OAuth refresh tokens, "eventually decrypted" is functionally equivalent to "decrypted".
21
22 ## The Solution
23
24 Wrap every on-device AI artifact in a PQC-protected envelope:
25
26 - **ML-KEM-768** (FIPS 203, NIST PQC) for the session key that the enclave unwraps.
27 - **AES-256-GCM** (FIPS 197) for the artifact body. Key is 32 bytes, nonce 12 bytes, tag 16 bytes.
28 - **SHA3-256** content hash authenticated via AES-GCM AAD - any metadata tampering breaks decryption.
29 - **ML-DSA** (FIPS 204) signatures for device attestations that commit to what was stored.
30 - Pluggable backends: `iOSEnclaveBackend`, `AndroidEnclaveBackend`, `QSEEBackend`, plus `InMemoryEnclaveBackend` for tests.
31
32 ## Installation
33
34 ```bash
35 pip install pqc-enclave-sdk
36 ```
37
38 Development:
39
40 ```bash
41 pip install -e ".[dev]"
42 ```
43
44 ## Quick Start
45
46 ```python
47 from pqc_enclave_sdk import (
48 ArtifactKind,
49 EnclaveVault,
50 InMemoryEnclaveBackend,
51 )
52
53 backend = InMemoryEnclaveBackend(device_id="iphone-alice", device_model="iphone-15-pro")
54 vault = EnclaveVault(backend=backend)
55
56 vault.unlock()
57 vault.put_artifact(
58 name="llama-3.2-1b-int4",
59 kind=ArtifactKind.MODEL_WEIGHTS,
60 content=weights_bytes,
61 version="1.0.0",
62 app_bundle_id="com.example.localllm",
63 )
64 vault.save()
65 vault.lock()
66
67 # Later, in the same process or another app launch:
68 vault.unlock()
69 weights = vault.get_artifact("llama-3.2-1b-int4").content
70 ```
71
72 ## Architecture
73
74 ```
75 Your App EnclaveVault EnclaveBackend Device Secure Enclave
76 -------- ------------ -------------- ---------------------
77 | | | |
78 | put_artifact(bytes) | | |
79 | ---------------------> | | |
80 | | 1. derive session key | |
81 | | via ML-KEM-768 | |
82 | | 2. AES-256-GCM encrypt | |
83 | | with content-hash AAD| |
84 | | 3. store_session_key ------------------------------>|
85 | | | wraps w/ hardware KEK |
86 | | 4. save_artifacts | |
87 | | ----------------------> | |
88 | | | persists ciphertext |
89 | | | to Keychain/Keystore |
90 | | | |
91 | get_artifact(name) | | |
92 | ---------------------> | | |
93 | | 5. load_session_key --------------------------------|
94 | | (unwrap inside SEP) |
95 | | 6. AES-256-GCM decrypt | |
96 | <--- plaintext | | |
97 ```
98
99 ## Artifact Kinds
100
101 | Kind | Purpose |
102 |---|---|
103 | `MODEL_WEIGHTS` | Full model weight tensors (INT4 / INT8 / FP16 on-device checkpoints). |
104 | `LORA_ADAPTER` | Low-rank fine-tune adapters; smaller but sensitive for proprietary tunes. |
105 | `TOKENIZER` | Tokenizer vocab + merges; lower-sensitivity but integrity-critical. |
106 | `CREDENTIAL` | API keys, OAuth tokens, auth bearer tokens. |
107 | `BIOMETRIC_TEMPLATE` | Encoded face / fingerprint templates. Highest sensitivity. |
108 | `INFERENCE_CACHE` | KV-cache blobs from prior conversations. |
109 | `SAFETY_MODEL` | Jailbreak classifier / content-safety adapter. |
110 | `OTHER` | Everything else. |
111
112 ## Cryptography
113
114 | Primitive | Role | Standard |
115 |---|---|---|
116 | **ML-KEM-768** | Session-key encapsulation to the enclave's PQC public key | FIPS 203 |
117 | **AES-256-GCM** | Symmetric encryption of every artifact body | FIPS 197 / SP 800-38D |
118 | **SHA3-256** | Content hash + canonical AAD hashing | FIPS 202 |
119 | **ML-DSA-65 / 87** | Signatures over DeviceAttestations | FIPS 204 |
120
121 The AES-GCM AAD covers the full artifact metadata plus the content hash plus the key id - any metadata swap or cross-artifact key reuse is detected on decrypt.
122
123 ## Threat Model
124
125 | Threat | Mitigation |
126 |---|---|
127 | **Device theft** (attacker has the phone) | Symmetric key never leaves the enclave. Access control requires biometrics / device unlock. |
128 | **HNDL on stored weights** (exfiltrated encrypted blobs today, decrypted post-CRQC) | ML-KEM-768 session-key encapsulation; AES-256-GCM (Grover-adjusted 128-bit security). |
129 | **Rogue app reading another app's artifacts** | `AccessPolicy.allowed_bundle_ids` filters callers; OS Keychain / Keystore access-control flags enforce at the kernel level. |
130 | **Stale session key** (long-lived re-use) | `DEFAULT_SESSION_TTL = 3600`; `is_unlocked` re-checks expiration on every call. |
131 | **Post-quantum forgery of attestation** | `DeviceAttester` signs with ML-DSA, not ECDSA. |
132 | **Artifact swap** (attacker substitutes one encrypted blob for another) | AAD includes `artifact_id` and content hash; decryption of a swapped blob against the wrong metadata fails. |
133 | **Downgrade to classical crypto** | Algorithm is baked into the AAD; a rewrite requires access to the PQC session key. |
134
135 ## Backend Integration Guides
136
137 ### iOS Secure Enclave (CryptoKit sketch)
138
139 ```swift
140 import CryptoKit
141
142 // 1. Generate a non-extractable SEP key at app install.
143 let sepKey = try SecureEnclave.P256.KeyAgreement.PrivateKey(
144 accessControl: SecAccessControlCreateWithFlags(
145 nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
146 [.privateKeyUsage, .biometryCurrentSet], nil)!
147 )
148
149 // 2. On unlock, receive the 32-byte AES-GCM key from the Python SDK
150 // (ideally via an ML-KEM-768 ciphertext the SEP decapsulates). Wrap it
151 // with the SEP key and write the sealed blob to the Keychain:
152 let sealedBox = try AES.GCM.seal(sessionKey, using: sepSymmetricKey)
153 SecItemAdd([
154 kSecClass: kSecClassGenericPassword,
155 kSecAttrService: "com.dyber.pqc.enclave",
156 kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
157 kSecValueData: sealedBox.combined!,
158 ] as CFDictionary, nil)
159 ```
160
161 ### Android StrongBox (Kotlin sketch)
162
163 ```kotlin
164 val spec = KeyGenParameterSpec.Builder(
165 "com.dyber.pqc.enclave.session",
166 KeyProperties.PURPOSE_WRAP_KEY or KeyProperties.PURPOSE_ENCRYPT
167 ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
168 .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
169 .setIsStrongBoxBacked(true) // Titan M / Knox Vault
170 .setUserAuthenticationRequired(true)
171 .setUnlockedDeviceRequired(true)
172 .build()
173 val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
174 kpg.initialize(spec)
175 kpg.generateKeyPair()
176 ```
177
178 ### Qualcomm QSEE (Trusted App sketch)
179
180 ```c
181 // Signed TA running inside QSEE; the Python SDK talks to it via QSEECom.
182 int pqc_enclave_ta_store_session(uint8_t *session_key, uint32_t len) {
183 sealed_key_t sealed;
184 ta_kek_wrap(g_ta_kek, session_key, len, &sealed);
185 return qseecom_write_sealed_blob(&sealed); // persists to Keystore
186 }
187 ```
188
189 ## API Reference
190
191 ### `EnclaveVault`
192
193 | Method | Description |
194 |---|---|
195 | `unlock(ttl_seconds=3600)` | Derive a session key via ML-KEM-768 and mark the vault usable. |
196 | `lock()` | Wipe the session key from memory. |
197 | `put_artifact(name, kind, content, ...)` | AES-256-GCM encrypt and store. Returns the `EncryptedArtifact`. |
198 | `get_artifact(name_or_id)` | Decrypt and return `EnclaveArtifact` (metadata + plaintext). |
199 | `delete_artifact(name_or_id)` | Remove by name or id. |
200 | `list_artifacts()` | List `ArtifactMetadata` for everything in the vault. |
201 | `save()` | Persist the encrypted store to the backend. |
202 | `is_unlocked` | Property; also re-checks session expiry. |
203
204 ### `EnclaveArtifact`
205
206 | Field / Method | Description |
207 |---|---|
208 | `metadata` | `ArtifactMetadata` frozen dataclass. |
209 | `content` | Plaintext bytes. |
210 | `sha3_256_hex()` | SHA3-256 of the content, hex. |
211 | `content_hash(bytes)` (static) | SHA3-256 helper. |
212
213 ### `AccessPolicy` / `ArtifactPolicy`
214
215 | Method | Description |
216 |---|---|
217 | `AccessPolicy().add(rule)` | Attach a rule for an `ArtifactKind`. |
218 | `.check(metadata, caller_bundle_id)` | Raises `PolicyViolationError` on deny. |
219 | `ArtifactPolicy(kind, allowed_bundle_ids, require_biometric, max_uses_per_hour)` | Per-kind rule. |
220
221 ### `DeviceAttester`
222
223 | Method | Description |
224 |---|---|
225 | `DeviceAttester(identity, device_id, device_model, enclave_vendor)` | Bind an `AgentIdentity` to a device. |
226 | `.attest(artifact_id, content_hash)` | Produce a signed `DeviceAttestation`. |
227 | `DeviceAttester.verify(att)` (static) | Returns True / False. |
228 | `DeviceAttester.verify_or_raise(att)` (static) | Raises `AttestationError` on invalid. |
229
230 ### Exceptions
231
232 | Exception | When |
233 |---|---|
234 | `EnclaveSDKError` | Base class. |
235 | `UnknownArtifactError` | `get_artifact` / `delete_artifact` against a missing id or name. |
236 | `EnclaveLockedError` | Operation attempted on a locked vault. |
237 | `DecryptionError` | AES-GCM tag rejected ciphertext or AAD. |
238 | `BackendError` | iOS / Android / QSEE backend refused or is stubbed. |
239 | `AttestationError` | `DeviceAttester.verify_or_raise` saw an invalid signature. |
240 | `PolicyViolationError` | `AccessPolicy.check` denied the caller. |
241
242 ## Why PQC for On-Device AI
243
244 On-device model weights live on a user's phone for **five or more years** - longer than any reasonable cryptanalytic lead time against classical RSA/ECDSA. Proprietary fine-tunes, biometric templates, and OAuth refresh tokens embedded in those artifacts are exactly the kind of data a patient adversary will harvest now to decrypt later.
245
246 This is the HNDL threat model at its most concrete: the ciphertext blob is already on the user's device, already in cloud backups, and already syncing through MDM pipes. Every one of those copies is at risk the instant a CRQC arrives. ML-KEM-768 and AES-256-GCM close that window today - no platform-vendor timeline dependency, no waiting for iOS 19 or Android 16 to ship their post-quantum Keystore updates.
247
248 ## Examples
249
250 See the `examples/` directory:
251
252 - **`store_model_weights.py`** - 256 KB model weight lifecycle through an in-memory vault.
253 - **`store_credentials.py`** - three API credentials across three different app bundles.
254 - **`device_attestation.py`** - sign and verify a DeviceAttestation, and show the tamper case.
255
256 Run them:
257
258 ```bash
259 python examples/store_model_weights.py
260 python examples/store_credentials.py
261 python examples/device_attestation.py
262 ```
263
264 ## Development
265
266 ```bash
267 pip install -e ".[dev]"
268 pytest
269 ruff check src/ tests/ examples/
270 ```
271
272 ## Related
273
274 Part of the [QuantaMrkt](https://quantamrkt.com) post-quantum tooling registry. See also:
275
276 - **QuantumShield** - the underlying PQC toolkit (`AgentIdentity`, `SignatureAlgorithm`, `generate_kem_keypair`, `sign / verify`).
277 - **PQC Agent Wallet** - sister tool for passphrase-unlocked credential vaults.
278 - **PQC GPU Driver** - sister tool for keeping tensors encrypted on discrete accelerators.
279 - **PQC Hypervisor Attestation** - sister tool for confidential-VM memory attestation.
280
281 ## License
282
283 Apache License 2.0. See [LICENSE](LICENSE).
284