@@ -772,6 +772,58 @@ int test_wc_ecc_import_x963(void)
772772 return EXPECT_RESULT ();
773773} /* END wc_ecc_import_x963 */
774774
775+ /*
776+ * testing wc_ecc_import_x963() rejects an off-curve public point.
777+ *
778+ * Regression coverage for the invalid-curve attack: the legacy wrapper
779+ * wc_ecc_import_x963_ex (called by wc_ecc_import_x963()) must pass untrusted=1
780+ * to wc_ecc_import_x963_ex2 so that ECIES, PKCS#7 KARI, and EVP ECDH callers
781+ * validate that the imported point actually lies on the curve. Without that,
782+ * an attacker can feed a point from a weak twist and leak the victim's private
783+ * scalar modulo small primes (Biehl-Meyer-Mueller).
784+ */
785+ int test_wc_ecc_import_x963_off_curve (void )
786+ {
787+ EXPECT_DECLS ;
788+ #if defined(HAVE_ECC ) && defined(HAVE_ECC_KEY_IMPORT ) && \
789+ !defined(NO_ECC256 ) && !defined(NO_ECC_SECP ) && \
790+ (!defined(HAVE_FIPS ) || FIPS_VERSION_GE (7 ,0 )) && !defined(HAVE_SELFTEST )
791+ ecc_key pubKey ;
792+ /* Uncompressed X9.63 P-256 point: 0x04 || Gx || Gy with the last byte
793+ * of Gy flipped by 1. Gx/Gy are the NIST P-256 generator coordinates;
794+ * modifying a single bit of Gy produces a point that is not on the
795+ * curve, so wc_ecc_import_x963 must reject it. */
796+ static const byte offCurveX963 [] = {
797+ 0x04 ,
798+ 0x6B , 0x17 , 0xD1 , 0xF2 , 0xE1 , 0x2C , 0x42 , 0x47 ,
799+ 0xF8 , 0xBC , 0xE6 , 0xE5 , 0x63 , 0xA4 , 0x40 , 0xF2 ,
800+ 0x77 , 0x03 , 0x7D , 0x81 , 0x2D , 0xEB , 0x33 , 0xA0 ,
801+ 0xF4 , 0xA1 , 0x39 , 0x45 , 0xD8 , 0x98 , 0xC2 , 0x96 ,
802+ 0x4F , 0xE3 , 0x42 , 0xE2 , 0xFE , 0x1A , 0x7F , 0x9B ,
803+ 0x8E , 0xE7 , 0xEB , 0x4A , 0x7C , 0x0F , 0x9E , 0x16 ,
804+ 0x2B , 0xCE , 0x33 , 0x57 , 0x6B , 0x31 , 0x5E , 0xCE ,
805+ 0xCB , 0xB6 , 0x40 , 0x68 , 0x37 , 0xBF , 0x51 , 0xF4
806+ };
807+
808+ XMEMSET (& pubKey , 0 , sizeof (ecc_key ));
809+
810+ ExpectIntEQ (wc_ecc_init (& pubKey ), 0 );
811+
812+ /* Importing an off-curve point must fail. wc_ecc_import_x963() calls
813+ * wc_ecc_import_x963_ex() which ultimately calls wc_ecc_import_x963_ex2()
814+ * with the required untrusted=1 flag. */
815+ ExpectIntNE (wc_ecc_import_x963 (offCurveX963 , (word32 )sizeof (offCurveX963 ),
816+ & pubKey ), 0 );
817+
818+ wc_ecc_free (& pubKey );
819+
820+ #ifdef FP_ECC
821+ wc_ecc_fp_free ();
822+ #endif
823+ #endif
824+ return EXPECT_RESULT ();
825+ } /* END test_wc_ecc_import_x963_off_curve */
826+
775827/*
776828 * testing wc_ecc_import_private_key()
777829 */
0 commit comments