Skip to content

Commit e27068b

Browse files
committed
FIPS202: Provide alignment constraints for output buffers
mlkem-native will only call the FIPS202 API with aligned output buffers which can be useful for users providing their own FIPS202 implementation. This commit extends the documentation and CBMC contracts accordingly, and adds debug assertions for checking alignment. Note that CBMC does, to the best of my knowledge, not support talking about absolute pointer alignment, so we cannot rely on proof here. We also take the opportunity to align internal buffers used in the default C implementation of mlk_shake256x4. Signed-off-by: Hanno Becker <[email protected]>
1 parent 6048cab commit e27068b

File tree

8 files changed

+159
-5
lines changed

8 files changed

+159
-5
lines changed

mlkem/mlkem_native.S

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,8 +280,10 @@
280280
#undef mlk_assert
281281
#undef mlk_assert_abs_bound
282282
#undef mlk_assert_abs_bound_2d
283+
#undef mlk_assert_alignment
283284
#undef mlk_assert_bound
284285
#undef mlk_assert_bound_2d
286+
#undef mlk_debug_check_alignment
285287
#undef mlk_debug_check_assert
286288
#undef mlk_debug_check_bounds
287289
/* mlkem/src/poly.h */

mlkem/mlkem_native.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,8 +269,10 @@
269269
#undef mlk_assert
270270
#undef mlk_assert_abs_bound
271271
#undef mlk_assert_abs_bound_2d
272+
#undef mlk_assert_alignment
272273
#undef mlk_assert_bound
273274
#undef mlk_assert_bound_2d
275+
#undef mlk_debug_check_alignment
274276
#undef mlk_debug_check_assert
275277
#undef mlk_debug_check_bounds
276278
/* mlkem/src/poly.h */

mlkem/src/debug.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,28 @@
1616

1717
#define MLK_DEBUG_ERROR_HEADER "[ERROR:%s:%04d] "
1818

19+
void mlk_debug_check_alignment(const char *file, int line, const void *ptr,
20+
size_t ptr_len, unsigned alignment)
21+
{
22+
uintptr_t ptr_uint = (uintptr_t)ptr;
23+
if (ptr_uint % alignment != 0)
24+
{
25+
fprintf(stderr,
26+
MLK_DEBUG_ERROR_HEADER
27+
"Alignment assertion failed for address %p)\n",
28+
file, line, ptr);
29+
exit(1);
30+
}
31+
32+
if (ptr_len % alignment != 0)
33+
{
34+
fprintf(stderr,
35+
MLK_DEBUG_ERROR_HEADER
36+
"Alignment assertion failed for length %u)\n",
37+
file, line, (unsigned)ptr_len);
38+
exit(1);
39+
}
40+
}
1941
void mlk_debug_check_assert(const char *file, int line, const int val)
2042
{
2143
if (val == 0)

mlkem/src/debug.h

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,31 @@ void mlk_debug_check_bounds(const char *file, int line, const int16_t *ptr,
4545
unsigned len, int lower_bound_exclusive,
4646
int upper_bound_exclusive);
4747

48+
/*************************************************
49+
* Name: mlk_debug_check_alignment
50+
*
51+
* Description: Check whether a pointer has the specified alignment.
52+
*
53+
* Prints an error message to stderr and calls
54+
* exit(1) if not.
55+
*
56+
* Arguments: - file: filename
57+
* - line: line number
58+
* - ptr: pointer to be checked for alignment
59+
* - alignment: alignment to check for (e.g. 8, 16)
60+
**************************************************/
61+
#define mlk_debug_check_alignment MLK_NAMESPACE(mlkem_debug_check_alignment)
62+
void mlk_debug_check_alignment(const char *file, int line, const void *ptr,
63+
size_t ptr_len, unsigned alignment);
64+
65+
66+
/* Check that pointer and length is aligned to given alignment. */
67+
#define mlk_assert_alignment(ptr, len, alignment) \
68+
do \
69+
{ \
70+
mlk_debug_check_alignment(__FILE__, __LINE__, (ptr), (len), (alignment)); \
71+
} while (0)
72+
4873
/* Check assertion, calling exit() upon failure
4974
*
5075
* val: Value that's asserted to be non-zero
@@ -79,6 +104,11 @@ void mlk_debug_check_bounds(const char *file, int line, const int16_t *ptr,
79104
#elif defined(CBMC)
80105
#include "cbmc.h"
81106

107+
/* CBMC's memory model does not support talking about pointer alignment,
108+
* so we only check the alignment of the buffer length. */
109+
#define mlk_assert_alignment(ptr, len, alignment) \
110+
mlk_assert((len) % (alignment) == 0)
111+
82112
#define mlk_assert(val) cassert(val)
83113

84114
#define mlk_assert_bound(ptr, len, value_lb, value_ub) \
@@ -101,6 +131,11 @@ void mlk_debug_check_bounds(const char *file, int line, const int16_t *ptr,
101131

102132
#else /* !MLKEM_DEBUG && CBMC */
103133

134+
#define mlk_assert_alignment(ptr, len, alignment) \
135+
do \
136+
{ \
137+
} while (0)
138+
104139
#define mlk_assert(val) \
105140
do \
106141
{ \

mlkem/src/fips202/fips202.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636

3737
#include <stdint.h>
3838

39+
#include "../debug.h"
3940
#include "../verify.h"
4041
#include "fips202.h"
4142
#include "keccakf1600.h"
@@ -196,6 +197,7 @@ void mlk_shake128_absorb_once(mlk_shake128ctx *state, const uint8_t *input,
196197
void mlk_shake128_squeezeblocks(uint8_t *output, size_t nblocks,
197198
mlk_shake128ctx *state)
198199
{
200+
mlk_assert_alignment(output, nblocks * SHAKE128_RATE, 8);
199201
mlk_keccak_squeezeblocks(output, nblocks, state->ctx, SHAKE128_RATE);
200202
}
201203

@@ -212,6 +214,8 @@ void mlk_shake256(uint8_t *output, size_t outlen, const uint8_t *input,
212214
size_t inlen)
213215
{
214216
mlk_shake256ctx state;
217+
mlk_assert_alignment(output, outlen, 8);
218+
215219
/* Absorb input */
216220
mlk_keccak_absorb_once(state.ctx, SHAKE256_RATE, input, inlen, 0x1F);
217221
/* Squeeze output */
@@ -224,6 +228,8 @@ void mlk_shake256(uint8_t *output, size_t outlen, const uint8_t *input,
224228
void mlk_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
225229
{
226230
uint64_t ctx[25];
231+
mlk_assert_alignment(output, SHA3_256_RATE, 8);
232+
227233
/* Absorb input */
228234
mlk_keccak_absorb_once(ctx, SHA3_256_RATE, input, inlen, 0x06);
229235
/* Squeeze output */
@@ -236,6 +242,8 @@ void mlk_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
236242
void mlk_sha3_512(uint8_t *output, const uint8_t *input, size_t inlen)
237243
{
238244
uint64_t ctx[25];
245+
mlk_assert_alignment(output, SHA3_512_RATE, 8);
246+
239247
/* Absorb input */
240248
mlk_keccak_absorb_once(ctx, SHA3_512_RATE, input, inlen, 0x06);
241249
/* Squeeze output */

mlkem/src/fips202/fips202.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ __contract__(
6262
* multiple times to keep squeezing, i.e., is incremental.
6363
*
6464
* Arguments: - uint8_t *output: pointer to output blocks
65+
* Can be assumed to be 8-byte aligned.
6566
* - size_t nblocks: number of blocks to be squeezed (written
6667
* to output)
6768
* - mlk_shake128ctx *state: pointer to in/output Keccak state
@@ -71,6 +72,7 @@ void mlk_shake128_squeezeblocks(uint8_t *output, size_t nblocks,
7172
__contract__(
7273
requires(nblocks <= 8 /* somewhat arbitrary bound */)
7374
requires(memory_no_alias(state, sizeof(mlk_shake128ctx)))
75+
/* We can't express alignment of output as a CBMC precondition. */
7476
requires(memory_no_alias(output, nblocks * SHAKE128_RATE))
7577
assigns(memory_slice(output, nblocks * SHAKE128_RATE), memory_slice(state, sizeof(mlk_shake128ctx)))
7678
);
@@ -90,7 +92,9 @@ void mlk_shake128_release(mlk_shake128ctx *state);
9092
* Description: SHAKE256 XOF with non-incremental API
9193
*
9294
* Arguments: - uint8_t *output: pointer to output
95+
* Can be assumed to be 8-byte aligned.
9396
* - size_t outlen: requested output length in bytes
97+
* Can be assumed to be 8-byte aligned.
9498
* - const uint8_t *input: pointer to input
9599
* - size_t inlen: length of input in bytes
96100
**************************************************/
@@ -99,7 +103,12 @@ void mlk_shake256(uint8_t *output, size_t outlen, const uint8_t *input,
99103
__contract__(
100104
requires(inlen <= MLK_MAX_BUFFER_SIZE)
101105
requires(outlen <= MLK_MAX_BUFFER_SIZE)
106+
/* The alignment constraint is not needed for the implementation, but
107+
* serves as an additional precondition for users wishing to use an
108+
* alternative FIPS202 implementation. */
109+
requires(outlen % 8 == 0)
102110
requires(memory_no_alias(input, inlen))
111+
/* We can't express alignment of output as a CBMC precondition. */
103112
requires(memory_no_alias(output, outlen))
104113
assigns(memory_slice(output, outlen))
105114
);
@@ -114,13 +123,15 @@ __contract__(
114123
* Description: SHA3-256 with non-incremental API
115124
*
116125
* Arguments: - uint8_t *output: pointer to output
126+
* Can be assumed to be 8-byte aligned.
117127
* - const uint8_t *input: pointer to input
118128
* - size_t inlen: length of input in bytes
119129
**************************************************/
120130
void mlk_sha3_256(uint8_t *output, const uint8_t *input, size_t inlen)
121131
__contract__(
122132
requires(inlen <= MLK_MAX_BUFFER_SIZE)
123133
requires(memory_no_alias(input, inlen))
134+
/* We can't express alignment of output as a CBMC precondition. */
124135
requires(memory_no_alias(output, SHA3_256_HASHBYTES))
125136
assigns(memory_slice(output, SHA3_256_HASHBYTES))
126137
);
@@ -135,13 +146,15 @@ __contract__(
135146
* Description: SHA3-512 with non-incremental API
136147
*
137148
* Arguments: - uint8_t *output: pointer to output
149+
* Can be assumed to be 8-byte aligned.
138150
* - const uint8_t *input: pointer to input
139-
* - size_t inlen: length of input in bytes
151+
* - size_t inlen: length of input in bytes.
140152
**************************************************/
141153
void mlk_sha3_512(uint8_t *output, const uint8_t *input, size_t inlen)
142154
__contract__(
143155
requires(inlen <= MLK_MAX_BUFFER_SIZE)
144156
requires(memory_no_alias(input, inlen))
157+
/* We can't express alignment of output and output as a CBMC precondition. */
145158
requires(memory_no_alias(output, SHA3_512_HASHBYTES))
146159
assigns(memory_slice(output, SHA3_512_HASHBYTES))
147160
);

mlkem/src/fips202/fips202x4.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
#include "../common.h"
1616
#if !defined(MLK_CONFIG_MULTILEVEL_NO_SHARED)
1717

18+
#include "../debug.h"
1819
#include "../verify.h"
20+
1921
#include "fips202.h"
2022
#include "fips202x4.h"
2123
#include "keccakf1600.h"
@@ -131,6 +133,11 @@ void mlk_shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2,
131133
uint8_t *out3, size_t nblocks,
132134
mlk_shake128x4ctx *state)
133135
{
136+
mlk_assert_alignment(out0, nblocks * SHAKE128_RATE, 8);
137+
mlk_assert_alignment(out1, nblocks * SHAKE128_RATE, 8);
138+
mlk_assert_alignment(out2, nblocks * SHAKE128_RATE, 8);
139+
mlk_assert_alignment(out3, nblocks * SHAKE128_RATE, 8);
140+
134141
mlk_keccak_squeezeblocks_x4(out0, out1, out2, out3, nblocks, state->ctx,
135142
SHAKE128_RATE);
136143
}
@@ -168,10 +175,15 @@ void mlk_shake256x4(uint8_t *out0, uint8_t *out1, uint8_t *out2, uint8_t *out3,
168175
{
169176
mlk_shake256x4_ctx statex;
170177
size_t nblocks = outlen / SHAKE256_RATE;
171-
uint8_t tmp0[SHAKE256_RATE];
172-
uint8_t tmp1[SHAKE256_RATE];
173-
uint8_t tmp2[SHAKE256_RATE];
174-
uint8_t tmp3[SHAKE256_RATE];
178+
uint8_t MLK_ALIGN tmp0[SHAKE256_RATE];
179+
uint8_t MLK_ALIGN tmp1[SHAKE256_RATE];
180+
uint8_t MLK_ALIGN tmp2[SHAKE256_RATE];
181+
uint8_t MLK_ALIGN tmp3[SHAKE256_RATE];
182+
183+
mlk_assert_alignment(out0, outlen, 8);
184+
mlk_assert_alignment(out1, outlen, 8);
185+
mlk_assert_alignment(out2, outlen, 8);
186+
mlk_assert_alignment(out3, outlen, 8);
175187

176188
mlk_shake256x4_absorb_once(&statex, in0, in1, in2, in3, inlen);
177189
mlk_shake256x4_squeezeblocks(out0, out1, out2, out3, nblocks, &statex);

mlkem/src/fips202/fips202x4.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,30 @@ typedef struct
2020
} MLK_ALIGN mlk_shake128x4ctx;
2121

2222
#define mlk_shake128x4_absorb_once MLK_NAMESPACE(shake128x4_absorb_once)
23+
/*************************************************
24+
* Name: mlk_shake128x4_absorb_once
25+
*
26+
* Description: 4x-batched one-shot absorb step of the SHAKE128 XOF.
27+
*
28+
* For call-sites (in mlkem-native):
29+
* - This function MUST ONLY be called straight after
30+
* mlk_shake128x4_init().
31+
* - This function MUST ONLY be called once.
32+
*
33+
* Consequently, for providers of custom FIPS202 code
34+
* to be used with mlkem-native:
35+
* - You may assume that the input context is
36+
* freshly initialized via mlk_shake128x4_init().
37+
* - You may assume that this function is
38+
* called exactly once.
39+
*
40+
* Arguments: - mlk_shake128x4ctx *state:
41+
* pointer to SHAKE128x4 context
42+
* - const uint8_t *in0, *in1, *in2, *in3:
43+
* pointers to inputs to be absorbed.
44+
* - size_t inlen:
45+
* length of input buffers in bytes
46+
**************************************************/
2347
void mlk_shake128x4_absorb_once(mlk_shake128x4ctx *state, const uint8_t *in0,
2448
const uint8_t *in1, const uint8_t *in2,
2549
const uint8_t *in3, size_t inlen)
@@ -34,12 +58,27 @@ __contract__(
3458
);
3559

3660
#define mlk_shake128x4_squeezeblocks MLK_NAMESPACE(shake128x4_squeezeblocks)
61+
/*************************************************
62+
* Name: mlk_shake128x4_squeezeblocks
63+
*
64+
* Description: 4x-batched squeeze step of SHAKE128 XOF. Squeezes full blocks of
65+
* SHAKE128_RATE bytes each. Modifies the state. Can be called
66+
* multiple times to keep squeezing, i.e., is incremental.
67+
*
68+
* Arguments: - uint8_t *out0, *out1, *out2, *out3:
69+
* pointers to output blocks
70+
* Can be assumed to be 8-byte aligned.
71+
* - size_t nblocks:
72+
* number of blocks to be squeezed (written to output)
73+
* - mlk_shake128x4ctx *state: pointer to in/output Keccak state
74+
**************************************************/
3775
void mlk_shake128x4_squeezeblocks(uint8_t *out0, uint8_t *out1, uint8_t *out2,
3876
uint8_t *out3, size_t nblocks,
3977
mlk_shake128x4ctx *state)
4078
__contract__(
4179
requires(nblocks <= 8 /* somewhat arbitrary bound */)
4280
requires(memory_no_alias(state, sizeof(mlk_shake128x4ctx)))
81+
/* We can't express alignment of out{0,1,2,3} as a CBMC preconditions. */
4382
requires(memory_no_alias(out0, nblocks * SHAKE128_RATE))
4483
requires(memory_no_alias(out1, nblocks * SHAKE128_RATE))
4584
requires(memory_no_alias(out2, nblocks * SHAKE128_RATE))
@@ -58,12 +97,33 @@ void mlk_shake128x4_init(mlk_shake128x4ctx *state);
5897
void mlk_shake128x4_release(mlk_shake128x4ctx *state);
5998

6099
#define mlk_shake256x4 MLK_NAMESPACE(shake256x4)
100+
/*************************************************
101+
* Name: mlk_shake256x4
102+
*
103+
* Description: 4x-batched SHAKE256 XOF with non-incremental API
104+
*
105+
* Arguments: - uint8_t *out0, *out1, *out2, *out3:
106+
* pointers to output buffers
107+
* Can be assumed to be 8-byte aligned.
108+
* - size_t outlen:
109+
* requested output length in bytes
110+
* Can be assumed to be 8-byte aligned.
111+
* - const uint8_t *input:
112+
* pointer to input
113+
* - size_t inlen:
114+
* length of input in bytes
115+
**************************************************/
61116
void mlk_shake256x4(uint8_t *out0, uint8_t *out1, uint8_t *out2, uint8_t *out3,
62117
size_t outlen, uint8_t *in0, uint8_t *in1, uint8_t *in2,
63118
uint8_t *in3, size_t inlen)
64119
__contract__(
65120
requires(inlen <= MLK_MAX_BUFFER_SIZE)
66121
requires(outlen <= MLK_MAX_BUFFER_SIZE)
122+
/* The alignment constraint is not needed for the implementation, but
123+
* serves as an additional preconditions for users wishing to use an
124+
* alternative FIPS202 implementation. */
125+
requires(outlen % 8 == 0)
126+
/* We can't express alignment of out{0,1,2,3} as a CBMC preconditions. */
67127
requires(memory_no_alias(in0, inlen))
68128
requires(memory_no_alias(in1, inlen))
69129
requires(memory_no_alias(in2, inlen))

0 commit comments

Comments
 (0)