aeat.adapters.persistence.storage.master_key._master_key module¶
Master-key acquisition for the at-rest crypto stack.
Three concrete providers implement the MasterKeyProvider
protocol:
KeyringMasterKeyProvider— backed by thekeyringpackage (Windows Credential Manager, macOS Keychain, Linux Secret Service via libsecret). The master key is stored under a fixed service name and account; explicit enrollment mints a 32-byte random key and persists it.FileFallbackMasterKeyProvider— backed by a passphrase- derived KEK (Argon2id) wrapping an AES-256-GCM master key. The per-store random salt is carried insidemaster.kdf(salt_b64).EphemeralMasterKeyProvider— an in-memory provider used exclusively by tests; the key vanishes when the provider object is garbage-collected.
The get_master_key_provider() factory selects a provider per
Settings.aeat_secret_store_backend. The auto backend tries
the OS keychain and falls back to the file backend only when the
keychain is unusable. The keyring backend refuses to fall back; the
file backend never consults the keychain.
The on-disk file backend persists two artefacts in
Settings.aeat_secret_store_dir:
master.key— the AES-256-GCM ciphertext of the master key, plus its 12-byte nonce, plus the 16-byte tag, base64-encoded.master.kdf— a small JSON document carrying the Argon2id parameters (including the per-store randomsalt_b64) used to derive the KEK from the operator’s passphrase. This file is human-readable; onlymaster.keyis sensitive.
Passphrase resolution: AEAT_SECRET_PASSPHRASE env var is consulted
first; absent that, the passphrase is prompted interactively via
getpass.getpass().
- NIST_PASSPHRASE_MIN_LENGTH: Final[int]¶
NIST SP 800-63B §5.1.1.1 verifier-side minimum passphrase length.
- KEYRING_SERVICE: Final[str]¶
Stable service identifier under which the keyring backend stores the key.
- class MasterKeyProvider(*args, **kwargs)[source]¶
Bases:
ProtocolSource of the master key used by every at-rest crypto consumer.
Providers are context managers: entering activates the backend’s session (idle-timeout guard, in-memory key cache) and exiting tears it down. Every concrete provider implements the protocol verbatim.
The
_session/_activation_cmslots are the bookkeeping the shared enter/exit machinery binds onto: entering stores the openedBucketSessionand its activation context manager, exiting tears both down. Every concrete provider declares them in__init__.
- class KeyringClient(*args, **kwargs)[source]¶
Bases:
ProtocolInjection seam for the OS-keychain operations the master-key provider depends on.
The real implementation wraps the third-party
keyringmodule’sget_password/set_passwordcalls plus the backend probe that rejectsfail.Keyringandnull.Keyring. Tests inject a real in-memory implementation rather than mutating the third-party module at runtime.- probe_backend()[source]¶
Raise
KeyringUnavailableErrorwhen the active backend cannot persist a master key.No-op fail / null backends trigger this error.
- Return type:
- class KeyringMasterKeyProvider(*, service='aeat:secure-persistence', username='master', client=None)[source]¶
Bases:
objectOS-keychain-backed master-key provider.
The provider lazily imports the
keyringpackage and lazily queries the active backend. Before any read or write, the active keyring backend is inspected; the no-opfail.Keyringandnull.Keyringbackends raiseKeyringUnavailableErrorso the auto fallback can route to the file backend without silently dropping the master key into a sink.Older builds kept an in-process key cache keyed by
(service, username). That cache has retired in favour ofBucketSession; this provider resolves through the keyring on each call.The optional
clientargument injects aKeyringClientimplementation so tests exercise the provider’s contract against a real in-memory implementation rather than mutating the third-partykeyringmodule at runtime.- Parameters:
service (str)
username (str)
client (KeyringClient | None)
- get_master_key()[source]¶
Fetch the master key via the OS keychain.
Resolves on every call: process-global caching has retired in favour of
BucketSessioninstance state. Production consumers should activate a session viaactivate_session()and read throughget_active_master_key()rather than call this method in a tight loop.Absent key material is a provisioning error, not permission to create storage implicitly. Explicit enrollment calls
provision_master_key().- Return type:
- class FileFallbackMasterKeyProvider(*, store_dir, passphrase_callback=None)[source]¶
Bases:
objectEncrypted-file-backed master-key provider.
Persists
master.key(plus a human-readablemaster.kdfparameters document carrying the per-storesalt_b64) underSettings.aeat_secret_store_dir. The KEK is derived from a passphrase via Argon2id and wraps the master key with AES-256-GCM.- Parameters:
store_dir (Path)
passphrase_callback (PassphraseCallback | None)
- get_master_key()[source]¶
Unwrap and return the 32-byte master key from the encrypted file store.
Resolves the operator passphrase, then serialises the unwrap-or-refuse decision under an exclusive
master.lockso two first-time callers cannot race-mint conflictingmaster.key/master.kdfpairs. When both artefacts (master.key,master.kdf) are present, derives the Argon2id key-encryption key (KEK) from the passphrase and uses it to unwrap the wrapped master key. A partial artefact set is a torn install – a prior mint or recovery crashed mid-write – and is refused rather than silently re-minted, which would orphan records encrypted under the lost key.- Return type:
- Returns:
The 32-byte AES-256 master key.
- Raises:
MasterKeyMaterialMissingError – When the store is unprovisioned or in a torn state.
MasterKeyPassphraseMismatchError – When the passphrase fails to unwrap the stored key.
MasterKeyKdfVersionError – When
master.kdfcarries an unsupported parameter version.MasterKeyUnavailableError – When an artefact is malformed or unreadable.
PassphraseTooShortError – When the resolved passphrase is shorter than the NIST verifier minimum.
- provision_master_key(*, force=False)[source]¶
Mint the file-fallback master key for explicit enrollment.
- Parameters:
force (
bool) – When True, replace complete existing material. Reserved for explicit re-provision flows; normal enrollment leaves it False.- Return type:
- Returns:
The newly minted 32-byte master key.
- Raises:
SecretAlreadyExistsError – When the store is already provisioned and
forceis False.MasterKeyMaterialMissingError – When the store is in a torn state.
- complete_recovery(master_key)[source]¶
Re-mint the file-fallback artefacts under recovered key bytes.
Writes
master.kdfandmaster.keyfor the operator’s current passphrase (via the configured callback), wrappingmaster_keyunder a freshly-derived Argon2id KEK. Both artefacts are written via the atomic tempfile-and-replace pattern so a crash between writes leaves the existing on-disk state untouched.Use after a recovery-key unwrap (unwrap_master_key) to bind the recovered master-key bytes to a new passphrase. The substrate’s in-process cache is invalidated so subsequent
get_master_key()calls re-read the freshly-written artefacts under the new passphrase.- Parameters:
master_key (
bytes) – The 32-byte recovered master-key value.- Raises:
SecretStoreError – When the master key has the wrong length, the resolved passphrase is empty, or the target directory is not writable.
- Return type:
- activate_master_key_provider(provider, *, fallback_bucket_id=None, allow_bucket_dek_enrollment=False)[source]¶
Activate
providerfor encrypted storage within the current block.fallback_bucket_idis used by bootstrap flows such as profile creation, where the command knows the bucket being provisioned but the active-profile pointer does not exist until the transaction completes.- Parameters:
provider (
MasterKeyProvider) – TheMasterKeyProviderto activate.fallback_bucket_id (
str|None) – Optional bucket identifier used when no active profile pointer is present (bootstrap flows only).allow_bucket_dek_enrollment (
bool) – WhenTrue, a missing per-bucket DEK file is minted on first activation rather than raising.
- Return type:
- class UnsecuredMasterKeyProvider[source]¶
Bases:
objectMaster-key provider for testing / throwaway scenarios.
Returns a published deterministic 32-byte master key. The substrate’s encryption pipeline is unchanged; only the wrapping key is publicly known. Provides ZERO confidentiality.
Activation requires both signals:
AEAT_ALLOW_UNENCRYPTED=1environment variable (the hostile- named opt-out gate).aeat_secret_store_backend=unsecuredsetting (or equivalent explicit backend selection at the substrate boundary).
Refused at profile-load time when the operator profile carries a valid NIF/NIE/CIF (NIF-canary) — see
refuse_unsecured_with_real_nif()in the consumer modules. Real tax data is incompatible with a published deterministic master key.- get_master_key()[source]¶
Return the published deterministic master key for unsecured mode.
The returned bytes are publicly known by design, so the wrapping key provides ZERO confidentiality; the substrate’s encryption pipeline is otherwise intact. Intended only for testing, tutorial, and throwaway scenarios that are fenced off from real tax data by the NIF-canary at the profile-load boundary.
- Return type:
- Returns:
The 32-byte published deterministic master key.
- provision_master_key()[source]¶
Return the published deterministic key without minting material.
There is nothing to provision for the unsecured backend: the key is a fixed published constant, so enrollment and retrieval return the same bytes. Provides ZERO confidentiality – see
get_master_key.- Return type:
- Returns:
The 32-byte published deterministic master key.
- refuse_unsecured_with_real_nif(tax_id, *, provider)[source]¶
Refuse the unsecured backend when the operator profile is real.
Called at the profile-load / profile-write boundary. When the active master-key provider is
UnsecuredMasterKeyProviderAND the profile’s tax id parses as a real NIF / NIE / CIF (perlooks_like_real_tax_id()), raisesUnsecuredModeRefusedError. No-op when the provider is any other class.- Parameters:
tax_id (
str) – The operator profile’s tax id.provider (
MasterKeyProvider) – The activeMasterKeyProvider. The check is a no-op for any provider that is notUnsecuredMasterKeyProvider.
- Raises:
UnsecuredModeRefusedError – When the unsecured backend is active and the tax id is real.
- Return type:
- get_master_key_provider(*, backend=None, settings_override=None, passphrase_callback=None, keyring_client=None)[source]¶
Resolve the active
MasterKeyProviderper project settings.- Parameters:
backend (
str|None) – Optional explicit backend selector (auto/keyring/file). Overrides the value resolved from settings.settings_override (
Settings|None) – Optional pre-built settings instance. Tests inject a settings object bound totmp_pathso the file backend writes inside the test sandbox.passphrase_callback (
Callable[[],str] |None) – Optional override for passphrase resolution; only consulted by the file backend.keyring_client (
KeyringClient|None) – OptionalKeyringClientimplementation threaded into any constructedKeyringMasterKeyProvider. Tests inject a real fake type rather than patching the third-partykeyringmodule.
- Return type:
- Returns:
A live provider instance honouring the resolved backend.
- Raises:
SecretStoreError – When
backendis not a known value.UnsecuredModeRefusedError – When the unsecured backend is selected with a real tax id.
MasterKeyKeychainLockedError – When the keyring backend detects no usable keychain.