Skip to content

Commit aed837f

Browse files
authored
Merge pull request #1231 from pq-code-package/serialize-keccaks
Add MLK_CONFIG_SERIAL_FIPS202_ONLY option
2 parents 65ee9ee + be7e184 commit aed837f

27 files changed

+981
-43
lines changed

.github/actions/config-variations/action.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ inputs:
77
description: 'GitHub token'
88
required: true
99
tests:
10-
description: 'List of tests to run (space-separated IDs) or "all" for all tests. Available IDs: pct-enabled, pct-enabled-broken, custom-zeroize, native-cap-ON, native-cap-OFF, native-cap-ID_AA64PFR1_EL1, native-cap-CPUID_AVX2, no-asm, custom-randombytes, custom-memcpy, custom-memset, custom-stdlib, nblocks-1, nblocks-2, nblocks-4'
10+
description: 'List of tests to run (space-separated IDs) or "all" for all tests. Available IDs: pct-enabled, pct-enabled-broken, custom-zeroize, native-cap-ON, native-cap-OFF, native-cap-ID_AA64PFR1_EL1, native-cap-CPUID_AVX2, no-asm, serial-fips202, custom-randombytes, custom-memcpy, custom-memset, custom-stdlib, nblocks-1, nblocks-2, nblocks-4'
1111
required: false
1212
default: 'all'
1313
opt:
@@ -117,6 +117,18 @@ runs:
117117
acvp: true
118118
opt: ${{ inputs.opt }}
119119
examples: false # Some examples use a custom config themselves
120+
- name: "Serial FIPS202 (no batched Keccak)"
121+
if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'serial-fips202') }}
122+
uses: ./.github/actions/multi-functest
123+
with:
124+
gh_token: ${{ inputs.gh_token }}
125+
compile_mode: native
126+
cflags: "-std=c11 -D_GNU_SOURCE -DMLK_CONFIG_FILE=\\\\\\\"../../test/serial_fips202_config.h\\\\\\\" -fsanitize=address -fsanitize=undefined -fno-sanitize-recover=all"
127+
func: true
128+
kat: true
129+
acvp: true
130+
opt: ${{ inputs.opt }}
131+
examples: false # Some examples use a custom config themselves
120132
- name: "Custom randombytes"
121133
if: ${{ inputs.tests == 'all' || contains(inputs.tests, 'custom-randombytes') }}
122134
uses: ./.github/actions/multi-functest

BIBLIOGRAPHY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ source code and documentation.
4343
- [test/custom_stdlib_config.h](test/custom_stdlib_config.h)
4444
- [test/custom_zeroize_config.h](test/custom_zeroize_config.h)
4545
- [test/no_asm_config.h](test/no_asm_config.h)
46+
- [test/serial_fips202_config.h](test/serial_fips202_config.h)
4647

4748
### `FIPS202`
4849

examples/basic_deterministic/mlkem_native/custom_no_randomized_config.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,28 @@
506506
#endif
507507
*/
508508

509+
/******************************************************************************
510+
* Name: MLK_CONFIG_SERIAL_FIPS202_ONLY
511+
*
512+
* Description: Set this to use a FIPS202 implementation with global state
513+
* that supports only one active Keccak computation at a time
514+
* (e.g. some hardware accelerators).
515+
*
516+
* If this option is set, batched Keccak operations are
517+
* disabled for rejection sampling during matrix generation.
518+
* Instead, matrix entries will be generated one at a time.
519+
*
520+
* This allows offloading Keccak computations to a hardware
521+
* accelerator that holds only a single Keccak state locally,
522+
* rather than requiring support for batched (4x) Keccak states.
523+
*
524+
* NOTE: Depending on the target CPU, disabling batched Keccak
525+
* may reduce performance when using software FIPS202
526+
* implementations. Only enable this when you have to.
527+
*
528+
*****************************************************************************/
529+
/* #define MLK_CONFIG_SERIAL_FIPS202_ONLY */
530+
509531
/************************* Config internals ********************************/
510532

511533
/* Default namespace

mlkem/src/config.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,28 @@
506506
#endif
507507
*/
508508

509+
/******************************************************************************
510+
* Name: MLK_CONFIG_SERIAL_FIPS202_ONLY
511+
*
512+
* Description: Set this to use a FIPS202 implementation with global state
513+
* that supports only one active Keccak computation at a time
514+
* (e.g. some hardware accelerators).
515+
*
516+
* If this option is set, batched Keccak operations are
517+
* disabled for rejection sampling during matrix generation.
518+
* Instead, matrix entries will be generated one at a time.
519+
*
520+
* This allows offloading Keccak computations to a hardware
521+
* accelerator that holds only a single Keccak state locally,
522+
* rather than requiring support for batched (4x) Keccak states.
523+
*
524+
* NOTE: Depending on the target CPU, disabling batched Keccak
525+
* may reduce performance when using software FIPS202
526+
* implementations. Only enable this when you have to.
527+
*
528+
*****************************************************************************/
529+
/* #define MLK_CONFIG_SERIAL_FIPS202_ONLY */
530+
509531
/************************* Config internals ********************************/
510532

511533
/* Default namespace

mlkem/src/indcpa.c

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@
3939
#define mlk_pack_ciphertext MLK_ADD_PARAM_SET(mlk_pack_ciphertext)
4040
#define mlk_unpack_ciphertext MLK_ADD_PARAM_SET(mlk_unpack_ciphertext)
4141
#define mlk_matvec_mul MLK_ADD_PARAM_SET(mlk_matvec_mul)
42+
#define mlk_polymat_permute_bitrev_to_custom \
43+
MLK_ADD_PARAM_SET(mlk_polymat_permute_bitrev_to_custom)
4244
/* End of parameter set namespacing */
4345

4446
/*************************************************
@@ -175,21 +177,40 @@ static void mlk_unpack_ciphertext(mlk_polyvec b, mlk_poly *v,
175177
mlk_poly_decompress_dv(v, c + MLKEM_POLYVECCOMPRESSEDBYTES_DU);
176178
}
177179

178-
#if !defined(MLK_USE_NATIVE_NTT_CUSTOM_ORDER)
179-
/* This namespacing is not done at the top to avoid a naming conflict
180-
* with native backends, which are currently not yet namespaced. */
181-
#define mlk_poly_permute_bitrev_to_custom \
182-
MLK_ADD_PARAM_SET(mlk_poly_permute_bitrev_to_custom)
183-
184-
static MLK_INLINE void mlk_poly_permute_bitrev_to_custom(int16_t data[MLKEM_N])
180+
/* Helper function to ensure that the polynomial entries in the output
181+
* of gen_matrix use the standard (bitreversed) ordering of coefficients.
182+
* No-op unless a native backend with a custom ordering is used.
183+
*
184+
* We don't inline this into gen_matrix to avoid having to split the CBMC
185+
* proof for gen_matrix based on MLK_USE_NATIVE_NTT_CUSTOM_ORDER. */
186+
static void mlk_polymat_permute_bitrev_to_custom(mlk_polymat a)
185187
__contract__(
186188
/* We don't specify that this should be a permutation, but only
187189
* that it does not change the bound established at the end of mlk_gen_matrix. */
188-
requires(memory_no_alias(data, sizeof(int16_t) * MLKEM_N))
189-
requires(array_bound(data, 0, MLKEM_N, 0, MLKEM_Q))
190-
assigns(memory_slice(data, sizeof(mlk_poly)))
191-
ensures(array_bound(data, 0, MLKEM_N, 0, MLKEM_Q))) { ((void)data); }
190+
requires(memory_no_alias(a, sizeof(mlk_polymat)))
191+
requires(forall(x, 0, MLKEM_K * MLKEM_K,
192+
array_bound(a[x].coeffs, 0, MLKEM_N, 0, MLKEM_Q)))
193+
assigns(object_whole(a))
194+
ensures(forall(x, 0, MLKEM_K * MLKEM_K,
195+
array_bound(a[x].coeffs, 0, MLKEM_N, 0, MLKEM_Q))))
196+
{
197+
#if defined(MLK_USE_NATIVE_NTT_CUSTOM_ORDER)
198+
unsigned i;
199+
for (i = 0; i < MLKEM_K * MLKEM_K; i++)
200+
__loop__(
201+
assigns(i, object_whole(a))
202+
invariant(i <= MLKEM_K * MLKEM_K)
203+
invariant(forall(x, 0, MLKEM_K * MLKEM_K,
204+
array_bound(a[x].coeffs, 0, MLKEM_N, 0, MLKEM_Q)))
205+
)
206+
{
207+
mlk_poly_permute_bitrev_to_custom(a[i].coeffs);
208+
}
209+
#else /* MLK_USE_NATIVE_NTT_CUSTOM_ORDER */
210+
/* Nothing to do */
211+
(void)a;
192212
#endif /* !MLK_USE_NATIVE_NTT_CUSTOM_ORDER */
213+
}
193214

194215
/* Reference: `gen_matrix()` in the reference implementation @[REF].
195216
* - We use a special subroutine to generate 4 polynomials
@@ -203,19 +224,14 @@ void mlk_gen_matrix(mlk_polymat a, const uint8_t seed[MLKEM_SYMBYTES],
203224
int transposed)
204225
{
205226
unsigned i, j;
206-
/*
207-
* We generate four separate seed arrays rather than a single one to work
208-
* around limitations in CBMC function contracts dealing with disjoint slices
209-
* of the same parent object.
210-
*/
211-
212227
MLK_ALIGN uint8_t seed_ext[4][MLK_ALIGN_UP(MLKEM_SYMBYTES + 2)];
213228

214229
for (j = 0; j < 4; j++)
215230
{
216231
mlk_memcpy(seed_ext[j], seed, MLKEM_SYMBYTES);
217232
}
218233

234+
#if !defined(MLK_CONFIG_SERIAL_FIPS202_ONLY)
219235
/* Sample 4 matrix entries a time. */
220236
for (i = 0; i < (MLKEM_K * MLKEM_K / 4) * 4; i += 4)
221237
{
@@ -239,9 +255,15 @@ void mlk_gen_matrix(mlk_polymat a, const uint8_t seed[MLKEM_SYMBYTES],
239255

240256
mlk_poly_rej_uniform_x4(&a[i], &a[i + 1], &a[i + 2], &a[i + 3], seed_ext);
241257
}
242-
243-
/* For MLKEM_K == 3, sample the last entry individually. */
244-
if (i < MLKEM_K * MLKEM_K)
258+
#else /* !MLK_CONFIG_SERIAL_FIPS202_ONLY */
259+
/* When using serial FIPS202, sample all entries individually. */
260+
i = 0;
261+
#endif /* MLK_CONFIG_SERIAL_FIPS202_ONLY */
262+
263+
/* For MLKEM_K == 3, sample the last entry individually.
264+
* When MLK_CONFIG_SERIAL_FIPS202_ONLY is set, sample all entries
265+
* individually. */
266+
for (; i < MLKEM_K * MLKEM_K; i++)
245267
{
246268
uint8_t x, y;
247269
/* MLKEM_K <= 4, so the values fit in uint8_t. */
@@ -260,7 +282,6 @@ void mlk_gen_matrix(mlk_polymat a, const uint8_t seed[MLKEM_SYMBYTES],
260282
}
261283

262284
mlk_poly_rej_uniform(&a[i], seed_ext[0]);
263-
i++;
264285
}
265286

266287
mlk_assert(i == MLKEM_K * MLKEM_K);
@@ -269,10 +290,7 @@ void mlk_gen_matrix(mlk_polymat a, const uint8_t seed[MLKEM_SYMBYTES],
269290
* The public matrix is generated in NTT domain. If the native backend
270291
* uses a custom order in NTT domain, permute A accordingly.
271292
*/
272-
for (i = 0; i < MLKEM_K * MLKEM_K; i++)
273-
{
274-
mlk_poly_permute_bitrev_to_custom(a[i].coeffs);
275-
}
293+
mlk_polymat_permute_bitrev_to_custom(a);
276294

277295
/* Specification: Partially implements
278296
* @[FIPS203, Section 3.3, Destruction of intermediate values] */
@@ -524,4 +542,4 @@ void mlk_indcpa_dec(uint8_t m[MLKEM_INDCPA_MSGBYTES],
524542
#undef mlk_pack_ciphertext
525543
#undef mlk_unpack_ciphertext
526544
#undef mlk_matvec_mul
527-
#undef mlk_poly_permute_bitrev_to_custom
545+
#undef mlk_polymat_permute_bitrev_to_custom

mlkem/src/sampling.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ __contract__(
143143
((12 * MLKEM_N / 8 * (1 << 12) / MLKEM_Q + MLK_XOF_RATE) / MLK_XOF_RATE)
144144
#endif
145145

146+
#if !defined(MLK_CONFIG_SERIAL_FIPS202_ONLY)
146147
/* Reference: Does not exist in the reference implementation @[REF].
147148
* - x4-batched version of `rej_uniform()` from the
148149
* reference implementation, leveraging x4-batched Keccak-f1600. */
@@ -211,6 +212,7 @@ void mlk_poly_rej_uniform_x4(mlk_poly *vec0, mlk_poly *vec1, mlk_poly *vec2,
211212
* @[FIPS203, Section 3.3, Destruction of intermediate values] */
212213
mlk_zeroize(buf, sizeof(buf));
213214
}
215+
#endif /* !MLK_CONFIG_SERIAL_FIPS202_ONLY */
214216

215217
MLK_INTERNAL_API
216218
void mlk_poly_rej_uniform(mlk_poly *entry, uint8_t seed[MLKEM_SYMBYTES + 2])

mlkem/src/sampling.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
#define MLK_SAMPLING_H
1717

1818
#include <stdint.h>
19-
#include <stdlib.h>
2019
#include "cbmc.h"
2120
#include "common.h"
2221
#include "poly.h"
@@ -58,6 +57,7 @@ MLK_INTERNAL_API
5857
void mlk_poly_cbd3(mlk_poly *r, const uint8_t buf[3 * MLKEM_N / 4]);
5958
#endif /* MLK_CONFIG_MULTILEVEL_WITH_SHARED || MLKEM_ETA1 == 3 */
6059

60+
#if !defined(MLK_CONFIG_SERIAL_FIPS202_ONLY)
6161
#define mlk_poly_rej_uniform_x4 MLK_NAMESPACE(poly_rej_uniform_x4)
6262
/*************************************************
6363
* Name: mlk_poly_rej_uniform_x4
@@ -92,6 +92,7 @@ __contract__(
9292
ensures(array_bound(vec1->coeffs, 0, MLKEM_N, 0, MLKEM_Q))
9393
ensures(array_bound(vec2->coeffs, 0, MLKEM_N, 0, MLKEM_Q))
9494
ensures(array_bound(vec3->coeffs, 0, MLKEM_N, 0, MLKEM_Q)));
95+
#endif /* !MLK_CONFIG_SERIAL_FIPS202_ONLY */
9596

9697
#define mlk_poly_rej_uniform MLK_NAMESPACE(poly_rej_uniform)
9798
/*************************************************

proofs/cbmc/gen_matrix/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
2020
PROJECT_SOURCES += $(SRCDIR)/mlkem/src/indcpa.c
2121

2222
CHECK_FUNCTION_CONTRACTS=mlk_gen_matrix
23-
USE_FUNCTION_CONTRACTS=mlk_poly_rej_uniform mlk_poly_rej_uniform_x4 mlk_poly_permute_bitrev_to_custom mlk_zeroize
23+
USE_FUNCTION_CONTRACTS=mlk_poly_rej_uniform mlk_poly_rej_uniform_x4 mlk_polymat_permute_bitrev_to_custom mlk_zeroize
2424
APPLY_LOOP_CONTRACTS=on
2525
USE_DYNAMIC_FRAMES=1
2626

proofs/cbmc/gen_matrix_native/Makefile renamed to proofs/cbmc/gen_matrix_serial/Makefile

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,31 +4,31 @@
44
include ../Makefile_params.common
55

66
HARNESS_ENTRY = harness
7-
HARNESS_FILE = gen_matrix_native_harness
7+
HARNESS_FILE = gen_matrix_serial_harness
88

99
# This should be a unique identifier for this proof, and will appear on the
1010
# Litani dashboard. It can be human-readable and contain spaces if you wish.
11-
PROOF_UID = gen_matrix_native
11+
PROOF_UID = mlk_gen_matrix_serial
1212

13-
DEFINES += -DMLK_CONFIG_USE_NATIVE_BACKEND_ARITH -DMLK_CONFIG_ARITH_BACKEND_FILE="\"dummy_backend.h\""
13+
DEFINES += -DMLK_CONFIG_SERIAL_FIPS202_ONLY
1414
INCLUDES +=
1515

1616
REMOVE_FUNCTION_BODY +=
17-
UNWINDSET += mlk_gen_matrix.0:4 mlk_gen_matrix.1:4 mlk_gen_matrix.2:4 mlk_gen_matrix.3:16
17+
UNWINDSET += mlk_gen_matrix.0:4 mlk_gen_matrix.1:16
1818

1919
PROOF_SOURCES += $(PROOFDIR)/$(HARNESS_FILE).c
2020
PROJECT_SOURCES += $(SRCDIR)/mlkem/src/indcpa.c
2121

2222
CHECK_FUNCTION_CONTRACTS=mlk_gen_matrix
23-
USE_FUNCTION_CONTRACTS=mlk_poly_rej_uniform mlk_poly_rej_uniform_x4 mlk_poly_permute_bitrev_to_custom
23+
USE_FUNCTION_CONTRACTS=mlk_poly_rej_uniform mlk_polymat_permute_bitrev_to_custom mlk_zeroize
2424
APPLY_LOOP_CONTRACTS=on
2525
USE_DYNAMIC_FRAMES=1
2626

2727
# Disable any setting of EXTERNAL_SAT_SOLVER, and choose SMT backend instead
2828
EXTERNAL_SAT_SOLVER=
2929
CBMCFLAGS=--smt2
3030

31-
FUNCTION_NAME = mlk_gen_matrix_native
31+
FUNCTION_NAME = mlk_gen_matrix_serial
3232

3333
# If this proof is found to consume huge amounts of RAM, you can set the
3434
# EXPENSIVE variable. With new enough versions of the proof tools, this will

0 commit comments

Comments
 (0)