Skip to content

Signing

Ed25519 and ML-DSA-65 (post-quantum) signing for agent manifests. See ADR-0005 for the signature design.

Key generation

generate_ed25519

generate_ed25519() -> Ed25519KeyPair

Generate a fresh Ed25519 key pair.

Ed25519KeyPair dataclass

Signing

Ed25519Signer

Signs manifest dicts with Ed25519 (RFC 8032, deterministic).

For production use on high-value keys, prefer a HSM implementation of hedged signing (draft-irtf-cfrg-det-sigs-with-noise) to protect against fault attacks on the deterministic nonce derivation.

sign

sign(manifest_dict: dict[str, Any]) -> dict[str, Any]

Return a signature block dict suitable for ManifestSignature.

Verification

Ed25519Verifier

Verifies Ed25519 signatures using OpenSSL's cofactorless equation.

verify

verify(manifest_dict: dict[str, Any], signature_value: str) -> None

Verify signature_value over manifest_dict's signed fields.

Raises:

Type Description
InvalidSignature

Verification failed or wrong length.

ValueError

Signature string contains non-URL-safe base64 characters.

Signing internals

signing_pre_image

signing_pre_image(manifest_dict: dict[str, Any]) -> bytes

Return the RFC 8785 canonical bytes that are signed.

Extracts only the SIGNED_FIELDS subset from manifest_dict and canonicalizes the result. Fields absent from the manifest are omitted (null-exclusion already applied by canonicalize's exclude_none default).

Normalization rule (spec Section 3.6, ADR-0006 as amended): the value of hitl_record.approvals is normalized to [] before canonicalization. The HITL requirement itself stays tamper-evident under the issuer signature while approvals attach post-issuance without re-signing; each approval is authenticated separately by its own approval_signature.

This function is the single source of truth for the pre-image - both signers and verifiers MUST call this function to guarantee identical byte sequences.

SIGNED_FIELDS module-attribute

SIGNED_FIELDS: tuple[str, ...] = ('@context', '@type', 'manifest_id', 'previous_manifest_id', 'agent_id', 'version', 'min_verifier_version', 'issued_at', 'expires_at', 'issuer', 'crypto_profile', 'artifacts', 'delegation_chain', 'hitl_record', 'prior_transparency_log_entry', 'log_retention', 'data_scope', 'operational_lifecycle')

Canonicalisation

canonicalize

canonicalize(obj: Any, *, exclude_none: bool = True) -> bytes

Return RFC 8785 canonical JSON bytes for obj.

Parameters:

Name Type Description Default
obj Any

Any JSON-serializable Python value.

required
exclude_none bool

When True (default, per spec Section 4.3), mapping entries whose value is None are omitted from the output. Set to False only when verifying round-trips with external producers that include explicit null fields.

True

Returns:

Type Description
bytes

UTF-8 encoded bytes with no trailing newline.

Raises:

Type Description
TypeError

If obj contains a type that cannot be serialized.

ValueError

If a float value is NaN or Infinity, or nesting exceeds the maximum depth.

canonical_hash

canonical_hash(obj: Any, *, algorithm: str = 'sha256', exclude_none: bool = True) -> str

Canonicalize obj and return a prefixed hex digest.

Returns:

Type Description
str

String in HashValue format: "sha256:<64-hex>" or

str

"shake256:<64-hex>".