pub struct DhGroup { /* private fields */ }Expand description
A MODP DH group: a prime p, a generator g, and a recommended
private-exponent bit length — either over a safe prime (q = (p - 1) / 2
also prime, g of order q or 2q) or, for an explicit-subgroup group,
with g generating a prime-order-q subgroup for a stated q | p − 1.
Constructed via the group14..group18 free functions for the
RFC 3526 groups, via DhGroup::from_custom for an RFC 4419
group-exchange–supplied (p, g) pair, or via DhGroup::from_params for
an RFC 5114 / X9.42 (p, g, q) triple.
Implementations§
Source§impl DhGroup
impl DhGroup
Sourcepub const MIN_CUSTOM_GROUP_BITS: usize = 2048
pub const MIN_CUSTOM_GROUP_BITS: usize = 2048
Minimum bit length accepted by from_custom.
RFC 4419 §3 states implementations SHOULD NOT accept group-exchange
primes shorter than 1024 bits; modern guidance (RFC 8270 §2, NIST
SP 800-56A Rev 3 §5.5.1.1) raises that floor to 2048. Anything below
2048 has fewer than ~112 bits of security against the General Number
Field Sieve and falls under the precomputation attacks demonstrated
by the LogJam paper. Callers with a documented legacy interop need
can sidestep this floor by using from_custom_unchecked.
Sourcepub const MIN_CUSTOM_PRIV_BITS: usize = 224
pub const MIN_CUSTOM_PRIV_BITS: usize = 224
Minimum private-exponent bit length accepted by
from_custom.
The exponent must be at least twice the target security level (RFC
7919 §A, NIST SP 800-56A Rev 3 §5.6.1.1): the best generic attack on
the exponent is Pollard rho / BSGS at 2^(priv_bits/2) work, so a
224-bit exponent buys the 112 bits of security a 2048-bit prime is
worth and anything shorter makes the exponent — not the prime — the
weakest part of the exchange. from_custom_unchecked keeps the old
permissive floor (any non-zero value) for pinned legacy interop.
Sourcepub const MAX_CUSTOM_GROUP_BITS: usize = 16384
pub const MAX_CUSTOM_GROUP_BITS: usize = 16384
Maximum bit length accepted by from_custom and
from_custom_unchecked.
The prime is peer-chosen in RFC 4419 group exchange, and every
operation on it — the Montgomery precomputation, each Miller-Rabin
exponentiation, every g^x mod p — is quadratic to cubic in its
width. Without a cap a malicious server can hand over a
megabit “prime” and pin the client’s CPU for minutes before the
primality test even finishes (a 65536-bit modulus already costs
~90 s to validate). 16384 bits is twice the largest RFC 3526 group
(group18, 8192 bits) and matches the RSA modulus cap; 8192 is the
largest size any deployment should actually accept, and RFC 4419
clients bound the size they ask for anyway (max in the request).
Sourcepub const CUSTOM_GROUP_MR_ROUNDS: usize = 64
pub const CUSTOM_GROUP_MR_ROUNDS: usize = 64
Miller-Rabin rounds from_custom spends on
q = (p − 1) / 2 for primes of at most 3072 bits. For a candidate
that was not built against this specific test, a composite survives
one round with probability ≤ 1/4, so 64 rounds bound the false-accept
probability at 4⁻⁶⁴ = 2⁻¹²⁸ — the worst-case guidance for untrusted
candidates (FIPS 186-5 §B.3, RFC 4419 §3 server-supplied groups).
The bases are HMAC-DRBG-derived from p itself (no ambient RNG is
plumbed through this API, and the verdict must be deterministic per
group). That derivation is public, so this is not an adversarial
bound: an attacker who grinds candidates can, in principle, search for
a composite tailored to the bases its own p derives (the
Arnault-style pseudoprime construction) — expensive, and steeply more
so per round, but not provably out of reach. What keeps the verdict
sound is the structure around the test: p itself is proved prime
by a Lucas / Pocklington witness over the factorization p − 1 = 2q,
so only q’s primality rests on Miller-Rabin. Treat a peer-supplied
group as validated, not certified, and prefer the named RFC 3526
groups where the choice is yours.
p itself needs no Miller-Rabin rounds: with p − 1 = 2q fully
factored it is proved prime by a Lucas / Pocklington witness (about
two exponentiations; see bignum::prime::is_safe_prime_boxed).
Sourcepub const CUSTOM_GROUP_MR_ROUNDS_LARGE: usize = 32
pub const CUSTOM_GROUP_MR_ROUNDS_LARGE: usize = 32
Miller-Rabin rounds for primes wider than 3072 bits: 32, i.e. a
4⁻³² = 2⁻⁶⁴ false-accept bound per candidate. Each round on an
8192-bit prime is a full 8192-bit exponentiation (~0.17 s), so the
2⁻¹²⁸ target would cost ~11 s per group; 2⁻⁶⁴ is far below any
practical attack budget against a single group-exchange peer and
keeps group18-sized validation around 5 s. The 1/4-per-round bound
holds for these sizes for the same reason as above (bases derived
from p, not attacker-chosen).
Sourcepub const MIN_PARAMS_GROUP_BITS: usize = 1024
pub const MIN_PARAMS_GROUP_BITS: usize = 1024
Minimum bit length of p accepted by
from_params.
Lower than MIN_CUSTOM_GROUP_BITS
because the explicit-subgroup groups in actual use start at 1024 bits:
RFC 5114 §2.1’s 1024/160 group (IKE group 22, the dh_1024_160
OpenSSL ships) and the 1024-bit X9.42 parameters older PKIX / S/MIME
deployments carry. 1024 is also RFC 4419 §3’s floor. It is an interop
floor, not a security one — the LogJam caveats on
MIN_CUSTOM_GROUP_BITS apply in full.
Sourcepub const MIN_PARAMS_Q_BITS: usize = 160
pub const MIN_PARAMS_Q_BITS: usize = 160
Minimum bit length of q accepted by
from_params: 160, the smallest subgroup
RFC 5114 / FIPS 186-2 use. Private exponents live in [1, q − 1],
so Pollard rho in the subgroup costs 2^(N/2) and a narrower q
would be the weakest part of the exchange.
Sourcepub fn byte_size(&self) -> usize
pub fn byte_size(&self) -> usize
The byte length of p, used for wire-format public-key padding and
shared-secret length.
Sourcepub fn q(&self) -> Option<&BoxedUint>
pub fn q(&self) -> Option<&BoxedUint>
The subgroup order q of an explicit-subgroup group
(from_params); None for the safe-prime
groups, where it is (p − 1) / 2 by construction.
Sourcepub fn custom_group_mr_rounds(bits: usize) -> usize
pub fn custom_group_mr_rounds(bits: usize) -> usize
The Miller-Rabin round count from_custom uses
for a bits-bit prime: CUSTOM_GROUP_MR_ROUNDS up to 3072 bits,
CUSTOM_GROUP_MR_ROUNDS_LARGE above.
Sourcepub fn from_custom(
p: BoxedUint,
g: BoxedUint,
priv_bits: usize,
) -> Result<Self, Error>
pub fn from_custom( p: BoxedUint, g: BoxedUint, priv_bits: usize, ) -> Result<Self, Error>
Builds a custom group from a caller-supplied (p, g) pair. Used for
RFC 4419 SSH group-exchange where the server transmits the prime and
generator.
This constructor checks:
pis odd (Montgomery arithmetic requires an odd modulus, and any safe prime is odd anyway);MIN_CUSTOM_GROUP_BITS ≤ p.bit_len() ≤ MAX_CUSTOM_GROUP_BITS(2048..=16384) — below the floor the DLP is broken in practice, see RFC 4419 §3 and the LogJam precomputation results; above the cap a peer-chosen prime is a CPU-exhaustion vector. Pinned legacy interop can bypass the floor (not the cap) viafrom_custom_unchecked;g ∈ [2, p - 2](the only excluded values are 0, 1, andp - 1, which are tiny-order elements);priv_bits ≥ MIN_CUSTOM_PRIV_BITS(224) — a short private exponent is attackable in2^(priv_bits/2)work regardless of how strong the prime is;pis a safe prime:q = (p − 1) / 2passes trial division andcustom_group_mr_roundsrounds of Miller-Rabin, thenpis proved prime by a Lucas witness over the now-known factorizationp − 1 = 2q. The runtime subgroup-confinement check inshared_secret(y^q ∈ {1, p − 1}) only prevents small-subgroup attacks whenpis a safe prime — for a non-safepwith smooth(p − 1) / 2, a malicious group leaks the private exponent modulo the small factors. The Miller-Rabin bases are drawn from an HMAC-DRBG seeded withpitself, which makes the verdict deterministic per group but also public — seeCUSTOM_GROUP_MR_ROUNDSfor what that does and does not rule out;ghas orderqor2q, i.e.g^q ∈ {1, p − 1}— anything else is impossible for a genuine safe prime and would mean the group is not what it claims to be. Both orders are accepted: OpenSSH’smodulifile ships generators that are primitive roots (g = 2withp ≡ 3 (mod 8),g = 5), whose public values fall outside the order-qsubgroup for every odd private exponent.
The validation costs custom_group_mr_rounds(bits) + ~3 modular
exponentiations (about 0.2 s for a 2048-bit group, ~5 s for an
8192-bit one in an optimized build), so RFC 4419 group-exchange
callers should validate a server-supplied group once and cache the
result rather than re-validating on every handshake.
Sourcepub fn from_custom_unchecked(
p: BoxedUint,
g: BoxedUint,
priv_bits: usize,
) -> Result<Self, Error>
pub fn from_custom_unchecked( p: BoxedUint, g: BoxedUint, priv_bits: usize, ) -> Result<Self, Error>
Like from_custom but without the
MIN_CUSTOM_GROUP_BITS floor, the
safe-prime (Miller-Rabin) validation, or the generator-order check —
intended only for tests and pinned legacy interop where the caller
has documented why a sub-2048-bit or externally validated prime is
acceptable. The structural checks (odd modulus no wider than
MAX_CUSTOM_GROUP_BITS, generator in
range, sane private-exponent bit budget) still apply. Note that the
small-subgroup defense in shared_secret assumes a safe prime; the
caller owns that property here.
Sourcepub fn from_params(
p: BoxedUint,
g: BoxedUint,
q: BoxedUint,
) -> Result<Self, Error>
pub fn from_params( p: BoxedUint, g: BoxedUint, q: BoxedUint, ) -> Result<Self, Error>
Builds a group from an explicit (p, g, q) triple: g generates the
order-q subgroup of Z_p^* for a prime q | p − 1 — the RFC 5114
groups and the ANSI X9.42 DomainParameters (openssl dhparam -dsaparam, X9.42 DH PARAMETERS PEM files), which are not safe
primes and so cannot go through from_custom.
Checks:
MIN_PARAMS_GROUP_BITS ≤ p.bit_len() ≤ MAX_CUSTOM_GROUP_BITS(1024..=16384) andq.bit_len() ≥ MIN_PARAMS_Q_BITS(160), before any arithmetic;pandqodd,g ∈ [2, p − 2](asfrom_custom_unchecked);q | p − 1;g^q ≡ 1 (mod p)— withg ≠ 1andqprime,ghas order exactlyq;qpasses trial division andCUSTOM_GROUP_MR_ROUNDSMiller-Rabin rounds, andpcustom_group_mr_roundsrounds — both with bases HMAC-DRBG-derived from the candidate, i.e. deterministic and public, with the caveat spelled out onCUSTOM_GROUP_MR_ROUNDS. Unlike the safe-prime casepcannot be proved prime fromq(qis far below√p), so both verdicts rest on Miller-Rabin.
The resulting group draws private keys uniformly from [1, q − 1]
(priv_bits reports q’s width; NIST SP 800-56A
Rev 3 §5.6.1.1.4), and DhPrivateKey::shared_secret requires the
peer value to satisfy y^q ≡ 1 (mod p) on top of 2 ≤ y ≤ p − 2
(the SP 800-56A §5.6.2.3.1 full public-key validation for FFC
groups that are not safe primes). Passing a safe prime with
q = (p − 1) / 2 works too, but then the order-2q values that
from_custom admits are rejected.
Validation costs about custom_group_mr_rounds(L) full-width
exponentiations (dominated by the p test); validate a group once
and clone it.