@@ -2549,6 +2549,59 @@ static int s_ecc_test_recovery(void)
25492549}
25502550#endif
25512551
2552+ #ifdef LTC_ECC_SECP112R2
2553+ /* https://github.com/libtom/libtomcrypt/issues/764 - small-subgroup attack regression test
2554+ SECP112R2 has cofactor 4, so the curve contains a subgroup of order 4 outside the prime-order generator subgroup
2555+ A point in that small subgroup is on the curve so Test 2 in ltc_ecc_verify_key passes. However n * P != O, so Test 3 must reject it
2556+ The point below has order 4: n * P gives -P, not the point at infinity. Therefore, importing it as a public key must fail
2557+ */
2558+ static int s_ecc_issue764 (void )
2559+ {
2560+ const ltc_ecc_curve * cu ;
2561+ ecc_key key , priv ;
2562+ int err ;
2563+ unsigned char shared [14 ];
2564+ unsigned long sharedlen ;
2565+ const unsigned char pub [] = {
2566+ 0x04 , /* uncompressed */
2567+ 0xB1 ,0xFD ,0x8D ,0xE1 ,0x27 ,0xD4 ,0x65 ,0x6B ,0x57 ,0x3E ,0xB5 ,0x13 ,0x98 ,0x4C , /* x = B1FD8DE127D4656B573EB513984C */
2568+ 0x2F ,0x8C ,0xD8 ,0x80 ,0x3D ,0xB9 ,0x62 ,0x0F ,0xA3 ,0xA6 ,0x0E ,0x5B ,0x31 ,0xE2 /* y = 2F8CD8803DB9620FA3A60E5B31E2 */
2569+ };
2570+ DO (ecc_find_curve ("SECP112R2" , & cu ));
2571+
2572+ /* ecc_set_key must reject the malicious public key (verify_key Test 3) */
2573+ DO (ecc_set_curve (cu , & key ));
2574+ err = ecc_set_key (pub , sizeof (pub ), PK_PUBLIC , & key ); /* must fail */
2575+ if (err == CRYPT_OK ) {
2576+ ecc_free (& key );
2577+ return CRYPT_FAIL_TESTVECTOR ;
2578+ }
2579+
2580+ /* ecc_shared_secret must reject the malicious peer key even when the caller bypassed ecc_set_key by directly loading the pubkey
2581+ priv.k = 4 (a multiple of the malicious point's order 4) so that k * pubkey is deterministically the point at infinity
2582+ */
2583+ DO (ecc_set_curve (cu , & priv ));
2584+ DO (ltc_mp_set (priv .k , 4 ));
2585+ priv .type = PK_PRIVATE ;
2586+
2587+ /* load the malicious point directly to simulate an attacker-supplied pubkey reaching ecc_shared_secret without import checks */
2588+ DO (ecc_set_curve (cu , & key ));
2589+ DO (ltc_mp_read_unsigned_bin (key .pubkey .x , (unsigned char * )pub + 1 , 14 ));
2590+ DO (ltc_mp_read_unsigned_bin (key .pubkey .y , (unsigned char * )pub + 15 , 14 ));
2591+ DO (ltc_mp_set (key .pubkey .z , 1 ));
2592+ key .type = PK_PUBLIC ;
2593+
2594+ sharedlen = sizeof (shared );
2595+ err = ecc_shared_secret (& priv , & key , shared , & sharedlen ); /* must fail */
2596+ ecc_free (& priv );
2597+ ecc_free (& key );
2598+ if (err == CRYPT_OK ) {
2599+ return CRYPT_FAIL_TESTVECTOR ;
2600+ }
2601+ return CRYPT_OK ;
2602+ }
2603+ #endif
2604+
25522605int ecc_test (void )
25532606{
25542607 if (ltc_mp .name == NULL ) return CRYPT_NOP ;
@@ -2574,6 +2627,9 @@ int ecc_test(void)
25742627 DO (s_ecc_issue443_447 ());
25752628 DO (s_ecc_issue630 ());
25762629 DO (s_ecc_issue116 ());
2630+ #ifdef LTC_ECC_SECP112R2
2631+ DO (s_ecc_issue764 ());
2632+ #endif
25772633#ifdef LTC_ECC_SHAMIR
25782634 DO (s_ecc_test_shamir ());
25792635 DO (s_ecc_test_recovery ());
0 commit comments