Skip to content

Commit f72d82f

Browse files
committed
Constant-time: Make signature declassifications explicit in verification
Previously, the signature would be declassified in the end of signing (which is the most common threat model). However, for rare use cases the signature may be secret in which case it is useful to know which part of the code has to be adapted. This commit removes the declassification from the end of signing, explicitly marks the signature as secret in the constant time tests (to also classify the previously declassified challenge c), and then adds necessary declassifications in verifiation. We may consider eliminating some of those declassifications later. In particular, the data dependencies in use_hint are not hard to eliminate (in fact, the native implementations are already constant time). The final comparison of the challenges can also easily be turned into a constant-time comparison. Signed-off-by: Matthias J. Kannwischer <matthias@kannwischer.eu>
1 parent 40ff385 commit f72d82f

File tree

2 files changed

+67
-8
lines changed

2 files changed

+67
-8
lines changed

mldsa/src/sign.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -616,10 +616,6 @@ __contract__(
616616
}
617617

618618
/* All is well - write signature */
619-
/* Constant time: At this point it is clear that the signature is valid - it
620-
* can, hence, be considered public. */
621-
MLD_CT_TESTING_DECLASSIFY(h, sizeof(*h));
622-
MLD_CT_TESTING_DECLASSIFY(z, sizeof(*z));
623619
mld_pack_sig(sig, challenge_bytes, z, h, n);
624620

625621
ret = 0; /* success */
@@ -874,6 +870,7 @@ int crypto_sign_verify_internal(const uint8_t *sig, size_t siglen,
874870
const uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
875871
int externalmu)
876872
{
873+
uint32_t z_invalid;
877874
unsigned int i;
878875
int ret;
879876
MLD_ALLOC(buf, uint8_t, (MLDSA_K * MLDSA_POLYW1_PACKEDBYTES));
@@ -908,12 +905,24 @@ int crypto_sign_verify_internal(const uint8_t *sig, size_t siglen,
908905
/* mld_unpack_sig and mld_polyvecl_chknorm signal failure through a
909906
* single non-zero error code that's not yet aligned with MLD_ERR_XXX.
910907
* Map it to MLD_ERR_FAIL explicitly. */
908+
909+
/* Constant-time: The signature is commonly considered public. However, we
910+
* mark it as secret to make explicit which parts of the code take time
911+
* depending on the signature.
912+
*
913+
* mld_unpack_sig uses conditional branches to unpack the hint vector h.
914+
* The h-component of the signature, hence, needs to be declassified.
915+
*/
911916
if (mld_unpack_sig(c, z, h, sig))
912917
{
913918
ret = MLD_ERR_FAIL;
914919
goto cleanup;
915920
}
916-
if (mld_polyvecl_chknorm(z, MLDSA_GAMMA1 - MLDSA_BETA))
921+
922+
z_invalid = mld_polyvecl_chknorm(z, MLDSA_GAMMA1 - MLDSA_BETA);
923+
/* Constant-time: We only leak if z is invalid or not. */
924+
MLD_CT_TESTING_DECLASSIFY(&z_invalid, sizeof(uint32_t));
925+
if (z_invalid)
917926
{
918927
ret = MLD_ERR_FAIL;
919928
goto cleanup;
@@ -937,6 +946,8 @@ int crypto_sign_verify_internal(const uint8_t *sig, size_t siglen,
937946
}
938947

939948
/* Matrix-vector multiplication; compute Az - c2^dt1 */
949+
/* Constant-time: poly_challenge uses conditional branches depending on c. */
950+
MLD_CT_TESTING_DECLASSIFY(c, MLDSA_CTILDEBYTES);
940951
mld_poly_challenge(cp, c);
941952
mld_polyvec_matrix_expand(mat, rho);
942953

@@ -955,6 +966,9 @@ int crypto_sign_verify_internal(const uint8_t *sig, size_t siglen,
955966

956967
/* Reconstruct w1 */
957968
mld_polyveck_caddq(w1);
969+
970+
/* Constant-time: use_hint uses conditional branches depending on w1. */
971+
MLD_CT_TESTING_DECLASSIFY(w1, sizeof(mld_polyveck));
958972
mld_polyveck_use_hint(tmp, w1, h);
959973
mld_polyveck_pack_w1(buf, tmp);
960974
/* Call random oracle and verify challenge */

test/test_mldsa.c

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,11 @@
2929
} while (0)
3030

3131

32+
#define MLD_CT_TESTING_DECLASSIFY_H(s) \
33+
MLD_CT_TESTING_DECLASSIFY( \
34+
(s) + MLDSA_CTILDEBYTES + MLDSA_L * MLDSA_POLYZ_PACKEDBYTES, \
35+
MLDSA_POLYVECH_PACKEDBYTES);
36+
3237
static int test_sign_core(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
3338
uint8_t sk[MLDSA_CRYPTO_SECRETKEYBYTES],
3439
uint8_t sm[MLEN + MLDSA_CRYPTO_BYTES],
@@ -49,10 +54,17 @@ static int test_sign_core(uint8_t pk[MLDSA_CRYPTO_PUBLICKEYBYTES],
4954

5055
CHECK(crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk) == 0);
5156

57+
/* Mark signature as secret to force explcit declassification in verification
58+
*/
59+
MLD_CT_TESTING_SECRET(sm, MLEN + MLDSA_CRYPTO_BYTES);
60+
/* Constant-time: The unpacking of the h-component of the signature requires
61+
* conditional branches.*/
62+
MLD_CT_TESTING_DECLASSIFY_H(sm);
63+
5264
rc = crypto_sign_open(m2, &mlen, sm, smlen, ctx, CTXLEN, pk);
5365

5466
/* Constant time: Declassify outputs to check them. */
55-
MLD_CT_TESTING_DECLASSIFY(rc, sizeof(int));
67+
MLD_CT_TESTING_DECLASSIFY(&rc, sizeof(int));
5668
MLD_CT_TESTING_DECLASSIFY(m, MLEN);
5769
MLD_CT_TESTING_DECLASSIFY(m2, (MLEN + MLDSA_CRYPTO_BYTES));
5870

@@ -120,6 +132,12 @@ static int test_sign_extmu(void)
120132
MLD_CT_TESTING_SECRET(mu, sizeof(mu));
121133

122134
CHECK(crypto_sign_signature_extmu(sig, &siglen, mu, sk) == 0);
135+
/* Mark signature as secret to force explcit declassification in verification
136+
*/
137+
MLD_CT_TESTING_SECRET(sig, sizeof(sig));
138+
/* Constant-time: The unpacking of the h-component of the signature requires
139+
* conditional branches.*/
140+
MLD_CT_TESTING_DECLASSIFY_H(sig);
123141
CHECK(crypto_sign_verify_extmu(sig, siglen, mu, pk) == 0);
124142

125143
return 0;
@@ -147,6 +165,12 @@ static int test_sign_pre_hash(void)
147165

148166
CHECK(crypto_sign_signature_pre_hash_shake256(sig, &siglen, m, MLEN, ctx,
149167
CTXLEN, rnd, sk) == 0);
168+
/* Mark signature as secret to force explcit declassification in verification
169+
*/
170+
MLD_CT_TESTING_SECRET(sig, sizeof(sig));
171+
/* Constant-time: The unpacking of the h-component of the signature requires
172+
* conditional branches.*/
173+
MLD_CT_TESTING_DECLASSIFY_H(sig);
150174
CHECK(crypto_sign_verify_pre_hash_shake256(sig, siglen, m, MLEN, ctx, CTXLEN,
151175
pk) == 0);
152176

@@ -234,6 +258,13 @@ static int test_wrong_pk(void)
234258

235259
CHECK(crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk) == 0);
236260

261+
/* Mark signature as secret to force explcit declassification in verification
262+
*/
263+
MLD_CT_TESTING_SECRET(sm, sizeof(sm));
264+
/* Constant-time: The unpacking of the h-component of the signature requires
265+
* conditional branches.*/
266+
MLD_CT_TESTING_DECLASSIFY_H(sm);
267+
237268
/* flip bit in public key */
238269
randombytes((uint8_t *)&idx, sizeof(size_t));
239270
idx %= MLDSA_CRYPTO_PUBLICKEYBYTES;
@@ -285,6 +316,13 @@ static int test_wrong_sig(void)
285316

286317
CHECK(crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk) == 0);
287318

319+
/* Mark signature as secret to force explcit declassification in verification
320+
*/
321+
MLD_CT_TESTING_SECRET(sm, sizeof(sm));
322+
/* Constant-time: The unpacking of the h-component of the signature requires
323+
* conditional branches.*/
324+
MLD_CT_TESTING_DECLASSIFY_H(sm);
325+
288326
/* flip bit in signed message */
289327
randombytes((uint8_t *)&idx, sizeof(size_t));
290328
idx %= MLEN + MLDSA_CRYPTO_BYTES;
@@ -294,7 +332,7 @@ static int test_wrong_sig(void)
294332
rc = crypto_sign_open(m2, &mlen, sm, smlen, ctx, CTXLEN, pk);
295333

296334
/* Constant time: Declassify outputs to check them. */
297-
MLD_CT_TESTING_DECLASSIFY(rc, sizeof(int));
335+
MLD_CT_TESTING_DECLASSIFY(&rc, sizeof(int));
298336
MLD_CT_TESTING_DECLASSIFY(m2, sizeof(m2));
299337

300338
if (!rc)
@@ -337,6 +375,13 @@ static int test_wrong_ctx(void)
337375

338376
CHECK(crypto_sign(sm, &smlen, m, MLEN, ctx, CTXLEN, sk) == 0);
339377

378+
/* Mark signature as secret to force explcit declassification in verification
379+
*/
380+
MLD_CT_TESTING_SECRET(sm, sizeof(sm));
381+
/* Constant-time: The unpacking of the h-component of the signature requires
382+
* conditional branches.*/
383+
MLD_CT_TESTING_DECLASSIFY_H(sm);
384+
340385
/* flip bit in ctx */
341386
randombytes((uint8_t *)&idx, sizeof(size_t));
342387
idx %= CTXLEN;
@@ -346,7 +391,7 @@ static int test_wrong_ctx(void)
346391
rc = crypto_sign_open(m2, &mlen, sm, smlen, ctx, CTXLEN, pk);
347392

348393
/* Constant time: Declassify outputs to check them. */
349-
MLD_CT_TESTING_DECLASSIFY(rc, sizeof(int));
394+
MLD_CT_TESTING_DECLASSIFY(&rc, sizeof(int));
350395
MLD_CT_TESTING_DECLASSIFY(m2, sizeof(m2));
351396

352397
if (!rc)

0 commit comments

Comments
 (0)