Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_mlkem_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
26#include "cx_mlkem_poly.h"
27#include "cx_mlkem_util.h"
28#include "cx_mlkem_sample.h"
29
30/* ========================================================================
31 * Sampling
32 * ====================================================================== */
33
34#define XOF_BLOCKBYTES 168U
35#define REJ_UNIFORM_NBLOCKS 5U
36#define REJ_UNIFORM_BUFLEN (REJ_UNIFORM_NBLOCKS * XOF_BLOCKBYTES)
37
38/* CBD sampling */
39static uint32_t load32_le(const uint8_t *x)
40{
41 return (uint32_t) x[0] | ((uint32_t) x[1] << 8U) | ((uint32_t) x[2] << 16U)
42 | ((uint32_t) x[3] << 24U);
43}
44
45static uint32_t load24_le(const uint8_t *x)
46{
47 return (uint32_t) x[0] | ((uint32_t) x[1] << 8U) | ((uint32_t) x[2] << 16U);
48}
49
50void MLKEM_SAMPLE_cbd2(poly *r, const uint8_t *buf)
51{
52 for (uint32_t i = 0U; i < (MLKEM_N / 8U); i++) {
53 uint32_t t = load32_le(&buf[4U * i]);
54 uint32_t d = t & 0x55555555U;
55 d += (t >> 1U) & 0x55555555U;
56
57 for (uint32_t j = 0U; j < 8U; j++) {
58 int16_t a_val = (int16_t) ((d >> ((4U * j) + 0U)) & 0x3U);
59 int16_t b_val = (int16_t) ((d >> ((4U * j) + 2U)) & 0x3U);
60 r->coeffs[(8U * i) + j] = (int16_t) (a_val - b_val);
61 }
62 }
63}
64
65void MLKEM_SAMPLE_cbd3(poly *r, const uint8_t *buf)
66{
67 for (uint32_t i = 0U; i < (MLKEM_N / 4U); i++) {
68 uint32_t t = load24_le(&buf[3U * i]);
69 uint32_t d = t & 0x00249249U;
70 d += (t >> 1U) & 0x00249249U;
71 d += (t >> 2U) & 0x00249249U;
72
73 for (uint32_t j = 0U; j < 4U; j++) {
74 int16_t a_val = (int16_t) ((d >> ((6U * j) + 0U)) & 0x7U);
75 int16_t b_val = (int16_t) ((d >> ((6U * j) + 3U)) & 0x7U);
76 r->coeffs[(4U * i) + j] = (int16_t) (a_val - b_val);
77 }
78 }
79}
80
81void MLKEM_SAMPLE_getnoise(poly *r, const uint8_t *seed, uint8_t nonce, uint8_t eta)
82{
83 uint8_t buf[3 * MLKEM_N / 4] = {0}; // max size for eta=3
84 size_t buflen = (size_t) eta * MLKEM_N / 4;
85
86 MLKEM_UTIL_prf(buf, buflen, seed, nonce);
87
88 if (eta == 2) {
89 MLKEM_SAMPLE_cbd2(r, buf);
90 }
91 else { // eta == 3
92 MLKEM_SAMPLE_cbd3(r, buf);
93 }
94
95 explicit_bzero(buf, sizeof(buf));
96}
97
98#define REJ_UNIFORM_BUFLEN_EXT (REJ_UNIFORM_BUFLEN + (XOF_BLOCKBYTES * 3U))
99
100void MLKEM_SAMPLE_rej_uniform(poly *r, const uint8_t *seed, uint8_t x, uint8_t y)
101{
102 uint8_t buf[REJ_UNIFORM_BUFLEN_EXT] = {0};
103 uint32_t ctr = 0U;
104 uint32_t pos = 0U;
105
106 MLKEM_UTIL_xof_squeeze(buf, sizeof(buf), seed, x, y);
107
108 while ((ctr < MLKEM_N) && ((pos + 3U) <= REJ_UNIFORM_BUFLEN)) {
109 int16_t val0 = (int16_t) (((buf[pos] >> 0U) | ((uint16_t) buf[pos + 1U] << 8U)) & 0xFFFU);
110 int16_t val1
111 = (int16_t) (((buf[pos + 1U] >> 4U) | ((uint16_t) buf[pos + 2U] << 4U)) & 0xFFFU);
112 pos += 3U;
113
114 if (val0 < MLKEM_Q) {
115 r->coeffs[ctr++] = val0;
116 }
117 if ((ctr < MLKEM_N) && (val1 < MLKEM_Q)) {
118 r->coeffs[ctr++] = val1;
119 }
120 }
121
122 // Fallback: continue consuming the tail of the XOF output
123 while ((ctr < MLKEM_N) && ((pos + 3U) <= REJ_UNIFORM_BUFLEN_EXT)) {
124 int16_t val0 = (int16_t) (((buf[pos] >> 0U) | ((uint16_t) buf[pos + 1U] << 8U)) & 0xFFFU);
125 int16_t val1
126 = (int16_t) (((buf[pos + 1U] >> 4U) | ((uint16_t) buf[pos + 2U] << 4U)) & 0xFFFU);
127 pos += 3U;
128 if ((val0 < MLKEM_Q) && (ctr < MLKEM_N)) {
129 r->coeffs[ctr++] = val0;
130 }
131 if ((ctr < MLKEM_N) && (val1 < MLKEM_Q)) {
132 r->coeffs[ctr++] = val1;
133 }
134 }
135
136 explicit_bzero(buf, sizeof(buf));
137}
void MLKEM_SAMPLE_cbd2(poly *r, const uint8_t *buf)
Samples a polynomial using the centered binomial distribution with eta=2.
void MLKEM_SAMPLE_getnoise(poly *r, const uint8_t *seed, uint8_t nonce, uint8_t eta)
Samples a noise polynomial using PRF and CBD.
static uint32_t load24_le(const uint8_t *x)
void MLKEM_SAMPLE_cbd3(poly *r, const uint8_t *buf)
Samples a polynomial using the centered binomial distribution with eta=3.
#define REJ_UNIFORM_BUFLEN
#define REJ_UNIFORM_BUFLEN_EXT
void MLKEM_SAMPLE_rej_uniform(poly *r, const uint8_t *seed, uint8_t x, uint8_t y)
Samples a polynomial using rejection sampling from a uniform distribution.
static uint32_t load32_le(const uint8_t *x)
void MLKEM_UTIL_prf(uint8_t *out, size_t outlen, const uint8_t *key, uint8_t nonce)
Computes the PRF function (SHAKE256) as defined in FIPS 203.
void MLKEM_UTIL_xof_squeeze(uint8_t *out, size_t outlen, const uint8_t *seed, uint8_t x, uint8_t y)
Computes the XOF function (SHAKE128) as defined in FIPS 203.
#define MLKEM_Q
Definition lcx_mlkem.h:36
#define MLKEM_N
Definition lcx_mlkem.h:35
Polynomial with MLKEM_N coefficients.
int16_t coeffs[MLKEM_N]
static nbgl_touchStatePosition_t pos
Definition ux.c:56