Skip to main content

origin_crypto_sdk/signing/
postquantum.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Post-quantum signing primitives — Falcon, SLH-DSA, ML-DSA.
4//!
5//! These are NIST-standardized (or NIST-selected) post-quantum signature
6//! schemes, exposed individually. For most applications, prefer
7//! [`super::hybrid::HybridSigningKeyBundle`] which pairs a classical
8//! primitive with a post-quantum one — that gives defense in depth
9//! against either primitive being broken.
10//!
11//! # Algorithms
12//!
13//! - **Falcon-1024** — lattice-based, ~NIST level 5 (~256-bit security).
14//!   Compact signatures (~1280 bytes), fast verification. The post-quantum
15//!   half of the canonical hybrid signing scheme.
16//! - **Falcon-512** — lattice-based, ~NIST level 1 (~128-bit security).
17//!   Smaller signatures (~666 bytes) but weaker — for compatibility only.
18//! - **SLH-DSA** (FIPS 205) — hash-based. Requires no number-theoretic
19//!   assumptions; security reduces to SHAKE128 collision resistance.
20//!   Very large signatures (~7-50 KB depending on parameter set).
21//! - **ML-DSA** (FIPS 204) — lattice-based. Standardized as the "primary"
22//!   post-quantum signature algorithm.
23//!
24//! # Example
25//!
26//! ```rust
27//! use origin_crypto_sdk::signing::postquantum::Falcon1024Signer;
28//!
29//! let signer = Falcon1024Signer::from_seed(&[0x42u8; 32])
30//!     .expect("valid seed");
31//! let sig = signer.sign(b"hello").expect("Falcon sign");
32//! assert!(signer.verify(b"hello", &sig));
33//! ```
34
35// ──────────────────────────────────────────────────────────────────────
36// Falcon-1024
37// ──────────────────────────────────────────────────────────────────────
38
39impl std::fmt::Debug for Falcon1024Signer {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        // Redact `key` (FalconPrivateKey). Surface `public_key` for identity
42        // debugging — public keys are not secret.
43        f.debug_struct("Falcon1024Signer")
44            .field("public_key", &"<present, 1793 bytes>")
45            .finish_non_exhaustive()
46    }
47}
48
49/// Falcon-1024 signer (lattice-based, ~NIST level 5).
50pub struct Falcon1024Signer {
51    key: crate::pqc::falcon1024::FalconPrivateKey,
52    public_key: crate::pqc::falcon1024::FalconPublicKey,
53}
54
55impl Falcon1024Signer {
56    /// Create a signer from a 32-byte seed.
57    ///
58    /// Note: Falcon-1024 keygen is expensive (~100ms on first call;
59    /// ~10ms on cached re-derivation).
60    pub fn from_seed(seed: &[u8; 32]) -> Result<Self, crate::error::CryptoError> {
61        let (pk, sk) = crate::pqc::falcon1024::generate_keypair_from_seed(seed).map_err(|e| {
62            crate::error::CryptoError::InvalidKey(format!("Falcon-1024 keygen: {e}"))
63        })?;
64        Ok(Self {
65            key: sk,
66            public_key: pk,
67        })
68    }
69
70    /// Create from an existing private/public key pair.
71    pub fn from_keys(
72        sk: crate::pqc::falcon1024::FalconPrivateKey,
73        pk: crate::pqc::falcon1024::FalconPublicKey,
74    ) -> Self {
75        Self {
76            key: sk,
77            public_key: pk,
78        }
79    }
80
81    /// Sign a message. Returns a ~1280-byte Falcon-1024 signature.
82    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, crate::error::CryptoError> {
83        crate::pqc::falcon1024::sign(message, &self.key)
84            .map(|s| s.as_bytes().to_vec())
85            .map_err(|e| crate::error::CryptoError::InvalidKey(format!("Falcon-1024 sign: {e}")))
86    }
87
88    /// Verify a signature against this signer's public key.
89    pub fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
90        let Ok(sig) = crate::pqc::falcon1024::FalconSignature::from_bytes(signature) else {
91            return false;
92        };
93        crate::pqc::falcon1024::verify(message, &sig, &self.public_key).is_ok()
94    }
95
96    /// Verify a signature against an arbitrary Falcon-1024 public key.
97    pub fn verify_with_pubkey(pubkey: &[u8], message: &[u8], signature: &[u8]) -> bool {
98        let Ok(pk) = crate::pqc::falcon1024::FalconPublicKey::from_bytes(pubkey) else {
99            return false;
100        };
101        let Ok(sig) = crate::pqc::falcon1024::FalconSignature::from_bytes(signature) else {
102            return false;
103        };
104        crate::pqc::falcon1024::verify(message, &sig, &pk).is_ok()
105    }
106
107    /// Get the 1793-byte Falcon-1024 public key.
108    pub fn public_key_bytes(&self) -> Vec<u8> {
109        self.public_key.as_bytes().to_vec()
110    }
111}
112
113// ──────────────────────────────────────────────────────────────────────
114// Falcon-512 (smaller, weaker — for compatibility only)
115// ──────────────────────────────────────────────────────────────────────
116
117/// Falcon-512 signer (lattice-based, ~NIST level 1).
118///
119/// Smaller signatures (~666 bytes) but ~128-bit security. For new
120/// applications, prefer [`Falcon1024Signer`] which has ~256-bit security.
121impl std::fmt::Debug for Falcon512Signer {
122    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123        // Redact `sk` (Falcon512PrivateKey). Surface `pk` for identity
124        // debugging — public keys are not secret.
125        f.debug_struct("Falcon512Signer")
126            .field("pk", &"<present, public bytes>")
127            .finish_non_exhaustive()
128    }
129}
130
131pub struct Falcon512Signer {
132    sk: crate::pqc::falcon512::Falcon512PrivateKey,
133    pk: crate::pqc::falcon512::Falcon512PublicKey,
134}
135
136impl Falcon512Signer {
137    /// Create a signer from a 32-byte seed.
138    pub fn from_seed(seed: &[u8; 32]) -> Self {
139        let (sk, pk) = crate::pqc::falcon512::generate_keypair(*seed);
140        Self { sk, pk }
141    }
142
143    /// Sign a message. Returns a ~666-byte Falcon-512 signature.
144    pub fn sign(&self, message: &[u8]) -> Result<Vec<u8>, crate::error::CryptoError> {
145        crate::pqc::falcon512::sign(message, &self.sk)
146            .map(|s| s.0)
147            .map_err(|e| crate::error::CryptoError::InvalidKey(format!("Falcon-512 sign: {e}")))
148    }
149
150    /// Verify a signature against this signer's public key.
151    pub fn verify(&self, message: &[u8], signature: &[u8]) -> bool {
152        let sig = crate::pqc::falcon512::Falcon512Signature(signature.to_vec());
153        crate::pqc::falcon512::verify(message, &sig, &self.pk)
154    }
155}
156
157#[cfg(test)]
158mod tests {
159    use super::*;
160
161    #[test]
162    fn falcon1024_roundtrip() {
163        let s = Falcon1024Signer::from_seed(&[1u8; 32]).unwrap();
164        let msg = b"hello";
165        let sig = s.sign(msg).unwrap();
166        assert!(s.verify(msg, &sig));
167    }
168
169    #[test]
170    fn falcon1024_rejects_tampered() {
171        let s = Falcon1024Signer::from_seed(&[2u8; 32]).unwrap();
172        let sig = s.sign(b"original").unwrap();
173        assert!(!s.verify(b"tampered", &sig));
174    }
175
176    #[test]
177    fn falcon512_roundtrip() {
178        let s = Falcon512Signer::from_seed(&[3u8; 32]);
179        let msg = b"hello";
180        let sig = s.sign(msg).unwrap();
181        assert!(s.verify(msg, &sig));
182    }
183}