Skip to main content

fn_dsa_comm/
lib.rs

1#![no_std]
2// Suppress clippy warnings in reference implementation code
3#![allow(
4    clippy::too_many_arguments,
5    clippy::needless_range_loop,
6    clippy::uninlined_format_args,
7    clippy::must_use_candidate,
8    clippy::cast_precision_loss,
9    clippy::cast_lossless,
10    clippy::manual_clamp,
11    clippy::unused_self,
12    clippy::unnecessary_wraps,
13    clippy::let_and_return,
14    clippy::identity_op,
15    clippy::erasing_op,
16    clippy::struct_excessive_bools,
17    clippy::doc_markdown,
18    clippy::needless_borrows_for_generic_args,
19    clippy::assertions_on_constants
20)]
21
22//! This crate contains utility functions which are used by FN-DSA for
23//! key pair generation, signing, and verifying. It is not meant to
24//! be used directly.
25
26/// Encoding/decoding primitives.
27pub mod codec;
28
29/// Computations with polynomials modulo X^n+1 and modulo q = 12289.
30pub mod mq;
31
32/// SHAKE implementation.
33pub mod shake;
34
35/// Specialized versions of `mq` which use AVX2 opcodes (on x86 CPUs).
36#[cfg(all(
37    not(feature = "no_avx2"),
38    any(target_arch = "x86_64", target_arch = "x86")
39))]
40pub mod mq_avx2;
41
42// Re-export RNG traits to get a smooth dependency management.
43pub use rand_core::{
44    CryptoRng,
45    Infallible,
46    Rng,
47    TryCryptoRng,
48    TryRng,
49};
50
51/// Error type for RNG operations.
52///
53/// Note: The upstream reference uses `rand_core::Error` from rand_core 0.6.4,
54/// but this implementation uses rand_core 0.9.3 which does not export `Error`.
55/// We use `core::fmt::Error` instead, which is compatible with `no_std` and
56/// provides the same functionality for our use case.
57pub type RngError = core::fmt::Error;
58
59/// Symbolic constant for FN-DSA with degree 512 (`logn = 9`).
60pub const FN_DSA_LOGN_512: u32 = 9;
61
62/// Symbolic constant for FN-DSA with degree 1024 (`logn = 10`).
63pub const FN_DSA_LOGN_1024: u32 = 10;
64
65/// Get the size (in bytes) of a signing key for the provided degree
66/// (degree is `n = 2^logn`, with `2 <= logn <= 10`).
67pub const fn sign_key_size(logn: u32) -> usize {
68    let n = 1usize << logn;
69    let nbits_fg = match logn {
70        2..=5 => 8,
71        6..=7 => 7,
72        8..=9 => 6,
73        _ => 5,
74    };
75    1 + (nbits_fg << (logn - 2)) + n
76}
77
78/// Get the size (in bytes) of a verifying key for the provided degree
79/// (degree is `n = 2^logn`, with `2 <= logn <= 10`).
80pub const fn vrfy_key_size(logn: u32) -> usize {
81    1 + (7 << (logn - 2))
82}
83
84/// Get the size (in bytes) of a signature for the provided degree
85/// (degree is `n = 2^logn`, with `2 <= logn <= 10`).
86pub const fn signature_size(logn: u32) -> usize {
87    // logn   n      size
88    //   2      4      47
89    //   3      8      52
90    //   4     16      63
91    //   5     32      82
92    //   6     64     122
93    //   7    128     200
94    //   8    256     356
95    //   9    512     666
96    //  10   1024    1280
97    44 + 3 * (256 >> (10 - logn)) +
98        2 * (128 >> (10 - logn)) +
99        3 * (64 >> (10 - logn)) +
100        2 * (16 >> (10 - logn)) -
101        2 * (2 >> (10 - logn)) -
102        8 * (1 >> (10 - logn))
103}
104
105/// The message for which a signature is to be generated or verified is
106/// pre-hashed by the caller and provided as a hash value along with
107/// an identifier of the used hash function. The identifier is normally
108/// an encoded ASN.1 OID. A special identifier is used for "raw" messages
109/// (i.e. not pre-hashed at all); it uses a single byte of value 0x00.
110pub struct HashIdentifier<'a>(pub &'a [u8]);
111
112/// Hash function identifier: none.
113///
114/// This is the identifier used internally to specify that signature
115/// generation and verification are performed over a raw message, without
116/// pre-hashing.
117pub const HASH_ID_RAW: HashIdentifier = HashIdentifier(&[0x00]);
118
119// Original Falcon support has been removed for security reasons.
120// The original Falcon design bypassed domain separation, creating a
121// critical security vulnerability that could enable cross-protocol attacks.
122// All operations now use proper FN-DSA domain separation as specified
123// in the NIST standard.
124
125/// Hash function identifier: SHA-256
126pub const HASH_ID_SHA256: HashIdentifier = HashIdentifier(&[
127    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
128]);
129
130/// Hash function identifier: SHA-384
131pub const HASH_ID_SHA384: HashIdentifier = HashIdentifier(&[
132    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
133]);
134
135/// Hash function identifier: SHA-512
136pub const HASH_ID_SHA512: HashIdentifier = HashIdentifier(&[
137    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
138]);
139
140/// Hash function identifier: SHA-512-256
141pub const HASH_ID_SHA512_256: HashIdentifier = HashIdentifier(&[
142    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x06,
143]);
144
145/// Hash function identifier: SHA3-256
146pub const HASH_ID_SHA3_256: HashIdentifier = HashIdentifier(&[
147    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08,
148]);
149
150/// Hash function identifier: SHA3-384
151pub const HASH_ID_SHA3_384: HashIdentifier = HashIdentifier(&[
152    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09,
153]);
154
155/// Hash function identifier: SHA3-512
156pub const HASH_ID_SHA3_512: HashIdentifier = HashIdentifier(&[
157    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0A,
158]);
159
160/// Hash function identifier: SHAKE128
161pub const HASH_ID_SHAKE128: HashIdentifier = HashIdentifier(&[
162    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0B,
163]);
164
165/// Hash function identifier: SHAKE256
166pub const HASH_ID_SHAKE256: HashIdentifier = HashIdentifier(&[
167    0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0C,
168]);
169
170/// When a message is signed or verified, it is accompanied with a domain
171/// separation context, which is an arbitrary sequence of bytes of length
172/// at most 255. Such a context is wrapped in a `DomainContext` structure.
173pub struct DomainContext<'a>(pub &'a [u8]);
174
175/// Empty domain separation context.
176pub const DOMAIN_NONE: DomainContext = DomainContext(b"");
177
178/// Hash a message into a polynomial modulo q = 12289.
179///
180/// Parameters are:
181///
182///  - `nonce`:            40-byte random nonce
183///  - `hashed_vrfy_key`:  SHAKE256 hash of public (verifying) key (64 bytes)
184///  - `ctx`:              domain separation context
185///  - `id`:               identifier for pre-hash function
186///  - `hv`:               message (pre-hashed)
187///  - `c`:                output polynomial
188///
189/// If `id` is `HASH_ID_RAW`, then no-prehashing is applied and the message
190/// itself should be provided as `hv`. Otherwise, the caller is responsible
191/// for applying the pre-hashing, and `hv` shall be the hashed message.
192pub fn hash_to_point(
193    nonce: &[u8],
194    hashed_vrfy_key: &[u8],
195    ctx: &DomainContext,
196    id: &HashIdentifier,
197    hv: &[u8],
198    c: &mut [u16],
199) {
200    // Original Falcon support has been removed for security reasons.
201    // The implementation now properly enforces domain separation as required
202    // by the FN-DSA specification, preventing cross-protocol attacks.
203
204    // Input order:
205    //   With pre-hashing:
206    //     nonce || hashed_vrfy_key || 0x01 || len(ctx) || ctx || id || hv
207    //   Without pre-hashing:
208    //     nonce || hashed_vrfy_key || 0x00 || len(ctx) || ctx || message
209    // 'len(ctx)' is the length of the context over one byte (0 to 255).
210
211    assert!(nonce.len() == 40);
212    assert!(hashed_vrfy_key.len() == 64);
213    assert!(ctx.0.len() <= 255);
214    let raw_message = id.0.len() == 1 && id.0[0] == 0x00;
215    let mut sh = shake::SHAKE256::new();
216    sh.inject(nonce);
217
218    // Always use proper FN-DSA domain separation for security
219    sh.inject(hashed_vrfy_key);
220    sh.inject(&[if raw_message { 0u8 } else { 1u8 }]);
221    sh.inject(&[ctx.0.len() as u8]);
222    sh.inject(ctx.0);
223    if !raw_message {
224        sh.inject(id.0);
225    }
226    sh.inject(hv);
227    sh.flip();
228    let mut i = 0;
229    while i < c.len() {
230        let mut v = [0u8; 2];
231        sh.extract(&mut v);
232        let mut w = ((v[0] as u16) << 8) | (v[1] as u16);
233        if w < 61445 {
234            while w >= 12289 {
235                w -= 12289;
236            }
237            c[i] = w;
238            i += 1;
239        }
240    }
241}
242
243/// Trait for a deterministic pseudorandom generator.
244///
245/// The trait `PRNG` characterizes a stateful object that produces
246/// pseudorandom bytes (and larger values) in a cryptographically secure
247/// way; the object is created with a source seed, and the output is
248/// indistinguishable from uniform randomness up to exhaustive enumeration
249/// of the possible values of the seed.
250///
251/// `PRNG` instances must also implement `Copy` and `Clone` so that they
252/// may be embedded in clonable structures. This implies that copying a
253/// `PRNG` instance is supposed to clone its internal state, and the copy
254/// will output the same values as the original.
255pub trait PRNG: Copy + Clone {
256    /// Create a new instance over the provided seed.
257    fn new(seed: &[u8]) -> Self;
258    /// Get the next byte from the PRNG.
259    fn next_u8(&mut self) -> u8;
260    /// Get the 16-bit value from the PRNG.
261    fn next_u16(&mut self) -> u16;
262    /// Get the 64-bit value from the PRNG.
263    fn next_u64(&mut self) -> u64;
264}
265
266#[cfg(all(
267    not(feature = "no_avx2"),
268    any(target_arch = "x86_64", target_arch = "x86")
269))]
270cpufeatures::new!(cpuid_avx2, "avx2");
271
272/// Runtime check for AVX2 support (x86 and x86_64 only).
273///
274/// Uses `cpufeatures` for `no_std`-compatible runtime CPUID detection on x86/x86_64.
275/// Returns `false` on all other targets.
276#[cfg(all(
277    not(feature = "no_avx2"),
278    any(target_arch = "x86_64", target_arch = "x86")
279))]
280pub fn has_avx2() -> bool {
281    cpuid_avx2::get()
282}
283
284/// Fallback for non-x86 targets or when `no_avx2` feature is enabled.
285#[cfg(not(all(
286    not(feature = "no_avx2"),
287    any(target_arch = "x86_64", target_arch = "x86")
288)))]
289pub fn has_avx2() -> bool {
290    false
291}