Skip to main content

sqisign_verify/params/
level5.rs

1//!
2//! Prime: `p = 27 * 2^500 - 1` (505 bits).
3//! Field uses 9 limbs of 57-bit radix (unsaturated Montgomery form).
4
5use super::{Level5, SecurityLevel};
6use hybrid_array::sizes::{U128, U129, U257, U292, U420, U576, U64, U8, U9};
7
8/// The Level 5 prime `p = 27 * 2^500 - 1` as 64 little-endian bytes.
9///
10/// In hex: `0x01af...ff` (505 bits). The bottom 62 bytes are `0xff`,
11/// byte 62 is `0xAF`, and byte 63 is `0x01`.
12///
13/// Derivation: `p + 1 = 27 * 2^500 = 0x1B * 2^500`. Bit 500 lies at
14/// bit-position 4 within byte 62 (since 500 = 62 * 8 + 4). Subtracting
15/// 1 sets all 500 low bits to 1 and decrements the upper portion from
16/// `0x1B` to `0x1A`, which straddles bytes 62 and 63 as `0xAF` and
17/// `0x01` respectively.
18pub const PRIME_LE_BYTES: [u8; 64] = {
19    let mut bytes = [0xffu8; 64];
20    bytes[62] = 0xAF;
21    bytes[63] = 0x01;
22    bytes
23};
24
25impl SecurityLevel for Level5 {
26    /// 9 limbs × 57-bit radix = 513 bits of storage for the 505-bit prime.
27    type FpLimbs = U9;
28    /// 8 limbs × 64 bits = 512-bit scalars for order arithmetic.
29    type MpLimbs = U8;
30    /// `p` fits in 64 bytes (505 bits).
31    type FpEncodedBytes = U64;
32    /// Two `Fp` elements = 128 bytes.
33    type Fp2EncodedBytes = U128;
34    /// Public key: 1-byte header + 2 × 64 bytes for the `Fp2` j-invariant.
35    type PkLen = U129;
36    /// Signature: compressed response isogeny encoding (292 bytes).
37    type SigLen = U292;
38    /// Expanded signature (420 bytes).
39    type ExpandedSigLen = U420;
40    /// Compressed signature (257 bytes).
41    type CompressedSigLen = U257;
42    /// Secret key: ideal norm + generator coords + basis-change matrix.
43    /// Actual content is 572 bytes; U576 is the next upstream hybrid-array
44    /// size. The 4 trailing bytes are zero-padded.
45    type SkLen = U576;
46
47    fn prime_le_bytes() -> &'static [u8] {
48        &PRIME_LE_BYTES
49    }
50
51    /// 256-bit post-quantum security.
52    const LAMBDA: u32 = 256;
53
54    /// `p + 1 = 27 * 2^500`, so the full `2^500`-torsion is available.
55    const F_CHR: u32 = 500;
56    /// Response isogeny has degree `2^253`.
57    const E_RSP: u32 = 253;
58    /// Challenge scalar is 256 bits (matching `LAMBDA`).
59    const E_CHL: u32 = 256;
60    /// Up to 512 SHAKE256 squeeze attempts to find a valid challenge.
61    const HASH_ITERATIONS: u32 = 512;
62    /// 8 limbs × 64 = 512-bit scalar width.
63    const NWORDS_ORDER: usize = 8;
64    /// `v_2(p + 1) = 500`.
65    const TORSION_EVEN_POWER: u32 = 500;
66    /// `(p + 1) / 2^500 = 27 = 0b11011`, which is 5 bits.
67    const P_COFACTOR_FOR_2F_BITLENGTH: usize = 5;
68    /// Response isogeny length = 253 bits (same as `E_RSP`).
69    const SQISIGN_RESPONSE_LENGTH: u32 = 253;
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn level5_prime_is_correct() {
78        let bytes = Level5::prime_le_bytes();
79        assert_eq!(bytes.len(), 64);
80        for &b in &bytes[..62] {
81            assert_eq!(b, 0xFF, "low 62 bytes of p must all be 0xFF");
82        }
83        assert_eq!(bytes[62], 0xAF, "byte 62 of p must be 0xAF");
84        assert_eq!(bytes[63], 0x01, "top byte of p must be 0x01");
85    }
86
87    #[test]
88    fn level5_prime_is_3_mod_4() {
89        let bytes = Level5::prime_le_bytes();
90        assert_eq!(bytes[0] & 0b11, 3, "p mod 4 must be 3");
91    }
92
93    /// Verify the bit-length of p. The most significant byte is 0x01
94    /// (bit 504 set), giving 505 bits total. This matches `27 * 2^500 - 1`.
95    #[test]
96    fn level5_prime_bitlength() {
97        let bytes = Level5::prime_le_bytes();
98        // Byte 63 = 0x01 means bit 504 is set, bits 505-511 are zero.
99        assert_eq!(bytes[63], 0x01);
100        // Byte 62 = 0xAF = 0b1010_1111, so bit 503 (bit 7 of byte 62) is set.
101        // The topmost set bit is bit 504 (in byte 63), giving 505-bit prime.
102        assert_eq!(bytes[62] & 0x80, 0x80, "bit 503 must be set");
103    }
104
105    const _: () = assert!(Level5::F_CHR > Level5::LAMBDA);
106    const _: () = assert!(Level5::E_RSP > 0);
107
108    #[test]
109    fn level5_protocol_exponents_in_range() {
110        assert_eq!(Level5::LAMBDA, 256);
111        assert_eq!(Level5::F_CHR, 500);
112        assert_eq!(Level5::E_RSP, 253);
113    }
114}