Skip to content

Commit 744f89c

Browse files
committed
CT: Make polyvecl_chknorm and polyveck_chknorm constant flow
The reference implementation implements polyvecl_chknorm and polyveck_chknorm in variable time, i.e., it leaks which polynomial violated the rejection bound. 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 both funcitons to constant-time implementations in the hope tha the performance penalty is acceptable. A minor change is that the API 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. Signed-off-by: Matthias J. Kannwischer <[email protected]>
1 parent 4e2bdcb commit 744f89c

File tree

4 files changed

+28
-22
lines changed

4 files changed

+28
-22
lines changed

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/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)