pq-mayo 0.2.3

MAYO post-quantum signature scheme
Documentation

pq-mayo

Crate Docs Apache2/MIT licensed Downloads build MSRV

A Rust implementation of the MAYO post-quantum signature scheme, submitted to the NIST PQC standardization process.

Supported Parameter Sets

Parameter Set Security Level Signature Size Public Key Size Private Key Size
Mayo1 1 454 B 1420 B 24 B
Mayo2 1 186 B 4912 B 24 B
Mayo3 3 681 B 2986 B 32 B
Mayo5 5 964 B 5554 B 40 B

Performance

Benchmarked on Apple M1 (aarch64) with -C target-cpu=native:

Operation Time
Mayo1/keygen 269.19 µs
Mayo1/sign 698.57 µs
Mayo1/verify 192.85 µs
Mayo2/keygen 331.42 µs
Mayo2/sign 477.97 µs
Mayo2/verify 87.92 µs
Mayo3/keygen 765.33 µs
Mayo3/sign 1.899 ms
Mayo3/verify 434.75 µs
Mayo5/keygen 1.713 ms
Mayo5/sign 4.406 ms
Mayo5/verify 665.64 µs

Run your own benchmarks:

cargo bench

Usage

Basic Sign and Verify

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");

Using Different Parameter Sets

use pq_mayo::{KeyPair, Mayo2, Mayo3, Mayo5};
use signature::{Signer, Verifier};

let mut rng = rand::rng();

// NIST security level 2
let kp2 = KeyPair::<Mayo2>::generate(&mut rng).expect("keygen");
let sig2 = kp2.signing_key().try_sign(b"message").expect("sign");
kp2.verifying_key().verify(b"message", &sig2).expect("verify");

// NIST security level 3
let kp3 = KeyPair::<Mayo3>::generate(&mut rng).expect("keygen");

// NIST security level 5
let kp5 = KeyPair::<Mayo5>::generate(&mut rng).expect("keygen");

Key Serialization

Keys and signatures implement AsRef<[u8]> and TryFrom<&[u8]> for raw byte serialization:

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 serialized/deserialized
let sig_bytes: &[u8] = sig.as_ref();
let sig2 = Signature::<Mayo1>::try_from(sig_bytes).expect("valid signature");

Deriving a Verifying Key from a Signing Key

use pq_mayo::{KeyPair, Mayo1, VerifyingKey};
use signature::{Signer, Verifier};

let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");

// Derive the verifying (public) key from the signing (secret) key
let vk = VerifyingKey::<Mayo1>::from(keypair.signing_key());
let sig = keypair.signing_key().try_sign(b"test").expect("sign");
vk.verify(b"test", &sig).expect("verify");

Serde Support

Enable the serde feature for JSON/binary serialization:

[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");

// Serialize to JSON
let json = serde_json::to_string(&keypair).expect("serialize");
let restored: KeyPair<Mayo1> = serde_json::from_str(&json).expect("deserialize");

PKCS#8 and SPKI Support

Enable the pkcs8 feature for DER-encoded key serialization compatible with X.509 and PKCS#8 standards:

[dependencies]
pq-mayo = { version = "0.1", features = ["pkcs8"] }

This implements the standard RustCrypto key encoding traits:

  • EncodePrivateKey / DecodePrivateKey for KeyPair (PKCS#8 DER)
  • DecodePrivateKey for SigningKey (PKCS#8 DER)
  • EncodePublicKey / DecodePublicKey for VerifyingKey (SPKI DER)
use pq_mayo::{KeyPair, Mayo1, SigningKey, VerifyingKey};
use pkcs8::DecodePrivateKey;
use pkcs8::EncodePrivateKey;
use pkcs8::spki::{DecodePublicKey, EncodePublicKey};
use signature::{Signer, Verifier};

let mut rng = rand::rng();
let keypair = KeyPair::<Mayo1>::generate(&mut rng).expect("keygen");

// Encode private key to PKCS#8 DER
let sk_der = keypair.to_pkcs8_der().expect("encode");

// Decode private key from PKCS#8 DER
let restored = KeyPair::<Mayo1>::from_pkcs8_der(sk_der.as_bytes()).expect("decode");

// Encode public key to SPKI DER
let vk_der = keypair.verifying_key().to_public_key_der().expect("encode");

// Decode public key from SPKI DER
let restored_vk = VerifyingKey::<Mayo1>::from_public_key_der(vk_der.as_bytes()).expect("decode");

// Sign and verify with round-tripped keys
let sig = restored.signing_key().try_sign(b"hello").expect("sign");
restored_vk.verify(b"hello", &sig).expect("verify");

Since MAYO has not yet been standardized by NIST, experimental OIDs from the Open Quantum Safe project are used (1.3.9999.8.{1,2,3,5}.3). These will be replaced with official NIST OIDs upon standardization.

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"] }

Serialization

This crate has been tested against the following serde compatible formats:

  • serde_bare
  • bincode
  • postcard
  • serde_cbor
  • serde_json
  • serde_yaml
  • toml

Mitigations

There is a known fault injection attack against MAYO described in MAYO Key Recovery by Fixing Vinegar Seeds. This code contains mitigations to these attacks.

License

Licensed under either of Apache License, Version 2.0 or MIT License at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.