Coverage Report

Created: 2026-10-28 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openssl41/crypto/ml_dsa/ml_dsa_encoders.c
Line
Count
Source
1
/*
2
 * Copyright 2024-2026 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
65.7k
#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
3.72M
#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
23.6k
{
55
23.6k
    uint8_t *out;
56
23.6k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
57
58
23.6k
    if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(4), &out))
59
0
        return 0;
60
61
3.02M
    do {
62
3.02M
        uint32_t z0 = *in++;
63
3.02M
        uint32_t z1 = *in++;
64
65
3.02M
        *out++ = z0 | (z1 << 4);
66
3.02M
    } while (in < end);
67
23.6k
    return 1;
68
23.6k
}
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
8.70k
{
92
8.70k
    uint8_t *out;
93
8.70k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
94
95
8.70k
    if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(6), &out))
96
0
        return 0;
97
98
557k
    do {
99
557k
        uint32_t c0 = *in++;
100
557k
        uint32_t c1 = *in++;
101
557k
        uint32_t c2 = *in++;
102
557k
        uint32_t c3 = *in++;
103
104
557k
        *out++ = c0 | (c1 << 6);
105
557k
        *out++ = (c1 >> 2) | (c2 << 4);
106
557k
        *out++ = (c2 >> 4) | (c3 << 2);
107
557k
    } while (in < end);
108
8.70k
    return 1;
109
8.70k
}
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
14.5k
{
132
14.5k
    uint8_t *out;
133
14.5k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
134
135
14.5k
    if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(10), &out))
136
0
        return 0;
137
138
930k
    do {
139
930k
        uint32_t c0 = *in++;
140
930k
        uint32_t c1 = *in++;
141
930k
        uint32_t c2 = *in++;
142
930k
        uint32_t c3 = *in++;
143
144
930k
        *out++ = (uint8_t)c0;
145
930k
        *out++ = (uint8_t)((c0 >> 8) | (c1 << 2));
146
930k
        *out++ = (uint8_t)((c1 >> 6) | (c2 << 4));
147
930k
        *out++ = (uint8_t)((c2 >> 4) | (c3 << 6));
148
930k
        *out++ = (uint8_t)(c3 >> 2);
149
930k
    } while (in < end);
150
14.5k
    return 1;
151
14.5k
}
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
968
{
164
968
    const uint8_t *in = NULL;
165
968
    uint32_t v, w, mask = 0x3ff; /* 10 bits */
166
968
    uint32_t *out = p->coeff, *end = out + ML_DSA_NUM_POLY_COEFFICIENTS;
167
168
61.9k
    do {
169
61.9k
        if (!PACKET_get_bytes(pkt, &in, 5))
170
0
            return 0;
171
172
61.9k
        in = OPENSSL_load_u32_le(&v, in);
173
61.9k
        w = *in;
174
175
61.9k
        *out++ = v & mask;
176
61.9k
        *out++ = (v >> 10) & mask;
177
61.9k
        *out++ = (v >> 20) & mask;
178
61.9k
        *out++ = (v >> 30) | (w << 2);
179
61.9k
    } while (out < end);
180
968
    return 1;
181
968
}
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
8.59k
{
201
8.59k
    uint8_t *out;
202
8.59k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
203
204
8.59k
    if (!WPACKET_allocate_bytes(pkt, 32 * 4, &out))
205
0
        return 0;
206
207
1.09M
    do {
208
1.09M
        uint32_t z = mod_sub(4, *in++);
209
210
1.09M
        *out++ = z | (mod_sub(4, *in++) << 4);
211
1.09M
    } while (in < end);
212
8.59k
    return 1;
213
8.59k
}
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
145
{
227
145
    int i, ret = 0;
228
145
    uint32_t v, *out = p->coeff;
229
145
    const uint8_t *in;
230
145
    uint32_t msbs, mask;
231
232
3.05k
    for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) {
233
2.97k
        if (!PACKET_get_bytes(pkt, &in, 4))
234
0
            goto err;
235
2.97k
        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
2.97k
        msbs = v & 0x88888888u;
242
        /* For each nibble where the MSB is set, form a mask of all the other bits. */
243
2.97k
        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
2.97k
        if (value_barrier_32((mask & v) != 0))
249
61
            goto err;
250
251
2.90k
        *out++ = mod_sub(4, v & 15);
252
2.90k
        *out++ = mod_sub(4, (v >> 4) & 15);
253
2.90k
        *out++ = mod_sub(4, (v >> 8) & 15);
254
2.90k
        *out++ = mod_sub(4, (v >> 12) & 15);
255
2.90k
        *out++ = mod_sub(4, (v >> 16) & 15);
256
2.90k
        *out++ = mod_sub(4, (v >> 20) & 15);
257
2.90k
        *out++ = mod_sub(4, (v >> 24) & 15);
258
2.90k
        *out++ = mod_sub(4, v >> 28);
259
2.90k
    }
260
84
    ret = 1;
261
145
err:
262
145
    return ret;
263
84
}
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
18.9k
{
289
18.9k
    uint8_t *out;
290
18.9k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
291
292
18.9k
    if (!WPACKET_allocate_bytes(pkt, POLY_COEFF_NUM_BYTES(3), &out))
293
0
        return 0;
294
295
605k
    do {
296
605k
        uint32_t z;
297
298
605k
        z = mod_sub(2, *in++);
299
605k
        z |= mod_sub(2, *in++) << 3;
300
605k
        z |= mod_sub(2, *in++) << 6;
301
605k
        z |= mod_sub(2, *in++) << 9;
302
605k
        z |= mod_sub(2, *in++) << 12;
303
605k
        z |= mod_sub(2, *in++) << 15;
304
605k
        z |= mod_sub(2, *in++) << 18;
305
605k
        z |= mod_sub(2, *in++) << 21;
306
307
605k
        out = OPENSSL_store_u16_le(out, (uint16_t)z);
308
605k
        *out++ = (uint8_t)(z >> 16);
309
605k
    } while (in < end);
310
18.9k
    return 1;
311
18.9k
}
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
32
{
325
32
    int i, ret = 0;
326
32
    uint32_t u = 0, v = 0, *out = p->coeff;
327
32
    uint32_t msbs, mask;
328
32
    const uint8_t *in;
329
330
33
    for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) {
331
33
        if (!PACKET_get_bytes(pkt, &in, 3))
332
0
            goto err;
333
33
        memcpy(&u, in, 3);
334
33
        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
33
        msbs = v & 044444444;
342
        /* For each octal value where the MSB is set, form a mask of the 2 other bits. */
343
33
        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
33
        if (value_barrier_32((mask & v) != 0))
349
32
            goto err;
350
351
1
        *out++ = mod_sub(2, v & 7);
352
1
        *out++ = mod_sub(2, (v >> 3) & 7);
353
1
        *out++ = mod_sub(2, (v >> 6) & 7);
354
1
        *out++ = mod_sub(2, (v >> 9) & 7);
355
1
        *out++ = mod_sub(2, (v >> 12) & 7);
356
1
        *out++ = mod_sub(2, (v >> 15) & 7);
357
1
        *out++ = mod_sub(2, (v >> 18) & 7);
358
1
        *out++ = mod_sub(2, (v >> 21) & 7);
359
1
    }
360
0
    ret = 1;
361
32
err:
362
32
    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
14.5k
{
388
14.5k
    static const uint32_t range = 1u << 12;
389
14.5k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
390
391
465k
    do {
392
465k
        uint8_t *out;
393
465k
        uint64_t a1, a2;
394
395
465k
        if (!WPACKET_allocate_bytes(pkt, 13, &out))
396
0
            return 0;
397
398
465k
        a1 = mod_sub_64(range, *in++);
399
465k
        a1 |= mod_sub_64(range, *in++) << 13;
400
465k
        a1 |= mod_sub_64(range, *in++) << 26;
401
465k
        a1 |= mod_sub_64(range, *in++) << 39;
402
465k
        a1 |= (a2 = mod_sub_64(range, *in++)) << 52;
403
465k
        a2 = (a2 >> 12) | (mod_sub_64(range, *in++) << 1);
404
465k
        a2 |= mod_sub_64(range, *in++) << 14;
405
465k
        a2 |= mod_sub_64(range, *in++) << 27;
406
407
465k
        out = OPENSSL_store_u64_le(out, a1);
408
465k
        out = OPENSSL_store_u32_le(out, (uint32_t)a2);
409
465k
        *out = (uint8_t)(a2 >> 32);
410
465k
    } while (in < end);
411
14.5k
    return 1;
412
14.5k
}
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
12
{
425
12
    int i, ret = 0;
426
12
    uint32_t *out = p->coeff;
427
12
    const uint8_t *in;
428
12
    static const uint32_t range = 1u << 12;
429
12
    static const uint32_t mask_13_bits = (1u << 13) - 1;
430
431
396
    for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 8); i++) {
432
384
        uint64_t a1;
433
384
        uint32_t a2, b13;
434
435
384
        if (!PACKET_get_bytes(pkt, &in, 13))
436
0
            goto err;
437
384
        in = OPENSSL_load_u64_le(&a1, in);
438
384
        in = OPENSSL_load_u32_le(&a2, in);
439
384
        b13 = (uint32_t)*in;
440
441
384
        *out++ = mod_sub(range, a1 & mask_13_bits);
442
384
        *out++ = mod_sub(range, (a1 >> 13) & mask_13_bits);
443
384
        *out++ = mod_sub(range, (a1 >> 26) & mask_13_bits);
444
384
        *out++ = mod_sub(range, (a1 >> 39) & mask_13_bits);
445
384
        *out++ = mod_sub(range, (a1 >> 52) | ((a2 << 12) & mask_13_bits));
446
384
        *out++ = mod_sub(range, (a2 >> 1) & mask_13_bits);
447
384
        *out++ = mod_sub(range, (a2 >> 14) & mask_13_bits);
448
384
        *out++ = mod_sub(range, (a2 >> 27) | (b13 << 5));
449
384
    }
450
12
    ret = 1;
451
12
err:
452
12
    return ret;
453
12
}
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
3.65k
{
477
3.65k
    static const uint32_t range = 1u << 19;
478
3.65k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
479
480
233k
    do {
481
233k
        uint32_t z0, z1, z2;
482
233k
        uint8_t *out;
483
484
233k
        if (!WPACKET_allocate_bytes(pkt, 10, &out))
485
0
            return 0;
486
487
233k
        z0 = mod_sub(range, *in++);
488
233k
        z0 |= (z1 = mod_sub(range, *in++)) << 20;
489
233k
        z1 = (z1 >> 12) | (mod_sub(range, *in++) << 8);
490
233k
        z1 |= (z2 = mod_sub(range, *in++)) << 28;
491
492
233k
        out = OPENSSL_store_u32_le(out, z0);
493
233k
        out = OPENSSL_store_u32_le(out, z1);
494
233k
        out = OPENSSL_store_u16_le(out, (uint16_t)(z2 >> 4));
495
233k
    } while (in < end);
496
3.65k
    return 1;
497
3.65k
}
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
20.1k
{
510
20.1k
    int i, ret = 0;
511
20.1k
    uint32_t *out = p->coeff;
512
20.1k
    const uint8_t *in;
513
20.1k
    static const uint32_t range = 1u << 19;
514
20.1k
    static const uint32_t mask_20_bits = (1u << 20) - 1;
515
516
1.30M
    for (i = 0; i < (ML_DSA_NUM_POLY_COEFFICIENTS / 4); i++) {
517
1.28M
        uint32_t a1, a2;
518
1.28M
        uint16_t a3;
519
520
1.28M
        if (!PACKET_get_bytes(pkt, &in, 10))
521
0
            goto err;
522
1.28M
        in = OPENSSL_load_u32_le(&a1, in);
523
1.28M
        in = OPENSSL_load_u32_le(&a2, in);
524
1.28M
        in = OPENSSL_load_u16_le(&a3, in);
525
526
1.28M
        *out++ = mod_sub(range, a1 & mask_20_bits);
527
1.28M
        *out++ = mod_sub(range, (a1 >> 20) | ((a2 & 0xFF) << 12));
528
1.28M
        *out++ = mod_sub(range, (a2 >> 8) & mask_20_bits);
529
1.28M
        *out++ = mod_sub(range, (a2 >> 28) | (a3 << 4));
530
1.28M
    }
531
20.1k
    ret = 1;
532
20.1k
err:
533
20.1k
    return ret;
534
20.1k
}
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
1.64k
{
558
1.64k
    static const uint32_t range = 1u << 17;
559
1.64k
    const uint32_t *in = p->coeff, *end = in + ML_DSA_NUM_POLY_COEFFICIENTS;
560
561
104k
    do {
562
104k
        uint8_t *out;
563
104k
        uint32_t z0, z1, z2;
564
565
104k
        if (!WPACKET_allocate_bytes(pkt, 9, &out))
566
0
            return 0;
567
568
104k
        z0 = mod_sub(range, *in++);
569
104k
        z0 |= (z1 = mod_sub(range, *in++)) << 18;
570
104k
        z1 = (z1 >> 14) | (mod_sub(range, *in++) << 4);
571
104k
        z1 |= (z2 = mod_sub(range, *in++)) << 22;
572
573
104k
        out = OPENSSL_store_u32_le(out, z0);
574
104k
        out = OPENSSL_store_u32_le(out, z1);
575
104k
        *out = z2 >> 10;
576
104k
    } while (in < end);
577
1.64k
    return 1;
578
1.64k
}
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
8.95k
{
591
8.95k
    uint32_t *out = p->coeff;
592
8.95k
    const uint32_t *end = out + ML_DSA_NUM_POLY_COEFFICIENTS;
593
8.95k
    const uint8_t *in;
594
8.95k
    static const uint32_t range = 1u << 17;
595
8.95k
    static const uint32_t mask_18_bits = (1u << 18) - 1;
596
597
572k
    do {
598
572k
        uint32_t a1, a2, a3;
599
600
572k
        if (!PACKET_get_bytes(pkt, &in, 9))
601
0
            return 0;
602
572k
        in = OPENSSL_load_u32_le(&a1, in);
603
572k
        in = OPENSSL_load_u32_le(&a2, in);
604
572k
        a3 = (uint32_t)*in;
605
606
572k
        *out++ = mod_sub(range, a1 & mask_18_bits);
607
572k
        *out++ = mod_sub(range, (a1 >> 18) | ((a2 & 0xF) << 14));
608
572k
        *out++ = mod_sub(range, (a2 >> 4) & mask_18_bits);
609
572k
        *out++ = mod_sub(range, (a2 >> 22) | (a3 << 10));
610
572k
    } while (out < end);
611
8.95k
    return 1;
612
8.95k
}
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
2.47k
{
624
2.47k
    int ret = 0;
625
2.47k
    size_t i, written = 0;
626
2.47k
    const POLY *t1 = key->t1.poly;
627
2.47k
    size_t t1_len = key->t1.num_poly;
628
2.47k
    size_t enc_len = key->params->pk_len;
629
2.47k
    uint8_t *enc = OPENSSL_malloc(enc_len);
630
2.47k
    WPACKET pkt;
631
632
2.47k
    if (enc == NULL)
633
0
        return 0;
634
635
2.47k
    if (!WPACKET_init_static_len(&pkt, enc, enc_len, 0)
636
2.47k
        || !WPACKET_memcpy(&pkt, key->rho, sizeof(key->rho)))
637
0
        goto err;
638
17.0k
    for (i = 0; i < t1_len; i++)
639
14.5k
        if (!poly_encode_10_bits(t1 + i, &pkt))
640
0
            goto err;
641
2.47k
    if (!WPACKET_get_total_written(&pkt, &written)
642
2.47k
        || written != enc_len)
643
0
        goto err;
644
2.47k
    OPENSSL_free(key->pub_encoding);
645
2.47k
    key->pub_encoding = enc;
646
2.47k
    ret = 1;
647
2.47k
err:
648
2.47k
    WPACKET_finish(&pkt);
649
2.47k
    if (ret == 0)
650
0
        OPENSSL_free(enc);
651
2.47k
    return ret;
652
2.47k
}
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
221
{
666
221
    int ret = 0;
667
221
    size_t i;
668
221
    PACKET pkt;
669
221
    EVP_MD_CTX *ctx;
670
671
221
    if (key->priv_encoding != NULL || key->pub_encoding != NULL)
672
0
        return 0; /* Do not allow key mutation */
673
221
    if (in_len != key->params->pk_len)
674
0
        return 0;
675
676
221
    if (!ossl_ml_dsa_key_pub_alloc(key))
677
0
        return 0;
678
221
    ctx = EVP_MD_CTX_new();
679
221
    if (ctx == NULL)
680
0
        goto err;
681
221
    if (!PACKET_buf_init(&pkt, in, in_len)
682
221
        || !PACKET_copy_bytes(&pkt, key->rho, sizeof(key->rho)))
683
0
        goto err;
684
1.18k
    for (i = 0; i < key->t1.num_poly; i++)
685
968
        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
221
    if (!shake_xof(ctx, key->shake256_md, in, in_len, key->tr, sizeof(key->tr)))
690
0
        goto err;
691
692
221
    key->pub_encoding = OPENSSL_memdup(in, in_len);
693
221
    ret = (key->pub_encoding != NULL);
694
221
err:
695
221
    EVP_MD_CTX_free(ctx);
696
221
    return ret;
697
221
}
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
2.47k
{
709
2.47k
    int ret = 0;
710
2.47k
    const ML_DSA_PARAMS *params = key->params;
711
2.47k
    size_t i, written = 0, k = params->k, l = params->l;
712
2.47k
    ENCODE_FN *encode_fn;
713
2.47k
    size_t enc_len = params->sk_len;
714
2.47k
    const POLY *t0 = key->t0.poly;
715
2.47k
    WPACKET pkt;
716
2.47k
    uint8_t *enc = OPENSSL_secure_malloc(enc_len);
717
718
2.47k
    if (enc == NULL)
719
0
        return 0;
720
721
    /* eta is the range of private key coefficients (-eta...eta) */
722
2.47k
    if (params->eta == ML_DSA_ETA_4)
723
781
        encode_fn = poly_encode_signed_4;
724
1.69k
    else
725
1.69k
        encode_fn = poly_encode_signed_2;
726
727
2.47k
    if (!WPACKET_init_static_len(&pkt, enc, enc_len, 0)
728
2.47k
        || !WPACKET_memcpy(&pkt, key->rho, sizeof(key->rho))
729
2.47k
        || !WPACKET_memcpy(&pkt, key->K, sizeof(key->K))
730
2.47k
        || !WPACKET_memcpy(&pkt, key->tr, sizeof(key->tr)))
731
0
        goto err;
732
15.4k
    for (i = 0; i < l; ++i)
733
12.9k
        if (!encode_fn(key->s1.poly + i, &pkt))
734
0
            goto err;
735
17.0k
    for (i = 0; i < k; ++i)
736
14.5k
        if (!encode_fn(key->s2.poly + i, &pkt))
737
0
            goto err;
738
17.0k
    for (i = 0; i < k; ++i)
739
14.5k
        if (!poly_encode_signed_two_to_power_12(t0++, &pkt))
740
0
            goto err;
741
2.47k
    if (!WPACKET_get_total_written(&pkt, &written)
742
2.47k
        || written != enc_len)
743
0
        goto err;
744
2.47k
    OPENSSL_secure_clear_free(key->priv_encoding, enc_len);
745
2.47k
    key->priv_encoding = enc;
746
2.47k
    ret = 1;
747
2.47k
err:
748
2.47k
    WPACKET_finish(&pkt);
749
2.47k
    if (ret == 0)
750
0
        OPENSSL_secure_clear_free(enc, enc_len);
751
2.47k
    return ret;
752
2.47k
}
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
53
{
766
53
    DECODE_FN *decode_fn;
767
53
    const ML_DSA_PARAMS *params = key->params;
768
53
    size_t i, k = params->k, l = params->l;
769
53
    uint8_t input_tr[ML_DSA_TR_BYTES];
770
53
    PACKET pkt;
771
772
    /* When loading from an explicit key, drop the seed. */
773
53
    OPENSSL_secure_clear_free(key->seed, ML_DSA_SEED_BYTES);
774
53
    key->seed = NULL;
775
776
    /* Allow the key encoding to be already set to the provided pointer */
777
53
    if ((key->priv_encoding != NULL && key->priv_encoding != in)
778
53
        || key->pub_encoding != NULL)
779
0
        return 0; /* Do not allow key mutation */
780
53
    if (in_len != key->params->sk_len)
781
0
        return 0;
782
53
    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
53
    if (params->eta == ML_DSA_ETA_4)
787
27
        decode_fn = poly_decode_signed_4;
788
26
    else
789
26
        decode_fn = poly_decode_signed_2;
790
791
53
    if (!PACKET_buf_init(&pkt, in, in_len)
792
53
        || !PACKET_copy_bytes(&pkt, key->rho, sizeof(key->rho))
793
53
        || !PACKET_copy_bytes(&pkt, key->K, sizeof(key->K))
794
53
        || !PACKET_copy_bytes(&pkt, input_tr, sizeof(input_tr)))
795
0
        return 0;
796
797
53
    for (i = 0; i < l; ++i)
798
53
        if (!decode_fn(key->s1.poly + i, &pkt))
799
53
            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_secure_malloc(in_len);
810
0
        if (key->priv_encoding == NULL)
811
0
            goto err;
812
0
        memcpy(key->priv_encoding, in, in_len);
813
0
    }
814
    /*
815
     * Computing the public key also computes its hash, which must be equal to
816
     * the |tr| value in the private key, else the key was corrupted.
817
     */
818
0
    if (!ossl_ml_dsa_key_public_from_private(key)
819
0
        || memcmp(input_tr, key->tr, sizeof(input_tr)) != 0) {
820
0
        ERR_raise_data(ERR_LIB_PROV, PROV_R_INVALID_KEY,
821
0
            "%s private key does not match its pubkey part",
822
0
            key->params->alg);
823
0
        ossl_ml_dsa_key_reset(key);
824
0
        goto err;
825
0
    }
826
827
0
    return 1;
828
53
err:
829
53
    return 0;
830
0
}
831
832
/*
833
 * See FIPS 204, Algorithm 20, HintBitPack().
834
 * Hint is composed of k polynomials with binary coefficients where only 'omega'
835
 * of all the coefficients are set to 1.
836
 * This can be encoded as a byte array of 'omega' polynomial coefficient index
837
 * positions for the coefficients that are set, followed by
838
 * k values of the last coefficient index used in each polynomial.
839
 */
840
static int hint_bits_encode(const VECTOR *hint, WPACKET *pkt, uint32_t omega)
841
1.02k
{
842
1.02k
    int i, j, k = (int)hint->num_poly;
843
1.02k
    size_t coeff_index = 0;
844
1.02k
    POLY *p = hint->poly;
845
1.02k
    uint8_t *data;
846
847
1.02k
    if (!WPACKET_allocate_bytes(pkt, omega + k, &data))
848
0
        return 0;
849
1.02k
    memset(data, 0, omega + k);
850
851
6.93k
    for (i = 0; i < k; i++, p++) {
852
1.51M
        for (j = 0; j < ML_DSA_NUM_POLY_COEFFICIENTS; j++)
853
1.51M
            if (p->coeff[j] != 0)
854
54.4k
                data[coeff_index++] = j;
855
5.91k
        data[omega + i] = (uint8_t)coeff_index;
856
5.91k
    }
857
1.02k
    return 1;
858
1.02k
}
859
860
/*
861
 * @brief Reverse the process of hint_bits_encode()
862
 * See FIPS 204, Algorithm 21, HintBitUnpack()
863
 *
864
 * @returns 1 if the hints were successfully unpacked, or 0
865
 * if 'pkt' is too small or malformed.
866
 */
867
static int hint_bits_decode(VECTOR *hint, PACKET *pkt, uint32_t omega)
868
1.15k
{
869
1.15k
    size_t coeff_index = 0, k = hint->num_poly;
870
1.15k
    const uint8_t *in, *limits;
871
1.15k
    POLY *p = hint->poly, *end = p + k;
872
873
1.15k
    if (!PACKET_get_bytes(pkt, &in, omega)
874
1.15k
        || !PACKET_get_bytes(pkt, &limits, k))
875
0
        return 0;
876
877
1.15k
    vector_zero(hint); /* Set all coefficients to zero */
878
879
6.26k
    do {
880
6.26k
        const uint32_t limit = *limits++;
881
6.26k
        int last = -1;
882
883
6.26k
        if (limit < coeff_index || limit > omega)
884
26
            return 0;
885
886
63.0k
        while (coeff_index < limit) {
887
56.8k
            int byte = in[coeff_index++];
888
889
56.8k
            if (last >= 0 && byte <= last)
890
28
                return 0;
891
56.7k
            last = byte;
892
56.7k
            p->coeff[byte] = 1;
893
56.7k
        }
894
6.23k
    } while (++p < end);
895
896
22.3k
    for (; coeff_index < omega; coeff_index++)
897
21.2k
        if (in[coeff_index] != 0)
898
7
            return 0;
899
1.09k
    return 1;
900
1.09k
}
901
902
/*
903
 * @brief Encode a ML_DSA signature as an array of bytes.
904
 * See FIPS 204, Algorithm 26, sigEncode().
905
 *
906
 * @param
907
 * @param
908
 * @returns 1 if the signature was encoded successfully or 0 otherwise.
909
 */
910
int ossl_ml_dsa_sig_encode(const ML_DSA_SIG *sig, const ML_DSA_PARAMS *params,
911
    uint8_t *out)
912
1.02k
{
913
1.02k
    int ret = 0;
914
1.02k
    size_t i;
915
1.02k
    ENCODE_FN *encode_fn;
916
1.02k
    WPACKET pkt;
917
918
1.02k
    if (out == NULL)
919
0
        return 0;
920
921
1.02k
    if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19)
922
615
        encode_fn = poly_encode_signed_two_to_power_19;
923
410
    else
924
410
        encode_fn = poly_encode_signed_two_to_power_17;
925
926
1.02k
    if (!WPACKET_init_static_len(&pkt, out, params->sig_len, 0)
927
1.02k
        || !WPACKET_memcpy(&pkt, sig->c_tilde, sig->c_tilde_len))
928
0
        goto err;
929
930
6.32k
    for (i = 0; i < sig->z.num_poly; ++i)
931
5.29k
        if (!encode_fn(sig->z.poly + i, &pkt))
932
0
            goto err;
933
1.02k
    if (!hint_bits_encode(&sig->hint, &pkt, params->omega))
934
0
        goto err;
935
1.02k
    ret = 1;
936
1.02k
err:
937
1.02k
    WPACKET_finish(&pkt);
938
    /* Erase any partial signature output on failure */
939
1.02k
    if (ret == 0)
940
0
        OPENSSL_cleanse(out, params->sig_len);
941
1.02k
    return ret;
942
1.02k
}
943
944
/*
945
 * @param sig is a initialized signature object to decode into.
946
 * @param in An encoded signature
947
 * @param in_len The size of |in|
948
 * @param params contains constants for an ML-DSA algorithm (such as gamma1)
949
 * @returns 1 if the signature was successfully decoded or 0 otherwise.
950
 */
951
int ossl_ml_dsa_sig_decode(ML_DSA_SIG *sig, const uint8_t *in, size_t in_len,
952
    const ML_DSA_PARAMS *params)
953
1.15k
{
954
1.15k
    int ret = 0;
955
1.15k
    size_t i;
956
1.15k
    DECODE_FN *decode_fn;
957
1.15k
    PACKET pkt;
958
959
1.15k
    if (params->gamma1 == ML_DSA_GAMMA1_TWO_POWER_19)
960
615
        decode_fn = poly_decode_signed_two_to_power_19;
961
536
    else
962
536
        decode_fn = poly_decode_signed_two_to_power_17;
963
964
1.15k
    if (!PACKET_buf_init(&pkt, in, in_len)
965
1.15k
        || !PACKET_copy_bytes(&pkt, sig->c_tilde, sig->c_tilde_len))
966
0
        goto err;
967
6.95k
    for (i = 0; i < sig->z.num_poly; ++i)
968
5.79k
        if (!decode_fn(sig->z.poly + i, &pkt))
969
0
            goto err;
970
971
1.15k
    if (!hint_bits_decode(&sig->hint, &pkt, params->omega)
972
1.09k
        || PACKET_remaining(&pkt) != 0)
973
61
        goto err;
974
1.09k
    ret = 1;
975
1.15k
err:
976
1.15k
    return ret;
977
1.09k
}
978
979
int ossl_ml_dsa_poly_decode_expand_mask(POLY *out,
980
    const uint8_t *in, size_t in_len,
981
    uint32_t gamma1)
982
23.2k
{
983
23.2k
    PACKET pkt;
984
985
23.2k
    if (!PACKET_buf_init(&pkt, in, in_len))
986
0
        return 0;
987
23.2k
    if (gamma1 == ML_DSA_GAMMA1_TWO_POWER_19)
988
16.4k
        return poly_decode_signed_two_to_power_19(out, &pkt);
989
6.80k
    else
990
6.80k
        return poly_decode_signed_two_to_power_17(out, &pkt);
991
23.2k
}
992
993
/*
994
 * @brief Encode a polynomial vector as an array of bytes.
995
 * Where the polynomial coefficients have a range of [0..15] or [0..43]
996
 * depending on the value of gamma2.
997
 *
998
 * See FIPS 204, Algorithm 28, w1Encode().
999
 *
1000
 * @param w1 The vector to convert to bytes
1001
 * @param gamma2 either ML_DSA_GAMMA2_Q_MINUS1_DIV32 or ML_DSA_GAMMA2_Q_MINUS1_DIV88
1002
 * @returns 1 if the signature was encoded successfully or 0 otherwise.
1003
 */
1004
int ossl_ml_dsa_w1_encode(const VECTOR *w1, uint32_t gamma2,
1005
    uint8_t *out, size_t out_len)
1006
5.64k
{
1007
5.64k
    WPACKET pkt;
1008
5.64k
    ENCODE_FN *encode_fn;
1009
5.64k
    int ret = 0;
1010
5.64k
    size_t i;
1011
1012
5.64k
    if (!WPACKET_init_static_len(&pkt, out, out_len, 0))
1013
0
        return 0;
1014
5.64k
    if (gamma2 == ML_DSA_GAMMA2_Q_MINUS1_DIV32)
1015
3.47k
        encode_fn = poly_encode_4_bits;
1016
2.17k
    else
1017
2.17k
        encode_fn = poly_encode_6_bits;
1018
37.9k
    for (i = 0; i < w1->num_poly; ++i)
1019
32.3k
        if (!encode_fn(w1->poly + i, &pkt))
1020
0
            goto err;
1021
5.64k
    ret = 1;
1022
5.64k
err:
1023
5.64k
    WPACKET_finish(&pkt);
1024
5.64k
    return ret;
1025
5.64k
}