Skip to main content

qv_core/
falcon.rs

1//! Falcon-512 / Falcon-1024 post-quantum signatures.
2//!
3//! Wraps `pqcrypto-falcon` (PQClean C reference, linked via `cc`) behind a
4//! narrow QV-flavoured API. Falcon is attractive because its signatures are
5//! an order of magnitude smaller than ML-DSA-87:
6//!
7//! | Suite       | Sig (B, max) | VK (B) | SK (B) |
8//! |-------------|-------------:|-------:|-------:|
9//! | Falcon-512  |          666 |    897 |   1281 |
10//! | Falcon-1024 |         1280 |   1793 |   2305 |
11//!
12//! Note: Falcon detached signatures are **variable-length** — the values
13//! above are the upper bounds (`signature_bytes()`). Always transport the
14//! exact byte length; zero-pad is NOT sufficient because PQClean rejects
15//! trailing garbage.
16//!
17//! v4.1 status: core wrapper (this file) + FFI exposure. Suite adapter
18//! wiring into `issue_token` / `verify_token` arrives as part of the
19//! mutation-chain refactor in v4.2.
20
21use crate::error::{QVError, QVResult};
22use pqcrypto_traits::sign::{
23    DetachedSignature as _, PublicKey as _, SecretKey as _, VerificationError,
24};
25
26// ─────────────────────────────────────────────────────────────────────────
27// Falcon-512
28// ─────────────────────────────────────────────────────────────────────────
29pub mod falcon512 {
30    use super::*;
31    use pqcrypto_falcon::falcon512 as ffi;
32
33    /// Max signature bytes (variable-length; use `.len()` on the returned Vec).
34    pub const MAX_SIG_BYTES: usize = 666;
35    pub const VK_BYTES: usize = 897;
36    pub const SK_BYTES: usize = 1281;
37
38    #[derive(Clone)]
39    pub struct QVFalcon512SigningKey(ffi::SecretKey);
40
41    #[derive(Clone)]
42    pub struct QVFalcon512VerifyingKey(ffi::PublicKey);
43
44    impl QVFalcon512SigningKey {
45        pub fn to_bytes(&self) -> Vec<u8> {
46            self.0.as_bytes().to_vec()
47        }
48        pub fn from_bytes(bytes: &[u8]) -> QVResult<Self> {
49            ffi::SecretKey::from_bytes(bytes)
50                .map(Self)
51                .map_err(|_| QVError::SerializationError("Falcon-512 sk".into()))
52        }
53    }
54
55    impl QVFalcon512VerifyingKey {
56        pub fn to_bytes(&self) -> Vec<u8> {
57            self.0.as_bytes().to_vec()
58        }
59        pub fn from_bytes(bytes: &[u8]) -> QVResult<Self> {
60            ffi::PublicKey::from_bytes(bytes)
61                .map(Self)
62                .map_err(|_| QVError::SerializationError("Falcon-512 vk".into()))
63        }
64    }
65
66    /// Generate a fresh Falcon-512 keypair.
67    pub fn generate_keypair() -> QVResult<(QVFalcon512SigningKey, QVFalcon512VerifyingKey)> {
68        let (pk, sk) = ffi::keypair();
69        Ok((QVFalcon512SigningKey(sk), QVFalcon512VerifyingKey(pk)))
70    }
71
72    /// Produce a detached signature (variable length, up to MAX_SIG_BYTES).
73    pub fn sign(sk: &QVFalcon512SigningKey, msg: &[u8]) -> QVResult<Vec<u8>> {
74        let sig = ffi::detached_sign(msg, &sk.0);
75        Ok(sig.as_bytes().to_vec())
76    }
77
78    /// Verify a detached signature. Returns `Ok(())` on success.
79    pub fn verify(vk: &QVFalcon512VerifyingKey, msg: &[u8], sig: &[u8]) -> QVResult<()> {
80        let parsed = ffi::DetachedSignature::from_bytes(sig)
81            .map_err(|_| QVError::SignatureInvalid)?;
82        match ffi::verify_detached_signature(&parsed, msg, &vk.0) {
83            Ok(()) => Ok(()),
84            Err(VerificationError::InvalidSignature) => Err(QVError::SignatureInvalid),
85            Err(_) => Err(QVError::SignatureInvalid),
86        }
87    }
88}
89
90// ─────────────────────────────────────────────────────────────────────────
91// Falcon-1024
92// ─────────────────────────────────────────────────────────────────────────
93pub mod falcon1024 {
94    use super::*;
95    use pqcrypto_falcon::falcon1024 as ffi;
96
97    pub const MAX_SIG_BYTES: usize = 1280;
98    pub const VK_BYTES: usize = 1793;
99    pub const SK_BYTES: usize = 2305;
100
101    #[derive(Clone)]
102    pub struct QVFalcon1024SigningKey(ffi::SecretKey);
103
104    #[derive(Clone)]
105    pub struct QVFalcon1024VerifyingKey(ffi::PublicKey);
106
107    impl QVFalcon1024SigningKey {
108        pub fn to_bytes(&self) -> Vec<u8> {
109            self.0.as_bytes().to_vec()
110        }
111        pub fn from_bytes(bytes: &[u8]) -> QVResult<Self> {
112            ffi::SecretKey::from_bytes(bytes)
113                .map(Self)
114                .map_err(|_| QVError::SerializationError("Falcon-1024 sk".into()))
115        }
116    }
117
118    impl QVFalcon1024VerifyingKey {
119        pub fn to_bytes(&self) -> Vec<u8> {
120            self.0.as_bytes().to_vec()
121        }
122        pub fn from_bytes(bytes: &[u8]) -> QVResult<Self> {
123            ffi::PublicKey::from_bytes(bytes)
124                .map(Self)
125                .map_err(|_| QVError::SerializationError("Falcon-1024 vk".into()))
126        }
127    }
128
129    pub fn generate_keypair() -> QVResult<(QVFalcon1024SigningKey, QVFalcon1024VerifyingKey)> {
130        let (pk, sk) = ffi::keypair();
131        Ok((QVFalcon1024SigningKey(sk), QVFalcon1024VerifyingKey(pk)))
132    }
133
134    pub fn sign(sk: &QVFalcon1024SigningKey, msg: &[u8]) -> QVResult<Vec<u8>> {
135        let sig = ffi::detached_sign(msg, &sk.0);
136        Ok(sig.as_bytes().to_vec())
137    }
138
139    pub fn verify(vk: &QVFalcon1024VerifyingKey, msg: &[u8], sig: &[u8]) -> QVResult<()> {
140        let parsed = ffi::DetachedSignature::from_bytes(sig)
141            .map_err(|_| QVError::SignatureInvalid)?;
142        match ffi::verify_detached_signature(&parsed, msg, &vk.0) {
143            Ok(()) => Ok(()),
144            Err(VerificationError::InvalidSignature) => Err(QVError::SignatureInvalid),
145            Err(_) => Err(QVError::SignatureInvalid),
146        }
147    }
148}
149
150// ─────────────────────────────────────────────────────────────────────────
151// Tests
152// ─────────────────────────────────────────────────────────────────────────
153#[cfg(test)]
154mod tests {
155    use super::*;
156
157    #[test]
158    fn falcon512_roundtrip() {
159        let (sk, vk) = falcon512::generate_keypair().expect("keygen");
160        let msg = b"Sigvault v4.1 Falcon-512 roundtrip";
161        let sig = falcon512::sign(&sk, msg).expect("sign");
162        assert!(sig.len() <= falcon512::MAX_SIG_BYTES, "sig {} > max", sig.len());
163        assert!(sig.len() > 500, "sig suspiciously small: {}", sig.len());
164        falcon512::verify(&vk, msg, &sig).expect("verify");
165
166        // Tamper -> rejected.
167        let mut bad = sig.clone();
168        bad[10] ^= 0xFF;
169        assert!(falcon512::verify(&vk, msg, &bad).is_err());
170
171        // Key serialization roundtrip.
172        let vk_bytes = vk.to_bytes();
173        assert_eq!(vk_bytes.len(), falcon512::VK_BYTES);
174        let vk2 = falcon512::QVFalcon512VerifyingKey::from_bytes(&vk_bytes).expect("vk parse");
175        falcon512::verify(&vk2, msg, &sig).expect("verify after reparse");
176    }
177
178    #[test]
179    fn falcon1024_roundtrip() {
180        let (sk, vk) = falcon1024::generate_keypair().expect("keygen");
181        let msg = b"Sigvault v4.1 Falcon-1024 roundtrip";
182        let sig = falcon1024::sign(&sk, msg).expect("sign");
183        assert!(sig.len() <= falcon1024::MAX_SIG_BYTES, "sig {} > max", sig.len());
184        assert!(sig.len() > 1000, "sig suspiciously small: {}", sig.len());
185        falcon1024::verify(&vk, msg, &sig).expect("verify");
186
187        let sk_bytes = sk.to_bytes();
188        assert_eq!(sk_bytes.len(), falcon1024::SK_BYTES);
189    }
190
191    #[test]
192    fn size_advantage_vs_mldsa87() {
193        // Pure informational — proves why Falcon is worth the C dep.
194        let (sk, _vk) = falcon512::generate_keypair().expect("keygen");
195        let sig = falcon512::sign(&sk, b"size demo").expect("sign");
196        // ML-DSA-87 is 4627 bytes flat. Falcon-512 should be <= 666.
197        assert!(sig.len() < 4627 / 5, "expected at least 5x smaller than ML-DSA-87");
198    }
199}