miden-crypto/src/dsa/rpo_falcon512/polynomial.rs
src/dsa/rpo_falcon512/polynomial.rsRaw
1use alloc::vec::Vec;
2use core::ops::{Add, Mul, Sub};
3
4use super::{FalconError, Felt, LOG_N, MODULUS, MODULUS_MINUS_1_OVER_TWO, N, PK_LEN};
5
6// FALCON POLYNOMIAL
7// ================================================================================================
8
9/// A polynomial over Z_p\[x\]/(phi) where phi := x^512 + 1
10#[derive(Debug, Copy, Clone, PartialEq)]
11pub struct Polynomial([u16; N]);
12
13impl Polynomial {
14 // CONSTRUCTORS
15 // --------------------------------------------------------------------------------------------
16
17 /// Constructs a new polynomial from a list of coefficients.
18 ///
19 /// # Safety
20 /// This constructor validates that the coefficients are in the valid range only in debug mode.
21 pub unsafe fn new(data: [u16; N]) -> Self {
22 for value in data {
23 debug_assert!(value < MODULUS);
24 }
25
26 Self(data)
27 }
28
29 /// Decodes raw bytes representing a public key into a polynomial in Z_p\[x\]/(phi).
30 ///
31 /// # Errors
32 /// Returns an error if:
33 /// - The provided input is not exactly 897 bytes long.
34 /// - The first byte of the input is not equal to log2(512) i.e., 9.
35 /// - Any of the coefficients encoded in the provided input is greater than or equal to the
36 /// Falcon field modulus.
37 pub fn from_pub_key(input: &[u8]) -> Result<Self, FalconError> {
38 if input.len() != PK_LEN {
39 return Err(FalconError::PubKeyDecodingInvalidLength(input.len()));
40 }
41
42 if input[0] != LOG_N as u8 {
43 return Err(FalconError::PubKeyDecodingInvalidTag(input[0]));
44 }
45
46 let mut acc = 0_u32;
47 let mut acc_len = 0;
48
49 let mut output = [0_u16; N];
50 let mut output_idx = 0;
51
52 for &byte in input.iter().skip(1) {
53 acc = (acc << 8) | (byte as u32);
54 acc_len += 8;
55
56 if acc_len >= 14 {
57 acc_len -= 14;
58 let w = (acc >> acc_len) & 0x3FFF;
59 if w >= MODULUS as u32 {
60 return Err(FalconError::PubKeyDecodingInvalidCoefficient(w));
61 }
62 output[output_idx] = w as u16;
63 output_idx += 1;
64 }
65 }
66
67 if (acc & ((1u32 << acc_len) - 1)) == 0 {
68 Ok(Self(output))
69 } else {
70 Err(FalconError::PubKeyDecodingExtraData)
71 }
72 }
73
74 /// Decodes the signature into the coefficients of a polynomial in Z_p\[x\]/(phi). It assumes
75 /// that the signature has been encoded using the uncompressed format.
76 ///
77 /// # Errors
78 /// Returns an error if:
79 /// - The signature has been encoded using a different algorithm than the reference compressed
80 /// encoding algorithm.
81 /// - The encoded signature polynomial is in Z_p\[x\]/(phi') where phi' = x^N' + 1 and N' != 512.
82 /// - While decoding the high bits of a coefficient, the current accumulated value of its
83 /// high bits is larger than 2048.
84 /// - The decoded coefficient is -0.
85 /// - The remaining unused bits in the last byte of `input` are non-zero.
86 pub fn from_signature(input: &[u8]) -> Result<Self, FalconError> {
87 let (encoding, log_n) = (input[0] >> 4, input[0] & 0b00001111);
88
89 if encoding != 0b0011 {
90 return Err(FalconError::SigDecodingIncorrectEncodingAlgorithm);
91 }
92 if log_n != 0b1001 {
93 return Err(FalconError::SigDecodingNotSupportedDegree(log_n));
94 }
95
96 let input = &input[41..];
97 let mut input_idx = 0;
98 let mut acc = 0u32;
99 let mut acc_len = 0;
100 let mut output = [0_u16; N];
101
102 for e in output.iter_mut() {
103 acc = (acc << 8) | (input[input_idx] as u32);
104 input_idx += 1;
105 let b = acc >> acc_len;
106 let s = b & 128;
107 let mut m = b & 127;
108
109 loop {
110 if acc_len == 0 {
111 acc = (acc << 8) | (input[input_idx] as u32);
112 input_idx += 1;
113 acc_len = 8;
114 }
115 acc_len -= 1;
116 if ((acc >> acc_len) & 1) != 0 {
117 break;
118 }
119 m += 128;
120 if m >= 2048 {
121 return Err(FalconError::SigDecodingTooBigHighBits(m));
122 }
123 }
124 if s != 0 && m == 0 {
125 return Err(FalconError::SigDecodingMinusZero);
126 }
127
128 *e = if s != 0 { (MODULUS as u32 - m) as u16 } else { m as u16 };
129 }
130
131 if (acc & ((1 << acc_len) - 1)) != 0 {
132 return Err(FalconError::SigDecodingNonZeroUnusedBitsLastByte);
133 }
134
135 Ok(Self(output))
136 }
137
138 // PUBLIC ACCESSORS
139 // --------------------------------------------------------------------------------------------
140
141 /// Returns the coefficients of this polynomial as integers.
142 pub fn inner(&self) -> [u16; N] {
143 self.0
144 }
145
146 /// Returns the coefficients of this polynomial as field elements.
147 pub fn to_elements(&self) -> Vec<Felt> {
148 self.0.iter().map(|&a| Felt::from(a)).collect()
149 }
150
151 // POLYNOMIAL OPERATIONS
152 // --------------------------------------------------------------------------------------------
153
154 /// Multiplies two polynomials over Z_p\[x\] without reducing modulo p. Given that the degrees
155 /// of the input polynomials are less than 512 and their coefficients are less than the modulus
156 /// q equal to 12289, the resulting product polynomial is guaranteed to have coefficients less
157 /// than the Miden prime.
158 ///
159 /// Note that this multiplication is not over Z_p\[x\]/(phi).
160 pub fn mul_modulo_p(a: &Self, b: &Self) -> [u64; 1024] {
161 let mut c = [0; 2 * N];
162 for i in 0..N {
163 for j in 0..N {
164 c[i + j] += a.0[i] as u64 * b.0[j] as u64;
165 }
166 }
167
168 c
169 }
170
171 /// Reduces a polynomial, that is the product of two polynomials over Z_p\[x\], modulo
172 /// the irreducible polynomial phi. This results in an element in Z_p\[x\]/(phi).
173 pub fn reduce_negacyclic(a: &[u64; 1024]) -> Self {
174 let mut c = [0; N];
175 for i in 0..N {
176 let ai = a[N + i] % MODULUS as u64;
177 let neg_ai = (MODULUS - ai as u16) % MODULUS;
178
179 let bi = (a[i] % MODULUS as u64) as u16;
180 c[i] = (neg_ai + bi) % MODULUS;
181 }
182
183 Self(c)
184 }
185
186 /// Computes the norm squared of a polynomial in Z_p\[x\]/(phi) after normalizing its
187 /// coefficients to be in the interval (-p/2, p/2].
188 pub fn sq_norm(&self) -> u64 {
189 let mut res = 0;
190 for e in self.0 {
191 if e > MODULUS_MINUS_1_OVER_TWO {
192 res += (MODULUS - e) as u64 * (MODULUS - e) as u64
193 } else {
194 res += e as u64 * e as u64
195 }
196 }
197 res
198 }
199}
200
201// Returns a polynomial representing the zero polynomial i.e. default element.
202impl Default for Polynomial {
203 fn default() -> Self {
204 Self([0_u16; N])
205 }
206}
207
208/// Multiplication over Z_p\[x\]/(phi)
209impl Mul for Polynomial {
210 type Output = Self;
211
212 fn mul(self, other: Self) -> <Self as Mul<Self>>::Output {
213 let mut result = [0_u16; N];
214 for j in 0..N {
215 for k in 0..N {
216 let i = (j + k) % N;
217 let a = self.0[j] as usize;
218 let b = other.0[k] as usize;
219 let q = MODULUS as usize;
220 let mut prod = a * b % q;
221 if (N - 1) < (j + k) {
222 prod = (q - prod) % q;
223 }
224 result[i] = ((result[i] as usize + prod) % q) as u16;
225 }
226 }
227
228 Polynomial(result)
229 }
230}
231
232/// Addition over Z_p\[x\]/(phi)
233impl Add for Polynomial {
234 type Output = Self;
235
236 fn add(self, other: Self) -> <Self as Add<Self>>::Output {
237 let mut res = self;
238 res.0.iter_mut().zip(other.0.iter()).for_each(|(x, y)| *x = (*x + *y) % MODULUS);
239
240 res
241 }
242}
243
244/// Subtraction over Z_p\[x\]/(phi)
245impl Sub for Polynomial {
246 type Output = Self;
247
248 fn sub(self, other: Self) -> <Self as Add<Self>>::Output {
249 let mut res = self;
250 res.0
251 .iter_mut()
252 .zip(other.0.iter())
253 .for_each(|(x, y)| *x = (*x + MODULUS - *y) % MODULUS);
254
255 res
256 }
257}
258
259// TESTS
260// ================================================================================================
261
262#[cfg(test)]
263mod tests {
264 use super::{Polynomial, N};
265
266 #[test]
267 fn test_negacyclic_reduction() {
268 let coef1: [u16; N] = rand_utils::rand_array();
269 let coef2: [u16; N] = rand_utils::rand_array();
270
271 let poly1 = Polynomial(coef1);
272 let poly2 = Polynomial(coef2);
273
274 assert_eq!(
275 poly1 * poly2,
276 Polynomial::reduce_negacyclic(&Polynomial::mul_modulo_p(&poly1, &poly2))
277 );
278 }
279}