# [openssl-users] RFC 7919 DH parameters and OpenSSL DH_check()

Viktor Dukhovni openssl-users at dukhovni.org
Thu Jan 3 21:54:19 UTC 2019

```On Jan 3, 2019, at 3:18 PM, Andy Schmidt <andrewrobertschmidt at gmail.com> wrote:

> I am adding the RFC 7919 Diffie-Hellman parameters to our TLS
> servers, and I've found that these parameters won't pass OpenSSL's
> Diffie Hellman parameter check function DH_check(). The return code
> is DH_NOT_SUITABLE_GENERATOR. Looking at the source code, it appears
> to fail because the remainder of the prime divided by 24 is not 11.
> That its, p mod 24 != 11. I have a couple of questions:
>
> What relationship between the prime p and the generator g is this checking
> for? I thought that since p was a safe prime, as long as the generator g
> wasn't 1 the only choice is between the full group and the subgroup of the
> squares?

For a safe prime \$p = 2q + 1\$ with \$q\$ also prime, the order of \$2\$ is \$q\$
considering that \$q\$ is odd and not a multiple of 3 for any primes of interest,
gives \$p \cong 23 mod 24\$, which then gives \$q \cong 11 mod 12\$.

The order of \$2\$ is \$2q\$ when \$2\$ is not a quadratic residue mod \$p\$, which
then gives \$p \cong 3 mod 8\$ and so taking mod 3 into account
\$p \cong 11 mod 24\$.

So it seems that the check in question wants \$2\$ to generates the
full multiplicative group of order \$2q\$, rather than the prime-order
subgroup of quadratic residues of order \$q\$.

> I would like to use DH_check() to attempt to ensure that Diffie Hellman
> parameters haven't been tampered on operating systems that don't have
> digital signatures for executable binaries.
>
> The OpenSSL version in use is 1.0.2q.

The primes in RFC7919 are all 23 mod 24, which has \$o(2) = q\$ rather
than \$o(2) = 2q\$.  This is actually fine, perhaps even better, and
the code in DH_check() is too strict in wanting the generator to
generate the full multiplicative group mod \$p\$, rather than just
the subgroup of of quadratic residues of order \$q\$.

So a pull request for OpenSSL would be welcome.  Given that we
should not care whether the order of \$g\$ is \$q\$ or \$2q\$, it suffices
to just check that \$p\$ is plausibly prime and \$q = (p-1)/2 == (p
>> 1)\$ is plausibly prime.  The special checks for \$g == 2, 3 and
5\$ should be removed.  For 1.1.1 the patch would be something like:

--- a/crypto/dh/dh_check.c
+++ b/crypto/dh/dh_check.c
@@ -88,8 +88,6 @@ int DH_check_ex(const DH *dh)
DHerr(DH_F_DH_CHECK_EX, DH_R_CHECK_INVALID_Q_VALUE);
if ((errflags & DH_CHECK_INVALID_J_VALUE) != 0)
DHerr(DH_F_DH_CHECK_EX, DH_R_CHECK_INVALID_J_VALUE);
-    if ((errflags & DH_UNABLE_TO_CHECK_GENERATOR) != 0)
-        DHerr(DH_F_DH_CHECK_EX, DH_R_UNABLE_TO_CHECK_GENERATOR);
if ((errflags & DH_CHECK_P_NOT_PRIME) != 0)
DHerr(DH_F_DH_CHECK_EX, DH_R_CHECK_P_NOT_PRIME);
if ((errflags & DH_CHECK_P_NOT_SAFE_PRIME) != 0)
@@ -140,20 +138,7 @@ int DH_check(const DH *dh, int *ret)
if (dh->j && BN_cmp(dh->j, t1))
*ret |= DH_CHECK_INVALID_J_VALUE;

-    } else if (BN_is_word(dh->g, DH_GENERATOR_2)) {
-        l = BN_mod_word(dh->p, 24);
-        if (l == (BN_ULONG)-1)
-            goto err;
-        if (l != 11)
-            *ret |= DH_NOT_SUITABLE_GENERATOR;
-    } else if (BN_is_word(dh->g, DH_GENERATOR_5)) {
-        l = BN_mod_word(dh->p, 10);
-        if (l == (BN_ULONG)-1)
-            goto err;
-        if ((l != 3) && (l != 7))
-            *ret |= DH_NOT_SUITABLE_GENERATOR;
-    } else
-        *ret |= DH_UNABLE_TO_CHECK_GENERATOR;
+    }

r = BN_is_prime_ex(dh->p, BN_prime_checks, ctx, NULL);
if (r < 0)

--
Viktor.

```