summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authortb <>2026-01-23 08:21:52 +0000
committertb <>2026-01-23 08:21:52 +0000
commite8699208c6c4338d425f0979a68ec6ea04009819 (patch)
treeef36c010a2e7aca4c89ff821d120c112cea99020 /src
parentabaacba9756060fd447f010a2698e3150d3f5378 (diff)
downloadopenbsd-e8699208c6c4338d425f0979a68ec6ea04009819.tar.gz
openbsd-e8699208c6c4338d425f0979a68ec6ea04009819.tar.bz2
openbsd-e8699208c6c4338d425f0979a68ec6ea04009819.zip
Scapy special for DH_check()
The latest release of Scapy calls DH_check() on all the well-known Diffie-Hellman parameters for RFCs 2409, 3526, and 7919. It does this via pyca/cryptography at startup. Every single time. This is obviously very expensive, due to our 64 MR rounds (which are complete overkill now that we have BPSW). Instead of pondering the ideal number of rounds for BPSW with FFDH, simply skip the check if the parameter matches a well-known prime. These are known to be safe primes, so we can skip those super-expensive and pointless checks without any risk. This is only done for the public dh->p parameter. It could be further optimized, but with the follow-up commit adding the RFC 7919 primes this reduces the startup time to what it was before Scapy 2.7.0: < 1s. Reverting from 64 MR rounds to BN_check_primes rounds, we would still have ~8s startup time without this optimization, which isn't great for an interactive tool. Clearly, it's not entirely our fault, it's also Scapy and cryptography that do something ... suboptimal, but I think we're better off if DH_check() isn't a complete DoS vector. If you're using non-standard parameters with FFDH, you deserve it. We could consider adding a flag for non-well-known p and thus making DH_check() indicate failure for candidate primes larger than, say, 4k. https://github.com/pyca/cryptography/issues/14048 ok beck kenjiro
Diffstat (limited to 'src')
-rw-r--r--src/lib/libcrypto/dh/dh_check.c62
1 files changed, 60 insertions, 2 deletions
diff --git a/src/lib/libcrypto/dh/dh_check.c b/src/lib/libcrypto/dh/dh_check.c
index 1ba85bc824..143699b4f6 100644
--- a/src/lib/libcrypto/dh/dh_check.c
+++ b/src/lib/libcrypto/dh/dh_check.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: dh_check.c,v 1.31 2025/05/10 05:54:38 tb Exp $ */ 1/* $OpenBSD: dh_check.c,v 1.32 2026/01/23 08:21:52 tb Exp $ */
2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) 2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3 * All rights reserved. 3 * All rights reserved.
4 * 4 *
@@ -104,6 +104,58 @@ DH_check_params(const DH *dh, int *flags)
104 return ok; 104 return ok;
105} 105}
106 106
107typedef BIGNUM *(*get_p_fn)(BIGNUM *);
108
109static const get_p_fn get_well_known_p[] = {
110 BN_get_rfc2409_prime_768,
111 BN_get_rfc2409_prime_1024,
112 BN_get_rfc3526_prime_1536,
113 BN_get_rfc3526_prime_2048,
114 BN_get_rfc3526_prime_3072,
115 BN_get_rfc3526_prime_4096,
116 BN_get_rfc3526_prime_6144,
117 BN_get_rfc3526_prime_8192,
118};
119
120#define N_WELL_KNOWN_P_FN (sizeof(get_well_known_p) / sizeof(get_well_known_p[0]))
121
122/*
123 * Scapy special: on startup it now calls DH_check() on all the well-known DH
124 * primes, which is a sensible thing to do. In any case, using BN_is_prime_ex()
125 * on a standardized domain parameter is dumb, so avoid it.
126 */
127static int
128dh_is_well_known_p(const BIGNUM *p, BN_CTX *ctx, int *is_well_known)
129{
130 BIGNUM *bn;
131 size_t i;
132 int ret = 0;
133
134 *is_well_known = 0;
135
136 BN_CTX_start(ctx);
137 if ((bn = BN_CTX_get(ctx)) == NULL)
138 goto err;
139
140 for (i = 0; i < N_WELL_KNOWN_P_FN; i++) {
141 get_p_fn get_p = get_well_known_p[i];
142
143 if (get_p(bn) == NULL)
144 goto err;
145 if (BN_cmp(bn, p) == 0) {
146 *is_well_known = 1;
147 break;
148 }
149 }
150
151 ret = 1;
152
153 err:
154 BN_CTX_end(ctx);
155
156 return ret;
157}
158
107/* 159/*
108 * Check that p is a safe prime and that g is a suitable generator. 160 * Check that p is a safe prime and that g is a suitable generator.
109 */ 161 */
@@ -112,7 +164,7 @@ int
112DH_check(const DH *dh, int *flags) 164DH_check(const DH *dh, int *flags)
113{ 165{
114 BN_CTX *ctx = NULL; 166 BN_CTX *ctx = NULL;
115 int is_prime; 167 int is_prime, is_well_known;
116 int ok = 0; 168 int ok = 0;
117 169
118 *flags = 0; 170 *flags = 0;
@@ -150,6 +202,11 @@ DH_check(const DH *dh, int *flags)
150 *flags |= DH_CHECK_INVALID_Q_VALUE; 202 *flags |= DH_CHECK_INVALID_Q_VALUE;
151 } 203 }
152 204
205 if (!dh_is_well_known_p(dh->p, ctx, &is_well_known))
206 goto err;
207 if (is_well_known)
208 goto done;
209
153 is_prime = BN_is_prime_ex(dh->p, DH_NUMBER_ITERATIONS_FOR_PRIME, 210 is_prime = BN_is_prime_ex(dh->p, DH_NUMBER_ITERATIONS_FOR_PRIME,
154 ctx, NULL); 211 ctx, NULL);
155 if (is_prime < 0) 212 if (is_prime < 0)
@@ -171,6 +228,7 @@ DH_check(const DH *dh, int *flags)
171 *flags |= DH_CHECK_P_NOT_SAFE_PRIME; 228 *flags |= DH_CHECK_P_NOT_SAFE_PRIME;
172 } 229 }
173 230
231 done:
174 ok = 1; 232 ok = 1;
175 233
176 err: 234 err: