golang/go · critical

crypto/rsa: d too small

Error message

crypto/rsa: d too small

What it means

Thrown when the private exponent d is too small (BitLenVarTime <= N.BitLen()/2). Small-d RSA is vulnerable to Wiener's attack and related lattice attacks, which recover d (and thus the private key) efficiently when d < N^0.5. The check rejects such keys; the leaked magnitude of d is non-adaptive and acceptable.

Source

Thrown at src/crypto/internal/fips140/rsa/rsa.go:320

	} else {
		// p > q
		// diff = 0 - q mod p = p - q
		diff.ExpandFor(p).Sub(qP, p)
	}
	// A tiny bit of leakage is acceptable because it's not adaptive, an
	// attacker only learns the magnitude of p - q.
	if diff.BitLenVarTime() <= N.BitLen()/2-100 {
		return errors.New("crypto/rsa: |p - q| too small")
	}

	// Check that d > 2^(nlen/2).
	//
	// See section 3 of https://crypto.stanford.edu/~dabo/papers/RSA-survey.pdf
	// for more details about attacks on small d values.
	//
	// Likewise, the leakage of the magnitude of d is not adaptive.
	if priv.d.BitLenVarTime() <= N.BitLen()/2 {
		return errors.New("crypto/rsa: d too small")
	}

	return nil
}

func checkPublicKey(pub *PublicKey) (fipsApproved bool, err error) {
	fipsApproved = true
	if pub.N == nil {
		return false, errors.New("crypto/rsa: missing public modulus")
	}
	if pub.N.Nat().IsOdd() == 0 {
		return false, errors.New("crypto/rsa: public modulus is even")
	}
	// FIPS 186-5, Section 5.1: "This standard specifies the use of a modulus
	// whose bit length is an even integer and greater than or equal to 2048
	// bits."
	if pub.N.BitLen() < 2048 {
		fipsApproved = false

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Regenerate the key with rsa.GenerateKey, which yields a full-size d.
  2. Never choose d directly; let the generator pick d = e^{-1} mod λ(N) from random primes.
  3. Reject imported keys whose d bit length is <= half the modulus bit length.

Example fix

// before
// d hand-picked small for speed

// after
key, err := rsa.GenerateKey(rand.Reader, 2048) // d derived, full-size
Defensive patterns

Strategy: validation

Validate before calling

if d.BitLen() <= n.BitLen()/2 {
    return errors.New("d too small; vulnerable to Wiener/lattice attacks")
}

Type guard

func dLargeEnough(d, n *big.Int) bool { return d.BitLen() > n.BitLen()/2 }

Try / catch

err := validateKey(priv)
if err != nil && strings.Contains(err.Error(), "d too small") {
    return err // regenerate; never pick small d deliberately
}

Prevention

When it happens

Trigger: Key validation checks priv.d.BitLenVarTime() <= N.BitLen()/2 and rejects. Reached at the end of the private-key consistency check for generated or imported keys.

Common situations: A deliberately small d chosen for fast signing (a known insecure optimization). Imported keys from a legacy/broken library. Test fixtures with toy-sized d.

Related errors


AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12). Data as JSON: /api/errors/6f091a4969108318. Report an issue: GitHub.