Skip to main content

DhGroup

Struct DhGroup 

Source
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

Source

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.

Source

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.

Source

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).

Source

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).

Source

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).

Source

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.

Source

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.

Source

pub fn name(&self) -> &'static str

The group’s human-readable name ("group14", "custom", …).

Source

pub fn bit_size(&self) -> usize

The bit length of p — 2048 for group14, 3072 for group15, …

Source

pub fn byte_size(&self) -> usize

The byte length of p, used for wire-format public-key padding and shared-secret length.

Source

pub fn p(&self) -> &BoxedUint

The prime modulus p.

Source

pub fn g(&self) -> &BoxedUint

The generator g.

Source

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.

Source

pub fn priv_bits(&self) -> usize

The recommended private-exponent bit length for this group.

Source

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.

Source

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:

  • p is 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) via from_custom_unchecked;
  • g ∈ [2, p - 2] (the only excluded values are 0, 1, and p - 1, which are tiny-order elements);
  • priv_bits ≥ MIN_CUSTOM_PRIV_BITS (224) — a short private exponent is attackable in 2^(priv_bits/2) work regardless of how strong the prime is;
  • p is a safe prime: q = (p − 1) / 2 passes trial division and custom_group_mr_rounds rounds of Miller-Rabin, then p is proved prime by a Lucas witness over the now-known factorization p − 1 = 2q. The runtime subgroup-confinement check in shared_secret (y^q ∈ {1, p − 1}) only prevents small-subgroup attacks when p is a safe prime — for a non-safe p with 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 with p itself, which makes the verdict deterministic per group but also public — see CUSTOM_GROUP_MR_ROUNDS for what that does and does not rule out;
  • g has order q or 2q, 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’s moduli file ships generators that are primitive roots (g = 2 with p ≡ 3 (mod 8), g = 5), whose public values fall outside the order-q subgroup 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.

Source

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.

Source

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) and q.bit_len() ≥ MIN_PARAMS_Q_BITS (160), before any arithmetic;
  • p and q odd, g ∈ [2, p − 2] (as from_custom_unchecked);
  • q | p − 1;
  • g^q ≡ 1 (mod p) — with g ≠ 1 and q prime, g has order exactly q;
  • q passes trial division and CUSTOM_GROUP_MR_ROUNDS Miller-Rabin rounds, and p custom_group_mr_rounds rounds — both with bases HMAC-DRBG-derived from the candidate, i.e. deterministic and public, with the caveat spelled out on CUSTOM_GROUP_MR_ROUNDS. Unlike the safe-prime case p cannot be proved prime from q (q is 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.

Trait Implementations§

Source§

impl Clone for DhGroup

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.