aeat.core.corpus_manifest._bundle_signing module¶
Ed25519 authenticity signing for offline corpus bundles.
build_corpus_bundle() and
verify_corpus_bundle() give a distributable
corpus .zip an INTEGRITY guarantee: every archived file matches its
embedded SHA-256 manifest record, and the manifest itself is self-attesting.
That is deliberately silent on AUTHENTICITY – a bundle rebuilt by anyone from
an unmodified corpus tree verifies exactly as clean as one built by the
project’s maintainers, so an installer with only the checksum layer cannot
tell “this bundle’s checksums are internally consistent” from “this bundle was
actually published by the project”.
This module adds that authenticity layer on top, following the SAME pattern
_review_package_signing established for
review-package signing: sign the bundle’s self-attesting
manifest_sha256 digest with
an Ed25519 keypair (RFC 8032), so the signature transitively covers every
archived member (a tampered member is already caught by
verify_corpus_bundle() before the signature
check is even attempted; see
verify_corpus_bundle_signature()).
Key custody differs from the review-package case by necessity. A review
package is signed by one profile’s operator and verified by a peer who
receives the public key out of band; a corpus bundle is signed ONCE by the
project’s maintainers (an offline, pre-distribution act, with no profile
bucket in scope) and verified by every installer, including installers who
have never provisioned a profile at all. This module therefore has no
SecureObjectRepository dependency
(this is a core module; core may not import adapters or
application – see the core-not-outer import-linter contract) and
persists a maintainer’s private key as a hex-encoded file on disk instead,
hardened with restrict_file_permissions()
(the same best-effort POSIX chmod 0o600 / Windows ACL hardening the
AEAT-session-state writers use). The corresponding public key carries no
secrecy requirement: it is meant to be embedded in the aeat distribution
(or passed explicitly) so every installer can verify a downloaded bundle
against a key they already trust, without contacting anyone.
See also
corpus_manifestBuilds and integrity-verifies the bundle this module signs.
_review_package_signingThe sibling implementation of the same signing primitive, scoped to one profile bucket’s review-package authenticity instead of a maintainer-published corpus bundle.
- exception CorpusBundleSigningError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
AeatErrorBase error for corpus-bundle signing/verification failures.
- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- exception CorpusBundleSigningKeyNotFoundError(message=None, *, context=None, suggestion=None, translated_message=None)[source]¶
Bases:
CorpusBundleSigningErrorRaised when no signing keypair file exists at the requested path.
Callers should mint one via
generate_corpus_signing_keypair()before signing a bundle for the first time.- Parameters:
- Return type:
None
- code: ClassVar[ErrorCode]¶
- class CorpusSigningKeypair(**data)[source]¶
Bases:
BaseModelA maintainer’s Ed25519 corpus-signing keypair, private key included.
This model is the PLAINTEXT in-memory shape used only transiently around generation, persistence, and signing;
private_key()/public_key()reconstruct livecryptographykey objects from the stored raw hex bytes. The caller (generate_corpus_signing_keypair()/load_corpus_signing_keypair()) is responsible for keeping the on-disk file permission-hardened; this model carries no persistence logic of its own.- private_key_hex: str¶
- public_key_hex: str¶
- created_at: datetime¶
- class CorpusSigningPublicKey(**data)[source]¶
Bases:
BaseModelThe exportable, non-secret half of a maintainer’s corpus-signing keypair.
Safe to embed in the
aeatdistribution, print, or transmit so every installer can verify a downloaded bundle’s signature independently. Carries no secrecy requirement – unlikeCorpusSigningKeypair, this model is fine to write to a plaintext file.- public_key_hex: str¶
- created_at: datetime¶
- class SignedCorpusBundle(**data)[source]¶
Bases:
BaseModelSignature envelope binding a corpus bundle’s manifest digest to a signer.
manifest_sha256is the bundle’s own self-attestingmanifest_sha256(recovered viaverify_corpus_bundle()), NOT a re-derived hash of the archive bytes: signing the manifest digest transitively covers every archived member because the manifest digest already covers every per-file checksum record.- Parameters:
- envelope_version: int¶
- corpus_root_name: str¶
- manifest_sha256: str¶
- signature_hex: str¶
- public_key_hex: str¶
- signed_at: datetime¶
- generate_corpus_signing_keypair(*, private_key_path, generated_at=None)[source]¶
Mint a fresh Ed25519 keypair and persist it (private key included) to disk.
Writes the keypair as JSON to
private_key_path, then hardens the file’s permissions viarestrict_file_permissions()(best-effortchmod 0o600on POSIX; an ACL-strippingicaclscall on Windows). This is a maintainer-side, offline, one-time act: unlike the review-package keypair (minted per profile bucket, on demand), a corpus signing keypair is generated once by the project and its public half is then embedded in the distribution or handed to installers explicitly.- Parameters:
private_key_path (
Path) – Destination path for the keypair JSON. Parent directories are created as needed. An existing file at this path is overwritten – callers who want key rotation safety should back up the existing key first.generated_at (
datetime|None) – Optional override for the keypair’screated_attimestamp (tests only); defaults to the current UTC time.
- Return type:
- Returns:
The freshly minted
CorpusSigningKeypair.
- load_corpus_signing_keypair(private_key_path)[source]¶
Load a maintainer’s existing Ed25519 corpus-signing keypair from disk.
- Parameters:
private_key_path (
Path) – Path to the keypair JSON written bygenerate_corpus_signing_keypair().- Raises:
CorpusBundleSigningKeyNotFoundError – If no file exists at
private_key_path. Callgenerate_corpus_signing_keypair()first.CorpusBundleSigningError – If the file exists but is not a structurally valid keypair record.
- Return type:
- corpus_signing_public_key(keypair)[source]¶
Project the exportable public half out of a full keypair.
The projection never touches
private_key_hex; the returned model is safe to embed in the distribution or hand to an installer.- Return type:
- Returns:
A
CorpusSigningPublicKeycarrying only the public verifier material and creation timestamp.- Parameters:
keypair (CorpusSigningKeypair)
- sign_corpus_bundle(bundle_path, *, keypair, signed_at=None)[source]¶
Verify
bundle_path’s checksum manifest, then sign its digest.Delegates the integrity check entirely to
assert_corpus_bundle_verifies()(no hashing logic is re-derived here): a bundle that is not checksum-clean raises before any signature is produced, so a signature can never be minted over a bundle this module itself cannot vouch is intact.- Parameters:
bundle_path (
Path) – Path to the corpus bundle.zipto sign.keypair (
CorpusSigningKeypair) – The signer’sCorpusSigningKeypair(seegenerate_corpus_signing_keypair()).signed_at (
datetime|None) – Optional override for the envelope’ssigned_attimestamp (tests only); defaults to the current UTC time.
- Raises:
FileNotFoundError – If
bundle_pathdoes not exist.CorpusBundleError – If the bundle fails structural verification.
CorpusManifestTamperError – If the embedded manifest’s self-attesting digest does not match its body.
CorpusBundleVerificationError – If the bundle fails checksum-manifest verification (propagated from
assert_corpus_bundle_verifies()).
- Return type:
- verify_corpus_bundle_signature(bundle_path, signed_bundle, *, public_key_hex)[source]¶
Verify
signed_bundle’s signature againstpublic_key_hex.Re-runs the checksum-manifest integrity check (a fresh
verify_corpus_bundle()call, not a trust ofsigned_bundle.manifest_sha256) first. This matters becausemanifest_sha256is a digest over the embedded manifest’s OWN recorded per-file hashes – it does NOT change if an archived member’s bytes are swapped without touching the manifest metadata; only themismatchedcheck (re-hashing every archived file against its manifest record) catches that tamper. So a bundle whose CURRENT bytes are not checksum-clean, or whose current manifest digest no longer matches the signed digest, fails here even before the Ed25519 check runs.- Parameters:
bundle_path (
Path) – Path to the corpus bundle.zipto verify.signed_bundle (
SignedCorpusBundle) – TheSignedCorpusBundleenvelope produced bysign_corpus_bundle().public_key_hex (
str) – The signer’s raw public key, as 64 lowercase hex characters (seepublic_key_hex). Passed explicitly (never read offsigned_bundle) so a verifier must supply the key it actually trusts, rather than trusting whatever key the envelope claims.
- Return type:
- Returns:
Trueiff the bundle is currently checksum-clean, its manifest digest matches the signed digest, AND the Ed25519 signature verifies againstpublic_key_hex. ReturnsFalse(never raises) on any mismatch or invalid-signature outcome – signature verification is an authenticity check, not an assertion; callers that want a raising assertion should callassert_corpus_bundle_signature_verifies().
- assert_corpus_bundle_signature_verifies(bundle_path, signed_bundle, *, public_key_hex)[source]¶
Raise unless
signed_bundleverifies clean againstpublic_key_hex.The install-time assertion: call this before extracting or trusting a downloaded/copied corpus bundle whose signature you hold. Raises
CorpusBundleSigningErrornaming the bundle path on any failure – a checksum mismatch, a manifest-digest mismatch, or an invalid signature are not distinguished in the error message beyond naming the bundle, matchingverify_corpus_bundle_signature()’s deliberately coarse boolean contract (a tampered bundle and a forged signature must both refuse installation identically).- Return type:
- Parameters:
bundle_path (Path)
signed_bundle (SignedCorpusBundle)
public_key_hex (str)