@@ -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+
0 commit comments