Skip to content

Commit 52fb603

Browse files
committed
fix: curve25519 clamp check missing rule 3 (bit6 of byte31)
1 parent c098e53 commit 52fb603

3 files changed

Lines changed: 64 additions & 3 deletions

File tree

tests/api/test_curve25519.c

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,58 @@ int test_wc_curve25519_import_private(void)
547547
return EXPECT_RESULT();
548548
} /* END test_wc_curve25519_import */
549549

550+
/*
551+
* Test curve25519_priv_clamp_check via wc_curve25519_make_pub.
552+
*
553+
* RFC 7748 section 5 requires three clamping invariants on a Curve25519
554+
* private scalar before use:
555+
* Rule 1: bits 0-2 of byte 0 must be clear (scalar &= 0xF8)
556+
* Rule 2: bit 7 of byte 31 must be clear (scalar &= 0x7F)
557+
* Rule 3: bit 6 of byte 31 must be SET (scalar |= 0x40)
558+
*
559+
* Test vectors are derived from RFC 7748 s5; they are the independent oracle.
560+
* Before the fix, rule 3 was not checked, so a scalar with byte[31]==0x00
561+
* (bit 6 clear) was silently accepted -- regression covered below.
562+
*/
563+
int test_wc_curve25519_priv_clamp_check(void)
564+
{
565+
EXPECT_DECLS;
566+
#ifdef HAVE_CURVE25519
567+
/* Valid clamped scalar: all bytes 0x00 except byte[31] = 0x40
568+
* (bit 7 clear, bit 6 set, byte[0] bits 0-2 clear). */
569+
static const byte kValidPriv[CURVE25519_KEYSIZE] = {
570+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
571+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
572+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
573+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40
574+
};
575+
byte pub[CURVE25519_KEYSIZE];
576+
byte priv[CURVE25519_KEYSIZE];
577+
578+
/* Valid key succeeds. */
579+
ExpectIntEQ(wc_curve25519_make_pub(CURVE25519_KEYSIZE, pub,
580+
CURVE25519_KEYSIZE, kValidPriv), 0);
581+
582+
/* Rule 1 violation: bit 0 of byte[0] set (byte[0] = 0x01). */
583+
XMEMCPY(priv, kValidPriv, sizeof(priv));
584+
priv[0] |= 0x01;
585+
ExpectIntEQ(wc_curve25519_make_pub(CURVE25519_KEYSIZE, pub,
586+
CURVE25519_KEYSIZE, priv), WC_NO_ERR_TRACE(ECC_BAD_ARG_E));
587+
588+
/* Rule 2 violation: bit 7 of byte[31] set (byte[31] = 0xC0, keeping
589+
* bit 6 set so only rule 2 is violated). */
590+
XMEMCPY(priv, kValidPriv, sizeof(priv));
591+
priv[CURVE25519_KEYSIZE - 1] = 0xC0;
592+
ExpectIntEQ(wc_curve25519_make_pub(CURVE25519_KEYSIZE, pub,
593+
CURVE25519_KEYSIZE, priv), WC_NO_ERR_TRACE(ECC_BAD_ARG_E));
594+
595+
/* Rule 3 violation: bit 6 of byte[31] clear (byte[31] = 0x00).
596+
* Regression: this was silently accepted before the fix. */
597+
XMEMCPY(priv, kValidPriv, sizeof(priv));
598+
priv[CURVE25519_KEYSIZE - 1] = 0x00;
599+
ExpectIntEQ(wc_curve25519_make_pub(CURVE25519_KEYSIZE, pub,
600+
CURVE25519_KEYSIZE, priv), WC_NO_ERR_TRACE(ECC_BAD_ARG_E));
601+
#endif
602+
return EXPECT_RESULT();
603+
} /* END test_wc_curve25519_priv_clamp_check */
604+

tests/api/test_curve25519.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ int test_wc_curve25519_export_public_ex(void);
3535
int test_wc_curve25519_export_private_raw_ex(void);
3636
int test_wc_curve25519_import_private_raw_ex(void);
3737
int test_wc_curve25519_import_private(void);
38+
int test_wc_curve25519_priv_clamp_check(void);
3839

3940
#define TEST_CURVE25519_DECLS \
4041
TEST_DECL_GROUP("curve25519", test_wc_curve25519_init), \
@@ -47,6 +48,7 @@ int test_wc_curve25519_import_private(void);
4748
TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_public_ex), \
4849
TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_private_raw_ex), \
4950
TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private_raw_ex), \
50-
TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private)
51+
TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private), \
52+
TEST_DECL_GROUP("curve25519", test_wc_curve25519_priv_clamp_check)
5153

5254
#endif /* WOLFCRYPT_TEST_CURVE25519_H */

wolfcrypt/src/curve25519.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,14 @@ static WC_INLINE int curve25519_priv_clamp(byte* priv)
108108
}
109109
static WC_INLINE int curve25519_priv_clamp_check(const byte* priv)
110110
{
111-
/* check that private part of key has been clamped */
111+
/* check that private part of key has been clamped per RFC 7748 section 5:
112+
* bits 0-2 of byte 0 must be clear (priv[0] &= 248)
113+
* bit 7 of byte 31 must be clear (priv[31] &= 127)
114+
* bit 6 of byte 31 must be set (priv[31] |= 64) */
112115
int ret = 0;
113116
if ((priv[0] & ~248) ||
114-
(priv[CURVE25519_KEYSIZE-1] & 128)) {
117+
(priv[CURVE25519_KEYSIZE-1] & 128) ||
118+
!(priv[CURVE25519_KEYSIZE-1] & 64)) {
115119
ret = ECC_BAD_ARG_E;
116120
}
117121
return ret;

0 commit comments

Comments
 (0)