cadrumo.adapters.persistence.storage.recovery_key module¶
Recovery-code minting and its canonical grouped-code encoder.
A recovery code is an opaque high-entropy secret as far as every consumer is concerned; this module is the only thing in the substrate that can mint one strong enough to resist offline guessing. A profile’s recovery envelope wraps that profile’s DEK under its own supervised Argon2id parameters and takes the canonical code as its secret. The encoder is bound to no custody architecture, no file layout and no key schedule, and the substrate never persists the code.
Shape. Six groups of five symbols drawn from a 32-symbol alphabet that
omits the characters people misread (0/O, 1/I/L), joined with
hyphens: XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX. Thirty symbols at five bits
each carry 150 bits of entropy, which is more than the 128 bits behind the
password envelope’s own key. Operator input is normalised before proof:
case, hyphens and whitespace are cosmetic, so a code typed in lowercase
without separators proves possession exactly as the displayed form does.
Wipeable key material. The code is held in a bytearray rather than an
immutable str, so the substrate’s zeroise() primitive can overwrite
it in place once the operator has copied it down. The honest limit is the
one the session path already discloses: reading RecoveryKey.code
materialises a transient str copy whose lifetime the garbage collector
owns. What this module does not do is hold the only copy of a secret in a
form no wipe primitive can reach.
- class RecoveryKey(*, code)[source]¶
Bases:
objectWipeable container for one canonical recovery code.
Deliberately not a pydantic model. The sibling
BucketSessionsets the precedent for live key material: a slotted plain class keeps the buffer mutable, and – because there is nomodel_dump_json– makes it structurally impossible to serialise the secret by accident.- Parameters:
code (str)
- normalise_recovery_code(text)[source]¶
Reduce operator input to the bare symbol run the code was minted from.
Case, hyphens, underscores and whitespace are cosmetic and dropped. Any other character, or a run that is not exactly the minted length, is refused: a code that cannot be the minted one must not reach the KDF.
- Raises:
StorageValidationError – When
textcannot be a recovery code.- Return type:
- Parameters:
text (str)
- format_recovery_code(symbols)[source]¶
Render a bare symbol run in the canonical hyphen-grouped form.
- canonical_recovery_code(text)[source]¶
Return the canonical form of an operator-supplied code, or refuse it.
- generate_recovery_key()[source]¶
Mint a fresh
RecoveryKeyfrom the process CSPRNG.Each symbol is drawn independently with
secrets.choice(), so the code carries the full five bits per symbol. The returned record is the only in-memory copy; callers must arrange for the operator to copy the code down, thenRecoveryKey.wipe()it.- Return type: