src/pqc_enclave_sdk/backends/ios.py
3.6 KB · 86 lines · python Raw
1 """iOS Secure Enclave backend (stub interface).
2
3 Real integration uses Apple's CryptoKit + Keychain Services. This stub
4 documents the expected shape; app developers plug in their Swift / ObjC
5 bridge layer.
6
7 A production implementation of this backend is expected to:
8
9 * Generate a hardware-backed key in the Secure Enclave with
10 ``SecureEnclave.P256.KeyAgreement.PrivateKey`` (CryptoKit). The private
11 key is non-extractable and bound to the device's SEP.
12 * For :meth:`store_session_key`, wrap the 32-byte AES-GCM session key with
13 the SEP key via ``HPKE`` or ``AES.GCM.seal`` and persist the wrapped blob
14 to the Keychain with ``kSecAttrTokenIDSecureEnclave`` and access-control
15 flags ``kSecAccessControlBiometryCurrentSet | kSecAccessControlPrivateKeyUsage``.
16 * For :meth:`load_session_key`, query the Keychain for the wrapped key and
17 unwrap it inside the SEP - the plaintext symmetric key never leaves
18 enclave memory.
19 * For :meth:`save_artifacts` and :meth:`load_artifacts`, serialize the
20 encrypted artifact store to a file in the app's Data Protection
21 ``NSFileProtectionCompleteUntilFirstUserAuthentication`` container.
22 * Optionally attach a DeviceCheck / App Attest token to prove the binary
23 is genuine and the device has not been jailbroken.
24
25 ML-KEM-768 support on iOS is arriving via Apple's PQ3 iMessage stack and
26 CryptoKit post-quantum primitives - plug that in where ``generate_kem_keypair``
27 currently runs in-process.
28 """
29
30 from __future__ import annotations
31
32 from pqc_enclave_sdk.artifact import EncryptedArtifact
33 from pqc_enclave_sdk.backends.base import EnclaveBackend
34 from pqc_enclave_sdk.errors import BackendError
35
36
37 class iOSEnclaveBackend(EnclaveBackend):
38 """Stub Apple Secure Enclave backend.
39
40 Raises :class:`BackendError` when invoked without real SEP wiring.
41 """
42
43 name = "ios-secure-enclave"
44 platform = "ios"
45 enclave_vendor = "apple-se"
46
47 def __init__(
48 self,
49 device_id: str = "iphone-unknown",
50 device_model: str = "iphone-unknown",
51 keychain_service: str = "com.dyber.pqc.enclave",
52 ) -> None:
53 self.device_id = device_id
54 self.device_model = device_model
55 self.keychain_service = keychain_service
56
57 def store_session_key(self, key_id: str, key: bytes, expires_at: str) -> None:
58 raise BackendError(
59 "iOSEnclaveBackend.store_session_key is a stub. A real implementation "
60 f"wraps the 32-byte key with a SecureEnclave.P256 key and writes the "
61 f"wrapped blob to the Keychain service {self.keychain_service!r} with "
62 "kSecAttrTokenIDSecureEnclave + access-control flags."
63 )
64
65 def load_session_key(self, key_id: str) -> bytes | None:
66 raise BackendError(
67 "iOSEnclaveBackend.load_session_key is a stub. A real implementation "
68 f"queries the Keychain service {self.keychain_service!r} for key "
69 f"{key_id} and unwraps the blob inside the SEP."
70 )
71
72 def save_artifacts(self, artifacts: dict[str, EncryptedArtifact]) -> None:
73 raise BackendError(
74 "iOSEnclaveBackend.save_artifacts is a stub. A real implementation "
75 "serializes the encrypted artifacts to a file under "
76 "NSFileProtectionCompleteUntilFirstUserAuthentication in the app's "
77 "Data Protection container."
78 )
79
80 def load_artifacts(self) -> dict[str, EncryptedArtifact]:
81 raise BackendError(
82 "iOSEnclaveBackend.load_artifacts is a stub. A real implementation "
83 "reads the encrypted artifact file back from the Data Protection "
84 "container and deserializes it via EncryptedArtifact.from_dict."
85 )
86