Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_mldsa_sample.c
Go to the documentation of this file.
1/*****************************************************************************
2 * (c) 2026 Ledger SAS.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *****************************************************************************/
24#include <string.h>
25#include "cx_mldsa_sample.h"
26#include "cx_mldsa_util.h"
27
28// SHAKE128 block size for uniform sampling.
29#define MLDSA_XOF_BLOCKBYTES 168U
30
31// Rejection sampling buffer for uniform sampling.
32// 5 SHAKE128 blocks = 840 bytes = 280 candidates (3 bytes each).
33// Acceptance rate is q/2^23 ≈ 0.9994, so 280 candidates are vastly
34// more than the 256 needed.
35#define MLDSA_REJ_UNIFORM_NBLOCKS 5U
36#define MLDSA_REJ_UNIFORM_BUFLEN (MLDSA_REJ_UNIFORM_NBLOCKS * MLDSA_XOF_BLOCKBYTES)
37
38void MLDSA_SAMPLE_uniform(mldsa_poly *a, const uint8_t seed[MLDSA_SEEDBYTES], uint16_t nonce)
39{
40 uint8_t buf[MLDSA_REJ_UNIFORM_BUFLEN];
41 uint32_t ctr = 0U;
42
43 MLDSA_UTIL_shake128_seed_nonce(buf, sizeof(buf), seed, MLDSA_SEEDBYTES, nonce);
44
45 uint32_t pos = 0U;
46 while ((ctr < MLDSA_N) && ((pos + 3U) <= sizeof(buf))) {
47 uint32_t t;
48 t = (uint32_t) buf[pos++];
49 t |= (uint32_t) buf[pos++] << 8U;
50 t |= (uint32_t) buf[pos++] << 16U;
51 t &= 0x7FFFFFU;
52
53 if (t < (uint32_t) MLDSA_Q) {
54 a->coeffs[ctr] = (int32_t) t;
55 ctr++;
56 }
57 }
58
59 // Fill remaining coefficients with 0 if exhausted (unlikely)
60 while (ctr < MLDSA_N) {
61 a->coeffs[ctr++] = 0;
62 }
63
64 explicit_bzero(buf, sizeof(buf));
65}
66
68 const uint8_t seed[MLDSA_CRHBYTES],
69 uint16_t nonce,
70 uint8_t eta)
71{
72 // SHAKE256 rate is 136 bytes.
73 // For eta=2: P(accept) = 15/16 per nibble. 2 blocks = 272 bytes = 544 nibbles,
74 // expected ~510 accepted. Very safe for 256 needed.
75 // For eta=4: P(accept) = 9/16 per nibble. 4 blocks = 544 bytes = 1088 nibbles,
76 // expected ~612 accepted. Very safe for 256 needed.
77 uint8_t buf[136U * 4U];
78 size_t buflen;
79
80 if (eta == 2U) {
81 buflen = 136U * 2U;
82 }
83 else {
84 buflen = 136U * 4U;
85 }
86
87 MLDSA_UTIL_shake256_seed_nonce(buf, buflen, seed, MLDSA_CRHBYTES, nonce);
88
89 uint32_t ctr = 0U;
90 uint32_t pos = 0U;
91
92 if (eta == 2U) {
93 while ((ctr < MLDSA_N) && (pos < buflen)) {
94 uint8_t t0 = buf[pos] & 0x0FU;
95 uint8_t t1 = buf[pos] >> 4U;
96 pos++;
97
98 if (t0 < 15U) {
99 // Map [0,14] to [-2,2] via: eta - (t mod (2*eta+1)) = 2 - (t mod 5)
100 a->coeffs[ctr] = (int32_t) (2U - (t0 % 5U));
101 ctr++;
102 }
103 if ((t1 < 15U) && (ctr < MLDSA_N)) {
104 a->coeffs[ctr] = (int32_t) (2U - (t1 % 5U));
105 ctr++;
106 }
107 }
108 }
109 else {
110 // eta == 4
111 while ((ctr < MLDSA_N) && (pos < buflen)) {
112 uint8_t t0 = buf[pos] & 0x0FU;
113 uint8_t t1 = buf[pos] >> 4U;
114 pos++;
115
116 if (t0 < 9U) {
117 a->coeffs[ctr] = (int32_t) (4 - t0);
118 ctr++;
119 }
120 if ((t1 < 9U) && (ctr < MLDSA_N)) {
121 a->coeffs[ctr] = (int32_t) (4 - t1);
122 ctr++;
123 }
124 }
125 }
126
127 explicit_bzero(buf, sizeof(buf));
128}
129
131 const uint8_t seed[MLDSA_CRHBYTES],
132 uint16_t nonce,
133 int32_t gamma1)
134{
135 // gamma1 = 2^17 => 18 bits/coeff => 576 bytes.
136 // gamma1 = 2^19 => 20 bits/coeff => 640 bytes.
137 uint8_t buf[640U];
138 size_t buflen;
139
140 if (gamma1 == (1 << 17)) {
141 buflen = 576U;
142 }
143 else {
144 buflen = 640U;
145 }
146
147 MLDSA_UTIL_shake256_seed_nonce(buf, buflen, seed, MLDSA_CRHBYTES, nonce);
148
149 if (gamma1 == (1 << 17)) {
150 // 18 bits per coefficient
151 for (uint32_t i = 0U; i < MLDSA_N / 4U; i++) {
152 uint32_t t0, t1, t2, t3;
153 t0 = (uint32_t) buf[9U * i + 0U];
154 t0 |= (uint32_t) buf[9U * i + 1U] << 8U;
155 t0 |= (uint32_t) buf[9U * i + 2U] << 16U;
156 t0 &= 0x3FFFFU;
157
158 t1 = (uint32_t) buf[9U * i + 2U] >> 2U;
159 t1 |= (uint32_t) buf[9U * i + 3U] << 6U;
160 t1 |= (uint32_t) buf[9U * i + 4U] << 14U;
161 t1 &= 0x3FFFFU;
162
163 t2 = (uint32_t) buf[9U * i + 4U] >> 4U;
164 t2 |= (uint32_t) buf[9U * i + 5U] << 4U;
165 t2 |= (uint32_t) buf[9U * i + 6U] << 12U;
166 t2 &= 0x3FFFFU;
167
168 t3 = (uint32_t) buf[9U * i + 6U] >> 6U;
169 t3 |= (uint32_t) buf[9U * i + 7U] << 2U;
170 t3 |= (uint32_t) buf[9U * i + 8U] << 10U;
171 t3 &= 0x3FFFFU;
172
173 a->coeffs[4U * i + 0U] = gamma1 - (int32_t) t0;
174 a->coeffs[4U * i + 1U] = gamma1 - (int32_t) t1;
175 a->coeffs[4U * i + 2U] = gamma1 - (int32_t) t2;
176 a->coeffs[4U * i + 3U] = gamma1 - (int32_t) t3;
177 }
178 }
179 else {
180 // 20 bits per coefficient
181 for (uint32_t i = 0U; i < MLDSA_N / 2U; i++) {
182 uint32_t t0, t1;
183 t0 = (uint32_t) buf[5U * i + 0U];
184 t0 |= (uint32_t) buf[5U * i + 1U] << 8U;
185 t0 |= (uint32_t) buf[5U * i + 2U] << 16U;
186 t0 &= 0xFFFFFU;
187
188 t1 = (uint32_t) buf[5U * i + 2U] >> 4U;
189 t1 |= (uint32_t) buf[5U * i + 3U] << 4U;
190 t1 |= (uint32_t) buf[5U * i + 4U] << 12U;
191 t1 &= 0xFFFFFU;
192
193 a->coeffs[2U * i + 0U] = gamma1 - (int32_t) t0;
194 a->coeffs[2U * i + 1U] = gamma1 - (int32_t) t1;
195 }
196 }
197
198 explicit_bzero(buf, sizeof(buf));
199}
200
201void MLDSA_SAMPLE_challenge(mldsa_poly *c, const uint8_t *seed, size_t seedlen, uint8_t tau)
202{
203 // 2 SHAKE256 blocks = 272 bytes. Challenge sampling needs 8 sign bytes
204 // plus at most ~128 bytes for tau=60. 272 bytes is more than sufficient.
205 uint8_t buf[136U * 2U];
206 uint64_t signs;
207
208 MLDSA_UTIL_shake256(buf, sizeof(buf), seed, seedlen);
209
210 // First 8 bytes give the sign bits
211 signs = 0U;
212 for (uint32_t i = 0U; i < 8U; i++) {
213 signs |= (uint64_t) buf[i] << (8U * i);
214 }
215
216 // Zero out all coefficients
217 for (uint32_t i = 0U; i < MLDSA_N; i++) {
218 c->coeffs[i] = 0;
219 }
220
221 uint32_t pos = 8U;
222 for (uint32_t i = MLDSA_N - (uint32_t) tau; i < MLDSA_N; i++) {
223 uint32_t j;
224 // Sample j uniformly from [0, i]
225 do {
226 if (pos >= sizeof(buf)) {
227 // Buffer exhausted (unlikely with 272 bytes); default to no-op swap
228 j = i;
229 break;
230 }
231 j = (uint32_t) buf[pos++];
232 } while (j > i);
233
234 c->coeffs[i] = c->coeffs[j];
235 c->coeffs[j] = 1 - 2 * (int32_t) (signs & 1U);
236 signs >>= 1U;
237 }
238
239 explicit_bzero(buf, sizeof(buf));
240}
void MLDSA_SAMPLE_eta(mldsa_poly *a, const uint8_t seed[MLDSA_CRHBYTES], uint16_t nonce, uint8_t eta)
Sample polynomial with coefficients in [-eta, eta] from SHAKE256(seed||nonce).
void MLDSA_SAMPLE_uniform(mldsa_poly *a, const uint8_t seed[MLDSA_SEEDBYTES], uint16_t nonce)
Sample polynomial with uniformly random coefficients in [0, q-1] by performing rejection sampling on ...
void MLDSA_SAMPLE_challenge(mldsa_poly *c, const uint8_t *seed, size_t seedlen, uint8_t tau)
Sample challenge polynomial with TAU coefficients in {-1, +1}.
void MLDSA_SAMPLE_gamma1(mldsa_poly *a, const uint8_t seed[MLDSA_CRHBYTES], uint16_t nonce, int32_t gamma1)
Sample polynomial with coefficients in [-(gamma1-1), gamma1] from SHAKE256(seed||nonce).
#define MLDSA_REJ_UNIFORM_BUFLEN
void MLDSA_UTIL_shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
SHAKE256 hash wrapper.
void MLDSA_UTIL_shake256_seed_nonce(uint8_t *out, size_t outlen, const uint8_t *seed, size_t seedlen, uint16_t nonce)
SHAKE256 with seed || uint16_t nonce.
void MLDSA_UTIL_shake128_seed_nonce(uint8_t *out, size_t outlen, const uint8_t *seed, size_t seedlen, uint16_t nonce)
SHAKE128 with seed || uint16_t nonce.
#define MLDSA_N
Definition lcx_mldsa.h:37
#define MLDSA_SEEDBYTES
Definition lcx_mldsa.h:40
#define MLDSA_CRHBYTES
Definition lcx_mldsa.h:41
#define MLDSA_Q
Definition lcx_mldsa.h:38
Polynomial with MLDSA_N int32_t coefficients.
int32_t coeffs[MLDSA_N]
static nbgl_touchStatePosition_t pos
Definition ux.c:56