Source code for cadrumo.application.modelo.recipient_encryption
"""Application-owned contract for recipient-encrypted review packages.The review-package use cases need one capability for three operations: mintingthe bucket's recipient keypair, sealing bytes for a public key, and opening anenvelope with a private key. The capability deliberately exposes only theseapplication DTOs and errors. Its concrete implementation belongs to anoutward persistence/crypto adapter and is supplied by an executablecomposition root."""from__future__importannotationsfromdatetimeimportdatetime,timedeltafromtypingimportProtocolfrompydanticimportBaseModel,Field,field_serializer,field_validator,model_validatorfrom...core.errors.hierarchyimportpydantic_validation_boundaryfrom...core.heximportHEX_PATTERN_64as_HEX_PATTERN_64from...core.identity.bucketimportBucketIdfrom...core.modelsimportSTRICT_FROZEN_CONFIGas_STRICT_FROZENfrom...core.time.utcimportUtcInstant_RECIPIENT_ENCRYPTION_ENVELOPE_VERSION=1
[docs]classRecipientEncryptionKeypair(BaseModel):"""The transient, typed keypair payload owned by the application boundary. The adapter stores this payload through secure persistence. Private and public key material remain hex strings here so the application contract does not expose a cryptography implementation type. """model_config=_STRICT_FROZENbucket_id:BucketIdprivate_key_hex:str=Field(pattern=_HEX_PATTERN_64)public_key_hex:str=Field(pattern=_HEX_PATTERN_64)created_at:UtcInstant
[docs]classRecipientEncryptedPackage(BaseModel):"""Wire envelope for a review package sealed for one recipient."""model_config=_STRICT_FROZENenvelope_version:int=Field(default=_RECIPIENT_ENCRYPTION_ENVELOPE_VERSION,ge=1)ephemeral_public_key_hex:str=Field(pattern=_HEX_PATTERN_64)recipient_public_key_hex:str=Field(pattern=_HEX_PATTERN_64)ciphertext:bytes=Field(min_length=1)envelope_nonce_hex:str=Field(pattern=_HEX_PATTERN_64)issued_at:UtcInstantvalid_until:UtcInstant|None=Field(default=None)review_only:bool=Field(default=False)@field_validator("ciphertext",mode="before")@classmethod@pydantic_validation_boundarydef_ciphertext_accepts_hex_or_raw_bytes(cls,value:object)->object:ifisinstance(value,str):returnbytes.fromhex(value)returnvalue@field_serializer("ciphertext",when_used="json")def_ciphertext_as_hex_for_json(self,value:bytes)->str:returnvalue.hex()@model_validator(mode="after")@pydantic_validation_boundarydef_valid_until_is_after_issued_at(self)->RecipientEncryptedPackage:ifself.valid_untilisnotNoneandself.valid_until<=self.issued_at:raiseValueError("valid_until must be strictly after issued_at")returnself
[docs]classRecipientDecryptedPackage(BaseModel):"""Recovered package bytes plus the envelope's disposition flag."""model_config=_STRICT_FROZENpackage_bytes:bytes=Field(min_length=1)review_only:bool
[docs]classRecipientEncryptionCapability(Protocol):"""Application-facing recipient-encryption capability. Implementations may use persistence and cryptography internally, but they must translate those concrete DTOs and failures to this contract before returning or raising across the application boundary. """
[docs]defensure_keypair(self,*,bucket_id:str,generated_at:datetime|None=None,)->RecipientEncryptionKeypair:"""Return or mint the bucket-scoped recipient keypair."""...
[docs]defencrypt(self,package_bytes:bytes,*,recipient_public_key_hex:str,review_only:bool=False,valid_for:timedelta|None=None,issued_at:datetime|None=None,)->RecipientEncryptedPackage:"""Seal package bytes for one recipient public key."""...
[docs]defdecrypt(self,envelope:RecipientEncryptedPackage,*,recipient_private_key_hex:str,now:datetime|None=None,)->RecipientDecryptedPackage:"""Open one envelope with the recipient's private key material."""...
[docs]classRecipientEncryptionCapabilityFactory(Protocol):"""Construct a capability bound to one bucket's secure-object store."""def__call__(self,*,bucket_id:str)->RecipientEncryptionCapability:"""Return the capability used by one bucket-scoped invocation."""...