/src/openssl/crypto/ml_dsa/ml_dsa_encoders.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | #include <openssl/byteorder.h> |
11 | | #include <openssl/err.h> |
12 | | #include <openssl/evp.h> |
13 | | #include <openssl/proverr.h> |
14 | | #include "ml_dsa_hash.h" |
15 | | #include "ml_dsa_key.h" |
16 | | #include "ml_dsa_sign.h" |
17 | | #include "internal/packet.h" |
18 | | |
19 | 0 | #define POLY_COEFF_NUM_BYTES(bits) ((bits) * (ML_DSA_NUM_POLY_COEFFICIENTS / 8)) |
20 | | /* Cast mod_sub result in support of left-shifts that create 64-bit values. */ |
21 | 0 | #define mod_sub_64(a, b) ((uint64_t) mod_sub(a, b)) |
22 | | |
23 | | typedef int (ENCODE_FN)(const POLY *s, WPACKET *pkt); |
24 | | typedef int (DECODE_FN)(POLY *s, PACKET *pkt); |
25 | | |
26 | | static ENCODE_FN poly_encode_signed_2; |
27 | | static ENCODE_FN poly_encode_signed_4; |
28 | | static ENCODE_FN poly_encode_signed_two_to_power_17; |
29 | | static ENCODE_FN poly_encode_signed_two_to_power_19; |
30 | | static DECODE_FN poly_decode_signed_2; |
31 | | static DECODE_FN poly_decode_signed_4; |
32 | | static DECODE_FN poly_decode_signed_two_to_power_17; |
33 | | static DECODE_FN poly_decode_signed_two_to_power_19; |
34 | | |
35 | | /* Bit packing Algorithms */ |
36 | | |
37 | | /* |
38 | | * Encodes a polynomial into a byte string, assuming that all coefficients are |
39 | | * in the range 0..15 (4 bits). |
40 | | * |
41 | | * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 4 bits |
42 | | * |
43 | | * i.e. Use 4 bits from each coefficient and pack them into bytes |
44 | | * So every 2 coefficients fit into 1 byte. |
45 | | * |
46 | | * This is used to encode w1 when signing with ML-DSA-65 and ML-DSA-87 |
47 | | * |
48 | | * @param p A polynomial with coefficients all in the range (0..15) |
49 | | * @param pkt A packet object to write 128 bytes to. |
50 | | * |
51 | | * @returns 1 on success, or 0 on error. |
52 | | */ |
53 | | static int poly_encode_4_bits(const POLY *p, WPACKET *pkt) |
54 | 0 | { |
55 | 0 | uint8_t *out; |
56 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
57 | |
|
58 | 0 | if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(4), &out)) |
59 | 0 | return 0; |
60 | | |
61 | 0 | do { |
62 | 0 | uint32_t z0 = *in++; |
63 | 0 | uint32_t z1 = *in++; |
64 | |
|
65 | 0 | *out++ = z0 | (z1 << 4); |
66 | 0 | } while (in < end); |
67 | 0 | return 1; |
68 | 0 | } |
69 | | |
70 | | /* |
71 | | * Encodes a polynomial into a byte string, assuming that all coefficients are |
72 | | * in the range 0..43 (6 bits). |
73 | | * |
74 | | * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 43 |
75 | | * |
76 | | * i.e. Use 6 bits from each coefficient and pack them into bytes |
77 | | * So every 4 coefficients fit into 3 bytes. |
78 | | * |
79 | | * |c0||c1||c2||c3| |
80 | | * | /| /\ / |
81 | | * |6 2|4 4|2 6| |
82 | | * |
83 | | * This is used to encode w1 when signing with ML-DSA-44 |
84 | | * |
85 | | * @param p A polynomial with coefficients all in the range (0..43) |
86 | | * @param pkt A packet object to write 96 bytes to. |
87 | | * |
88 | | * @returns 1 on success, or 0 on error. |
89 | | */ |
90 | | static int poly_encode_6_bits(const POLY *p, WPACKET *pkt) |
91 | 0 | { |
92 | 0 | uint8_t *out; |
93 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
94 | |
|
95 | 0 | if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(6), &out)) |
96 | 0 | return 0; |
97 | | |
98 | 0 | do { |
99 | 0 | uint32_t c0 = *in++; |
100 | 0 | uint32_t c1 = *in++; |
101 | 0 | uint32_t c2 = *in++; |
102 | 0 | uint32_t c3 = *in++; |
103 | |
|
104 | 0 | *out++ = c0 | (c1 << 6); |
105 | 0 | *out++ = (c1 >> 2) | (c2 << 4); |
106 | 0 | *out++ = (c2 >> 4) | (c3 << 2); |
107 | 0 | } while (in < end); |
108 | 0 | return 1; |
109 | 0 | } |
110 | | |
111 | | /* |
112 | | * Encodes a polynomial into a byte string, assuming that all coefficients are |
113 | | * unsigned 10 bit values. |
114 | | * |
115 | | * See FIPS 204, Algorithm 16, SimpleBitPack(w, b) where b = 10 bits |
116 | | * |
117 | | * i.e. Use 10 bits from each coefficient and pack them into bytes |
118 | | * So every 4 coefficients (c0..c3) fit into 5 bytes. |
119 | | * |c0||c1||c2||c3| |
120 | | * |\ |\ |\ |\ |
121 | | * |8|2 6|4 4|6 2|8| |
122 | | * |
123 | | * This is used to save t1 (the high part of public key polynomial t) |
124 | | * |
125 | | * @param p A polynomial with coefficients all in the range (0..1023) |
126 | | * @param pkt A packet object to write 320 bytes to. |
127 | | * |
128 | | * @returns 1 on success, or 0 on error. |
129 | | */ |
130 | | static int poly_encode_10_bits(const POLY *p, WPACKET *pkt) |
131 | 0 | { |
132 | 0 | uint8_t *out; |
133 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
134 | |
|
135 | 0 | if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(10), &out)) |
136 | 0 | return 0; |
137 | | |
138 | 0 | do { |
139 | 0 | uint32_t c0 = *in++; |
140 | 0 | uint32_t c1 = *in++; |
141 | 0 | uint32_t c2 = *in++; |
142 | 0 | uint32_t c3 = *in++; |
143 | |
|
144 | 0 | *out++ = (uint8_t)c0; |
145 | 0 | *out++ = (uint8_t)((c0 >> 8) | (c1 << 2)); |
146 | 0 | *out++ = (uint8_t)((c1 >> 6) | (c2 << 4)); |
147 | 0 | *out++ = (uint8_t)((c2 >> 4) | (c3 << 6)); |
148 | 0 | *out++ = (uint8_t)(c3 >> 2); |
149 | 0 | } while (in < end); |
150 | 0 | return 1; |
151 | 0 | } |
152 | | |
153 | | /* |
154 | | * @brief Reverses the procedure of poly_encode_10_bits(). |
155 | | * See FIPS 204, Algorithm 18, SimpleBitUnpack(v, b) where b = 10. |
156 | | * |
157 | | * @param p A polynomial to write coefficients to. |
158 | | * @param pkt A packet object to read 320 bytes from. |
159 | | * |
160 | | * @returns 1 on success, or 0 on error. |
161 | | */ |
162 | | static int poly_decode_10_bits(POLY *p, PACKET *pkt) |
163 | 0 | { |
164 | 0 | const uint8_t *in = NULL; |
165 | 0 | uint32_t v, w, mask = 0x3ff; /* 10 bits */ |
166 | 0 | uint32_t *out = p->coeff, *end = out + ML_DSA_NUM_POLY_COEFFICIENTS; |
167 | |
|
168 | 0 | do { |
169 | 0 | if (!PACKET_get_bytes(pkt, &in, 5)) |
170 | 0 | return 0; |
171 | | |
172 | 0 | in = OPENSSL_load_u32_le(&v, in); |
173 | 0 | w = *in; |
174 | |
|
175 | 0 | *out++ = v & mask; |
176 | 0 | *out++ = (v >> 10) & mask; |
177 | 0 | *out++ = (v >> 20) & mask; |
178 | 0 | *out++ = (v >> 30) | (w << 2); |
179 | 0 | } while (out < end); |
180 | 0 | return 1; |
181 | 0 | } |
182 | | |
183 | | /* |
184 | | * @brief Encodes a polynomial into a byte string, assuming that all |
185 | | * coefficients are in the range -4..4. |
186 | | * See FIPS 204, Algorithm 17, BitPack(w, a, b). (a = 4, b = 4) |
187 | | * |
188 | | * It uses a nibble from each coefficient and packs them into bytes |
189 | | * So every 2 coefficients fit into 1 byte. |
190 | | * |
191 | | * This is used to encode the private key polynomial elements of s1 and s2 |
192 | | * for ML-DSA-65 (i.e. eta = 4) |
193 | | * |
194 | | * @param p An array of 256 coefficients all in the range -4..4 |
195 | | * @param pkt A packet to write 128 bytes of encoded polynomial coefficients to. |
196 | | * |
197 | | * @returns 1 on success, or 0 on error. |
198 | | */ |
199 | | static int poly_encode_signed_4(const POLY *p, WPACKET *pkt) |
200 | 0 | { |
201 | 0 | uint8_t *out; |
202 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
203 | |
|
204 | 0 | if (!WPACKET_allocate_bytes(pkt, 32 * 4, &out)) |
205 | 0 | return 0; |
206 | | |
207 | 0 | do { |
208 | 0 | uint32_t z = mod_sub(4, *in++); |
209 | |
|
210 | 0 | *out++ = z | (mod_sub(4, *in++) << 4); |
211 | 0 | } while (in < end); |
212 | 0 | return 1; |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | * @brief Reverses the procedure of poly_encode_signed_4(). |
217 | | * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = b = 4. |
218 | | * |
219 | | * @param p A polynomial to write coefficients to. |
220 | | * @param pkt A packet object to read 128 bytes from. |
221 | | * |
222 | | * @returns 1 on success, or 0 on error. An error will occur if any of the |
223 | | * coefficients are not in the correct range. |
224 | | */ |
225 | | static int poly_decode_signed_4(POLY *p, PACKET *pkt) |
226 | 0 | { |
227 | 0 | int i, ret = 0; |
228 | 0 | uint32_t v, *out = p->coeff; |
229 | 0 | const uint8_t *in; |
230 | 0 | uint32_t msbs, mask; |
231 | |
|
232 | 0 | for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) { |
233 | 0 | if (!PACKET_get_bytes(pkt, &in, 4)) |
234 | 0 | goto err; |
235 | 0 | in = OPENSSL_load_u32_le(&v, in); |
236 | | |
237 | | /* |
238 | | * None of the nibbles may be >= 9. So if the MSB of any nibble is set, |
239 | | * none of the other bits may be set. First, select all the MSBs. |
240 | | */ |
241 | 0 | msbs = v & 0x88888888u; |
242 | | /* For each nibble where the MSB is set, form a mask of all the other bits. */ |
243 | 0 | mask = (msbs >> 1) | (msbs >> 2) | (msbs >> 3); |
244 | | /* |
245 | | * A nibble is only out of range in the case of invalid input, in which case |
246 | | * it is okay to leak the value. |
247 | | */ |
248 | 0 | if (value_barrier_32((mask & v) != 0)) |
249 | 0 | goto err; |
250 | | |
251 | 0 | *out++ = mod_sub(4, v & 15); |
252 | 0 | *out++ = mod_sub(4, (v >> 4) & 15); |
253 | 0 | *out++ = mod_sub(4, (v >> 8) & 15); |
254 | 0 | *out++ = mod_sub(4, (v >> 12) & 15); |
255 | 0 | *out++ = mod_sub(4, (v >> 16) & 15); |
256 | 0 | *out++ = mod_sub(4, (v >> 20) & 15); |
257 | 0 | *out++ = mod_sub(4, (v >> 24) & 15); |
258 | 0 | *out++ = mod_sub(4, v >> 28); |
259 | 0 | } |
260 | 0 | ret = 1; |
261 | 0 | err: |
262 | 0 | return ret; |
263 | 0 | } |
264 | | |
265 | | /* |
266 | | * @brief Encodes a polynomial into a byte string, assuming that all |
267 | | * coefficients are in the range -2..2. |
268 | | * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = b = 2. |
269 | | * |
270 | | * This is used to encode the private key polynomial elements of s1 and s2 |
271 | | * for ML-DSA-44 and ML-DSA-87 (i.e. eta = 2) |
272 | | * |
273 | | * @param pkt A packet to write 128 bytes of encoded polynomial coefficients to. |
274 | | * @param p An array of 256 coefficients all in the range -2..2 |
275 | | * |
276 | | * Use 3 bits from each coefficient and pack them into bytes |
277 | | * So every 8 coefficients fit into 3 bytes. |
278 | | * |c0 c1 c2 c3 c4 c5 c6 c7| |
279 | | * | / / | | / / | | / |
280 | | * |3 3 2| 1 3 3 1| 2 3 3| |
281 | | * |
282 | | * @param p An array of 256 coefficients all in the range -2..2 |
283 | | * @param pkt A packet to write 64 bytes of encoded polynomial coefficients to. |
284 | | * |
285 | | * @returns 1 on success, or 0 on error. |
286 | | */ |
287 | | static int poly_encode_signed_2(const POLY *p, WPACKET *pkt) |
288 | 0 | { |
289 | 0 | uint8_t *out; |
290 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
291 | |
|
292 | 0 | if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(3), &out)) |
293 | 0 | return 0; |
294 | | |
295 | 0 | do { |
296 | 0 | uint32_t z; |
297 | |
|
298 | 0 | z = mod_sub(2, *in++); |
299 | 0 | z |= mod_sub(2, *in++) << 3; |
300 | 0 | z |= mod_sub(2, *in++) << 6; |
301 | 0 | z |= mod_sub(2, *in++) << 9; |
302 | 0 | z |= mod_sub(2, *in++) << 12; |
303 | 0 | z |= mod_sub(2, *in++) << 15; |
304 | 0 | z |= mod_sub(2, *in++) << 18; |
305 | 0 | z |= mod_sub(2, *in++) << 21; |
306 | |
|
307 | 0 | out = OPENSSL_store_u16_le(out, (uint16_t) z); |
308 | 0 | *out++ = (uint8_t) (z >> 16); |
309 | 0 | } while (in < end); |
310 | 0 | return 1; |
311 | 0 | } |
312 | | |
313 | | /* |
314 | | * @brief Reverses the procedure of poly_encode_signed_2(). |
315 | | * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = b = 2. |
316 | | * |
317 | | * @param p A polynomial to write coefficients to. |
318 | | * @param pkt A packet object to read 64 encoded bytes from. |
319 | | * |
320 | | * @returns 1 on success, or 0 on error. An error will occur if any of the |
321 | | * coefficients are not in the correct range. |
322 | | */ |
323 | | static int poly_decode_signed_2(POLY *p, PACKET *pkt) |
324 | 0 | { |
325 | 0 | int i, ret = 0; |
326 | 0 | uint32_t u = 0, v = 0, *out = p->coeff; |
327 | 0 | uint32_t msbs, mask; |
328 | 0 | const uint8_t *in; |
329 | |
|
330 | 0 | for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) { |
331 | 0 | if (!PACKET_get_bytes(pkt, &in, 3)) |
332 | 0 | goto err; |
333 | 0 | memcpy(&u, in, 3); |
334 | 0 | OPENSSL_load_u32_le(&v, (uint8_t *)&u); |
335 | | |
336 | | /* |
337 | | * Each octal value (3 bits) must be <= 4, So if the MSB is set then the |
338 | | * bottom 2 bits must not be set. |
339 | | * First, select all the MSBs (Use octal representation for the mask) |
340 | | */ |
341 | 0 | msbs = v & 044444444; |
342 | | /* For each octal value where the MSB is set, form a mask of the 2 other bits. */ |
343 | 0 | mask = (msbs >> 1) | (msbs >> 2); |
344 | | /* |
345 | | * A nibble is only out of range in the case of invalid input, in which |
346 | | * case it is okay to leak the value. |
347 | | */ |
348 | 0 | if (value_barrier_32((mask & v) != 0)) |
349 | 0 | goto err; |
350 | | |
351 | 0 | *out++ = mod_sub(2, v & 7); |
352 | 0 | *out++ = mod_sub(2, (v >> 3) & 7); |
353 | 0 | *out++ = mod_sub(2, (v >> 6) & 7); |
354 | 0 | *out++ = mod_sub(2, (v >> 9) & 7); |
355 | 0 | *out++ = mod_sub(2, (v >> 12) & 7); |
356 | 0 | *out++ = mod_sub(2, (v >> 15) & 7); |
357 | 0 | *out++ = mod_sub(2, (v >> 18) & 7); |
358 | 0 | *out++ = mod_sub(2, (v >> 21) & 7); |
359 | 0 | } |
360 | 0 | ret = 1; |
361 | 0 | err: |
362 | 0 | return ret; |
363 | 0 | } |
364 | | |
365 | | /* |
366 | | * @brief Encodes a polynomial into a byte string, assuming that all |
367 | | * coefficients are in the range (-2^12 + 1)..2^12. |
368 | | * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = 2^12 - 1, b = 2^12. |
369 | | * |
370 | | * This is used to encode the LSB of the public key polynomial elements of t0 |
371 | | * (which are encoded as part of the encoded private key). |
372 | | * |
373 | | * Use 13 bits from each coefficient and pack them into bytes |
374 | | * |
375 | | * The code below packs them into 2 64 bits blocks by doing.. |
376 | | * z0 z1 z2 z3 z4 z5 z6 z7 0 |
377 | | * | | | | / \ | | | | |
378 | | * |13 13 13 13 12 |1 13 13 13 24 |
379 | | * |
380 | | * @param p An array of 256 coefficients all in the range -2^12+1..2^12 |
381 | | * @param pkt A packet to write 416 (13 * 256 / 8) bytes of encoded polynomial |
382 | | * coefficients to. |
383 | | * |
384 | | * @returns 1 on success, or 0 on error. |
385 | | */ |
386 | | static int poly_encode_signed_two_to_power_12(const POLY *p, WPACKET *pkt) |
387 | 0 | { |
388 | 0 | static const uint32_t range = 1u << 12; |
389 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
390 | |
|
391 | 0 | do { |
392 | 0 | uint8_t *out; |
393 | 0 | uint64_t a1, a2; |
394 | |
|
395 | 0 | if (!WPACKET_allocate_bytes(pkt, 13, &out)) |
396 | 0 | return 0; |
397 | | |
398 | 0 | a1 = mod_sub_64(range, *in++); |
399 | 0 | a1 |= mod_sub_64(range, *in++) << 13; |
400 | 0 | a1 |= mod_sub_64(range, *in++) << 26; |
401 | 0 | a1 |= mod_sub_64(range, *in++) << 39; |
402 | 0 | a1 |= (a2 = mod_sub_64(range, *in++)) << 52; |
403 | 0 | a2 = (a2 >> 12) | (mod_sub_64(range, *in++) << 1); |
404 | 0 | a2 |= mod_sub_64(range, *in++) << 14; |
405 | 0 | a2 |= mod_sub_64(range, *in++) << 27; |
406 | |
|
407 | 0 | out = OPENSSL_store_u64_le(out, a1); |
408 | 0 | out = OPENSSL_store_u32_le(out, (uint32_t) a2); |
409 | 0 | *out = (uint8_t) (a2 >> 32); |
410 | 0 | } while (in < end); |
411 | 0 | return 1; |
412 | 0 | } |
413 | | |
414 | | /* |
415 | | * @brief Reverses the procedure of poly_encode_signed_two_to_power_12(). |
416 | | * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^12 - 1, b = 2^12. |
417 | | * |
418 | | * @param p A polynomial to write coefficients to. |
419 | | * @param pkt A packet object to read 416 encoded bytes from. |
420 | | * |
421 | | * @returns 1 on success, or 0 on error. |
422 | | */ |
423 | | static int poly_decode_signed_two_to_power_12(POLY *p, PACKET *pkt) |
424 | 0 | { |
425 | 0 | int i, ret = 0; |
426 | 0 | uint32_t *out = p->coeff; |
427 | 0 | const uint8_t *in; |
428 | 0 | static const uint32_t range = 1u << 12; |
429 | 0 | static const uint32_t mask_13_bits = (1u << 13) - 1; |
430 | |
|
431 | 0 | for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) { |
432 | 0 | uint64_t a1; |
433 | 0 | uint32_t a2, b13; |
434 | |
|
435 | 0 | if (!PACKET_get_bytes(pkt, &in, 13)) |
436 | 0 | goto err; |
437 | 0 | in = OPENSSL_load_u64_le(&a1, in); |
438 | 0 | in = OPENSSL_load_u32_le(&a2, in); |
439 | 0 | b13 = (uint32_t) *in; |
440 | |
|
441 | 0 | *out++ = mod_sub(range, a1 & mask_13_bits); |
442 | 0 | *out++ = mod_sub(range, (a1 >> 13) & mask_13_bits); |
443 | 0 | *out++ = mod_sub(range, (a1 >> 26) & mask_13_bits); |
444 | 0 | *out++ = mod_sub(range, (a1 >> 39) & mask_13_bits); |
445 | 0 | *out++ = mod_sub(range, (a1 >> 52) | ((a2 << 12) & mask_13_bits)); |
446 | 0 | *out++ = mod_sub(range, (a2 >> 1) & mask_13_bits); |
447 | 0 | *out++ = mod_sub(range, (a2 >> 14) & mask_13_bits); |
448 | 0 | *out++ = mod_sub(range, (a2 >> 27) | (b13 << 5)); |
449 | 0 | } |
450 | 0 | ret = 1; |
451 | 0 | err: |
452 | 0 | return ret; |
453 | 0 | } |
454 | | |
455 | | /* |
456 | | * @brief Encodes a polynomial into a byte string, assuming that all |
457 | | * coefficients are in the range (-2^19 + 1)..2^19. |
458 | | * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = 2^19 - 1, b = 2^19. |
459 | | * |
460 | | * This is used to encode signatures for ML-DSA-65 & ML-DSA-87 (gamma1 = 2^19) |
461 | | * |
462 | | * Use 20 bits from each coefficient and pack them into bytes |
463 | | * |
464 | | * The code below packs every 4 (20 bit) coefficients into 10 bytes |
465 | | * z0 z1 z2 z3 |
466 | | * | |\ | | \ |
467 | | * |20 12|8 20 4|16 |
468 | | * |
469 | | * @param p An array of 256 coefficients all in the range -2^19+1..2^19 |
470 | | * @param pkt A packet to write 640 (20 * 256 / 8) bytes of encoded polynomial |
471 | | * coefficients to. |
472 | | * |
473 | | * @returns 1 on success, or 0 on error. |
474 | | */ |
475 | | static int poly_encode_signed_two_to_power_19(const POLY *p, WPACKET *pkt) |
476 | 0 | { |
477 | 0 | static const uint32_t range = 1u << 19; |
478 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
479 | |
|
480 | 0 | do { |
481 | 0 | uint32_t z0, z1, z2; |
482 | 0 | uint8_t *out; |
483 | |
|
484 | 0 | if (!WPACKET_allocate_bytes(pkt, 10, &out)) |
485 | 0 | return 0; |
486 | | |
487 | 0 | z0 = mod_sub(range, *in++); |
488 | 0 | z0 |= (z1 = mod_sub(range, *in++)) << 20; |
489 | 0 | z1 = (z1 >> 12) | (mod_sub(range, *in++) << 8); |
490 | 0 | z1 |= (z2 = mod_sub(range, *in++)) << 28; |
491 | |
|
492 | 0 | out = OPENSSL_store_u32_le(out, z0); |
493 | 0 | out = OPENSSL_store_u32_le(out, z1); |
494 | 0 | out = OPENSSL_store_u16_le(out, (uint16_t) (z2 >> 4)); |
495 | 0 | } while (in < end); |
496 | 0 | return 1; |
497 | 0 | } |
498 | | |
499 | | /* |
500 | | * @brief Reverses the procedure of poly_encode_signed_two_to_power_19(). |
501 | | * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^19 - 1, b = 2^19. |
502 | | * |
503 | | * @param p A polynomial to write coefficients to. |
504 | | * @param pkt A packet object to read 640 encoded bytes from. |
505 | | * |
506 | | * @returns 1 on success, or 0 on error. |
507 | | */ |
508 | | static int poly_decode_signed_two_to_power_19(POLY *p, PACKET *pkt) |
509 | 0 | { |
510 | 0 | int i, ret = 0; |
511 | 0 | uint32_t *out = p->coeff; |
512 | 0 | const uint8_t *in; |
513 | 0 | static const uint32_t range = 1u << 19; |
514 | 0 | static const uint32_t mask_20_bits = (1u << 20) - 1; |
515 | |
|
516 | 0 | for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 4); i++) { |
517 | 0 | uint32_t a1, a2; |
518 | 0 | uint16_t a3; |
519 | |
|
520 | 0 | if (!PACKET_get_bytes(pkt, &in, 10)) |
521 | 0 | goto err; |
522 | 0 | in = OPENSSL_load_u32_le(&a1, in); |
523 | 0 | in = OPENSSL_load_u32_le(&a2, in); |
524 | 0 | in = OPENSSL_load_u16_le(&a3, in); |
525 | |
|
526 | 0 | *out++ = mod_sub(range, a1 & mask_20_bits); |
527 | 0 | *out++ = mod_sub(range, (a1 >> 20) | ((a2 & 0xFF) << 12)); |
528 | 0 | *out++ = mod_sub(range, (a2 >> 8) & mask_20_bits); |
529 | 0 | *out++ = mod_sub(range, (a2 >> 28) | (a3 << 4)); |
530 | 0 | } |
531 | 0 | ret = 1; |
532 | 0 | err: |
533 | 0 | return ret; |
534 | 0 | } |
535 | | |
536 | | /* |
537 | | * @brief Encodes a polynomial into a byte string, assuming that all |
538 | | * coefficients are in the range (-2^17 + 1)..2^17. |
539 | | * See FIPS 204, Algorithm 17, BitPack(w, a, b). where a = 2^17 - 1, b = 2^17. |
540 | | * |
541 | | * This is used to encode signatures for ML-DSA-44 (where gamma1 = 2^17) |
542 | | * |
543 | | * Use 18 bits from each coefficient and pack them into bytes |
544 | | * |
545 | | * The code below packs every 4 (18 bit) coefficients into 9 bytes |
546 | | * z0 z1 z2 z3 |
547 | | * | |\ | | \ |
548 | | * |18 14|4 18 10| 8 |
549 | | * |
550 | | * @param p An array of 256 coefficients all in the range -2^17+1..2^17 |
551 | | * @param pkt A packet to write 576 (18 * 256 / 8) bytes of encoded polynomial |
552 | | * coefficients to. |
553 | | * |
554 | | * @returns 1 on success, or 0 on error. |
555 | | */ |
556 | | static int poly_encode_signed_two_to_power_17(const POLY *p, WPACKET *pkt) |
557 | 0 | { |
558 | 0 | static const uint32_t range = 1u << 17; |
559 | 0 | const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS; |
560 | |
|
561 | 0 | do { |
562 | 0 | uint8_t *out; |
563 | 0 | uint32_t z0, z1, z2; |
564 | |
|
565 | 0 | if (!WPACKET_allocate_bytes(pkt, 9, &out)) |
566 | 0 | return 0; |
567 | | |
568 | 0 | z0 = mod_sub(range, *in++); |
569 | 0 | z0 |= (z1 = mod_sub(range, *in++)) << 18; |
570 | 0 | z1 = (z1 >> 14) | (mod_sub(range, *in++) << 4); |
571 | 0 | z1 |= (z2 = mod_sub(range, *in++)) << 22; |
572 | |
|
573 | 0 | out = OPENSSL_store_u32_le(out, z0); |
574 | 0 | out = OPENSSL_store_u32_le(out, z1); |
575 | 0 | *out = z2 >> 10; |
576 | 0 | } while (in < end); |
577 | 0 | return 1; |
578 | 0 | } |
579 | | |
580 | | /* |
581 | | * @brief Reverses the procedure of poly_encode_signed_two_to_power_17(). |
582 | | * See FIPS 204, Algorithm 19, BitUnpack(v, a, b) where a = 2^17 - 1, b = 2^17. |
583 | | * |
584 | | * @param p A polynomial to write coefficients to. |
585 | | * @param pkt A packet object to read 576 encoded bytes from. |
586 | | * |
587 | | * @returns 1 on success, or 0 on error. |
588 | | */ |
589 | | static int poly_decode_signed_two_to_power_17(POLY *p, PACKET *pkt) |
590 | 0 | { |
591 | 0 | uint32_t *out = p->coeff; |
592 | 0 | const uint32_t *end = out + ML_DSA_NUM_POLY_COEFFICIENTS; |
593 | 0 | const uint8_t *in; |
594 | 0 | static const uint32_t range = 1u << 17; |
595 | 0 | static const uint32_t mask_18_bits = (1u << 18) - 1; |
596 | |
|
597 | 0 | do { |
598 | 0 | uint32_t a1, a2, a3; |
599 | |
|
600 | 0 | if (!PACKET_get_bytes(pkt, &in, 9)) |
601 | 0 | return 0; |
602 | 0 | in = OPENSSL_load_u32_le(&a1, in); |
603 | 0 | in = OPENSSL_load_u32_le(&a2, in); |
604 | 0 | a3 = (uint32_t) *in; |
605 | |
|
606 | 0 | *out++ = mod_sub(range, a1 & mask_18_bits); |
607 | 0 | *out++ = mod_sub(range, (a1 >> 18) | ((a2 & 0xF) << 14)); |
608 | 0 | *out++ = mod_sub(range, (a2 >> 4) & mask_18_bits); |
609 | 0 | *out++ = mod_sub(range, (a2 >> 22) | (a3 << 10)); |
610 | 0 | } while (out < end); |
611 | 0 | return 1; |
612 | 0 | } |
613 | | |
614 | | /* |
615 | | * @brief Encode the public key as an array of bytes. |
616 | | * See FIPS 204, Algorithm 22, pkEncode(). |
617 | | * |
618 | | * @param key A key object containing public key values. The encoded public |
619 | | * key data is stored in this key. |
620 | | * @returns 1 if the public key was encoded successfully or 0 otherwise. |
621 | | */ |
622 | | int ossl_ml_dsa_pk_encode(ML_DSA_KEY *key) |
623 | 0 | { |
624 | 0 | int ret = 0; |
625 | 0 | size_t i, written = 0; |
626 | 0 | const POLY *t1 = key->t1.poly; |
627 | 0 | size_t t1_len = key->t1.num_poly; |
628 | 0 | size_t enc_len = key->params->pk_len; |
629 | 0 | uint8_t *enc = OPENSSL_malloc(enc_len); |
630 | 0 | WPACKET pkt; |
631 | |
|
632 | 0 | if (enc == NULL) |
633 | 0 | return 0; |
634 | | |
635 | 0 | if (!WPACKET_init_static_len(&pkt, enc, enc_len, 0) |
636 | 0 | || !WPACKET_memcpy(&pkt, key->rho, sizeof(key->rho))) |
637 | 0 | goto err; |
638 | 0 | for (i = 0; i < t1_len; i++) |
639 | 0 | if (!poly_encode_10_bits(t1 + i, &pkt)) |
640 | 0 | goto err; |
641 | 0 | if (!WPACKET_get_total_written(&pkt, &written) |
642 | 0 | || written != enc_len) |
643 | 0 | goto err; |
644 | 0 | OPENSSL_free(key->pub_encoding); |
645 | 0 | key->pub_encoding = enc; |
646 | 0 | ret = 1; |
647 | 0 | err: |
648 | 0 | WPACKET_finish(&pkt); |
649 | 0 | if (ret == 0) |
650 | 0 | OPENSSL_free(enc); |
651 | 0 | return ret; |
652 | 0 | } |
653 | | |
654 | | /* |
655 | | * @brief The reverse of ossl_ml_dsa_pk_encode(). |
656 | | * See FIPS 204, Algorithm 23, pkDecode(). |
657 | | * |
658 | | * @param in An encoded public key. |
659 | | * @param in_len The size of |in| |
660 | | * @param key A key object to store the decoded public key into. |
661 | | * |
662 | | * @returns 1 if the public key was decoded successfully or 0 otherwise. |
663 | | */ |
664 | | int ossl_ml_dsa_pk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len) |
665 | 0 | { |
666 | 0 | int ret = 0; |
667 | 0 | size_t i; |
668 | 0 | PACKET pkt; |
669 | 0 | EVP_MD_CTX *ctx; |
670 | |
|
671 | 0 | if (key->priv_encoding != NULL || key->pub_encoding != NULL) |
672 | 0 | return 0; /* Do not allow key mutation */ |
673 | 0 | if (in_len != key->params->pk_len) |
674 | 0 | return 0; |
675 | | |
676 | 0 | if (!ossl_ml_dsa_key_pub_alloc(key)) |
677 | 0 | return 0; |
678 | 0 | ctx = EVP_MD_CTX_new(); |
679 | 0 | if (ctx == NULL) |
680 | 0 | goto err; |
681 | 0 | if (!PACKET_buf_init(&pkt, in, in_len) |
682 | 0 | || !PACKET_copy_bytes(&pkt, key->rho, sizeof(key->rho))) |
683 | 0 | goto err; |
684 | 0 | for (i = 0; i < key->t1.num_poly; i++) |
685 | 0 | if (!poly_decode_10_bits(key->t1.poly + i, &pkt)) |
686 | 0 | goto err; |
687 | | |
688 | | /* cache the hash of the encoded public key */ |
689 | 0 | if (!shake_xof(ctx, key->shake256_md, in, in_len, key->tr, sizeof(key->tr))) |
690 | 0 | goto err; |
691 | | |
692 | 0 | key->pub_encoding = OPENSSL_memdup(in, in_len); |
693 | 0 | ret = (key->pub_encoding != NULL); |
694 | 0 | err: |
695 | 0 | EVP_MD_CTX_free(ctx); |
696 | 0 | return ret; |
697 | 0 | } |
698 | | |
699 | | /* |
700 | | * @brief Encode the private key as an array of bytes. |
701 | | * See FIPS 204, Algorithm 24, skEncode(). |
702 | | * |
703 | | * @param key A key object containing private key values. The encoded private |
704 | | * key data is stored in this key. |
705 | | * @returns 1 if the private key was encoded successfully or 0 otherwise. |
706 | | */ |
707 | | int ossl_ml_dsa_sk_encode(ML_DSA_KEY *key) |
708 | 0 | { |
709 | 0 | int ret = 0; |
710 | 0 | const ML_DSA_PARAMS *params = key->params; |
711 | 0 | size_t i, written = 0, k = params->k, l = params->l; |
712 | 0 | ENCODE_FN *encode_fn; |
713 | 0 | size_t enc_len = params->sk_len; |
714 | 0 | const POLY *t0 = key->t0.poly; |
715 | 0 | WPACKET pkt; |
716 | 0 | uint8_t *enc = OPENSSL_malloc(enc_len); |
717 | |
|
718 | 0 | if (enc == NULL) |
719 | 0 | return 0; |
720 | | |
721 | | /* eta is the range of private key coefficients (-eta...eta) */ |
722 | 0 | if (params->eta == ML_DSA_ETA_4) |
723 | 0 | encode_fn = poly_encode_signed_4; |
724 | 0 | else |
725 | 0 | encode_fn = poly_encode_signed_2; |
726 | |
|
727 | 0 | if (!WPACKET_init_static_len(&pkt, enc, enc_len, 0) |
728 | 0 | || !WPACKET_memcpy(&pkt, key->rho, sizeof(key->rho)) |
729 | 0 | || !WPACKET_memcpy(&pkt, key->K, sizeof(key->K)) |
730 | 0 | || !WPACKET_memcpy(&pkt, key->tr, sizeof(key->tr))) |
731 | 0 | goto err; |
732 | 0 | for (i = 0; i < l; ++i) |
733 | 0 | if (!encode_fn(key->s1.poly + i, &pkt)) |
734 | 0 | goto err; |
735 | 0 | for (i = 0; i < k; ++i) |
736 | 0 | if (!encode_fn(key->s2.poly + i, &pkt)) |
737 | 0 | goto err; |
738 | 0 | for (i = 0; i < k; ++i) |
739 | 0 | if (!poly_encode_signed_two_to_power_12(t0++, &pkt)) |
740 | 0 | goto err; |
741 | 0 | if (!WPACKET_get_total_written(&pkt, &written) |
742 | 0 | || written != enc_len) |
743 | 0 | goto err; |
744 | 0 | OPENSSL_clear_free(key->priv_encoding, enc_len); |
745 | 0 | key->priv_encoding = enc; |
746 | 0 | ret = 1; |
747 | 0 | err: |
748 | 0 | WPACKET_finish(&pkt); |
749 | 0 | if (ret == 0) |
750 | 0 | OPENSSL_clear_free(enc, enc_len); |
751 | 0 | return ret; |
752 | 0 | } |
753 | | |
754 | | /* |
755 | | * @brief The reverse of ossl_ml_dsa_sk_encode(). |
756 | | * See FIPS 204, Algorithm 24, skDecode(). |
757 | | * |
758 | | * @param in An encoded private key. |
759 | | * @param in_len The size of |in| |
760 | | * @param key A key object to store the decoded private key into. |
761 | | * |
762 | | * @returns 1 if the private key was decoded successfully or 0 otherwise. |
763 | | */ |
764 | | int ossl_ml_dsa_sk_decode(ML_DSA_KEY *key, const uint8_t *in, size_t in_len) |
765 | 0 | { |
766 | 0 | DECODE_FN *decode_fn; |
767 | 0 | const ML_DSA_PARAMS *params = key->params; |
768 | 0 | size_t i, k = params->k, l = params->l; |
769 | 0 | uint8_t input_tr[ML_DSA_TR_BYTES]; |
770 | 0 | PACKET pkt; |
771 | | |
772 | | /* When loading from an explicit key, drop the seed. */ |
773 | 0 | OPENSSL_clear_free(key->seed, ML_DSA_SEED_BYTES); |
774 | 0 | key->seed = NULL; |
775 | | |
776 | | /* Allow the key encoding to be already set to the provided pointer */ |
777 | 0 | if ((key->priv_encoding != NULL && key->priv_encoding != in) |
778 | 0 | || key->pub_encoding != NULL) |
779 | 0 | return 0; /* Do not allow key mutation */ |
780 | 0 | if (in_len != key->params->sk_len) |
781 | 0 | return 0; |
782 | 0 | if (!ossl_ml_dsa_key_priv_alloc(key)) |
783 | 0 | return 0; |
784 | | |
785 | | /* eta is the range of private key coefficients (-eta...eta) */ |
786 | 0 | if (params->eta == ML_DSA_ETA_4) |
787 | 0 | decode_fn = poly_decode_signed_4; |
788 | 0 | else |
789 | 0 | decode_fn = poly_decode_signed_2; |
790 | |
|
791 | 0 | if (!PACKET_buf_init(&pkt, in, in_len) |
792 | 0 | || !PACKET_copy_bytes(&pkt, key->rho, sizeof(key->rho)) |
793 | 0 | || !PACKET_copy_bytes(&pkt, key->K, sizeof(key->K)) |
794 | 0 | || !PACKET_copy_bytes(&pkt, input_tr, sizeof(input_tr))) |
795 | 0 | return 0; |
796 | | |
797 | 0 | for (i = 0; i < l; ++i) |
798 | 0 | if (!decode_fn(key->s1.poly + i, &pkt)) |
799 | 0 | goto err; |
800 | 0 | for (i = 0; i < k; ++i) |
801 | 0 | if (!decode_fn(key->s2.poly + i, &pkt)) |
802 | 0 | goto err; |
803 | 0 | for (i = 0; i < k; ++i) |
804 | 0 | if (!poly_decode_signed_two_to_power_12(key->t0.poly + i, &pkt)) |
805 | 0 | goto err; |
806 | 0 | if (PACKET_remaining(&pkt) != 0) |
807 | 0 | goto err; |
808 | 0 | if (key->priv_encoding == NULL |
809 | 0 | && (key->priv_encoding = OPENSSL_memdup(in, in_len)) == NULL) |
810 | 0 | goto err; |
811 | | /* |
812 | | * Computing the public key also computes its hash, which must be equal to |
813 | | * the |tr| value in the private key, else the key was corrupted. |
814 | | */ |
815 | 0 | if (!ossl_ml_dsa_key_public_from_private(key) |
816 | 0 | || memcmp(input_tr, key->tr, sizeof(input_tr)) != 0) { |
817 | 0 | ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY, |
818 | 0 | "%s private key does not match its pubkey part", |
819 | 0 | key->params->alg); |
820 | 0 | ossl_ml_dsa_key_reset(key); |
821 | 0 | goto err; |
822 | 0 | } |
823 | | |
824 | 0 | return 1; |
825 | 0 | err: |
826 | 0 | return 0; |
827 | 0 | } |
828 | | |
829 | | /* |
830 | | * See FIPS 204, Algorithm 20, HintBitPack(). |
831 | | * Hint is composed of k polynomials with binary coefficients where only 'omega' |
832 | | * of all the coefficients are set to 1. |
833 | | * This can be encoded as a byte array of 'omega' polynomial coefficient index |
834 | | * positions for the coefficients that are set, followed by |
835 | | * k values of the last coefficient index used in each polynomial. |
836 | | */ |
837 | | static int hint_bits_encode(const VECTOR *hint, WPACKET *pkt, uint32_t omega) |
838 | 0 | { |
839 | 0 | int i, j, k = hint->num_poly; |
840 | 0 | size_t coeff_index = 0; |
841 | 0 | POLY *p = hint->poly; |
842 | 0 | uint8_t *data; |
843 | |
|
844 | 0 | if (!WPACKET_allocate_bytes(pkt, omega + k, &data)) |
845 | 0 | return 0; |
846 | 0 | memset(data, 0, omega + k); |
847 | |
|
848 | 0 | for (i = 0; i < k; i++, p++) { |
849 | 0 | for (j = 0; j < ML_DSA_NUM_POLY_COEFFICIENTS; j++) |
850 | 0 | if (p->coeff[j] != 0) |
851 | 0 | data[coeff_index++] = j; |
852 | 0 | data[omega + i] = (uint8_t)coeff_index; |
853 | 0 | } |
854 | 0 | return 1; |
855 | 0 | } |
856 | | |
857 | | /* |
858 | | * @brief Reverse the process of hint_bits_encode() |
859 | | * See FIPS 204, Algorithm 21, HintBitUnpack() |
860 | | * |
861 | | * @returns 1 if the hints were successfully unpacked, or 0 |
862 | | * if 'pkt' is too small or malformed. |
863 | | */ |
864 | | static int hint_bits_decode(VECTOR *hint, PACKET *pkt, uint32_t omega) |
865 | 0 | { |
866 | 0 | size_t coeff_index = 0, k = hint->num_poly; |
867 | 0 | const uint8_t *in, *limits; |
868 | 0 | POLY *p = hint->poly, *end = p + k; |
869 | |
|
870 | 0 | if (!PACKET_get_bytes(pkt, &in, omega) |
871 | 0 | || !PACKET_get_bytes(pkt, &limits, k)) |
872 | 0 | return 0; |
873 | | |
874 | 0 | vector_zero(hint); /* Set all coefficients to zero */ |
875 | |
|
876 | 0 | do { |
877 | 0 | const uint32_t limit = *limits++; |
878 | 0 | int last = -1; |
879 | |
|
880 | 0 | if (limit < coeff_index || limit > omega) |
881 | 0 | return 0; |
882 | | |
883 | 0 | while (coeff_index < limit) { |
884 | 0 | int byte = in[coeff_index++]; |
885 | |
|
886 | 0 | if (last >= 0 && byte <= last) |
887 | 0 | return 0; |
888 | 0 | last = byte; |
889 | 0 | p->coeff[byte] = 1; |
890 | 0 | } |
891 | 0 | } while (++p < end); |
892 | | |
893 | 0 | for (; coeff_index < omega; coeff_index++) |
894 | 0 | if (in[coeff_index] != 0) |
895 | 0 | return 0; |
896 | 0 | return 1; |
897 | 0 | } |
898 | | |
899 | | /* |
900 | | * @brief Encode a ML_DSA signature as an array of bytes. |
901 | | * See FIPS 204, Algorithm 26, sigEncode(). |
902 | | * |
903 | | * @param |
904 | | * @param |
905 | | * @returns 1 if the signature was encoded successfully or 0 otherwise. |
906 | | */ |
907 | | int ossl_ml_dsa_sig_encode(const ML_DSA_SIG *sig, const ML_DSA_PARAMS *params, |
908 | | uint8_t *out) |
909 | 0 | { |
910 | 0 | int ret = 0; |
911 | 0 | size_t i; |
912 | 0 | ENCODE_FN *encode_fn; |
913 | 0 | WPACKET pkt; |
914 | |
|
915 | 0 | if (out == NULL) |
916 | 0 | return 0; |
917 | | |
918 | 0 | if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) |
919 | 0 | encode_fn = poly_encode_signed_two_to_power_19; |
920 | 0 | else |
921 | 0 | encode_fn = poly_encode_signed_two_to_power_17; |
922 | |
|
923 | 0 | if (!WPACKET_init_static_len(&pkt, out, params->sig_len, 0) |
924 | 0 | || !WPACKET_memcpy(&pkt, sig->c_tilde, sig->c_tilde_len)) |
925 | 0 | goto err; |
926 | | |
927 | 0 | for (i = 0; i < sig->z.num_poly; ++i) |
928 | 0 | if (!encode_fn(sig->z.poly + i, &pkt)) |
929 | 0 | goto err; |
930 | 0 | if (!hint_bits_encode(&sig->hint, &pkt, params->omega)) |
931 | 0 | goto err; |
932 | 0 | ret = 1; |
933 | 0 | err: |
934 | 0 | WPACKET_finish(&pkt); |
935 | 0 | return ret; |
936 | 0 | } |
937 | | |
938 | | /* |
939 | | * @param sig is a initialized signature object to decode into. |
940 | | * @param in An encoded signature |
941 | | * @param in_len The size of |in| |
942 | | * @param params contains constants for an ML-DSA algorithm (such as gamma1) |
943 | | * @returns 1 if the signature was successfully decoded or 0 otherwise. |
944 | | */ |
945 | | int ossl_ml_dsa_sig_decode(ML_DSA_SIG *sig, const uint8_t *in, size_t in_len, |
946 | | const ML_DSA_PARAMS *params) |
947 | 0 | { |
948 | 0 | int ret = 0; |
949 | 0 | size_t i; |
950 | 0 | DECODE_FN *decode_fn; |
951 | 0 | PACKET pkt; |
952 | |
|
953 | 0 | if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) |
954 | 0 | decode_fn = poly_decode_signed_two_to_power_19; |
955 | 0 | else |
956 | 0 | decode_fn = poly_decode_signed_two_to_power_17; |
957 | |
|
958 | 0 | if (!PACKET_buf_init(&pkt, in, in_len) |
959 | 0 | || !PACKET_copy_bytes(&pkt, sig->c_tilde, sig->c_tilde_len)) |
960 | 0 | goto err; |
961 | 0 | for (i = 0; i < sig->z.num_poly; ++i) |
962 | 0 | if (!decode_fn(sig->z.poly + i, &pkt)) |
963 | 0 | goto err; |
964 | | |
965 | 0 | if (!hint_bits_decode(&sig->hint, &pkt, params->omega) |
966 | 0 | || PACKET_remaining(&pkt) != 0) |
967 | 0 | goto err; |
968 | 0 | ret = 1; |
969 | 0 | err: |
970 | 0 | return ret; |
971 | 0 | } |
972 | | |
973 | | int ossl_ml_dsa_poly_decode_expand_mask(POLY *out, |
974 | | const uint8_t *in, size_t in_len, |
975 | | uint32_t gamma1) |
976 | 0 | { |
977 | 0 | PACKET pkt; |
978 | |
|
979 | 0 | if (!PACKET_buf_init(&pkt, in, in_len)) |
980 | 0 | return 0; |
981 | 0 | if (gamma1 == ML_DSA_GAMMA1_TWO_POWER_19) |
982 | 0 | return poly_decode_signed_two_to_power_19(out, &pkt); |
983 | 0 | else |
984 | 0 | return poly_decode_signed_two_to_power_17(out, &pkt); |
985 | 0 | } |
986 | | |
987 | | /* |
988 | | * @brief Encode a polynomial vector as an array of bytes. |
989 | | * Where the polynomial coefficients have a range of [0..15] or [0..43] |
990 | | * depending on the value of gamma2. |
991 | | * |
992 | | * See FIPS 204, Algorithm 28, w1Encode(). |
993 | | * |
994 | | * @param w1 The vector to convert to bytes |
995 | | * @param gamma2 either ML_DSA_GAMMA2_Q_MINUS1_DIV32 or ML_DSA_GAMMA2_Q_MINUS1_DIV88 |
996 | | * @returns 1 if the signature was encoded successfully or 0 otherwise. |
997 | | */ |
998 | | int ossl_ml_dsa_w1_encode(const VECTOR *w1, uint32_t gamma2, |
999 | | uint8_t *out, size_t out_len) |
1000 | 0 | { |
1001 | 0 | WPACKET pkt; |
1002 | 0 | ENCODE_FN *encode_fn; |
1003 | 0 | int ret = 0; |
1004 | 0 | size_t i; |
1005 | |
|
1006 | 0 | if (!WPACKET_init_static_len(&pkt, out, out_len, 0)) |
1007 | 0 | return 0; |
1008 | 0 | if (gamma2 == ML_DSA_GAMMA2_Q_MINUS1_DIV32) |
1009 | 0 | encode_fn = poly_encode_4_bits; |
1010 | 0 | else |
1011 | 0 | encode_fn = poly_encode_6_bits; |
1012 | 0 | for (i = 0; i < w1->num_poly; ++i) |
1013 | 0 | if (!encode_fn(w1->poly + i, &pkt)) |
1014 | 0 | goto err; |
1015 | 0 | ret = 1; |
1016 | 0 | err: |
1017 | 0 | WPACKET_finish(&pkt); |
1018 | 0 | return ret; |
1019 | 0 | } |