Metadata-Version: 2.4
Name: pqc-enclave-sdk
Version: 0.1.0
Summary: PQC Secure Enclave SDK for on-device AI. ML-KEM-768 key encapsulation + AES-256-GCM encrypted model weights and credentials, pluggable backends for iOS Secure Enclave, Android StrongBox, Qualcomm QSEE.
Author: Dyber PQC
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: aes-gcm,android,ios,ml-kem,neural-engine,on-device-ai,pqc,secure-enclave,strongbox
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Security :: Cryptography
Requires-Python: >=3.10
Requires-Dist: cryptography>=41.0
Requires-Dist: quantumshield>=0.1.0
Provides-Extra: dev
Requires-Dist: mypy; extra == 'dev'
Requires-Dist: pytest; extra == 'dev'
Requires-Dist: ruff; extra == 'dev'
Description-Content-Type: text/markdown

# PQC Secure Enclave SDK

![PQC Native](https://img.shields.io/badge/PQC-Native-blue)
![ML-KEM-768](https://img.shields.io/badge/ML--KEM--768-FIPS%20203-green)
![AES-256-GCM](https://img.shields.io/badge/AES--256--GCM-FIPS%20197-green)
![iOS + Android Ready](https://img.shields.io/badge/iOS%20%2B%20Android-Ready-black)
![License](https://img.shields.io/badge/License-Apache%202.0-orange)
![Version](https://img.shields.io/badge/version-0.1.0-lightgrey)

**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.

## The Problem

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.

This is the **HNDL threat model** (Harvest Now, Decrypt Later) applied to on-device AI:

- An attacker who exfiltrates encrypted weight files today - from backups, compromised cloud sync, supply-chain tooling, or forensic device imaging - can store them indefinitely.
- When a cryptographically relevant quantum computer arrives, every RSA/ECDSA-wrapped symmetric key is retroactively broken and the plaintext weights fall out.
- For proprietary fine-tunes, biometric templates, and long-lived OAuth refresh tokens, "eventually decrypted" is functionally equivalent to "decrypted".

## The Solution

Wrap every on-device AI artifact in a PQC-protected envelope:

- **ML-KEM-768** (FIPS 203, NIST PQC) for the session key that the enclave unwraps.
- **AES-256-GCM** (FIPS 197) for the artifact body. Key is 32 bytes, nonce 12 bytes, tag 16 bytes.
- **SHA3-256** content hash authenticated via AES-GCM AAD - any metadata tampering breaks decryption.
- **ML-DSA** (FIPS 204) signatures for device attestations that commit to what was stored.
- Pluggable backends: `iOSEnclaveBackend`, `AndroidEnclaveBackend`, `QSEEBackend`, plus `InMemoryEnclaveBackend` for tests.

## Installation

```bash
pip install pqc-enclave-sdk
```

Development:

```bash
pip install -e ".[dev]"
```

## Quick Start

```python
from pqc_enclave_sdk import (
    ArtifactKind,
    EnclaveVault,
    InMemoryEnclaveBackend,
)

backend = InMemoryEnclaveBackend(device_id="iphone-alice", device_model="iphone-15-pro")
vault = EnclaveVault(backend=backend)

vault.unlock()
vault.put_artifact(
    name="llama-3.2-1b-int4",
    kind=ArtifactKind.MODEL_WEIGHTS,
    content=weights_bytes,
    version="1.0.0",
    app_bundle_id="com.example.localllm",
)
vault.save()
vault.lock()

# Later, in the same process or another app launch:
vault.unlock()
weights = vault.get_artifact("llama-3.2-1b-int4").content
```

## Architecture

```
  Your App                EnclaveVault            EnclaveBackend          Device Secure Enclave
  --------                ------------            --------------          ---------------------
      |                        |                         |                          |
      | put_artifact(bytes)    |                         |                          |
      | ---------------------> |                         |                          |
      |                        | 1. derive session key   |                          |
      |                        |    via ML-KEM-768       |                          |
      |                        | 2. AES-256-GCM encrypt  |                          |
      |                        |    with content-hash AAD|                          |
      |                        | 3. store_session_key ------------------------------>|
      |                        |                         | wraps w/ hardware KEK    |
      |                        | 4. save_artifacts       |                          |
      |                        | ----------------------> |                          |
      |                        |                         | persists ciphertext      |
      |                        |                         | to Keychain/Keystore     |
      |                        |                         |                          |
      | get_artifact(name)     |                         |                          |
      | ---------------------> |                         |                          |
      |                        | 5. load_session_key --------------------------------|
      |                        |    (unwrap inside SEP)                             |
      |                        | 6. AES-256-GCM decrypt  |                          |
      | <--- plaintext         |                         |                          |
```

## Artifact Kinds

| Kind | Purpose |
|---|---|
| `MODEL_WEIGHTS` | Full model weight tensors (INT4 / INT8 / FP16 on-device checkpoints). |
| `LORA_ADAPTER` | Low-rank fine-tune adapters; smaller but sensitive for proprietary tunes. |
| `TOKENIZER` | Tokenizer vocab + merges; lower-sensitivity but integrity-critical. |
| `CREDENTIAL` | API keys, OAuth tokens, auth bearer tokens. |
| `BIOMETRIC_TEMPLATE` | Encoded face / fingerprint templates. Highest sensitivity. |
| `INFERENCE_CACHE` | KV-cache blobs from prior conversations. |
| `SAFETY_MODEL` | Jailbreak classifier / content-safety adapter. |
| `OTHER` | Everything else. |

## Cryptography

| Primitive | Role | Standard |
|---|---|---|
| **ML-KEM-768** | Session-key encapsulation to the enclave's PQC public key | FIPS 203 |
| **AES-256-GCM** | Symmetric encryption of every artifact body | FIPS 197 / SP 800-38D |
| **SHA3-256** | Content hash + canonical AAD hashing | FIPS 202 |
| **ML-DSA-65 / 87** | Signatures over DeviceAttestations | FIPS 204 |

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.

## Threat Model

| Threat | Mitigation |
|---|---|
| **Device theft** (attacker has the phone) | Symmetric key never leaves the enclave. Access control requires biometrics / device unlock. |
| **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). |
| **Rogue app reading another app's artifacts** | `AccessPolicy.allowed_bundle_ids` filters callers; OS Keychain / Keystore access-control flags enforce at the kernel level. |
| **Stale session key** (long-lived re-use) | `DEFAULT_SESSION_TTL = 3600`; `is_unlocked` re-checks expiration on every call. |
| **Post-quantum forgery of attestation** | `DeviceAttester` signs with ML-DSA, not ECDSA. |
| **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. |
| **Downgrade to classical crypto** | Algorithm is baked into the AAD; a rewrite requires access to the PQC session key. |

## Backend Integration Guides

### iOS Secure Enclave (CryptoKit sketch)

```swift
import CryptoKit

// 1. Generate a non-extractable SEP key at app install.
let sepKey = try SecureEnclave.P256.KeyAgreement.PrivateKey(
    accessControl: SecAccessControlCreateWithFlags(
        nil, kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
        [.privateKeyUsage, .biometryCurrentSet], nil)!
)

// 2. On unlock, receive the 32-byte AES-GCM key from the Python SDK
// (ideally via an ML-KEM-768 ciphertext the SEP decapsulates). Wrap it
// with the SEP key and write the sealed blob to the Keychain:
let sealedBox = try AES.GCM.seal(sessionKey, using: sepSymmetricKey)
SecItemAdd([
    kSecClass: kSecClassGenericPassword,
    kSecAttrService: "com.dyber.pqc.enclave",
    kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
    kSecValueData: sealedBox.combined!,
] as CFDictionary, nil)
```

### Android StrongBox (Kotlin sketch)

```kotlin
val spec = KeyGenParameterSpec.Builder(
    "com.dyber.pqc.enclave.session",
    KeyProperties.PURPOSE_WRAP_KEY or KeyProperties.PURPOSE_ENCRYPT
  ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
   .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
   .setIsStrongBoxBacked(true)              // Titan M / Knox Vault
   .setUserAuthenticationRequired(true)
   .setUnlockedDeviceRequired(true)
   .build()
val kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, "AndroidKeyStore")
kpg.initialize(spec)
kpg.generateKeyPair()
```

### Qualcomm QSEE (Trusted App sketch)

```c
// Signed TA running inside QSEE; the Python SDK talks to it via QSEECom.
int pqc_enclave_ta_store_session(uint8_t *session_key, uint32_t len) {
    sealed_key_t sealed;
    ta_kek_wrap(g_ta_kek, session_key, len, &sealed);
    return qseecom_write_sealed_blob(&sealed);   // persists to Keystore
}
```

## API Reference

### `EnclaveVault`

| Method | Description |
|---|---|
| `unlock(ttl_seconds=3600)` | Derive a session key via ML-KEM-768 and mark the vault usable. |
| `lock()` | Wipe the session key from memory. |
| `put_artifact(name, kind, content, ...)` | AES-256-GCM encrypt and store. Returns the `EncryptedArtifact`. |
| `get_artifact(name_or_id)` | Decrypt and return `EnclaveArtifact` (metadata + plaintext). |
| `delete_artifact(name_or_id)` | Remove by name or id. |
| `list_artifacts()` | List `ArtifactMetadata` for everything in the vault. |
| `save()` | Persist the encrypted store to the backend. |
| `is_unlocked` | Property; also re-checks session expiry. |

### `EnclaveArtifact`

| Field / Method | Description |
|---|---|
| `metadata` | `ArtifactMetadata` frozen dataclass. |
| `content` | Plaintext bytes. |
| `sha3_256_hex()` | SHA3-256 of the content, hex. |
| `content_hash(bytes)` (static) | SHA3-256 helper. |

### `AccessPolicy` / `ArtifactPolicy`

| Method | Description |
|---|---|
| `AccessPolicy().add(rule)` | Attach a rule for an `ArtifactKind`. |
| `.check(metadata, caller_bundle_id)` | Raises `PolicyViolationError` on deny. |
| `ArtifactPolicy(kind, allowed_bundle_ids, require_biometric, max_uses_per_hour)` | Per-kind rule. |

### `DeviceAttester`

| Method | Description |
|---|---|
| `DeviceAttester(identity, device_id, device_model, enclave_vendor)` | Bind an `AgentIdentity` to a device. |
| `.attest(artifact_id, content_hash)` | Produce a signed `DeviceAttestation`. |
| `DeviceAttester.verify(att)` (static) | Returns True / False. |
| `DeviceAttester.verify_or_raise(att)` (static) | Raises `AttestationError` on invalid. |

### Exceptions

| Exception | When |
|---|---|
| `EnclaveSDKError` | Base class. |
| `UnknownArtifactError` | `get_artifact` / `delete_artifact` against a missing id or name. |
| `EnclaveLockedError` | Operation attempted on a locked vault. |
| `DecryptionError` | AES-GCM tag rejected ciphertext or AAD. |
| `BackendError` | iOS / Android / QSEE backend refused or is stubbed. |
| `AttestationError` | `DeviceAttester.verify_or_raise` saw an invalid signature. |
| `PolicyViolationError` | `AccessPolicy.check` denied the caller. |

## Why PQC for On-Device AI

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.

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.

## Examples

See the `examples/` directory:

- **`store_model_weights.py`** - 256 KB model weight lifecycle through an in-memory vault.
- **`store_credentials.py`** - three API credentials across three different app bundles.
- **`device_attestation.py`** - sign and verify a DeviceAttestation, and show the tamper case.

Run them:

```bash
python examples/store_model_weights.py
python examples/store_credentials.py
python examples/device_attestation.py
```

## Development

```bash
pip install -e ".[dev]"
pytest
ruff check src/ tests/ examples/
```

## Related

Part of the [QuantaMrkt](https://quantamrkt.com) post-quantum tooling registry. See also:

- **QuantumShield** - the underlying PQC toolkit (`AgentIdentity`, `SignatureAlgorithm`, `generate_kem_keypair`, `sign / verify`).
- **PQC Agent Wallet** - sister tool for passphrase-unlocked credential vaults.
- **PQC GPU Driver** - sister tool for keeping tensors encrypted on discrete accelerators.
- **PQC Hypervisor Attestation** - sister tool for confidential-VM memory attestation.

## License

Apache License 2.0. See [LICENSE](LICENSE).
