Skip to main content

sqisign_verify/params/
level3.rs

1//!
2//! Prime: `p = 65 * 2^376 - 1` (383 bits).
3//! Field uses 7 limbs of 55-bit radix (unsaturated Montgomery form).
4
5use super::{Level3, SecurityLevel};
6use hybrid_array::sizes::{U196, U224, U316, U432, U48, U6, U7, U96, U97};
7
8/// The Level 3 prime `p = 65 * 2^376 - 1` as 48 little-endian bytes.
9///
10/// In hex: `0x40ff...ff` (383 bits). All bytes are `0xff` except the
11/// top byte which is `0x40`.
12pub const PRIME_LE_BYTES: [u8; 48] = {
13    let mut bytes = [0xffu8; 48];
14    bytes[47] = 0x40;
15    bytes
16};
17
18impl SecurityLevel for Level3 {
19    /// 7 limbs × 55-bit radix = 385 bits of storage for the 383-bit prime.
20    type FpLimbs = U7;
21    /// 6 limbs × 64 bits = 384-bit scalars for order arithmetic.
22    type MpLimbs = U6;
23    /// `p` fits in 48 bytes (383 bits).
24    type FpEncodedBytes = U48;
25    /// Two `Fp` elements = 96 bytes.
26    type Fp2EncodedBytes = U96;
27    /// Public key: 1-byte header + 2 × 48 bytes for the `Fp2` j-invariant.
28    type PkLen = U97;
29    /// Signature: compressed response isogeny encoding (224 bytes).
30    type SigLen = U224;
31    /// Expanded signature (316 bytes).
32    type ExpandedSigLen = U316;
33    /// Compressed signature (196 bytes).
34    type CompressedSigLen = U196;
35    /// Secret key: ideal norm + generator coords + basis-change matrix (432 bytes).
36    type SkLen = U432;
37
38    fn prime_le_bytes() -> &'static [u8] {
39        &PRIME_LE_BYTES
40    }
41
42    /// 192-bit post-quantum security.
43    const LAMBDA: u32 = 192;
44
45    /// `p + 1 = 65 × 2^376`, so the full `2^376`-torsion is available.
46    const F_CHR: u32 = 376;
47    /// Response isogeny has degree `2^192`.
48    const E_RSP: u32 = 192;
49    /// Challenge scalar is 192 bits (matching `LAMBDA`).
50    const E_CHL: u32 = 192;
51    /// Up to 256 SHAKE256 squeeze attempts to find a valid challenge.
52    const HASH_ITERATIONS: u32 = 256;
53    /// 6 limbs × 64 = 384-bit scalar width.
54    const NWORDS_ORDER: usize = 6;
55    /// `v_2(p + 1) = 376`.
56    const TORSION_EVEN_POWER: u32 = 376;
57    /// `(p + 1) / 2^376 = 65`, which is 7 bits.
58    const P_COFACTOR_FOR_2F_BITLENGTH: usize = 7;
59    /// Response isogeny length = 192 bits (same as `E_RSP`).
60    const SQISIGN_RESPONSE_LENGTH: u32 = 192;
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn level3_prime_is_correct() {
69        let bytes = Level3::prime_le_bytes();
70        assert_eq!(bytes.len(), 48);
71        for &b in &bytes[..47] {
72            assert_eq!(b, 0xFF, "low 47 bytes of p must all be 0xFF");
73        }
74        assert_eq!(bytes[47], 0x40, "top byte of p must be 0x40");
75    }
76
77    #[test]
78    fn level3_prime_is_3_mod_4() {
79        let bytes = Level3::prime_le_bytes();
80        assert_eq!(bytes[0] & 0b11, 3, "p mod 4 must be 3");
81    }
82
83    const _: () = assert!(Level3::F_CHR > Level3::LAMBDA);
84    const _: () = assert!(Level3::E_RSP > 0);
85
86    #[test]
87    fn level3_protocol_exponents_in_range() {
88        assert_eq!(Level3::LAMBDA, 192);
89        assert_eq!(Level3::F_CHR, 376);
90        assert_eq!(Level3::E_RSP, 192);
91    }
92}