src/pqc_enclave_sdk/backends/android.py
3.6 KB · 87 lines · python Raw
1 """Android StrongBox + Keystore backend (stub interface).
2
3 Real integration uses the AndroidKeyStore provider with StrongBox where
4 available (Pixel Titan M, Samsung Knox Vault, etc.). This stub documents
5 the expected shape; app developers plug in their Kotlin / JNI bridge.
6
7 A production implementation of this backend is expected to:
8
9 * Request a hardware-backed key via ``KeyGenParameterSpec.Builder`` with
10 ``setIsStrongBoxBacked(true)`` so the private key material lives in the
11 dedicated StrongBox secure element, not the TEE.
12 * For :meth:`store_session_key`, wrap the 32-byte AES-GCM session key with
13 the StrongBox key (``Cipher.WRAP_MODE`` over AES-GCM) and persist the
14 wrapped bytes to EncryptedSharedPreferences or the app's private files
15 directory.
16 * For :meth:`load_session_key`, unwrap the blob inside StrongBox via
17 ``Cipher.UNWRAP_MODE`` - the plaintext symmetric key never leaves the
18 secure element.
19 * Set ``setUserAuthenticationRequired(true)`` and ``setUnlockedDeviceRequired(true)``
20 to require biometric or device credential before the key is usable.
21 * For :meth:`save_artifacts` and :meth:`load_artifacts`, serialize the
22 encrypted artifact store to a file under ``Context.filesDir`` and wrap
23 reads/writes in EncryptedFile from androidx.security.
24
25 ML-KEM-768 is available in BoringSSL and is being integrated into
26 AndroidKeyStore - plug that in where ``generate_kem_keypair`` currently
27 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 AndroidEnclaveBackend(EnclaveBackend):
38 """Stub Android StrongBox / Keystore backend.
39
40 Raises :class:`BackendError` when invoked without real Keystore wiring.
41 """
42
43 name = "android-strongbox"
44 platform = "android"
45 enclave_vendor = "android-strongbox"
46
47 KEY_STORE_PROVIDER = "AndroidKeyStore"
48
49 def __init__(
50 self,
51 device_id: str = "android-unknown",
52 device_model: str = "android-unknown",
53 keystore_alias: str = "com.dyber.pqc.enclave.session",
54 ) -> None:
55 self.device_id = device_id
56 self.device_model = device_model
57 self.keystore_alias = keystore_alias
58
59 def store_session_key(self, key_id: str, key: bytes, expires_at: str) -> None:
60 raise BackendError(
61 "AndroidEnclaveBackend.store_session_key is a stub. A real "
62 f"implementation wraps the 32-byte key with the {self.keystore_alias!r} "
63 f"key in the {self.KEY_STORE_PROVIDER} provider (StrongBox-backed) "
64 "and persists the wrapped blob."
65 )
66
67 def load_session_key(self, key_id: str) -> bytes | None:
68 raise BackendError(
69 "AndroidEnclaveBackend.load_session_key is a stub. A real "
70 f"implementation unwraps the blob via Cipher.UNWRAP_MODE on the "
71 f"{self.keystore_alias!r} StrongBox key for key_id {key_id}."
72 )
73
74 def save_artifacts(self, artifacts: dict[str, EncryptedArtifact]) -> None:
75 raise BackendError(
76 "AndroidEnclaveBackend.save_artifacts is a stub. A real implementation "
77 "persists the encrypted artifacts via androidx.security.EncryptedFile "
78 "under Context.filesDir."
79 )
80
81 def load_artifacts(self) -> dict[str, EncryptedArtifact]:
82 raise BackendError(
83 "AndroidEnclaveBackend.load_artifacts is a stub. A real implementation "
84 "reads the EncryptedFile back and deserializes it via "
85 "EncryptedArtifact.from_dict."
86 )
87