@@ -1023,6 +1023,98 @@ TEST(DHTest, DHCheckForStandardParams) {
10231023 EXPECT_EQ (flags, 0 );
10241024}
10251025
1026+ TEST (DHTest, DHCheckNamedGroupFastPath) {
1027+ auto make_bare_group = [](BIGNUM *p) -> bssl::UniquePtr<DH > {
1028+ bssl::UniquePtr<BIGNUM > p_owner (p);
1029+ if (p_owner == nullptr ) {
1030+ return nullptr ;
1031+ }
1032+ bssl::UniquePtr<BIGNUM > g (BN_new ());
1033+ if (g == nullptr || !BN_set_word (g.get (), 2 )) {
1034+ return nullptr ;
1035+ }
1036+ // NewDHGroup does not take ownership; it dups the inputs.
1037+ return NewDHGroup (p_owner.get (), /* q=*/ nullptr , g.get ());
1038+ };
1039+
1040+ // All RFC 3526 MODP moduli, as a bare (p, g=2) group.
1041+ BIGNUM *(*const kRFC3526 [])(BIGNUM *) = {
1042+ BN_get_rfc3526_prime_1536, BN_get_rfc3526_prime_2048,
1043+ BN_get_rfc3526_prime_3072, BN_get_rfc3526_prime_4096,
1044+ BN_get_rfc3526_prime_6144, BN_get_rfc3526_prime_8192,
1045+ };
1046+ for (auto getter : kRFC3526 ) {
1047+ bssl::UniquePtr<DH > dh = make_bare_group (getter (nullptr ));
1048+ ASSERT_TRUE (dh);
1049+ int flags = -1 ;
1050+ ASSERT_TRUE (DH_check (dh.get (), &flags));
1051+ EXPECT_EQ (flags, 0 );
1052+ }
1053+
1054+ // All RFC 7919 ffdhe groups. Tested twice: once in their native form (which
1055+ // carries q = (p-1)/2 and g = 2), and once as a bare (p, g=2) group. Both are
1056+ // accepted by the fast path with flags == 0.
1057+ DH *(*const kRFC7919 [])(void ) = {
1058+ DH_get_rfc7919_2048, DH_get_rfc7919_3072, DH_get_rfc7919_4096,
1059+ DH_get_rfc7919_8192,
1060+ };
1061+ for (auto getter : kRFC7919 ) {
1062+ bssl::UniquePtr<DH > group (getter ());
1063+ ASSERT_TRUE (group);
1064+ int flags = -1 ;
1065+ ASSERT_TRUE (DH_check (group.get (), &flags));
1066+ EXPECT_EQ (flags, 0 );
1067+
1068+ bssl::UniquePtr<DH > bare = make_bare_group (BN_dup (DH_get0_p (group.get ())));
1069+ ASSERT_TRUE (bare);
1070+ flags = -1 ;
1071+ ASSERT_TRUE (DH_check (bare.get (), &flags));
1072+ EXPECT_EQ (flags, 0 );
1073+ }
1074+
1075+ // A modulus that is one bit off from a named group must NOT be accepted by
1076+ // the fast path. DH_check should fall through to full validation and flag it
1077+ // as composite. This guards against the fast path masking bad parameters.
1078+ {
1079+ bssl::UniquePtr<BIGNUM > p (BN_get_rfc3526_prime_2048 (nullptr ));
1080+ ASSERT_TRUE (p);
1081+ // Clear a bit that is set in the real prime so the value stays odd but is
1082+ // no longer the named prime (and is composite).
1083+ ASSERT_TRUE (BN_is_bit_set (p.get (), 5 ));
1084+ ASSERT_TRUE (BN_clear_bit (p.get (), 5 ));
1085+ bssl::UniquePtr<BIGNUM > g (BN_new ());
1086+ ASSERT_TRUE (g);
1087+ ASSERT_TRUE (BN_set_word (g.get (), 2 ));
1088+ bssl::UniquePtr<DH > dh = NewDHGroup (p.get (), /* q=*/ nullptr , g.get ());
1089+ ASSERT_TRUE (dh);
1090+ int flags = -1 ;
1091+ ASSERT_TRUE (DH_check (dh.get (), &flags));
1092+ EXPECT_TRUE (flags & DH_CHECK_P_NOT_PRIME );
1093+ }
1094+
1095+ // A recognized modulus with a q that is NOT the group's subgroup order must
1096+ // not be waved through by the fast path. Here we perturb ffdhe2048's q so it
1097+ // no longer equals (p-1)/2; the fast path must decline and full validation
1098+ // must reject it (q no longer divides p-1 and is composite).
1099+ {
1100+ bssl::UniquePtr<DH > group (DH_get_rfc7919_2048 ());
1101+ ASSERT_TRUE (group);
1102+ bssl::UniquePtr<BIGNUM > q (BN_dup (DH_get0_q (group.get ())));
1103+ ASSERT_TRUE (q);
1104+ ASSERT_TRUE (BN_add_word (q.get (), 2 )); // q := (p-1)/2 + 2, no longer valid
1105+ bssl::UniquePtr<BIGNUM > g (BN_new ());
1106+ ASSERT_TRUE (g);
1107+ ASSERT_TRUE (BN_set_word (g.get (), 2 ));
1108+ bssl::UniquePtr<DH > dh =
1109+ NewDHGroup (DH_get0_p (group.get ()), q.get (), g.get ());
1110+ ASSERT_TRUE (dh);
1111+ int flags = -1 ;
1112+ ASSERT_TRUE (DH_check (dh.get (), &flags));
1113+ // If the fast path had wrongly accepted this, flags would be 0.
1114+ EXPECT_TRUE (flags & DH_CHECK_INVALID_Q_VALUE );
1115+ }
1116+ }
1117+
10261118TEST (DHTest, DHMarshalPubKey) {
10271119 const char * dh512_pem =
10281120 " -----BEGIN DH PARAMETERS-----\n "
0 commit comments