Expand description
MAYO post-quantum signature scheme.
This crate implements the MAYO signature scheme, a post-quantum multivariate-based signature scheme submitted to the NIST PQC standardization process. MAYO is based on the “oil and vinegar” trapdoor and produces compact signatures with small public keys relative to other multivariate schemes.
§Supported Parameter Sets
| Type | Security Level | Signature Size | Public Key Size | Private Key Size |
|---|---|---|---|---|
Mayo1 | 1 | 454 B | 1,420 B | 24 B |
Mayo2 | 1 | 186 B | 4,912 B | 24 B |
Mayo3 | 3 | 681 B | 2,986 B | 32 B |
Mayo5 | 5 | 964 B | 5,554 B | 40 B |
All parameter sets implement the MayoParameter trait and can be used
interchangeably as the generic parameter on KeyPair, SigningKey,
VerifyingKey, and Signature.
§Quick Start
use pq_mayo::{KeyPair, Mayo1};
use signature::{Signer, Verifier};
let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
let msg = b"hello world";
let sig = keypair.signing_key().try_sign(msg).expect("sign");
keypair.verifying_key().verify(msg, &sig).expect("verify");use pq_mayo::{KeyPair, Mayo1, Mayo2, Mayo3, Mayo5};
use signature::{Signer, Verifier};
let mut rng = rand::rng();
// NIST security level 1 — smallest signatures
let kp1 = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
// NIST security level 2 — smallest signatures overall (186 bytes)
let kp2 = KeyPair::<Mayo2>::generate(&mut rng).expect("keygen");
let sig = kp2.signing_key().try_sign(b"message").expect("sign");
kp2.verifying_key().verify(b"message", &sig).expect("verify");
// NIST security level 3
let kp3 = KeyPair::<Mayo3>::generate(&mut rng).expect("keygen");
// NIST security level 5 — strongest security
let kp5 = KeyPair::<Mayo5>::generate(&mut rng).expect("keygen");§Key Serialization
Keys and signatures implement [AsRef<[u8]>] for exporting raw bytes
and [TryFrom<&[u8]>] for importing them. This allows easy
integration with any transport or storage layer.
use pq_mayo::{KeyPair, Mayo1, SigningKey, VerifyingKey, Signature};
use signature::{Signer, Verifier};
let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
// Export keys as raw bytes
let sk_bytes: &[u8] = keypair.signing_key().as_ref();
let vk_bytes: &[u8] = keypair.verifying_key().as_ref();
// Reconstruct keys from bytes
let sk = SigningKey::<Mayo1>::try_from(sk_bytes).expect("valid signing key");
let vk = VerifyingKey::<Mayo1>::try_from(vk_bytes).expect("valid verifying key");
// Sign with reconstructed key, verify with reconstructed key
let sig = sk.try_sign(b"hello").expect("sign");
vk.verify(b"hello", &sig).expect("verify");
// Signatures can also be round-tripped through bytes
let sig_bytes: &[u8] = sig.as_ref();
let sig2 = Signature::<Mayo1>::try_from(sig_bytes).expect("valid signature");
vk.verify(b"hello", &sig2).expect("verify restored sig");§Deriving a Verifying Key from a Signing Key
A VerifyingKey can be derived from a SigningKey without
needing the original KeyPair. This is useful when only the
secret key was stored or transmitted.
use pq_mayo::{KeyPair, Mayo1, VerifyingKey};
use signature::{Signer, Verifier};
let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
let vk = VerifyingKey::<Mayo1>::from(keypair.signing_key());
assert_eq!(&vk, keypair.verifying_key());
let sig = keypair.signing_key().try_sign(b"test").expect("sign");
vk.verify(b"test", &sig).expect("verify");§Deterministic Key Generation from a Seed
KeyPair::from_seed generates a keypair deterministically from a
fixed-length seed. The seed length depends on the parameter set
(24 bytes for Mayo1/Mayo2, 32 bytes for Mayo3, 40 bytes for Mayo5).
use pq_mayo::{KeyPair, Mayo1};
let seed = [42u8; 24]; // Mayo1 requires a 24-byte seed
let kp1 = KeyPair::<Mayo1>::from_seed(&seed).expect("from seed");
let kp2 = KeyPair::<Mayo1>::from_seed(&seed).expect("from seed");
// Same seed always produces the same keypair
assert_eq!(
kp1.verifying_key().as_ref(),
kp2.verifying_key().as_ref()
);§Signing with a Caller-Provided RNG
The SigningKey::sign_with_rng method allows passing a custom
CryptoRng for salt generation. This is useful
for reproducible testing or when a specific entropy source is required.
use pq_mayo::{KeyPair, Mayo1};
use signature::Verifier;
let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
let sig = keypair.signing_key().sign_with_rng(&mut rng, b"data").expect("sign");
keypair.verifying_key().verify(b"data", &sig).expect("verify");§Error Handling
All fallible operations return error::Result<T>, which
uses the Error enum. Key and signature deserialization validate lengths
and will return descriptive errors on mismatch.
use pq_mayo::{Error, SigningKey, VerifyingKey, Signature, Mayo1};
// Wrong-length byte slices are rejected
let result = SigningKey::<Mayo1>::try_from(&[0u8; 3][..]);
assert!(result.is_err());
let result = VerifyingKey::<Mayo1>::try_from(&[0u8; 3][..]);
assert!(result.is_err());
let result = Signature::<Mayo1>::try_from(&[0u8; 3][..]);
assert!(result.is_err());§Serde Support
Enable the serde feature for serialization with any serde-compatible
format. Keys and signatures are serialized as hex strings in
human-readable formats (JSON, TOML, YAML) and as raw bytes in binary
formats (bincode, postcard, CBOR).
[dependencies]
pq-mayo = { version = "0.1", features = ["serde"] }use pq_mayo::{KeyPair, Mayo1};
let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");
// JSON (human-readable, hex-encoded)
let json = serde_json::to_string(&keypair).expect("serialize");
let restored: KeyPair<Mayo1> = serde_json::from_str(&json).expect("deserialize");
assert_eq!(keypair, restored);
// Postcard (binary, compact)
let bytes = postcard::to_stdvec(&keypair).expect("serialize");
let restored: KeyPair<Mayo1> = postcard::from_bytes(&bytes).expect("deserialize");
assert_eq!(keypair, restored);§WebAssembly Support
This crate compiles to wasm32-unknown-unknown using pure Rust
implementations for all cryptographic primitives. Enable the js
feature to use the browser’s crypto.getRandomValues for randomness:
[dependencies]
pq-mayo = { version = "0.1", features = ["js"] }§Security Considerations
- All operations are implemented in constant time to resist timing side-channel attacks. There are no secret-dependent branches or memory accesses.
- Signing keys are zeroized on drop via the
zeroizecrate to prevent secret material from lingering in memory. - The
Debugimplementation forSigningKeyredacts the key bytes, printing**FILTERED**instead.
§PKCS#8 and SPKI Support
Enable the pkcs8 feature for DER-encoded key serialization compatible
with X.509 and PKCS#8 standards. This implements the EncodePrivateKey,
DecodePrivateKey, EncodePublicKey, and DecodePublicKey traits
from the pkcs8 ecosystem.
Since MAYO has not yet been standardized by NIST, experimental OIDs from the Open Quantum Safe project are used. These will be replaced with official NIST OIDs upon standardization.
[dependencies]
pq-mayo = { version = "0.1", features = ["pkcs8"] }Structs§
- KeyPair
- A MAYO keypair containing both signing and verifying keys.
- Mayo1
- MAYO parameter set MAYO_1.
- Mayo2
- MAYO parameter set MAYO_2.
- Mayo3
- MAYO parameter set MAYO_3.
- Mayo5
- MAYO parameter set MAYO_5.
- Signature
- A MAYO signature.
- Signing
Key - A MAYO signing key (compact secret key = seed).
- Verifying
Key - A MAYO verifying key (compact public key).
Enums§
- Error
- Errors that can occur during MAYO operations.
Constants§
- MAYO1_
OID - Experimental OID for MAYO-1 (
1.3.9999.8.1.3). - MAYO2_
OID - Experimental OID for MAYO-2 (
1.3.9999.8.2.3). - MAYO3_
OID - Experimental OID for MAYO-3 (
1.3.9999.8.3.3). - MAYO5_
OID - Experimental OID for MAYO-5 (
1.3.9999.8.5.3).
Traits§
- Mayo
Parameter - Trait defining all constants for a MAYO parameter set.