Signatures (quantum_safe.signatures)

Digital signature operations. The high-level entry point is HybridSign.

class quantum_safe.signatures.hybrid.HybridSign(classical='Ed25519', pqc='ML-DSA-65', backend='auto', hedged=True, validate=True)[source]

Bases: object

Hybrid signature scheme: classical + PQC combined.

Default: Ed25519 + ML-DSA-65. Both sub-signatures must be valid for verification to pass.

Parameters:
  • classical (str) – Classical signature algorithm: “Ed25519” or “P-256”.

  • pqc (str) – PQC signature algorithm. Default: “ML-DSA-65”.

  • backend (str) – PQC backend: “auto”, “liboqs”, “rustcrypto”.

  • hedged (bool) – Hedged signing mode (default True). Prepends 32 random bytes before signing to prevent fault injection.

  • validate (bool) – Validate that the combination is approved (default True).

Example:

from quantum_safe import HybridSign

signer = HybridSign()
kp     = signer.generate_keypair()
sm     = signer.sign(b"document", kp.secret, context=b"myapp-v1")
signer.verify(sm, kp.public)
generate_keypair()[source]

Generate a hybrid signing key pair.

Return type:

KeyPair

Returns:

KeyPair where public/secret contain both classical and PQC sub-keys, packed with a length prefix.

sign(message, secret_key, context=b'')[source]

Sign a message with both classical and PQC sub-keys.

Parameters:
  • message (bytes) – Bytes to sign.

  • secret_key (SecretKey) – Hybrid secret key from generate_keypair().

  • context (bytes) – Domain-separation context (up to 255 bytes).

Return type:

SignedMessage

Returns:

SignedMessage. The .signature field contains a CBOR-encoded HybridSignature with both sub-signatures. Both must be valid for verify() to succeed.

sign_with_fingerprint(message, keypair, context=b'')[source]

Like sign(), but stores the signer’s public key fingerprint.

Parameters:
Return type:

SignedMessage

verify(signed_message, public_key)[source]

Verify a hybrid signed message. Both sub-signatures must be valid.

Parameters:
  • signed_message (SignedMessage) – A SignedMessage from sign().

  • public_key (PublicKey) – The signer’s hybrid public key.

Raises:
Return type:

None

class quantum_safe.signatures.core.Sign(algorithm='ML-DSA-65', backend='auto', hedged=True, strict=False)[source]

Bases: object

Single-algorithm PQC signature scheme.

Parameters:
  • algorithm (str) – PQC signature algorithm. Defaults to ML-DSA-65.

  • backend (str) – Backend name: “auto”, “liboqs”, “rustcrypto”.

  • hedged (bool) – If True (default), prepend 32 random bytes before signing to prevent fault injection attacks.

  • strict (bool) – If True, raise instead of warn for non-standard configs.

Example:

from quantum_safe.signatures import Sign

signer = Sign()                    # ML-DSA-65, hedged
kp     = signer.generate_keypair()
sm     = signer.sign(b"hello", kp.secret, context=b"myapp-v1")
signer.verify(sm, kp.public)       # raises VerificationError if invalid
generate_keypair()[source]

Generate a signing key pair.

Return type:

KeyPair

Returns:

KeyPair with .public and .secret. The secret key is needed to sign; the public key is needed to verify.

sign(message, secret_key, context=b'')[source]

Sign a message.

Parameters:
  • message (bytes) – Arbitrary bytes to sign. There is no length limit, but for very large messages (> a few MB) consider signing a hash of the message instead.

  • secret_key (SecretKey) – The signer’s secret key, matching this algorithm.

  • context (bytes) – Domain-separation context. Up to 255 bytes. Should include your app name and protocol version. Example: b”myapp-v2-user-attestation”

Return type:

SignedMessage

Returns:

SignedMessage containing the message, signature, algorithm, context, signer fingerprint, and timestamp. The SignedMessage is self-contained — pass it to verify() directly.

Raises:

UnsupportedAlgorithm – if the key’s algorithm doesn’t match.

sign_with_fingerprint(message, keypair, context=b'')[source]

Like sign(), but also stores the signer’s public key fingerprint.

Use this when the verifier won’t have the public key in advance and needs to look it up from a key store by fingerprint.

Parameters:
Return type:

SignedMessage

verify(signed_message, public_key)[source]

Verify a signed message.

Parameters:
  • signed_message (SignedMessage) – A SignedMessage returned by sign().

  • public_key (PublicKey) – The signer’s public key.

Return type:

None

Returns:

None on success.

Raises:
  • VerificationError – if the signature is invalid, the algorithm doesn’t match, or the context doesn’t match.

  • UnsupportedAlgorithm – if the SignedMessage’s algorithm differs from this Sign instance’s algorithm.

verify_bytes(message, signature_blob, public_key, context=b'')[source]

Verify a raw signature blob (for interop with external signers).

Use this when the signature was produced outside this library and you have raw bytes rather than a SignedMessage. The blob must be in our packed format (rand_prefix_len || rand_prefix || raw_sig).

For fully external signatures (produced by liboqs directly or another tool), use verify_raw() instead.

Parameters:
Return type:

None

verify_raw(message, raw_signature, public_key, context=b'')[source]

Verify a raw signature produced outside this library.

Use this for interoperability with other ML-DSA implementations. Note: hedged mode doesn’t apply here — you’re verifying a raw (non-hedged) signature.

Parameters:
Return type:

None

Algorithm registry

quantum_safe.signatures.algorithms.get_algorithm_spec(name)[source]
Parameters:

name (str)

Return type:

SignatureAlgorithmSpec

quantum_safe.signatures.algorithms.canonical_hybrid_name(classical, pqc)[source]
Parameters:
Return type:

str

quantum_safe.signatures.algorithms.parse_hybrid_name(name)[source]
Parameters:

name (str)

Return type:

tuple[str, str]

quantum_safe.signatures.algorithms.validate_hybrid_combination(classical, pqc)[source]
Parameters:
Return type:

None