-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathpoly.h
336 lines (315 loc) · 12.1 KB
/
poly.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
/*
* Copyright (c) 2024-2025 The mlkem-native project authors
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef MLK_POLY_H
#define MLK_POLY_H
#include <stddef.h>
#include <stdint.h>
#include "cbmc.h"
#include "common.h"
#include "debug.h"
#include "verify.h"
/* Absolute exclusive upper bound for the output of the inverse NTT */
#define MLK_INVNTT_BOUND (8 * MLKEM_Q)
/* Absolute exclusive upper bound for the output of the forward NTT */
#define MLK_NTT_BOUND (8 * MLKEM_Q)
/*
* Elements of R_q = Z_q[X]/(X^n + 1). Represents polynomial
* coeffs[0] + X*coeffs[1] + X^2*coeffs[2] + ... + X^{n-1}*coeffs[n-1]
*/
typedef struct
{
int16_t coeffs[MLKEM_N];
} MLK_ALIGN mlk_poly;
/*
* INTERNAL presentation of precomputed data speeding up
* the base multiplication of two polynomials in NTT domain.
*/
typedef struct
{
int16_t coeffs[MLKEM_N >> 1];
} mlk_poly_mulcache;
/*************************************************
* Name: mlk_cast_uint16_to_int16
*
* Description: Cast uint16 value to int16
*
* Returns:
* input x in 0 .. 32767: returns value unchanged
* input x in 32768 .. 65535: returns (x - 65536)
**************************************************/
#ifdef CBMC
#pragma CPROVER check push
#pragma CPROVER check disable "conversion"
#endif
static MLK_ALWAYS_INLINE int16_t mlk_cast_uint16_to_int16(uint16_t x)
{
/*
* PORTABILITY: This relies on uint16_t -> int16_t
* being implemented as the inverse of int16_t -> uint16_t,
* which is implementation-defined (C99 6.3.1.3 (3))
* CBMC (correctly) fails to prove this conversion is OK,
* so we have to suppress that check here
*/
return (int16_t)x;
}
#ifdef CBMC
#pragma CPROVER check pop
#endif
/*************************************************
* Name: mlk_montgomery_reduce
*
* Description: Generic Montgomery reduction; given a 32-bit integer a, computes
* 16-bit integer congruent to a * R^-1 mod q, where R=2^16
*
* Arguments: - int32_t a: input integer to be reduced, of absolute value
* smaller or equal to INT32_MAX - 2^15 * MLKEM_Q.
*
* Returns: integer congruent to a * R^-1 modulo q, with absolute value
* <= ceil(|a| / 2^16) + (MLKEM_Q + 1)/2
*
**************************************************/
static MLK_ALWAYS_INLINE int16_t mlk_montgomery_reduce(int32_t a)
__contract__(
requires(a < +(INT32_MAX - (((int32_t)1 << 15) * MLKEM_Q)) &&
a > -(INT32_MAX - (((int32_t)1 << 15) * MLKEM_Q)))
/* We don't attempt to express an input-dependent output bound
* as the post-condition here. There are two call-sites for this
* function:
* - The base multiplication: Here, we need no output bound.
* - mlk_fqmul: Here, we inline this function and prove another spec
* for mlk_fqmul which does have a post-condition bound. */
)
{
/* check-magic: 62209 == unsigned_mod(pow(MLKEM_Q, -1, 2^16), 2^16) */
const uint32_t QINV = 62209;
/* Compute a*q^{-1} mod 2^16 in unsigned representatives */
const uint16_t a_reduced = a & UINT16_MAX;
const uint16_t a_inverted = (a_reduced * QINV) & UINT16_MAX;
/* Lift to signed canonical representative mod 2^16. */
const int16_t t = mlk_cast_uint16_to_int16(a_inverted);
int32_t r;
mlk_assert(a < +(INT32_MAX - (((int32_t)1 << 15) * MLKEM_Q)) &&
a > -(INT32_MAX - (((int32_t)1 << 15) * MLKEM_Q)));
r = a - ((int32_t)t * MLKEM_Q);
/*
* PORTABILITY: Right-shift on a signed integer is, strictly-speaking,
* implementation-defined for negative left argument. Here,
* we assume it's sign-preserving "arithmetic" shift right. (C99 6.5.7 (5))
*/
r = r >> 16;
/* Bounds: |r >> 16| <= ceil(|r| / 2^16)
* <= ceil(|a| / 2^16 + MLKEM_Q / 2)
* <= ceil(|a| / 2^16) + (MLKEM_Q + 1) / 2
*
* (Note that |a >> n| = ceil(|a| / 2^16) for negative a)
*/
return (int16_t)r;
}
#define mlk_poly_tomont MLK_NAMESPACE(poly_tomont)
/*************************************************
* Name: mlk_poly_tomont
*
* Description: Inplace conversion of all coefficients of a polynomial
* from normal domain to Montgomery domain
*
* Bounds: Output < q in absolute value.
*
* Arguments: - mlk_poly *r: pointer to input/output polynomial
*
* Specification: Internal normalization required in `mlk_indcpa_keypair_derand`
* as part of matrix-vector multiplication
* [FIPS 203, Algorithm 13, K-PKE.KeyGen, L18].
*
**************************************************/
MLK_INTERNAL_API
void mlk_poly_tomont(mlk_poly *r)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
assigns(memory_slice(r, sizeof(mlk_poly)))
ensures(array_abs_bound(r->coeffs, 0, MLKEM_N, MLKEM_Q))
);
#define mlk_poly_mulcache_compute MLK_NAMESPACE(poly_mulcache_compute)
/************************************************************
* Name: mlk_poly_mulcache_compute
*
* Description: Computes the mulcache for a polynomial in NTT domain
*
* The mulcache of a degree-2 polynomial b := b0 + b1*X
* in Fq[X]/(X^2-zeta) is the value b1*zeta, needed when
* computing products of b in Fq[X]/(X^2-zeta).
*
* The mulcache of a polynomial in NTT domain -- which is
* a 128-tuple of degree-2 polynomials in Fq[X]/(X^2-zeta),
* for varying zeta, is the 128-tuple of mulcaches of those
* polynomials.
*
* Arguments: - x: Pointer to mulcache to be populated
* - a: Pointer to input polynomial
*
* Specification:
* - Caches `b_1 * \gamma` in [FIPS 203, Algorithm 12, BaseCaseMultiply, L1]
*
************************************************************/
/*
* NOTE: The default C implementation of this function populates
* the mulcache with values in (-q,q), but this is not needed for the
* higher level safety proofs, and thus not part of the spec.
*/
MLK_INTERNAL_API
void mlk_poly_mulcache_compute(mlk_poly_mulcache *x, const mlk_poly *a)
__contract__(
requires(memory_no_alias(x, sizeof(mlk_poly_mulcache)))
requires(memory_no_alias(a, sizeof(mlk_poly)))
assigns(object_whole(x))
);
#define mlk_poly_reduce MLK_NAMESPACE(poly_reduce)
/*************************************************
* Name: mlk_poly_reduce
*
* Description: Converts polynomial to _unsigned canonical_ representatives.
*
* The input coefficients can be arbitrary integers in int16_t.
* The output coefficients are in [0,1,...,MLKEM_Q-1].
*
* Arguments: - mlk_poly *r: pointer to input/output polynomial
*
* Specification: Normalizes on unsigned canoncial representatives
* ahead of calling [FIPS 203, Compress_d, Eq (4.7)].
* This is not made explicit in FIPS 203.
*
**************************************************/
/*
* NOTE: The semantics of mlk_poly_reduce() is different in
* the reference implementation, which requires
* signed canonical output data. Unsigned canonical
* outputs are better suited to the only remaining
* use of mlk_poly_reduce() in the context of (de)serialization.
*/
MLK_INTERNAL_API
void mlk_poly_reduce(mlk_poly *r)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
assigns(memory_slice(r, sizeof(mlk_poly)))
ensures(array_bound(r->coeffs, 0, MLKEM_N, 0, MLKEM_Q))
);
#define mlk_poly_add MLK_NAMESPACE(poly_add)
/************************************************************
* Name: mlk_poly_add
*
* Description: Adds two polynomials in place
*
* Arguments: - r: Pointer to input-output polynomial to be added to.
* - b: Pointer to input polynomial that should be added
* to r. Must be disjoint from r.
*
* The coefficients of r and b must be so that the addition does
* not overflow. Otherwise, the behaviour of this function is undefined.
*
* Specification:
* - [FIPS 203, 2.4.5, Arithmetic With Polynomials and NTT Representations]
* - Used in [FIPS 203, Algorithm 14 (K-PKE.Encrypt), L21]
*
************************************************************/
/*
* NOTE: The reference implementation uses a 3-argument mlk_poly_add.
* We specialize to the accumulator form to avoid reasoning about aliasing.
*/
MLK_INTERNAL_API
void mlk_poly_add(mlk_poly *r, const mlk_poly *b)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
requires(memory_no_alias(b, sizeof(mlk_poly)))
requires(forall(k0, 0, MLKEM_N, (int32_t) r->coeffs[k0] + b->coeffs[k0] <= INT16_MAX))
requires(forall(k1, 0, MLKEM_N, (int32_t) r->coeffs[k1] + b->coeffs[k1] >= INT16_MIN))
ensures(forall(k, 0, MLKEM_N, r->coeffs[k] == old(*r).coeffs[k] + b->coeffs[k]))
assigns(memory_slice(r, sizeof(mlk_poly)))
);
#define mlk_poly_sub MLK_NAMESPACE(poly_sub)
/*************************************************
* Name: mlk_poly_sub
*
* Description: Subtract two polynomials; no modular reduction is performed
*
* Arguments: - mlk_poly *r: Pointer to input-output polynomial to be added to.
* - const mlk_poly *b: Pointer to second input polynomial
*
* Specification:
* - [FIPS 203, 2.4.5, Arithmetic With Polynomials and NTT Representations]
* - Used in [FIPS 203, Algorithm 15, K-PKE.Decrypt, L6]
*
**************************************************/
/*
* NOTE: The reference implementation uses a 3-argument mlk_poly_sub.
* We specialize to the accumulator form to avoid reasoning about aliasing.
*/
MLK_INTERNAL_API
void mlk_poly_sub(mlk_poly *r, const mlk_poly *b)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
requires(memory_no_alias(b, sizeof(mlk_poly)))
requires(forall(k0, 0, MLKEM_N, (int32_t) r->coeffs[k0] - b->coeffs[k0] <= INT16_MAX))
requires(forall(k1, 0, MLKEM_N, (int32_t) r->coeffs[k1] - b->coeffs[k1] >= INT16_MIN))
ensures(forall(k, 0, MLKEM_N, r->coeffs[k] == old(*r).coeffs[k] - b->coeffs[k]))
assigns(object_whole(r))
);
#define mlk_poly_ntt MLK_NAMESPACE(poly_ntt)
/*************************************************
* Name: mlk_poly_ntt
*
* Description: Computes negacyclic number-theoretic transform (NTT) of
* a polynomial in place.
*
* The input is assumed to be in normal order and
* coefficient-wise bound by MLKEM_Q in absolute value.
*
* The output polynomial is in bitreversed order, and
* coefficient-wise bound by MLK_NTT_BOUND in absolute value.
*
* (NOTE: Sometimes the input to the NTT is actually smaller,
* which gives better bounds.)
*
* Arguments: - mlk_poly *p: pointer to in/output polynomial
*
* Specification: Implements [FIPS 203, Algorithm 9, NTT]
*
**************************************************/
MLK_INTERNAL_API
void mlk_poly_ntt(mlk_poly *r)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
requires(array_abs_bound(r->coeffs, 0, MLKEM_N, MLKEM_Q))
assigns(memory_slice(r, sizeof(mlk_poly)))
ensures(array_abs_bound(r->coeffs, 0, MLKEM_N, MLK_NTT_BOUND))
);
#define mlk_poly_invntt_tomont MLK_NAMESPACE(poly_invntt_tomont)
/*************************************************
* Name: mlk_poly_invntt_tomont
*
* Description: Computes inverse of negacyclic number-theoretic transform (NTT)
* of a polynomial in place;
* inputs assumed to be in bitreversed order, output in normal
* order
*
* The input is assumed to be in bitreversed order, and can
* have arbitrary coefficients in int16_t.
*
* The output polynomial is in normal order, and
* coefficient-wise bound by MLK_INVNTT_BOUND in absolute value.
*
* Arguments: - uint16_t *a: pointer to in/output polynomial
*
* Specification: Implements composition of [FIPS 203, Algorithm 10, NTT^{-1}]
* and elementwise modular multiplication with a suitable
* Montgomery factor introduced during the base multiplication.
*
**************************************************/
MLK_INTERNAL_API
void mlk_poly_invntt_tomont(mlk_poly *r)
__contract__(
requires(memory_no_alias(r, sizeof(mlk_poly)))
assigns(memory_slice(r, sizeof(mlk_poly)))
ensures(array_abs_bound(r->coeffs, 0, MLKEM_N, MLK_INVNTT_BOUND))
);
#endif /* MLK_POLY_H */