@@ -3540,9 +3540,13 @@ static int test_DhGexGroup16KeyAgree(void)
35403540 goto out;
35413541 }
35423542
3543- /* Hand the client's public value to the server and run the agreement. */
3544- WMEMCPY(ssh->handshake->e, cPub, cPubSz);
3545- ssh->handshake->eSz = cPubSz;
3543+ /* Hand the client's public value to the server as the mpint
3544+ * SendKexDhInit() would send, and run the agreement. */
3545+ ssh->handshake->eSz = 0;
3546+ if (cPub[0] & 0x80)
3547+ ssh->handshake->e[ssh->handshake->eSz++] = 0;
3548+ WMEMCPY(ssh->handshake->e + ssh->handshake->eSz, cPub, cPubSz);
3549+ ssh->handshake->eSz += cPubSz;
35463550 ssh->kSz = MAX_KEX_KEY_SZ;
35473551
35483552 ret = wolfSSH_TestKeyAgreeDh_server(ssh, WC_HASH_TYPE_SHA256, f, &fSz);
@@ -20678,7 +20682,7 @@ static int test_KeyAgreeDh_server_rejectsOutOfRangePeer(void)
2067820682 hs->eSz = 1;
2067920683 fSz = (word32)sizeof(f);
2068020684 ret = wolfSSH_TestKeyAgreeDh_server(ssh, WC_HASH_TYPE_SHA256, f, &fSz);
20681- if (ret == WS_SUCCESS ) {
20685+ if (ret != WS_CRYPTO_FAILED ) {
2068220686 result = -743;
2068320687 break;
2068420688 }
@@ -20750,6 +20754,111 @@ static int test_KeyAgreeDh_client_rejectsOutOfRangePeer(void)
2075020754 return result;
2075120755}
2075220756#endif /* HAVE_FFDHE_2048 */
20757+
20758+ /* Verify both DH key agreement sides reject a peer public value encoded
20759+ * as a negative mpint (RFC 4251 section 5). 0x80 is 128 read unsigned,
20760+ * inside [2, p-2], so only the sign check can reject it; 0x00 0x80 is
20761+ * the same value encoded correctly and must be accepted. */
20762+ static int test_KeyAgreeDh_rejectsNegativePeer(void)
20763+ {
20764+ WOLFSSH_CTX* ctx = NULL;
20765+ WOLFSSH* ssh = NULL;
20766+ HandshakeInfo* hs = NULL;
20767+ byte f[MAX_KEX_KEY_SZ];
20768+ word32 fSz;
20769+ int ret;
20770+ int result = 0;
20771+ static const byte negPeer[1] = { 0x80 };
20772+ static const byte posPeer[2] = { 0x00, 0x80 };
20773+ byte kexId;
20774+
20775+ /* server: the peer value is the client's e */
20776+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
20777+ if (ctx == NULL)
20778+ return -760;
20779+ ssh = wolfSSH_new(ctx);
20780+ if (ssh == NULL || ssh->handshake == NULL) {
20781+ result = -761;
20782+ goto cleanup;
20783+ }
20784+ #ifndef WOLFSSH_NO_DH_GROUP14_SHA256
20785+ kexId = ID_DH_GROUP14_SHA256;
20786+ #elif !defined(WOLFSSH_NO_DH_GROUP16_SHA512)
20787+ kexId = ID_DH_GROUP16_SHA512;
20788+ #elif !defined(WOLFSSH_NO_DH_GROUP14_SHA1)
20789+ kexId = ID_DH_GROUP14_SHA1;
20790+ #elif !defined(WOLFSSH_NO_DH_GROUP1_SHA1)
20791+ kexId = ID_DH_GROUP1_SHA1;
20792+ #else
20793+ /* No fixed DH group enabled; fail rather than silently pass. */
20794+ result = -762;
20795+ goto cleanup;
20796+ #endif
20797+ hs = ssh->handshake;
20798+ hs->kexId = kexId;
20799+
20800+ WMEMCPY(hs->e, negPeer, sizeof(negPeer));
20801+ hs->eSz = (word32)sizeof(negPeer);
20802+ fSz = (word32)sizeof(f);
20803+ ret = wolfSSH_TestKeyAgreeDh_server(ssh, WC_HASH_TYPE_SHA256, f, &fSz);
20804+ if (ret != WS_CRYPTO_FAILED) {
20805+ result = -763;
20806+ goto cleanup;
20807+ }
20808+
20809+ WMEMCPY(hs->e, posPeer, sizeof(posPeer));
20810+ hs->eSz = (word32)sizeof(posPeer);
20811+ fSz = (word32)sizeof(f);
20812+ ret = wolfSSH_TestKeyAgreeDh_server(ssh, WC_HASH_TYPE_SHA256, f, &fSz);
20813+ if (ret != WS_SUCCESS) {
20814+ result = -764;
20815+ goto cleanup;
20816+ }
20817+
20818+ wolfSSH_free(ssh);
20819+ wolfSSH_CTX_free(ctx);
20820+
20821+ /* client: the peer value is the server's f */
20822+ ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_CLIENT, NULL);
20823+ if (ctx == NULL)
20824+ return -765;
20825+ ssh = wolfSSH_new(ctx);
20826+ if (ssh == NULL || ssh->handshake == NULL) {
20827+ result = -766;
20828+ goto cleanup;
20829+ }
20830+ ssh->handshake->kexId = kexId;
20831+
20832+ /* The hook frees the DH key on return, so set it up for each call. */
20833+ if (wolfSSH_TestSetDhKexKey(ssh) != 0) {
20834+ result = -767;
20835+ goto cleanup;
20836+ }
20837+ ret = wolfSSH_TestKeyAgreeDh_client(ssh, WC_HASH_TYPE_SHA256,
20838+ negPeer, (word32)sizeof(negPeer));
20839+ if (ret != WS_CRYPTO_FAILED) {
20840+ result = -768;
20841+ goto cleanup;
20842+ }
20843+
20844+ if (wolfSSH_TestSetDhKexKey(ssh) != 0) {
20845+ result = -769;
20846+ goto cleanup;
20847+ }
20848+ ret = wolfSSH_TestKeyAgreeDh_client(ssh, WC_HASH_TYPE_SHA256,
20849+ posPeer, (word32)sizeof(posPeer));
20850+ if (ret != WS_SUCCESS) {
20851+ result = -770;
20852+ goto cleanup;
20853+ }
20854+
20855+ cleanup:
20856+ if (ssh != NULL)
20857+ wolfSSH_free(ssh);
20858+ if (ctx != NULL)
20859+ wolfSSH_CTX_free(ctx);
20860+ return result;
20861+ }
2075320862#endif /* !WOLFSSH_NO_DH */
2075420863
2075520864#if !defined(WOLFSSH_NO_ECDH) && !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
@@ -26512,6 +26621,11 @@ int wolfSSH_UnitTest(int argc, char** argv)
2651226621 (unitResult == 0 ? "SUCCESS" : "FAILED"));
2651326622 testResult = testResult || unitResult;
2651426623#endif
26624+
26625+ unitResult = test_KeyAgreeDh_rejectsNegativePeer();
26626+ printf("KeyAgreeDh_rejectsNegativePeer: %s (result=%d)\n",
26627+ (unitResult == 0 ? "SUCCESS" : "FAILED"), unitResult);
26628+ testResult = testResult || unitResult;
2651526629#endif /* !WOLFSSH_NO_DH */
2651626630
2651726631#if !defined(WOLFSSH_NO_ECDH) && !defined(WOLFSSH_NO_ECDH_SHA2_NISTP256)
0 commit comments