Skip to content

Commit 8582a06

Browse files
authored
Merge pull request #763 from pq-code-package/zeroize
Destruct intermediate values
2 parents 695ed0f + 200f1da commit 8582a06

File tree

25 files changed

+254
-44
lines changed

25 files changed

+254
-44
lines changed

.github/workflows/ct-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ jobs:
1818
name: CT test ${{ matrix.nix-shell }} ${{ matrix.system }}
1919
strategy:
2020
fail-fast: false
21-
max-parallel: 4
21+
max-parallel: 10
2222
matrix:
2323
system: [ubuntu-latest, pqcp-arm64]
2424
nix-shell:

dev/x86_64/src/basemul.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#if defined(MLK_ARITH_BACKEND_X86_64_DEFAULT) && \
99
!defined(MLK_MULTILEVEL_BUILD_NO_SHARED)
1010

11+
#include "../../../verify.h"
1112
#include "arith_native_x86_64.h"
1213
#include "consts.h"
1314

@@ -57,6 +58,9 @@ void polyvec_basemul_acc_montgomery_cached_avx2(unsigned k, int16_t r[MLKEM_N],
5758
poly_basemul_montgomery_avx2(t, &a[i * MLKEM_N], &b[i * MLKEM_N]);
5859
poly_add_avx2(r, r, t);
5960
}
61+
62+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
63+
ct_zeroize(t, sizeof(t));
6064
}
6165

6266
#else /* defined(MLK_ARITH_BACKEND_X86_64_DEFAULT) && \

examples/monolithic_build/mlkem_native_monobuild.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,13 +287,15 @@
287287
#undef MLK_CT_TESTING_DECLASSIFY
288288
#undef MLK_CT_TESTING_SECRET
289289
#undef MLK_DEFAULT_ALIGN
290+
#undef MLK_HAVE_INLINE_ASM
290291
#undef MLK_INLINE
291292
#undef MLK_RESTRICT
292293
#undef MLK_SYS_AARCH64
293294
#undef MLK_SYS_AARCH64_EB
294295
#undef MLK_SYS_BIG_ENDIAN
295296
#undef MLK_SYS_H
296297
#undef MLK_SYS_LITTLE_ENDIAN
298+
#undef MLK_SYS_WINDOWS
297299
#undef MLK_SYS_X86_64
298300
#undef MLK_SYS_X86_64_AVX2
299301
/* mlkem/verify.h */
@@ -307,6 +309,7 @@
307309
#undef ct_opt_blocker_u64
308310
#undef ct_sel_int16
309311
#undef ct_sel_uint8
312+
#undef ct_zeroize
310313
#undef value_barrier_i32
311314
#undef value_barrier_u32
312315
#undef value_barrier_u8

mlkem/config.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,46 @@
235235
*****************************************************************************/
236236
/* #define MLK_FIPS202X4_CUSTOM_HEADER "SOME_FILE.h" */
237237

238+
/******************************************************************************
239+
* Name: MLK_USE_CT_ZEROIZE_NATIVE
240+
*
241+
* Description: In compliance with FIPS-203 Section 3.3, mlkem-native zeroizes
242+
* intermediate stack buffers before returning from function calls.
243+
*
244+
* Set this option and define `ct_zeroize_native` if you want to
245+
* use a custom method to zeroize intermediate stack buffers.
246+
* The default implementation uses SecureZeroMemory on Windows
247+
* and a memset + compiler barrier otherwise. If neither of those
248+
* is available on the target platform, compilation will fail,
249+
* and you will need to use MLK_USE_CT_ZEROIZE_NATIVE to provide
250+
* a custom implementation of `ct_zeroize_native()`.
251+
*
252+
* WARNING:
253+
* The explicit stack zeroization conducted by mlkem-native
254+
* reduces the likelihood of data leaking on the stack, but
255+
* does not eliminate it! The C standard makes no guarantee about
256+
* where a compiler allocates structures and whether/where it makes
257+
* copies of them. Also, in addition to entire structures, there
258+
* may also be potentially exploitable leakage of individual values
259+
* on the stack.
260+
*
261+
* If you need bullet-proof zeroization of the stack, you need to
262+
* consider additional measures instead of of what this feature
263+
* provides. In this case, you can set ct_zeroize_native to a
264+
* no-op.
265+
*
266+
*****************************************************************************/
267+
/* #define MLK_USE_CT_ZEROIZE_NATIVE
268+
#if !defined(__ASSEMBLER__)
269+
#include <stdint.h>
270+
#include "sys.h"
271+
static MLK_INLINE void ct_zeroize_native(void *ptr, size_t len)
272+
{
273+
... your implementation ...
274+
}
275+
#endif
276+
*/
277+
238278
/************************* Config internals ********************************/
239279

240280
/* Default namespace

mlkem/fips202/fips202.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <stddef.h>
1818
#include <stdint.h>
1919
#include <string.h>
20+
#include "../verify.h"
2021
#include "fips202.h"
2122
#include "keccakf1600.h"
2223

@@ -184,7 +185,11 @@ void shake128_squeezeblocks(uint8_t *output, size_t nblocks, shake128ctx *state)
184185
}
185186

186187
void shake128_init(shake128ctx *state) { (void)state; }
187-
void shake128_release(shake128ctx *state) { (void)state; }
188+
void shake128_release(shake128ctx *state)
189+
{
190+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
191+
ct_zeroize(state, sizeof(shake128ctx));
192+
}
188193

189194
#define shake256ctx MLK_NAMESPACE(shake256ctx)
190195
typedef shake128ctx shake256ctx;
@@ -196,6 +201,8 @@ void shake256(uint8_t *output, size_t outlen, const uint8_t *input,
196201
keccak_absorb_once(state.ctx, SHAKE256_RATE, input, inlen, 0x1F);
197202
/* Squeeze output */
198203
keccak_squeeze_once(output, outlen, state.ctx, SHAKE256_RATE);
204+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
205+
ct_zeroize(&state, sizeof(state));
199206
}
200207

201208
void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
@@ -205,6 +212,8 @@ void sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
205212
keccak_absorb_once(ctx, SHA3_256_RATE, input, inlen, 0x06);
206213
/* Squeeze output */
207214
keccak_squeeze_once(output, 32, ctx, SHA3_256_RATE);
215+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
216+
ct_zeroize(ctx, sizeof(ctx));
208217
}
209218

210219
void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen)
@@ -214,6 +223,8 @@ void sha3_512(uint8_t *output, const uint8_t *input, size_t inlen)
214223
keccak_absorb_once(ctx, SHA3_512_RATE, input, inlen, 0x06);
215224
/* Squeeze output */
216225
keccak_squeeze_once(output, 64, ctx, SHA3_512_RATE);
226+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
227+
ct_zeroize(ctx, sizeof(ctx));
217228
}
218229

219230
#else /* MLK_MULTILEVEL_BUILD_NO_SHARED */

mlkem/fips202/fips202x4.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#if !defined(MLK_MULTILEVEL_BUILD_NO_SHARED)
77

88
#include <string.h>
9+
#include "../verify.h"
910
#include "fips202.h"
1011
#include "fips202x4.h"
1112
#include "keccakf1600.h"
@@ -132,7 +133,11 @@ void shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2,
132133
}
133134

134135
void shake128x4_init(shake128x4ctx *state) { (void)state; }
135-
void shake128x4_release(shake128x4ctx *state) { (void)state; }
136+
void shake128x4_release(shake128x4ctx *state)
137+
{
138+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
139+
ct_zeroize(state, sizeof(shake128x4ctx));
140+
}
136141

137142
static void shake256x4_absorb_once(shake256x4_ctx *state, const uint8_t *in0,
138143
const uint8_t *in1, const uint8_t *in2,
@@ -180,6 +185,13 @@ void shake256x4(uint8_t *out0, uint8_t *out1, uint8_t *out2, uint8_t *out3,
180185
memcpy(out2, tmp2, outlen);
181186
memcpy(out3, tmp3, outlen);
182187
}
188+
189+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
190+
ct_zeroize(&statex, sizeof(statex));
191+
ct_zeroize(tmp0, sizeof(tmp0));
192+
ct_zeroize(tmp1, sizeof(tmp1));
193+
ct_zeroize(tmp2, sizeof(tmp2));
194+
ct_zeroize(tmp3, sizeof(tmp3));
183195
}
184196

185197
#else /* MLK_MULTILEVEL_BUILD_NO_SHARED */

mlkem/indcpa.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,12 @@ void gen_matrix(polyvec *a, const uint8_t seed[MLKEM_SYMBYTES], int transposed)
243243
poly_permute_bitrev_to_custom(a[i].vec[j].coeffs);
244244
}
245245
}
246+
247+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
248+
ct_zeroize(seed0, sizeof(seed0));
249+
ct_zeroize(seed1, sizeof(seed1));
250+
ct_zeroize(seed2, sizeof(seed2));
251+
ct_zeroize(seed3, sizeof(seed3));
246252
}
247253

248254
/*************************************************
@@ -343,6 +349,14 @@ void indcpa_keypair_derand(uint8_t pk[MLKEM_INDCPA_PUBLICKEYBYTES],
343349

344350
pack_sk(sk, &skpv);
345351
pack_pk(pk, &pkpv, publicseed);
352+
353+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
354+
ct_zeroize(buf, sizeof(buf));
355+
ct_zeroize(coins_with_domain_separator, sizeof(coins_with_domain_separator));
356+
ct_zeroize(a, sizeof(a));
357+
ct_zeroize(&e, sizeof(e));
358+
ct_zeroize(&skpv, sizeof(skpv));
359+
ct_zeroize(&skpv_cache, sizeof(skpv_cache));
346360
}
347361

348362

@@ -409,6 +423,17 @@ void indcpa_enc(uint8_t c[MLKEM_INDCPA_BYTES],
409423
poly_reduce(&v);
410424

411425
pack_ciphertext(c, &b, &v);
426+
427+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
428+
ct_zeroize(seed, sizeof(seed));
429+
ct_zeroize(&sp, sizeof(sp));
430+
ct_zeroize(&sp_cache, sizeof(sp_cache));
431+
ct_zeroize(&b, sizeof(b));
432+
ct_zeroize(&v, sizeof(v));
433+
ct_zeroize(at, sizeof(at));
434+
ct_zeroize(&k, sizeof(k));
435+
ct_zeroize(&ep, sizeof(ep));
436+
ct_zeroize(&epp, sizeof(epp));
412437
}
413438

414439
MLK_INTERNAL_API
@@ -432,6 +457,13 @@ void indcpa_dec(uint8_t m[MLKEM_INDCPA_MSGBYTES],
432457
poly_reduce(&v);
433458

434459
poly_tomsg(m, &v);
460+
461+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
462+
ct_zeroize(&skpv, sizeof(skpv));
463+
ct_zeroize(&b, sizeof(b));
464+
ct_zeroize(&b_cache, sizeof(b_cache));
465+
ct_zeroize(&v, sizeof(v));
466+
ct_zeroize(&sb, sizeof(sb));
435467
}
436468

437469
/* To facilitate single-compilation-unit (SCU) builds, undefine all macros.

mlkem/kem.c

Lines changed: 47 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -44,17 +44,21 @@ __contract__(
4444
**************************************************/
4545
static int check_pk(const uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES])
4646
{
47+
int res;
4748
polyvec p;
4849
uint8_t p_reencoded[MLKEM_POLYVECBYTES];
50+
4951
polyvec_frombytes(&p, pk);
5052
polyvec_reduce(&p);
5153
polyvec_tobytes(p_reencoded, &p);
54+
5255
/* Data is public, so a variable-time memcmp() is OK */
53-
if (memcmp(pk, p_reencoded, MLKEM_POLYVECBYTES))
54-
{
55-
return -1;
56-
}
57-
return 0;
56+
res = memcmp(pk, p_reencoded, MLKEM_POLYVECBYTES) ? -1 : 0;
57+
58+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
59+
ct_zeroize(p_reencoded, sizeof(p_reencoded));
60+
ct_zeroize(&p, sizeof(p));
61+
return res;
5862
}
5963

6064
/*************************************************
@@ -73,6 +77,7 @@ static int check_pk(const uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES])
7377
**************************************************/
7478
static int check_sk(const uint8_t sk[MLKEM_INDCCA_SECRETKEYBYTES])
7579
{
80+
int res;
7681
MLK_ALIGN uint8_t test[MLKEM_SYMBYTES];
7782
/*
7883
* The parts of `sk` being hashed and compared here are public, so
@@ -87,12 +92,14 @@ static int check_sk(const uint8_t sk[MLKEM_INDCCA_SECRETKEYBYTES])
8792
sk + MLKEM_INDCCA_SECRETKEYBYTES - 2 * MLKEM_SYMBYTES, MLKEM_SYMBYTES);
8893

8994
hash_h(test, sk + MLKEM_INDCPA_SECRETKEYBYTES, MLKEM_INDCCA_PUBLICKEYBYTES);
90-
if (memcmp(sk + MLKEM_INDCCA_SECRETKEYBYTES - 2 * MLKEM_SYMBYTES, test,
91-
MLKEM_SYMBYTES))
92-
{
93-
return -1;
94-
}
95-
return 0;
95+
res = memcmp(sk + MLKEM_INDCCA_SECRETKEYBYTES - 2 * MLKEM_SYMBYTES, test,
96+
MLKEM_SYMBYTES)
97+
? -1
98+
: 0;
99+
100+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
101+
ct_zeroize(test, sizeof(test));
102+
return res;
96103
}
97104

98105
int crypto_kem_keypair_derand(uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES],
@@ -115,6 +122,10 @@ int crypto_kem_keypair(uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES],
115122
MLK_ALIGN uint8_t coins[2 * MLKEM_SYMBYTES];
116123
randombytes(coins, 2 * MLKEM_SYMBYTES);
117124
crypto_kem_keypair_derand(pk, sk, coins);
125+
126+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
127+
ct_zeroize(coins, sizeof(coins));
128+
118129
return 0;
119130
}
120131

@@ -142,16 +153,26 @@ int crypto_kem_enc_derand(uint8_t ct[MLKEM_INDCCA_CIPHERTEXTBYTES],
142153
indcpa_enc(ct, buf, pk, kr + MLKEM_SYMBYTES);
143154

144155
memcpy(ss, kr, MLKEM_SYMBYTES);
156+
157+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
158+
ct_zeroize(buf, sizeof(buf));
159+
ct_zeroize(kr, sizeof(kr));
160+
145161
return 0;
146162
}
147163

148164
int crypto_kem_enc(uint8_t ct[MLKEM_INDCCA_CIPHERTEXTBYTES],
149165
uint8_t ss[MLKEM_SSBYTES],
150166
const uint8_t pk[MLKEM_INDCCA_PUBLICKEYBYTES])
151167
{
168+
int res;
152169
MLK_ALIGN uint8_t coins[MLKEM_SYMBYTES];
153170
randombytes(coins, MLKEM_SYMBYTES);
154-
return crypto_kem_enc_derand(ct, ss, pk, coins);
171+
res = crypto_kem_enc_derand(ct, ss, pk, coins);
172+
173+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
174+
ct_zeroize(coins, sizeof(coins));
175+
return res;
155176
}
156177

157178
int crypto_kem_dec(uint8_t ss[MLKEM_SSBYTES],
@@ -162,6 +183,8 @@ int crypto_kem_dec(uint8_t ss[MLKEM_SSBYTES],
162183
MLK_ALIGN uint8_t buf[2 * MLKEM_SYMBYTES];
163184
/* Will contain key, coins */
164185
MLK_ALIGN uint8_t kr[2 * MLKEM_SYMBYTES];
186+
MLK_ALIGN uint8_t tmp[MLKEM_SYMBYTES + MLKEM_INDCCA_CIPHERTEXTBYTES];
187+
165188
const uint8_t *pk = sk + MLKEM_INDCPA_SECRETKEYBYTES;
166189

167190
if (check_sk(sk))
@@ -177,27 +200,24 @@ int crypto_kem_dec(uint8_t ss[MLKEM_SSBYTES],
177200
hash_g(kr, buf, 2 * MLKEM_SYMBYTES);
178201

179202
/* Recompute and compare ciphertext */
180-
{
181-
/* Temporary buffer */
182-
MLK_ALIGN uint8_t cmp[MLKEM_INDCCA_CIPHERTEXTBYTES];
183-
/* coins are in kr+MLKEM_SYMBYTES */
184-
indcpa_enc(cmp, buf, pk, kr + MLKEM_SYMBYTES);
185-
fail = ct_memcmp(ct, cmp, MLKEM_INDCCA_CIPHERTEXTBYTES);
186-
}
203+
/* coins are in kr+MLKEM_SYMBYTES */
204+
indcpa_enc(tmp, buf, pk, kr + MLKEM_SYMBYTES);
205+
fail = ct_memcmp(ct, tmp, MLKEM_INDCCA_CIPHERTEXTBYTES);
187206

188207
/* Compute rejection key */
189-
{
190-
/* Temporary buffer */
191-
MLK_ALIGN uint8_t tmp[MLKEM_SYMBYTES + MLKEM_INDCCA_CIPHERTEXTBYTES];
192-
memcpy(tmp, sk + MLKEM_INDCCA_SECRETKEYBYTES - MLKEM_SYMBYTES,
193-
MLKEM_SYMBYTES);
194-
memcpy(tmp + MLKEM_SYMBYTES, ct, MLKEM_INDCCA_CIPHERTEXTBYTES);
195-
hash_j(ss, tmp, sizeof(tmp));
196-
}
208+
memcpy(tmp, sk + MLKEM_INDCCA_SECRETKEYBYTES - MLKEM_SYMBYTES,
209+
MLKEM_SYMBYTES);
210+
memcpy(tmp + MLKEM_SYMBYTES, ct, MLKEM_INDCCA_CIPHERTEXTBYTES);
211+
hash_j(ss, tmp, sizeof(tmp));
197212

198213
/* Copy true key to return buffer if fail is 0 */
199214
ct_cmov_zero(ss, kr, MLKEM_SYMBYTES, fail);
200215

216+
/* FIPS 203. Section 3.3 Destruction of intermediate values. */
217+
ct_zeroize(buf, sizeof(buf));
218+
ct_zeroize(kr, sizeof(kr));
219+
ct_zeroize(tmp, sizeof(tmp));
220+
201221
return 0;
202222
}
203223

0 commit comments

Comments
 (0)