golang/go · error

crypto/rsa: prime P is nil

Error message

crypto/rsa: prime P is nil

What it means

Thrown by precompute() when priv.Primes[0] (prime P) is nil, reached only when len(priv.Primes)==2. P is required to compute the CRT values (Dp, Qinv); a nil P makes CRT signing impossible. The guard prevents a nil-pointer dereference in subsequent big.Int operations.

Source

Thrown at src/crypto/rsa/rsa.go:594

}

// precompute calculates the PrecomputedValues for priv and returns them.
//
// It does NOT modify priv and is safe for concurrent use.
func (priv *PrivateKey) precompute() (PrecomputedValues, error) {
	var precomputed PrecomputedValues

	if priv.N == nil {
		return precomputed, errors.New("crypto/rsa: missing public modulus")
	}
	if priv.D == nil {
		return precomputed, errors.New("crypto/rsa: missing private exponent")
	}
	if len(priv.Primes) != 2 {
		return priv.precomputeLegacy()
	}
	if priv.Primes[0] == nil {
		return precomputed, errors.New("crypto/rsa: prime P is nil")
	}
	if priv.Primes[1] == nil {
		return precomputed, errors.New("crypto/rsa: prime Q is nil")
	}

	// If the CRT values are already set, use them.
	if priv.Precomputed.Dp != nil && priv.Precomputed.Dq != nil && priv.Precomputed.Qinv != nil {
		k, err := rsa.NewPrivateKeyWithPrecomputation(priv.N.Bytes(), priv.E, priv.D.Bytes(),
			priv.Primes[0].Bytes(), priv.Primes[1].Bytes(),
			priv.Precomputed.Dp.Bytes(), priv.Precomputed.Dq.Bytes(), priv.Precomputed.Qinv.Bytes())
		if err != nil {
			return precomputed, err
		}
		precomputed = priv.Precomputed
		precomputed.fips = k
		precomputed.CRTValues = make([]CRTValue, 0)
		return precomputed, nil
	}

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Use rsa.GenerateKey (or rsa.GenerateMultiPrimeKey) so Primes is always populated correctly.
  2. After parsing, call priv.Validate() to catch missing primes before any private operation.
  3. When constructing manually, set priv.Primes[0] and priv.Primes[1] to valid *big.Int primes.
  4. Re-derive a key from a complete PKCS#1/PKCS#8 encoding rather than assembling fields by hand.

Example fix

// before
priv := &rsa.PrivateKey{
    PublicKey: rsa.PublicKey{N: n, E: e},
    D: d,
    Primes: []*big.Int{nil, q}, // P missing
}
err := priv.Validate() // -> prime P is nil

// after
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { return err }
// or: priv.Primes = []*big.Int{p, q} with both non-nil
Defensive patterns

Strategy: validation

Validate before calling

func checkPrimesFilled(priv *rsa.PrivateKey) error {
    if len(priv.Primes) != 2 {
        return fmt.Errorf("rsa: expected 2 primes, got %d", len(priv.Primes))
    }
    if priv.Primes[0] == nil || priv.Primes[1] == nil {
        return errors.New("rsa: prime P or Q is nil")
    }
    return priv.Validate()
}

Prevention

When it happens

Trigger: Sign/Decrypt/Validate/Precompute on a 2-prime PrivateKey whose Primes slice has length 2 but Primes[0]==nil. Happens with hand-built keys, partially zero-value structs, or deserialization that allocated the slice but left entries nil.

Common situations: Constructing rsa.PrivateKey{Primes: make([]*big.Int, 2)} without filling it; JSON/encoding round-trips that omitted zero/big.Int fields; test fixtures that set N and D but forgot Primes.

Related errors


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