Skip to content

Commit 7797682

Browse files
authored
Merge pull request #392 from pq-code-package/constant-time-2
CT: Make poly_chknorm constant flow
2 parents 6369a7e + 744f89c commit 7797682

File tree

9 files changed

+53
-45
lines changed

9 files changed

+53
-45
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

mldsa/polyvec.c

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -292,23 +292,26 @@ void mld_polyvecl_pointwise_acc_montgomery(mld_poly *w, const mld_polyvecl *u,
292292
}
293293

294294

295-
int mld_polyvecl_chknorm(const mld_polyvecl *v, int32_t bound)
295+
uint32_t mld_polyvecl_chknorm(const mld_polyvecl *v, int32_t bound)
296296
{
297297
unsigned int i;
298+
uint32_t t = 0;
298299

299300
for (i = 0; i < MLDSA_L; ++i)
300301
__loop__(
301302
invariant(i <= MLDSA_L)
302-
invariant(forall(k1, 0, i, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, bound)))
303+
invariant(t == 0 || t == 0xFFFFFFFF)
304+
invariant((t == 0) == forall(k1, 0, i, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, bound)))
303305
)
304306
{
305-
if (mld_poly_chknorm(&v->vec[i], bound))
306-
{
307-
return 1;
308-
}
307+
/* Reference: Leaks which polynomial violates the bound via a conditional.
308+
* We are more conservative to reduce the number of declassifications in
309+
* constant-time testing.
310+
*/
311+
t |= mld_poly_chknorm(&v->vec[i], bound);
309312
}
310313

311-
return 0;
314+
return t;
312315
}
313316

314317
/**************************************************************/
@@ -447,23 +450,26 @@ void mld_polyveck_pointwise_poly_montgomery(mld_polyveck *r, const mld_poly *a,
447450
}
448451

449452

450-
int mld_polyveck_chknorm(const mld_polyveck *v, int32_t bound)
453+
uint32_t mld_polyveck_chknorm(const mld_polyveck *v, int32_t bound)
451454
{
452455
unsigned int i;
456+
uint32_t t = 0;
453457

454458
for (i = 0; i < MLDSA_K; ++i)
455459
__loop__(
456460
invariant(i <= MLDSA_K)
457-
invariant(forall(k1, 0, i, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, bound)))
461+
invariant(t == 0 || t == 0xFFFFFFFF)
462+
invariant((t == 0) == forall(k1, 0, i, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, bound)))
458463
)
459464
{
460-
if (mld_poly_chknorm(&v->vec[i], bound))
461-
{
462-
return 1;
463-
}
465+
/* Reference: Leaks which polynomial violates the bound via a conditional.
466+
* We are more conservative to reduce the number of declassifications in
467+
* constant-time testing.
468+
*/
469+
t |= mld_poly_chknorm(&v->vec[i], bound);
464470
}
465471

466-
return 0;
472+
return t;
467473
}
468474

469475
void mld_polyveck_power2round(mld_polyveck *v1, mld_polyveck *v0,

mldsa/polyvec.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,15 @@ __contract__(
195195
* - int32_t B: norm bound
196196
*
197197
* Returns 0 if norm of all polynomials is strictly smaller than B <=
198-
* (MLDSA_Q-1)/8 and 1 otherwise.
198+
* (MLDSA_Q-1)/8 and 0xFFFFFFFF otherwise.
199199
**************************************************/
200-
int mld_polyvecl_chknorm(const mld_polyvecl *v, int32_t B)
200+
uint32_t mld_polyvecl_chknorm(const mld_polyvecl *v, int32_t B)
201201
__contract__(
202202
requires(memory_no_alias(v, sizeof(mld_polyvecl)))
203203
requires(0 <= B && B <= (MLDSA_Q - 1) / 8)
204204
requires(forall(k0, 0, MLDSA_L,
205205
array_bound(v->vec[k0].coeffs, 0, MLDSA_N, -REDUCE32_RANGE_MAX, REDUCE32_RANGE_MAX)))
206-
ensures(return_value == 0 || return_value == 1)
206+
ensures(return_value == 0 || return_value == 0xFFFFFFFF)
207207
ensures((return_value == 0) == forall(k1, 0, MLDSA_L, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, B)))
208208
);
209209

@@ -384,16 +384,16 @@ __contract__(
384384
* - int32_t B: norm bound
385385
*
386386
* Returns 0 if norm of all polynomials are strictly smaller than B <=
387-
*(MLDSA_Q-1)/8 and 1 otherwise.
387+
*(MLDSA_Q-1)/8 and 0xFFFFFFFF otherwise.
388388
**************************************************/
389-
int mld_polyveck_chknorm(const mld_polyveck *v, int32_t B)
389+
uint32_t mld_polyveck_chknorm(const mld_polyveck *v, int32_t B)
390390
__contract__(
391391
requires(memory_no_alias(v, sizeof(mld_polyveck)))
392392
requires(0 <= B && B <= (MLDSA_Q - 1) / 8)
393393
requires(forall(k0, 0, MLDSA_K,
394394
array_bound(v->vec[k0].coeffs, 0, MLDSA_N,
395395
-REDUCE32_RANGE_MAX, REDUCE32_RANGE_MAX)))
396-
ensures(return_value == 0 || return_value == 1)
396+
ensures(return_value == 0 || return_value == 0xFFFFFFFF)
397397
ensures((return_value == 0) == forall(k1, 0, MLDSA_K, array_abs_bound(v->vec[k1].coeffs, 0, MLDSA_N, B)))
398398
);
399399

mldsa/sign.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ __contract__(
313313
mld_polyvecl y, z;
314314
mld_polyveck w2, w1, w0, h;
315315
mld_poly cp;
316-
int z_invalid, w0_invalid, h_invalid;
316+
uint32_t z_invalid, w0_invalid, h_invalid;
317317

318318
/* Sample intermediate vector y */
319319
mld_polyvecl_uniform_gamma1(&y, rhoprime, nonce);

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
}

proofs/cbmc/polyveck_chknorm/polyveck_chknorm_harness.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@ void harness(void)
77
{
88
int32_t b;
99
mld_polyveck *v;
10-
int r;
10+
uint32_t r;
1111
r = mld_polyveck_chknorm(v, b);
1212
}

0 commit comments

Comments
 (0)