@@ -707,6 +707,66 @@ static void test_pubkey_auth_ecc(void)
707707
708708 run_pubkey_test (& sCtx , & cCtx , WS_SUCCESS );
709709}
710+
711+ /* Negative test: correct ECC public key presented (passes the authorized-key
712+ * hash check) but the client signs with a corrupted private key. The wrong
713+ * signature must reach and be rejected by DoUserAuthRequestEcc rather than
714+ * failing on the earlier key-type mismatch path.
715+ */
716+ static void test_pubkey_auth_ecc_bad_sig (void )
717+ {
718+ PubkeyServerCtx sCtx ;
719+ PubkeyClientCtx cCtx ;
720+ byte pubKeyBuf [256 ];
721+ byte * p = pubKeyBuf ;
722+ word32 pubKeySz = sizeof (pubKeyBuf );
723+ const byte * pubKeyType = NULL ;
724+ word32 pubKeyTypeSz = 0 ;
725+ /* Flip one byte inside the private scalar of the DER blob to produce a
726+ * structurally valid ECC key whose signatures won't verify against the
727+ * original public key.
728+ *
729+ * Per-curve scalar layout in the RFC 5915 ECPrivateKey DER encoding:
730+ * P-256: header "30 77 02 01 01 04 20" = 7 bytes; scalar at offset 7.
731+ * Byte 10 = scalar[3]. Original 0xd3; after ^0xFF: 0x2c.
732+ * P-384: header "30 81 a4 02 01 01 04 30" = 8 bytes; scalar at offset 8.
733+ * Byte 10 = scalar[2]. Original 0xae; after ^0xFF: 0x51.
734+ * P-521: header "30 81 dc 02 01 01 04 42" = 8 bytes; scalar at offset 8.
735+ * Byte 10 = scalar[2]. Original 0x40; after ^0xFF: 0xbf.
736+ *
737+ * In all three cases the leading byte(s) of the scalar are far below the
738+ * high-order byte of n, so the modified value stays within [1, n-1] and
739+ * wc_EccPrivateKeyDecode accepts it without error. */
740+ byte badPrivDer [sizeof (hanselPrivateEcc )];
741+ byte badPrivBuf [256 ];
742+ byte * badPrivPtr = badPrivBuf ;
743+ word32 badPrivSz = sizeof (badPrivBuf );
744+ const byte * badPrivType = NULL ; /* required by API; value not used */
745+ word32 badPrivTypeSz = 0 ;
746+
747+ printf ("Testing ECC pubkey auth rejection with tampered signature\n" );
748+
749+ AssertIntEQ (wolfSSH_ReadKey_buffer ((const byte * )hanselPublicEcc ,
750+ (word32 )WSTRLEN (hanselPublicEcc ), WOLFSSH_FORMAT_SSH ,
751+ & p , & pubKeySz , & pubKeyType , & pubKeyTypeSz , NULL ), WS_SUCCESS );
752+ AssertIntEQ (wc_Sha256Hash (pubKeyBuf , pubKeySz , sCtx .hash ), 0 );
753+
754+ WMEMCPY (badPrivDer , hanselPrivateEcc , hanselPrivateEccSz );
755+ badPrivDer [10 ] ^= 0xFF ;
756+ AssertIntEQ (wolfSSH_ReadKey_buffer (badPrivDer , hanselPrivateEccSz ,
757+ WOLFSSH_FORMAT_ASN1 ,
758+ & badPrivPtr , & badPrivSz , & badPrivType , & badPrivTypeSz , NULL ),
759+ WS_SUCCESS );
760+
761+ cCtx .publicKeyType = pubKeyType ;
762+ cCtx .publicKeyTypeSz = pubKeyTypeSz ;
763+ cCtx .publicKey = pubKeyBuf ;
764+ cCtx .publicKeySz = pubKeySz ;
765+ cCtx .privateKey = badPrivBuf ;
766+ cCtx .privateKeySz = badPrivSz ;
767+
768+ run_pubkey_test (& sCtx , & cCtx , WS_FATAL_ERROR );
769+ }
710770#endif /* WOLFSSH_NO_ECC */
711771
712772#if !defined(WOLFSSH_NO_RSA ) && !defined(WOLFSSH_NO_ECC )
@@ -1511,6 +1571,7 @@ int wolfSSH_AuthTest(int argc, char** argv)
15111571#endif
15121572#ifndef WOLFSSH_NO_ECC
15131573 test_pubkey_auth_ecc ();
1574+ test_pubkey_auth_ecc_bad_sig ();
15141575#endif
15151576#if !defined(WOLFSSH_NO_RSA ) && !defined(WOLFSSH_NO_ECC )
15161577 test_pubkey_auth_wrong_key ();
0 commit comments