bouncycastle_mlkem/
matrix.rs1use core::ops::{Index, IndexMut};
5
6use crate::mlkem::{N, q};
7use crate::polynomial;
8use crate::polynomial::Polynomial;
9use bouncycastle_utils::secret::ZeroizablePrimitive;
10
11#[derive(Clone)]
12pub struct Matrix<const k: usize, const l: usize> {
14 mat: [[Polynomial; l]; k],
15}
16
17impl<const k: usize, const l: usize> Index<usize> for Matrix<k, l> {
19 type Output = [Polynomial; l];
20
21 fn index(&self, index: usize) -> &Self::Output {
22 &self.mat[index]
23 }
24}
25impl<const k: usize, const l: usize> IndexMut<usize> for Matrix<k, l> {
27 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
28 &mut self.mat[index]
29 }
30}
31
32impl<const k: usize, const l: usize> Matrix<k, l> {
33 pub(crate) fn new() -> Self {
34 Self { mat: [[(); l]; k].map(|_| [(); l].map(|_| Polynomial::new())) }
35 }
36
37 pub(crate) fn matrix_vector_ntt<const transpose: bool>(&self, v: &Vector<l>) -> Vector<k> {
46 let mut w = Vector::<k>::new();
47 for i in 0..k {
48 w[i] = if transpose {
50 polynomial::base_mult_montgomery(&self.mat[0][i], &v[0])
51 } else {
52 polynomial::base_mult_montgomery(&self.mat[i][0], &v[0])
53 };
54
55 let mut w1: Polynomial;
56 for j in 1..l {
57 w1 = if transpose {
61 polynomial::base_mult_montgomery(&self.mat[j][i], &v[j])
62 } else {
63 polynomial::base_mult_montgomery(&self.mat[i][j], &v[j])
64 };
65
66 w[i].add(&w1);
67 }
68 }
69
70 if transpose {
72 w.reduce();
73 } else {
74 w.convert_to_mont();
75 }
76
77 w
78 }
79}
80
81#[derive(Clone, Copy)]
82pub(crate) struct Vector<const k: usize> {
83 pub(crate) vec: [Polynomial; k],
84}
85
86impl<const k: usize> Index<usize> for Vector<k> {
88 type Output = Polynomial;
89
90 fn index(&self, index: usize) -> &Self::Output {
91 &self.vec[index]
92 }
93}
94impl<const k: usize> IndexMut<usize> for Vector<k> {
96 fn index_mut(&mut self, index: usize) -> &mut Self::Output {
97 &mut self.vec[index]
98 }
99}
100
101impl<const k: usize> ZeroizablePrimitive for Vector<k> {
102 const ZEROED: Self = Self::new();
103}
104
105impl<const k: usize> Vector<k> {
106 pub(crate) const fn new() -> Self {
107 Self { vec: [Polynomial::new(); k] }
108 }
109
110 pub(crate) fn add_vector_ntt(&mut self, s: &Self) {
116 for i in 0..k {
117 self[i].add(&s[i]);
119 }
120 }
121
122 pub(crate) fn dot_product(&self, v: &Self) -> Polynomial {
123 let mut w = polynomial::base_mult_montgomery(&self[0], &v[0]);
125
126 for i in 1..k {
127 let w1 = polynomial::base_mult_montgomery(&self[i], &v[i]);
128 w.add(&w1);
129 }
130 w
135 }
136
137 pub(crate) fn reduce(&mut self) {
138 for i in 0..k {
139 self[i].poly_reduce();
140 }
141 }
142
143 pub(crate) fn ntt(&mut self) {
144 for i in 0..k {
145 self[i].ntt();
146 }
147 }
148
149 pub(crate) fn inv_ntt(&mut self) {
150 for i in 0..k {
151 self[i].inv_ntt();
152 }
153 }
154
155 pub(crate) fn convert_to_mont(&mut self) {
156 for i in 0..k {
157 self[i].convert_to_mont();
158 }
159 }
160
161 pub(crate) fn compress_pol_vec<const du: i16>(&self, out: &mut [u8]) {
165 assert!(du == 10 || du == 11);
167
168 debug_assert_eq!(out.len(), k * (N * (du as usize) / 8));
171
172 let mut idx = 0;
182 match du {
183 10 => {
184 let mut t = [0i16; 4];
186 for i in 0..k {
187 for j in 0..N / 4 {
188 for (l, item) in t.iter_mut().enumerate() {
190 *item = (((((self[i][4 * j + l] as u32) << 10) as i32
191 + (q as i32 / 2))
192 / q as i32)
193 & 0x3FF) as i16;
194 }
195
196 out[idx] = t[0] as u8;
197 out[idx + 1] = ((t[0] >> 8) | (t[1] << 2)) as u8;
198 out[idx + 2] = ((t[1] >> 6) | (t[2] << 4)) as u8;
199 out[idx + 3] = ((t[2] >> 4) | (t[3] << 6)) as u8;
200 out[idx + 4] = (t[3] >> 2) as u8;
201 idx += 5;
202 }
203 }
204 }
205 11 => {
206 let mut t = [0i16; 8];
207 for i in 0..k {
208 for j in 0..N / 8 {
209 for (l, item) in t.iter_mut().enumerate() {
210 *item = (((((self[i][8 * j + l] as u32) << 11) as i32
211 + (q as i32 / 2))
212 / q as i32)
213 & 0x7FF) as i16;
214 }
215
216 out[idx] = t[0] as u8;
217 out[idx + 1] = ((t[0] >> 8) | (t[1] << 3)) as u8;
218 out[idx + 2] = ((t[1] >> 5) | (t[2] << 6)) as u8;
219 out[idx + 3] = (t[2] >> 2) as u8;
220 out[idx + 4] = ((t[2] >> 10) | (t[3] << 1)) as u8;
221 out[idx + 5] = ((t[3] >> 7) | (t[4] << 4)) as u8;
222 out[idx + 6] = ((t[4] >> 4) | (t[5] << 7)) as u8;
223 out[idx + 7] = (t[5] >> 1) as u8;
224 out[idx + 8] = ((t[5] >> 9) | (t[6] << 2)) as u8;
225 out[idx + 9] = ((t[6] >> 6) | (t[7] << 5)) as u8;
226 out[idx + 10] = (t[7] >> 3) as u8;
227 idx += 11;
228 }
229 }
230 }
231 _ => unreachable!(),
232 }
233 }
234
235 pub(crate) fn decompress_pol_vec<const du: i16>(compressed_u: &[u8]) -> Vector<k> {
236 let mut u = Vector::<k>::new();
237
238 assert!(du == 10 || du == 11);
240
241 debug_assert_eq!(compressed_u.len(), k * (N * (du as usize) / 8));
244
245 let mut idx = 0;
246
247 match du {
248 10 => {
249 let mut t = [0i16; 4];
251 for i in 0..k {
252 for j in 0..(N / 4) {
253 t[0] = ((compressed_u[idx] as u16) | (compressed_u[idx + 1] as u16) << 8)
254 as i16;
255 t[1] = (((compressed_u[idx + 1] as u16) >> 2)
256 | (compressed_u[idx + 2] as u16) << 6)
257 as i16;
258 t[2] = (((compressed_u[idx + 2] as u16) >> 4)
259 | (compressed_u[idx + 3] as u16) << 4)
260 as i16;
261 t[3] = (((compressed_u[idx + 3] as u16) >> 6)
262 | (compressed_u[idx + 4] as u16) << 2)
263 as i16;
264 idx += 5;
265 for (l, item) in t.iter().enumerate() {
266 u[i][4 * j + l] =
267 ((((*item & 0x3FF) as i32) * (q as i32) + 512) >> 10) as i16;
268 }
269 }
270 }
271 }
272 11 => {
273 let mut t = [0i16; 8];
275 for i in 0..k {
276 for j in 0..N / 8 {
277 t[0] = (compressed_u[idx] as i32
278 | ((compressed_u[idx + 1] as u16) as i32) << 8)
279 as i16;
280 t[1] = ((compressed_u[idx + 1] >> 3) as i32
281 | ((compressed_u[idx + 2] as u16) as i32) << 5)
282 as i16;
283 t[2] = ((compressed_u[idx + 2] >> 6) as i32
284 | ((compressed_u[idx + 3] as u16) as i32) << 2
285 | (((compressed_u[idx + 4] as i32) << 10) as u16) as i32)
286 as i16;
287 t[3] = ((compressed_u[idx + 4] >> 1) as i32
288 | ((compressed_u[idx + 5] as u16) as i32) << 7)
289 as i16;
290 t[4] = ((compressed_u[idx + 5] >> 4) as i32
291 | ((compressed_u[idx + 6] as u16) as i32) << 4)
292 as i16;
293 t[5] = ((compressed_u[idx + 6] >> 7) as i32
294 | ((compressed_u[idx + 7] as u16) as i32) << 1
295 | (((compressed_u[idx + 8] as i32) << 9) as u16) as i32)
296 as i16;
297 t[6] = ((compressed_u[idx + 8] >> 2) as i32
298 | ((compressed_u[idx + 9] as u16) as i32) << 6)
299 as i16;
300 t[7] = ((compressed_u[idx + 9] >> 5) as i32
301 | ((compressed_u[idx + 10] as u16) as i32) << 3)
302 as i16;
303 idx += 11;
304 for (l, item) in t.iter().enumerate() {
305 u[i][8 * j + l] =
306 ((((*item & 0x7FF) as i32) * (q as i32) + 1024) >> 11) as i16;
307 }
308 }
309 }
310 }
311 _ => unreachable!(),
312 }
313
314 u
315 }
316}