Skip to content

Commit 4e2bdcb

Browse files
committed
CT: Make poly_chknorm constant flow
The reference implementation implements poly_chknorm in variables time. It argues that while the input coefficients itself are secret in some call sites, it is okay to leak which coefficient lead to rejection. It, hence, does absolute value computation in constant-time and then checks the bound using a conditional. This approach appears safe, but somewhat unclean as it is still operating on _secret data_. When performing constant-time testing it also requires a number of declassifications. This commit takes a more conservative approach and changes poly_chknorm to a constant-time implementation in the hope tha the performance penalty is acceptable. A minor change is that the API of poly_chknorm is changed to returning 0xFFFFFFFF in the case of failure to be able to re-use existing constant-time primitives. CBMC proofs are adjusted accordingly. Resolves #153 Signed-off-by: Matthias J. Kannwischer <[email protected]>
1 parent 6369a7e commit 4e2bdcb

File tree

5 files changed

+25
-23
lines changed

5 files changed

+25
-23
lines changed

mldsa/ct.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ __contract__(
176176
**************************************************/
177177
/* TODO: proof */
178178
static MLD_INLINE uint32_t mld_ct_cmask_neg_i32(int32_t x)
179-
__contract__(ensures(return_value == ((x < 0) ? 0xFFFFFFFF : 0)))
179+
__contract__(
180+
ensures(return_value == ((x < 0) ? 0xFFFFFFFF : 0))
181+
)
180182
{
181183
int64_t tmp = mld_value_barrier_i64((int64_t)x);
182184
tmp >>= 31;
@@ -197,7 +199,7 @@ __contract__(ensures(return_value == ((x < 0) ? 0xFFFFFFFF : 0)))
197199
*
198200
**************************************************/
199201
/* TODO: proof */
200-
static MLD_INLINE uint32_t mld_ct_abs_i32(int32_t x)
202+
static MLD_INLINE int32_t mld_ct_abs_i32(int32_t x)
201203
__contract__(
202204
requires(x >= -INT32_MAX)
203205
ensures(return_value == ((x < 0) ? -x : x))

mldsa/poly.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -234,35 +234,35 @@ void mld_poly_use_hint(mld_poly *b, const mld_poly *a, const mld_poly *h)
234234
/* Reference: explicitly checks the bound B to be <= (MLDSA_Q - 1) / 8).
235235
* This is unnecessary as it's always a compile-time constant.
236236
* We instead model it as a precondition.
237+
* Checking the bound is performed using a conditional arguing
238+
* that it is okay to leak which coefficient violates the bound (while the
239+
* coefficient itself must remain secret).
240+
* We instead perform everything in constant-time.
237241
*/
238-
int mld_poly_chknorm(const mld_poly *a, int32_t B)
242+
uint32_t mld_poly_chknorm(const mld_poly *a, int32_t B)
239243
{
240244
unsigned int i;
241-
int rc = 0;
242-
int32_t t;
245+
uint32_t t = 0;
243246
mld_assert_bound(a->coeffs, MLDSA_N, -REDUCE32_RANGE_MAX, REDUCE32_RANGE_MAX);
244247

245-
/* It is ok to leak which coefficient violates the bound since
246-
the probability for each coefficient is independent of secret
247-
data but we must not leak the sign of the centralized representative. */
248248

249249
for (i = 0; i < MLDSA_N; ++i)
250250
__loop__(
251251
invariant(i <= MLDSA_N)
252-
invariant(rc == 0 || rc == 1)
253-
invariant((rc == 0) == array_abs_bound(a->coeffs, 0, i, B))
252+
invariant(t == 0 || t == 0xFFFFFFFF)
253+
invariant((t == 0) == array_abs_bound(a->coeffs, 0, i, B))
254254
)
255255
{
256-
/* Absolute value */
257-
t = mld_ct_abs_i32(a->coeffs[i]);
256+
/* Reference: Leaks which coefficient violates the bound via a conditional.
257+
* We are more conservative to reduce the number of declassifications in
258+
* constant-time testing.
259+
*/
258260

259-
if (t >= B)
260-
{
261-
rc = 1;
262-
}
261+
/* if (abs(a[i]) >= B) */
262+
t |= mld_ct_cmask_neg_i32(B - 1 - mld_ct_abs_i32(a->coeffs[i]));
263263
}
264264

265-
return rc;
265+
return t;
266266
}
267267

268268
/*************************************************

mldsa/poly.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -295,15 +295,15 @@ __contract__(
295295
* Arguments: - const mld_poly *a: pointer to polynomial
296296
* - int32_t B: norm bound
297297
*
298-
* Returns 0 if norm is strictly smaller than B <= (MLDSA_Q-1)/8 and 1
299-
*otherwise.
298+
* Returns 0 if norm is strictly smaller than B <= (MLDSA_Q-1)/8 and 0xFFFFFFFF
299+
* otherwise.
300300
**************************************************/
301-
int mld_poly_chknorm(const mld_poly *a, int32_t B)
301+
uint32_t mld_poly_chknorm(const mld_poly *a, int32_t B)
302302
__contract__(
303303
requires(memory_no_alias(a, sizeof(mld_poly)))
304304
requires(0 <= B && B <= (MLDSA_Q - 1) / 8)
305305
requires(array_bound(a->coeffs, 0, MLDSA_N, -REDUCE32_RANGE_MAX, REDUCE32_RANGE_MAX))
306-
ensures(return_value == 0 || return_value == 1)
306+
ensures(return_value == 0 || return_value == 0xFFFFFFFF)
307307
ensures((return_value == 0) == array_abs_bound(a->coeffs, 0, MLDSA_N, B))
308308
);
309309

proofs/cbmc/poly_chknorm/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)/mldsa/poly.c
2121

2222
CHECK_FUNCTION_CONTRACTS=$(MLD_NAMESPACE)poly_chknorm
23-
USE_FUNCTION_CONTRACTS=mld_ct_abs_i32
23+
USE_FUNCTION_CONTRACTS=mld_ct_abs_i32 mld_ct_cmask_neg_i32
2424
APPLY_LOOP_CONTRACTS=on
2525
USE_DYNAMIC_FRAMES=1
2626

proofs/cbmc/poly_chknorm/poly_chknorm_harness.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
void harness(void)
88
{
99
mld_poly *a;
10-
int r;
10+
uint32_t r;
1111
int32_t B;
1212
r = mld_poly_chknorm(a, B);
1313
}

0 commit comments

Comments
 (0)