aeat.application.modelo._review_package_recipient_replay_guard module

Replay-nonce ledger for recipient-encrypted review packages.

Every RecipientEncryptedPackage carries a fresh, unique envelope_nonce_hex minted at encryption time (see _review_package_recipient_encryption). This module lets the recipient side of decrypt_review_package_for_recipient() record which nonces have already been successfully decrypted, so a captured ciphertext replayed a second time against the same recipient bucket is refused rather than silently re-accepted.

The nonce ledger is a bucket-scoped append-only consumption record, following the exact governed-repository shape of RecipientFingerprintRegistryRepository: one FINANCIAL-sensitivity secure-object singleton per bucket, an empty ledger when absent, and mark_consumed refuses a nonce already on file. This is the composition-service-no-parallel-write-path companion to that registry – the decrypt primitive itself performs no persistence; a caller (the future CLI decrypt verb) composes this ledger’s check_and_consume around the existing, unmodified decrypt_review_package_for_recipient() call. The encrypted row’s storage policy is governed by SensitivityClass.

Nonce identity is clock-free (the nonce is a random 32-byte value minted once per encryption, never derived from a timestamp), so replay defence does not depend on wall-clock ordering the way the paired expiry check does – see _review_package_recipient_encryption for the issued_at / valid_until expiry fields, which are a distinct concern (a package can be replayed within its validity window, and expiry alone does not detect a same-nonce replay before the deadline).

See also

_review_package_recipient_encryption

Mints the envelope_nonce_hex this ledger consumes and defines the paired expiry fields.

_review_package_recipient_registry

The structural template this repository mirrors.

exception RecipientReplayGuardError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: AeatError

Base error for recipient-package replay-guard failures.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
exception RecipientPackageReplayedError(message=None, *, context=None, suggestion=None, translated_message=None)[source]

Bases: RecipientReplayGuardError

Raised when a nonce already recorded as consumed is presented again.

A captured recipient-encrypted package replayed against the same bucket (whether by an adversary or by an operator’s own accidental re-run) is refused – the nonce is single-use once consumed.

Parameters:
  • message (str | None)

  • context (Mapping[str, object] | None)

  • suggestion (str | None)

  • translated_message (str | None)

Return type:

None

code: ClassVar[ErrorCode]
class ConsumedNonceRecord(**data)[source]

Bases: BaseModel

One consumed envelope_nonce_hex on file, with its consumption time.

Parameters:
nonce_hex: str
consumed_at: datetime
class ConsumedNonceLedger(**data)[source]

Bases: BaseModel

A bucket’s full set of consumed recipient-package replay nonces.

Parameters:

records (tuple[ConsumedNonceRecord, ...])

records: tuple[ConsumedNonceRecord, ...]
class RecipientReplayGuardRepository(*, bucket_id=None, objects=None)[source]

Bases: object

Governed repository for the encrypted consumed-nonce ledger.

The singleton row is owned by MODELO_REVIEW_PACKAGE_RECIPIENT_REPLAY_GUARD_NAMESPACE and persisted through SecureObjectRepository, mirroring RecipientFingerprintRegistryRepository.

Parameters:
load()[source]

Load the ledger, returning an empty ledger when absent.

Raises:
  • RecipientReplayGuardError – When the envelope exists but the filesystem I/O itself fails.

  • DecryptionError – When the envelope exists but its ciphertext fails AEAD authentication (tampered or corrupted at rest) – propagated verbatim rather than re-wrapped, so a caller can distinguish “this ledger was tampered with” from a generic I/O failure. This is the anti-tautology proof this repository’s roundtrip tests require: a corrupted on-disk payload must be refused loudly, not silently coerced into a plausible-looking empty ledger (which would re-open every previously-consumed nonce to replay).

Return type:

ConsumedNonceLedger

is_consumed(nonce_hex)[source]

Return whether nonce_hex has already been recorded as consumed.

Return type:

bool

Parameters:

nonce_hex (str)

mark_consumed(nonce_hex, *, consumed_at=None)[source]

Atomically record nonce_hex as consumed.

Parameters:
  • nonce_hex (str) – The envelope’s envelope_nonce_hex (see RecipientEncryptedPackage).

  • consumed_at (datetime | None) – Optional override for the record’s consumed_at timestamp (tests only); defaults to the current UTC time.

Raises:

RecipientPackageReplayedError – When nonce_hex is already on file – the package has been presented for decryption before.

Return type:

ConsumedNonceLedger