Embedded SDK
Embedded SDK
Loading...
Searching...
No Matches
cx_mldsa_internal.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 *****************************************************************************/
25#include <string.h>
26#include "lcx_mldsa.h"
27#include "cx_mldsa_internal.h"
28#include "cx_mldsa_poly.h"
29#include "cx_mldsa_polyvec.h"
30#include "cx_mldsa_polymat.h"
31#include "cx_mldsa_sample.h"
32#include "cx_mldsa_packing.h"
33#include "cx_mldsa_rounding.h"
34#include "cx_mldsa_util.h"
35
36/*---------------------------------------------------------------------------
37 * Internal: KeyGen_internal (FIPS 204, Algorithm 6)
38 *---------------------------------------------------------------------------*/
39/*
40 * Low-RAM key generation.
41 *
42 * The matrix A and the secret vector s1 are never stored as full vectors.
43 * t = A*s1 + s2 is computed one output row at a time; within a row the
44 * inner product is accumulated element-by-element, re-expanding A[i][j]
45 * and s1[j] on the fly. Each produced polynomial (s1, s2, t0, t1) is
46 * packed straight into the pk/sk output buffers, so no unpacked polyvec
47 * is ever held in memory.
48 */
49cx_err_t MLDSA_internal_keygen(uint8_t *pk,
50 size_t pk_len,
51 uint8_t *sk,
52 size_t sk_len,
53 const uint8_t seed[MLDSA_SEEDBYTES],
54 MLDSA_param_t param)
55{
56 cx_err_t error = CX_INTERNAL_ERROR;
57 const MLDSA_param_info_t *p = NULL;
58 uint8_t seedbuf[2U * MLDSA_SEEDBYTES + MLDSA_CRHBYTES] = {0};
59 uint8_t buf[MLDSA_SEEDBYTES + 2U] = {0};
60 uint8_t tr[MLDSA_TRBYTES] = {0};
61 mldsa_poly tA = {0};
62 mldsa_poly tB = {0};
63 mldsa_poly tC = {0};
64
65 if ((pk == NULL) || (sk == NULL) || (seed == NULL)) {
66 error = CX_INVALID_PARAMETER;
67 goto cleanup;
68 }
69
70 if (param >= MLDSA_NUM_PARAM_SETS) {
71 error = CX_INVALID_PARAMETER_VALUE;
72 goto cleanup;
73 }
74
75 p = &MLDSA_PARAM[param];
76
77 if ((pk_len < p->pk_bytes) || (sk_len < p->sk_bytes)) {
78 error = CX_INVALID_PARAMETER_SIZE;
79 goto cleanup;
80 }
81
82 // Expand seed to (rho, rhoprime, K) using SHAKE256
83 memcpy(buf, seed, MLDSA_SEEDBYTES);
84 buf[MLDSA_SEEDBYTES] = p->k;
85 buf[MLDSA_SEEDBYTES + 1U] = p->l;
86 MLDSA_UTIL_shake256(seedbuf, sizeof(seedbuf), buf, MLDSA_SEEDBYTES + 2U);
87
88 const uint8_t *rho = seedbuf;
89 const uint8_t *rhoprime = &seedbuf[MLDSA_SEEDBYTES];
90 const uint8_t *K = &seedbuf[MLDSA_SEEDBYTES + MLDSA_CRHBYTES];
91
92 // Secret-key section offsets.
93 const size_t s1_off = 2U * MLDSA_SEEDBYTES + MLDSA_TRBYTES;
94 const size_t s2_off = s1_off + (size_t) p->l * p->polyeta_packed_bytes;
95 const size_t t0_off = s1_off + (size_t) (p->l + p->k) * p->polyeta_packed_bytes;
96
97 memcpy(sk, rho, MLDSA_SEEDBYTES);
98 memcpy(&sk[MLDSA_SEEDBYTES], K, MLDSA_SEEDBYTES);
99 memcpy(pk, rho, MLDSA_SEEDBYTES);
100
101 // Optimization: compute t = A*s1 + s2 one row at a time, holding only 3 polynomials.
102 for (uint8_t i = 0U; i < p->k; i++) {
103 for (uint8_t j = 0U; j < p->l; j++) {
104 // Expand s1[j] on the fly; pack it once (first output row only).
105 MLDSA_SAMPLE_eta(&tC, rhoprime, j, p->eta);
106 if (i == 0U) {
107 MLDSA_PACK_polyeta(&sk[s1_off + (size_t) j * p->polyeta_packed_bytes], &tC, p->eta);
108 }
109 MLDSA_POLY_ntt(&tC);
110
111 // Expand A[i][j] on the fly and accumulate into the row.
112 uint16_t nonce = ((uint16_t) i << 8U) | (uint16_t) j;
113 MLDSA_SAMPLE_uniform(&tB, rho, nonce);
114 MLDSA_POLY_pointwise_montgomery(&tA, &tB, &tC, (j == 0U) ? 1 : 0);
115 }
116
119
120 // Sample and pack the error term s2[i], then add it to the row.
121 MLDSA_SAMPLE_eta(&tB, rhoprime, (uint16_t) (p->l + i), p->eta);
122 MLDSA_PACK_polyeta(&sk[s2_off + (size_t) i * p->polyeta_packed_bytes], &tB, p->eta);
123 MLDSA_POLY_add(&tA, &tB);
125
126 // Split into (t1, t0)
127 MLDSA_ROUNDING_poly_power2round(&tC, &tB, &tA);
128 MLDSA_PACK_polyt0(&sk[t0_off + (size_t) i * MLDSA_POLYT0_PACKEDBYTES], &tB);
130 }
131
132 // Compute tr = H(pk) = SHAKE256(pk, 64) and store it in the secret key.
134 memcpy(&sk[2U * MLDSA_SEEDBYTES], tr, MLDSA_TRBYTES);
135
136 error = CX_OK;
137
138cleanup:
139 // Zeroize sensitive intermediates
140 explicit_bzero(seedbuf, sizeof(seedbuf));
141 explicit_bzero(buf, sizeof(buf));
142 explicit_bzero(&tA, sizeof(tA));
143 explicit_bzero(&tB, sizeof(tB));
144 explicit_bzero(&tC, sizeof(tC));
145 explicit_bzero(tr, sizeof(tr));
146
147 return error;
148}
149
150/*---------------------------------------------------------------------------
151 * Internal Sign (FIPS 204, Algorithms 2 & 7)
152 *---------------------------------------------------------------------------*/
153cx_err_t MLDSA_internal_sign(uint8_t *sig,
154 size_t sig_len,
155 size_t *sig_actual_len,
156 const uint8_t *mu,
157 uint8_t *rnd,
158 size_t rnd_len,
159 const uint8_t *sk,
160 size_t sk_len,
161 MLDSA_param_t param)
162{
163 if (mu == NULL) {
164 return CX_INVALID_PARAMETER;
165 }
166
168 sig, sig_len, sig_actual_len, NULL, mu, rnd, rnd_len, sk, sk_len, param);
169}
170
171/*---------------------------------------------------------------------------
172 * Internal Verify (FIPS 204, Algorithms 3 & 8)
173 *---------------------------------------------------------------------------*/
174cx_err_t MLDSA_internal_verify(const uint8_t *sig,
175 size_t sig_len,
176 const uint8_t *mu,
177 const uint8_t *pk,
178 size_t pk_len,
179 MLDSA_param_t param)
180{
181 if ((sig == NULL) || (pk == NULL) || (mu == NULL)) {
182 return CX_INVALID_PARAMETER;
183 }
184
185 return MLDSA_internal_verify_core(sig, sig_len, NULL, mu, pk, pk_len, param);
186}
cx_err_t MLDSA_internal_verify_core(const uint8_t *sig, size_t sig_len, const MLDSA_formatted_message_t *formatted_mprime, const uint8_t *precomputed_mu, const uint8_t *pk, size_t pk_len, MLDSA_param_t param)
Core ML-DSA verification routine (FIPS 204, Algorithms 3 & 8).
Definition cx_mldsa.c:577
cx_err_t MLDSA_internal_sign_core(uint8_t *sig, size_t sig_len, size_t *sig_actual_len, const MLDSA_formatted_message_t *formatted_mprime, const uint8_t *precomputed_mu, uint8_t *rnd, size_t rnd_len, const uint8_t *sk, size_t sk_len, MLDSA_param_t param)
Core ML-DSA signing routine (FIPS 204, Algorithms 2 & 7).
Definition cx_mldsa.c:317
cx_err_t MLDSA_internal_verify(const uint8_t *sig, size_t sig_len, const uint8_t *mu, const uint8_t *pk, size_t pk_len, MLDSA_param_t param)
Internal ML-DSA signature verification (FIPS 204, Algorithms 3 & 8).
cx_err_t MLDSA_internal_keygen(uint8_t *pk, size_t pk_len, uint8_t *sk, size_t sk_len, const uint8_t seed[MLDSA_SEEDBYTES], MLDSA_param_t param)
Generates an ML-DSA key pair from a seed (deterministic).
cx_err_t MLDSA_internal_sign(uint8_t *sig, size_t sig_len, size_t *sig_actual_len, const uint8_t *mu, uint8_t *rnd, size_t rnd_len, const uint8_t *sk, size_t sk_len, MLDSA_param_t param)
Internal ML-DSA signature generation (FIPS 204, Algorithms 2 & 7).
void MLDSA_PACK_polyt1(uint8_t r[MLDSA_POLYT1_PACKEDBYTES], const mldsa_poly *a)
Bit-pack polynomial with coefficients a1 from power2round (10-bit values). Each coefficient uses 10 b...
void MLDSA_PACK_polyt0(uint8_t r[MLDSA_POLYT0_PACKEDBYTES], const mldsa_poly *a)
Bit-pack polynomial with coefficients a0 from power2round. Coefficients in (-(2^{D-1}-1),...
uint32_t MLDSA_PACK_polyeta(uint8_t *r, const mldsa_poly *a, uint8_t eta)
Bit-pack polynomial with coefficients in [-eta, eta].
void MLDSA_POLY_reduce(mldsa_poly *a)
Applies reduce32 to all coefficients of a polynomial.
void MLDSA_POLY_ntt(mldsa_poly *a)
Forward NTT in place.
void MLDSA_POLY_caddq_all(mldsa_poly *a)
Applies caddq to all coefficients of a polynomial.
void MLDSA_POLY_add(mldsa_poly *a, const mldsa_poly *b)
Adds polynomial b to polynomial a in place.
void MLDSA_POLY_invntt_tomont(mldsa_poly *a)
Inverse NTT and multiply by Montgomery factor.
void MLDSA_POLY_pointwise_montgomery(mldsa_poly *c, const mldsa_poly *a, const mldsa_poly *b, int first)
Pointwise multiplication (Montgomery) with accumulation.
void MLDSA_ROUNDING_poly_power2round(mldsa_poly *a1, mldsa_poly *a0, const mldsa_poly *a)
Applies power2round to all coefficients of a polynomial.
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_UTIL_shake256(uint8_t *out, size_t outlen, const uint8_t *in, size_t inlen)
SHAKE256 hash wrapper.
#define MLDSA_TRBYTES
Definition lcx_mldsa.h:42
#define MLDSA_POLYT0_PACKEDBYTES
Definition lcx_mldsa.h:46
#define MLDSA_SEEDBYTES
Definition lcx_mldsa.h:40
#define MLDSA_CRHBYTES
Definition lcx_mldsa.h:41
#define MLDSA_POLYT1_PACKEDBYTES
Definition lcx_mldsa.h:45
ML-DSA (Module-Lattice Digital Signature Algorithm) public API.
const MLDSA_param_info_t MLDSA_PARAM[MLDSA_NUM_PARAM_SETS]
Lookup table of ML-DSA parameter sets indexed by MLDSA_param_t.
enum MLDSA_param_e MLDSA_param_t
ML-DSA parameter set selector.
#define MLDSA_NUM_PARAM_SETS
Definition lcx_mldsa.h:162
ML-DSA parameter set descriptor holding all derived sizes.
Definition lcx_mldsa.h:179
uint16_t polyeta_packed_bytes
Definition lcx_mldsa.h:189
Polynomial with MLDSA_N int32_t coefficients.