-
-
Notifications
You must be signed in to change notification settings - Fork 0
API Reference
Complete Python API reference for ama_cryptography. All modules, classes, functions, and their parameters.
| Module | Description |
|---|---|
crypto_api |
Algorithm-agnostic unified cryptographic interface |
pqc_backends |
Post-quantum cryptography backends |
key_management |
Key management, HD derivation, lifecycle |
secure_memory |
Secure memory operations |
hybrid_combiner |
Hybrid classical + PQC KEM |
adaptive_posture |
Runtime threat response |
rfc3161_timestamp |
RFC 3161 timestamps — wire format + §2.4.2 message-imprint binding (not TSA attestation) |
agent_binding |
Agent-instance key/signature binding (INVARIANT-30) |
monitoring |
3R runtime monitoring + agentic-abuse detectors |
exceptions |
Exception hierarchy |
The algorithm-agnostic entry point is AmaCryptography, selected by an
AlgorithmType. Every concrete backend (Ed25519, ML-DSA-65, ML-KEM-1024,
SLH-DSA-SHA2-256f, AES-256-GCM, hybrid signature, hybrid KEM) is a CryptoProvider
subclass that can also be used directly.
Defined in ama_cryptography/crypto_api.py:203–212.
from enum import Enum, auto
class AlgorithmType(Enum):
ML_DSA_65 = auto() # NIST FIPS 204 signature (post-quantum)
KYBER_1024 = auto() # NIST FIPS 203 KEM (post-quantum)
SPHINCS_256F = auto() # NIST FIPS 205 signature (hash-based)
ED25519 = auto() # RFC 8032 signature (classical)
AES_256_GCM = auto() # NIST SP 800-38D AEAD
HYBRID_SIG = auto() # Ed25519 || ML-DSA-65 (recommended default)
HYBRID_KEM = auto() # X25519 || ML-KEM-1024The underlying integer assigned to each member by
auto()is an implementation detail — it depends on declaration order and is not part of the public API. Compare by member (e.g.,alg == AlgorithmType.HYBRID_SIG) or by name (alg.name), never by.value. The numeric values may change across releases without notice.
Defined in ama_cryptography/crypto_api.py:215–220.
class CryptoBackend(Enum):
"""Available implementation backends."""
C_LIBRARY = auto() # libama_cryptography.so (fastest, native PQC) — default
CYTHON = auto() # Cython-optimized (fast); provider-level overlay
PURE_PYTHON = auto() # RESERVED — not a usable runtime modeINVARIANT-7 (revised): The library refuses to operate without the native C constant-time backend.
ama_cryptography.crypto_apiraisesRuntimeErrorat import time (seecrypto_api.py:105–114) if the native HMAC/HKDF accelerators are missing.CryptoBackend.PURE_PYTHONis therefore a reserved enum slot only — passing it does not select a pure-Python fallback; the library has none.CryptoBackend.CYTHONremains a provider-level overlay on top of the C library.There is no
CryptoBackend.PYTHONmember.
Defined in ama_cryptography/crypto_api.py:223–274. Not frozen=True;
sensitive fields use field(repr=False) so repr() never surfaces key
material.
@dataclass
class KeyPair:
public_key: bytes
secret_key: bytes = field(repr=False) # SENSITIVE
algorithm: AlgorithmType
metadata: Dict[str, Any]
@dataclass
class Signature:
signature: bytes
algorithm: AlgorithmType
message_hash: bytes
metadata: Dict[str, Any]
@dataclass
class EncapsulatedSecret:
ciphertext: bytes
shared_secret: bytes = field(repr=False) # SENSITIVE
algorithm: AlgorithmType
metadata: Dict[str, Any]High-level, algorithm-agnostic orchestrator. Used as-is for single-algorithm
workflows, or configured with HYBRID_SIG / HYBRID_KEM to transparently
drive the Ed25519+ML-DSA-65 or X25519+ML-KEM-1024 hybrid providers.
from ama_cryptography.crypto_api import AmaCryptography, AlgorithmType, CryptoBackend
crypto = AmaCryptography(
algorithm: AlgorithmType = AlgorithmType.HYBRID_SIG,
backend: CryptoBackend = CryptoBackend.C_LIBRARY,
)
# Signature primitives (ED25519, ML_DSA_65, SPHINCS_256F, HYBRID_SIG)
kp: KeyPair = crypto.generate_keypair()
sig: Signature = crypto.sign(message: bytes, secret_key: bytes | bytearray)
valid: bool = crypto.verify(message: bytes, signature: bytes | Signature, public_key: bytes)
# KEM primitives (KYBER_1024, HYBRID_KEM)
enc: EncapsulatedSecret = crypto.encapsulate(public_key: bytes)
shared: bytes = crypto.decapsulate(ciphertext: bytes, secret_key: bytes | bytearray)
# Static helpers
digest: bytes = AmaCryptography.hash_message(message: bytes, algorithm="sha3-256")
equal: bool = AmaCryptography.constant_time_compare(a: bytes, b: bytes)Invariants:
-
generate_keypair()/sign()/verify()are valid only when the selected algorithm is a signature scheme;encapsulate()/decapsulate()only for KEM schemes. Calling the wrong family raisesTypeError("Current algorithm does not support signing"/"...verification"/"...KEM"— seeama_cryptography/crypto_api.py:1607-1645).AESGCMProvideralso raisesTypeErrorfromgenerate_keypair()because AEAD does not produce asymmetric keypairs. -
INVARIANT-7 (no silent cryptographic fallback): if the native C
library is unavailable, the failure is raised at module import
time (
crypto_api.py:105-114hard-fails theimport ama_cryptography.crypto_apiwith aRuntimeError) — not later inAmaCryptography.__init__. A_enforce_invariant7()call in each primitive (crypto_api.py:149-168) also re-checks at call time, so a runtime patch that unsets the native lib fails fast.
Each provider implements either CryptoProvider (sign/verify) or
KEMProvider (encapsulate/decapsulate) and shares the data shapes above.
Use them when you need to pin a single algorithm without going through the
dispatcher.
| Class | Algorithm | Family |
|---|---|---|
Ed25519Provider |
Ed25519 (RFC 8032) | signature |
MLDSAProvider |
ML-DSA-65 (FIPS 204) | signature |
SphincsProvider |
SPHINCS+-SHA2-256f (FIPS 205) | signature |
HybridSignatureProvider |
Ed25519 ∥ ML-DSA-65 | signature |
KyberProvider |
ML-KEM-1024 (FIPS 203) | KEM |
HybridKEMProvider |
X25519 ∥ ML-KEM-1024 | KEM |
AESGCMProvider |
AES-256-GCM (SP 800-38D) | AEAD (separate encrypt / decrypt) |
Each provider shares the same constructor signature as AmaCryptography
(no algorithm argument — the class itself pins the algorithm).
cache = KeypairCache(algorithm: AlgorithmType = AlgorithmType.HYBRID_SIG)Fixed-size cache for hot-path keypair reuse. Constant-time-zeroed on eviction.
from ama_cryptography.crypto_api import AESGCMProvider
import os
aead = AESGCMProvider()
key = os.urandom(32) # 256-bit key
# Encrypt: nonce is auto-generated when omitted.
# Returns a dict with 'ciphertext', 'nonce', 'tag', 'aad', 'backend'.
result = aead.encrypt(plaintext: bytes, key: bytes,
nonce: bytes | None = None, aad: bytes = b"")
# Decrypt: caller passes the nonce and tag back in.
plaintext = aead.decrypt(
ciphertext: bytes,
key: bytes,
nonce: bytes,
tag: bytes,
aad: bytes = b"",
) # raises on tag mismatchNonces are 96-bit (NIST SP 800-38D); tags are 128-bit. Associated data
is authenticated but not encrypted. To reuse the same nonce/tag wire
layout across systems, the result dict fields can be concatenated as
nonce || ciphertext || tag; unpack them symmetrically on the
receive side.
Two module-level convenience dispatchers wrap the native C MAC/KDF kernels
with a one-call surface. Both are native-only (INVARIANT-1 — no stdlib
hmac/hashlib): an unsupported algorithm raises ValueError, and a
missing native backend raises RuntimeError.
Signatures (algorithm ∈ {"sha256", "sha384", "sha512", "sha3-256"}; HMAC
digest length is 32 / 48 / 64 / 32 bytes respectively):
def quick_hmac(key: bytes, message: bytes, algorithm: str = "sha256") -> bytes: ...
def quick_hkdf(
ikm: bytes,
length: int,
salt: bytes | None = None,
info: bytes = b"",
algorithm: str = "sha256",
) -> bytes: ...Usage:
from ama_cryptography.crypto_api import quick_hmac, quick_hkdf
# HMAC (RFC 2104 / FIPS 198-1).
tag = quick_hmac(b"key", b"message", "sha256")
# HKDF (RFC 5869): sha256/384/512 = interoperable HKDF-SHA-2 (TLS 1.3 / HPKE);
# sha3-256 = AMA's default HMAC-SHA3-256 HKDF.
okm = quick_hkdf(b"input-key-material", 32, salt=b"salt", info=b"context")These dispatch to the native native_hmac_* / native_hkdf_* interfaces in
pqc_backends; use those directly when you want to pin one
algorithm without the string dispatch.
DILITHIUM_AVAILABLE: bool # True if ML-DSA-65 is available
KYBER_AVAILABLE: bool # True if ML-KEM-1024 is available
SPHINCS_AVAILABLE: bool # True if SPHINCS+ is available# High-level rollup: returns PQCStatus.AVAILABLE if at least one PQC
# backend loaded, PQCStatus.UNAVAILABLE otherwise.
get_pqc_status() -> PQCStatus
# Detailed backend dict: per-algorithm availability + backend names,
# algorithm parameters (key/sig sizes), and hash/HMAC native-C status.
get_pqc_backend_info() -> dict
# Example keys: 'status', 'dilithium_available', 'dilithium_backend',
# 'kyber_available', 'sphincs_available', 'algorithms', 'SHA3-256',
# 'HMAC-SHA3-256'# Generate ML-DSA-65 key pair.
# Returns a DilithiumKeyPair dataclass with .public_key (1952 bytes),
# .secret_key (4032 bytes), and .wipe() for constant-time zeroing.
kp = generate_dilithium_keypair()
pk, sk = kp.public_key, kp.secret_key
# Sign a message -> 3309-byte signature
sig: bytes = dilithium_sign(message: bytes, secret_key: bytes) -> bytes
# Verify a signature
valid: bool = dilithium_verify(message: bytes, signature: bytes, public_key: bytes) -> bool# Generate ML-KEM-1024 key pair.
# Returns a KyberKeyPair dataclass with .public_key (1568 bytes),
# .secret_key (3168 bytes), and .wipe().
kp = generate_kyber_keypair()
pk, sk = kp.public_key, kp.secret_key
# Encapsulate (sender side).
# Returns a KyberEncapsulation dataclass with .ciphertext (1568 bytes)
# and .shared_secret (32 bytes).
enc = kyber_encapsulate(public_key: bytes)
# Decapsulate (receiver side) -> 32-byte shared secret
ss: bytes = kyber_decapsulate(ciphertext: bytes, secret_key: bytes) -> bytes# Generate SPHINCS+ key pair.
# Returns a SphincsKeyPair dataclass with .public_key (64 bytes),
# .secret_key (128 bytes), and .wipe().
kp = generate_sphincs_keypair()
pk, sk = kp.public_key, kp.secret_key
# Sign a message -> 49856-byte signature
sig: bytes = sphincs_sign(message: bytes, secret_key: bytes) -> bytes
# Verify a signature
valid: bool = sphincs_verify(message: bytes, signature: bytes, public_key: bytes) -> boolDirect one-call bindings to the native C kernels. All are INVARIANT-1
compliant (no stdlib hashlib / hmac) and raise RuntimeError when the
native backend is unavailable. The quick_hmac / quick_hkdf
dispatchers in crypto_api are thin string-selectable wrappers over these.
from ama_cryptography.pqc_backends import (
native_sha256, native_sha3_256, native_sha3_512, native_shake128, native_shake256,
native_hmac_sha256, native_hmac_sha384, native_hmac_sha512, native_hmac_sha3_256,
native_hkdf, native_hkdf_sha256, native_hkdf_sha384, native_hkdf_sha512,
)
# Raw hashes (FIPS 180-4 / FIPS 202) — byte-identical to the hashlib equivalents.
d = native_sha256(b"data") # 32-byte SHA-256
d = native_sha3_256(b"data") # 32-byte SHA3-256
d = native_sha3_512(b"data") # 64-byte SHA3-512
x = native_shake128(b"data", 32) # SHAKE-128 XOF; second arg is output length in bytes
x = native_shake256(b"data", 64) # SHAKE-256 XOF
# HMAC (RFC 2104 / FIPS 198-1) -> 32 / 48 / 64 / 32-byte tags.
t = native_hmac_sha256(b"key", b"msg")
t = native_hmac_sha384(b"key", b"msg")
t = native_hmac_sha512(b"key", b"msg")
t = native_hmac_sha3_256(b"key", b"msg")
# HKDF (RFC 5869): native_hkdf is the HMAC-SHA3-256 default; the _sha* variants
# are the interoperable HKDF-SHA-2 profiles. Signature (salt=None means a
# zero-length salt per RFC 5869; the _sha* variants share this signature):
# native_hkdf(ikm: bytes, length: int, salt: bytes | None = None, info: bytes = b"") -> bytes
okm = native_hkdf(b"ikm", 32, salt=b"salt", info=b"context")
okm = native_hkdf_sha256(b"ikm", 32, salt=b"salt", info=b"context")class KeyStatus(Enum):
ACTIVE
ROTATING
DEPRECATED
REVOKED
COMPROMISED@dataclass
class KeyMetadata:
key_id: str
created_at: datetime # timezone-aware (UTC)
expires_at: Optional[datetime] # timezone-aware (UTC), or None
status: KeyStatus
version: int
usage_count: int
max_usage: int
derivation_path: Optional[str]BIP32-style hierarchical deterministic key derivation. AMA uses the
BIP32-standard HMAC-SHA-512 PRF (delegated to the native C
accelerator via ama_cryptography.pqc_backends.native_hmac_sha512 to
satisfy INVARIANT-1 — no stdlib hmac). Both hardened and non-hardened
derivation are supported:
-
derive_key(purpose, account, change, index)is a convenience wrapper that always emits a fully hardened BIP-44 path. -
derive_path(path)accepts an explicit BIP32-style path and supports both hardened (44'or index ≥ 2^31) and non-hardened components. Non-hardened derivation uses the native secp256k1 public-key computation.
from ama_cryptography.key_management import HDKeyDerivation
hd = HDKeyDerivation(
seed: bytes | None = None, # 32–64 byte seed
seed_phrase: str | None = None, # BIP-39-style phrase (PBKDF2)
)
# Convenience: always fully-hardened BIP-44 path
key_material: bytes = hd.derive_key(
purpose: int, # e.g., 44
account: int = 0,
change: int = 0,
index: int = 0,
)
# Explicit BIP32 path — accepts both hardened (44') and non-hardened (44)
key, chain_code = hd.derive_path(path: str) # e.g. "m/44'/0'/0'/0'" or "m/44/0/0"from datetime import timedelta
from ama_cryptography.key_management import KeyRotationManager, KeyMetadata
mgr = KeyRotationManager(rotation_period: timedelta = timedelta(days=90))
# Register a key with the rotation policy
meta: KeyMetadata = mgr.register_key(
key_id: str,
purpose: str,
parent_id: str | None = None,
derivation_path: str | None = None,
expires_in: timedelta | None = None,
max_usage: int | None = None,
)
# Policy hooks
should: bool = mgr.should_rotate(key_id: str)
active: str | None = mgr.get_active_key()
mgr.initiate_rotation(old_key_id: str, new_key_id: str)
mgr.complete_rotation(old_key_id: str) # old key → DEPRECATED
mgr.increment_usage(key_id: str)
mgr.revoke_key(key_id: str, reason: str = "compromised")
metadata: dict = mgr.export_metadata(filepath: Path | None = None)Defined in ama_cryptography/key_management.py:537. The constructor takes
a storage directory and an optional master password — not a raw
encryption key. retrieve_key() returns the ciphertext-decrypted key
material as Optional[bytes] (or None if the id is missing); metadata
is stored separately as a JSON-serializable dict and is typically
retrieved via KeyRotationManager.
from pathlib import Path
from ama_cryptography.key_management import SecureKeyStorage
storage = SecureKeyStorage(
storage_path: Path,
master_password: Optional[str] = None,
)
# Store / retrieve / delete
storage.store_key(
key_id: str,
key_data: bytes,
metadata: Optional[Dict[str, Any]] = None,
) -> None
key_bytes: Optional[bytes] = storage.retrieve_key(key_id: str)
storage.delete_key(key_id: str) -> None
all_ids: list[str] = storage.list_keys()Available when PyKCS11 >= 1.5.18 is installed and HSM_AVAILABLE is
True. Raises AmaHSMUnavailableError (in ama_cryptography.exceptions)
when called without the dependency.
# Context manager: auto-zero buffer on exit
with SecureBuffer(size: int) as buf:
buf.data: bytearray # size bytes, initially zeroed
# Multi-pass overwrite (must be bytearray)
secure_memzero(buffer: bytearray) -> None
# Lock memory into RAM (prevent swap)
# Returns True if successful
locked: bool = secure_mlock(buffer: bytearray) -> bool
# Unlock memory (allow swap)
secure_munlock(buffer: bytearray) -> None
# Constant-time byte comparison (timing-safe)
equal: bool = constant_time_compare(a: bytes, b: bytes) -> boolThe combiner is KEM-agnostic: the caller supplies an encapsulate /
decapsulate callable for each half, letting the same class drive
X25519 ∥ ML-KEM-1024, ECDH ∥ Kyber, or any future pairing. Output is
derived with HKDF-SHA3-256 over a length-prefixed concatenation of both
shared secrets, both ciphertexts, and (optionally) both public keys —
length prefixing prevents the component-stripping attack fixed in
v2.1.5 (audit finding C6).
from ama_cryptography.hybrid_combiner import HybridCombiner, HybridEncapsulation
combiner = HybridCombiner()
# Sender: encapsulate using recipient's public keys
enc: HybridEncapsulation = combiner.encapsulate_hybrid(
classical_encapsulate: Callable, # e.g. X25519 encapsulate
pqc_encapsulate: Callable, # e.g. ML-KEM-1024 encapsulate
classical_pk: bytes,
pqc_pk: bytes,
)
# Receiver: decapsulate using secret keys
combined: bytes = combiner.decapsulate_hybrid(
classical_decapsulate: Callable,
pqc_decapsulate: Callable,
classical_ct: bytes,
pqc_ct: bytes,
classical_sk: bytes,
pqc_sk: bytes,
classical_pk: bytes = b"",
pqc_pk: bytes = b"",
)
# Low-level combine(): use when you already hold both shared secrets
shared: bytes = combiner.combine(
classical_ss: bytes,
pqc_ss: bytes,
classical_ct: bytes,
pqc_ct: bytes,
classical_pk: bytes = b"",
pqc_pk: bytes = b"",
output_len: int = 32,
)@dataclass
class HybridEncapsulation:
combined_secret: bytes # HKDF-SHA3-256 output (default 32 bytes)
classical_ciphertext: bytes # X25519 ephemeral public key (32 bytes)
pqc_ciphertext: bytes # ML-KEM-1024 ciphertext (1568 bytes)
classical_shared_secret: bytes # X25519 shared secret (32 bytes)
pqc_shared_secret: bytes # ML-KEM-1024 shared secret (32 bytes)from ama_cryptography.adaptive_posture import (
PostureEvaluator,
CryptoPostureController,
PostureEvaluation,
PostureAction,
ThreatLevel,
)
evaluator = PostureEvaluator()
# Evaluate a 3R-monitor report dict. `PostureEvaluator.evaluate()` takes
# a single positional `monitor_report: Dict[str, Any]` argument — NOT a
# keyword argument called `monitor_signals`.
evaluation: PostureEvaluation = evaluator.evaluate(monitor_report)
# PostureEvaluation fields (dataclass, see adaptive_posture.py:68):
# evaluation.threat_level : ThreatLevel
# evaluation.action : PostureAction # the recommended action
# evaluation.confidence : float (0.0 – 1.0)
# evaluation.signals : Dict[str, Any] # contributing anomaly signals
# evaluation.timestamp : float
# The controller's public entry point is `evaluate_and_respond()`, which
# internally calls the monitor, runs the evaluator, and dispatches the
# recommended action through its private `_execute_action(action)`
# machinery. There is NO public `execute_action(evaluation, ...)` method.
from ama_cryptography_monitor import AmaCryptographyMonitor
monitor = AmaCryptographyMonitor(enabled=True)
controller = CryptoPostureController(monitor=monitor)
evaluation = controller.evaluate_and_respond()
if evaluation.action != PostureAction.NONE:
# Application-level response (logging, paging, circuit-breaking, etc.).
# The controller has already applied the cryptographic action by the
# time evaluate_and_respond() returns.
...What a passing check establishes. AMA verifies the RFC 3161 §2.4.2 message-imprint binding — that a token refers to this data — plus the
PKIStatusInfoverdict and the TSA's nonce echo. It verifies no TSA signature and no certificate chain, so a token that binds your data is not evidence that a trusted authority issued it, andTSTInfo.genTimeis unauthenticated.RFC3161_CAPABILITIESis the machine-readable statement of this boundary; INVARIANT-37 enforces it against every document in the repository.
from ama_cryptography.rfc3161_timestamp import (
allow_mock_tsa,
describe_token_verification,
get_timestamp,
verify_timestamp_binding,
RFC3161_CAPABILITIES,
TokenVerification,
TimestampResult,
TimestampError,
TimestampUnavailableError,
RFC3161_AVAILABLE,
)
# Request a timestamp token.
# tsa_mode ∈ {"online", "mock", "disabled"}.
result: TimestampResult = get_timestamp(
data: bytes,
tsa_url: str | None = None, # defaults to FreeTSA in "online"
hash_algorithm: str = "sha3-256",
certificate_file: str | None = None, # REFUSED — raises TimestampError
tsa_mode: str = "online",
)
# Check that a previously obtained token binds the original data.
binds: bool = verify_timestamp_binding(
data: bytes,
timestamp_result: TimestampResult,
)
# The same verdict as a record, for an audit trail or compliance profile.
# TokenVerification raises TypeError on bool(), so it cannot collapse into
# an "if" that silently reports an unverified token as verified.
record: TokenVerification = describe_token_verification(data: bytes, token: bytes)
record.binding_verified # bool
record.signature_verified # always False — not implemented
record.chain_verified # always False — not implemented
record.not_verified # frozenset: {"tsa_signature",
# "tsa_certificate_chain", "gen_time"}
# Deprecated: same check, but the name claims attestation AMA does not perform.
# Emits DeprecationWarning; certificate_file raises.
valid: bool = verify_timestamp(data, timestamp_result, certificate_file=None)
# TimestampResult fields (frozen dataclass):
# token: bytes — DER-encoded RFC 3161 token
# tsa_url: str — TSA that produced the token
# hash_algorithm: str — hash used to imprint the message
# data_hash: bytes — imprint actually sent to the TSARFC 3161 is implemented in-tree on AMA's own DER codec and requires no
third-party package: the rfc3161ng dependency was removed under INVARIANT-1
and RFC3161_AVAILABLE is unconditionally True. Mock tokens carry their own
HMAC key, so both creating and honouring one is gated to a testing context —
open it with allow_mock_tsa().
certificate_file (here and on verify_timestamp) and
legacy_compat.verify_rfc3161_timestamp's tsa_cert_path all raise. They
request X.509 chain validation of the TSA's signing certificate, which AMA does
not implement; refusing is the only honest answer, and returning the binding
check's verdict instead would answer a weaker question while appearing to
answer that one.
Agent-instance key and signature binding (INVARIANT-30). A binding cryptographically forbids long-lived persistence material and successor-authorizing signatures unless a human-held operator key authorizes them. Domain separation and policy over the existing SHA3-256 / HMAC-SHA3-256 / HKDF primitives — no new algorithm.
import os
from ama_cryptography.agent_binding import (
AgentBinding, AgentLifetime, AgentCapability, EthicalBindingError,
)
# Ordinary ephemeral use needs no operator key and no ethical profile.
b = AgentBinding(instance_id=os.urandom(32),
capabilities=AgentCapability.DATA_SIGN)
session_key = b.derive_key(ikm, 32, info=b"session") # HKDF, binding folded into info
ctx = b.signing_context() # 32-byte ML-DSA / SLH-DSA ctx
# Persistence / self-replication require the operator.
p = AgentBinding(instance_id=iid,
lifetime=AgentLifetime.PERSISTENT,
capabilities=AgentCapability.PERSISTENCE,
ethical_profile_hash=sha3_256(profile_document))
p.authorize(authority_key) # operator-side; needs K_auth
root = p.derive_key(ikm, 32, authority_key=authority_key)
# Without authorize(), derive_key() raises EthicalBindingError and writes nothing.Refusal is fail-closed: no output bytes, a distinct error (EthicalBindingError /
AMA_ERROR_ETHICAL_BINDING), and no partial state. The policy check is constant-time
(verified by a strict dudect lane) and the native surface is fuzzed for its security
properties, not merely for memory safety.
The 3R runtime monitor (AmaCryptographyMonitor, create_monitor()) plus two optional
agentic-abuse detectors, on by default and advisory-only.
from ama_cryptography.monitoring import (
create_monitor, VolumeSpikeDetector, NoteArtifactDetector,
)
m = create_monitor() # both detectors active
m.record_operation_event("kyber_encaps", key_fingerprint=fp) # feeds the volume detector
signal = m.inspect_signed_payload(payload, label="note") # scores for note-like structure
# Opt out entirely:
m = create_monitor(detect_volume_spikes=False, detect_note_artifacts=False)-
VolumeSpikeDetector— anomalous KEM/signature bursts, scored in the Anscombe variance-stabilising transform (a quiet baseline cannot manufacture false spikes). Three gates (warmup, an absolute floor, a 6-sigma residual) must all pass; an optional key fingerprint separates ephemeral-key churn from a hot loop over one key. -
NoteArtifactDetector— signed payloads shaped like instructions addressed to a later instance. Calibrated against the repository's own text as a hard-negative corpus; advisory (it never blocks a signature).
Both are backed by Cython kernels with exact pure-Python twins, so the compiled extension is an optimisation and never a correctness dependency.
from ama_cryptography.exceptions import (
AmaCryptographyError, # catch-all root of the exception hierarchy (Exception)
CryptoModuleError, # FIPS 140-3 error-state module lock (also RuntimeError)
CryptoConfigError, # Configuration / environment problems
IntegrityError, # Integrity-check failure
SignatureVerificationError, # Signature rejected
KeyManagementError, # Key lifecycle errors
PQCUnavailableError, # Native C PQC library not loaded (also RuntimeError)
QuantumSignatureUnavailableError, # PQC signer requested but unavailable (subclass of PQCUnavailableError)
QuantumSignatureRequiredError, # Policy requires PQC; classical-only refused
AmaHSMUnavailableError, # HSM path requested without PyKCS11 (also RuntimeError)
SecurityWarning, # Non-fatal security warnings (UserWarning)
)Every error raised by the library derives — directly or transitively — from
the single root AmaCryptographyError, so except AmaCryptographyError
catches all of them (including the module-specific TimestampError,
SessionError, ChannelError, and SecureMemoryError defined in their own
modules). The classes that historically subclass RuntimeError
(PQCUnavailableError, CryptoModuleError, AmaHSMUnavailableError)
additionally inherit from RuntimeError, so existing except RuntimeError
sites keep working. SecurityWarning is intentionally not an
AmaCryptographyError — it is a UserWarning, not an error.
Exception (builtin)
└── AmaCryptographyError # catch-all root — every library error derives from this
├── PQCUnavailableError # also inherits RuntimeError
│ └── QuantumSignatureUnavailableError
├── QuantumSignatureRequiredError # note: NOT a PQCUnavailableError subclass
├── CryptoConfigError
├── KeyManagementError
├── SignatureVerificationError
├── IntegrityError
├── CryptoModuleError # FIPS 140-3 error-state module lock; also inherits RuntimeError
└── AmaHSMUnavailableError # PyKCS11 missing; also inherits RuntimeError
UserWarning (builtin)
└── SecurityWarning # warnings.warn() for non-fatal security issues
There is no
RFC3161Errorclass. The RFC 3161 timestamp module raisesTimestampError(request failure) andTimestampUnavailableError(optionalrfc3161ngdependency missing), both defined inama_cryptography/rfc3161_timestamp.py:53–62.
import ama_cryptography
# Always available
ama_cryptography.crypto_api
ama_cryptography.pqc_backends
ama_cryptography.key_management
ama_cryptography.secure_memory
ama_cryptography.hybrid_combiner
ama_cryptography.adaptive_posture
ama_cryptography.rfc3161_timestamp
ama_cryptography.exceptions
# Conditionally available (requires NumPy)
# Loaded lazily via PEP 562 __getattr__ to avoid hard dependency
ama_cryptography.equations # conditional import
ama_cryptography.double_helix_engine # conditional importSee C API Reference for the native C library, or Quick Start for hands-on examples.
AMA Cryptography — Post-Quantum Security System
Copyright 2025–2026 Steel Security Advisors LLC | Apache License 2.0
Contact: steel.sa.llc@gmail.com | Report a vulnerability | Contribute
Community-tested · Not externally audited · v4.0.0