golang/go · error

crypto/rsa: precomputed values are inconsistent with the key

Error message

crypto/rsa: precomputed values are inconsistent with the key

What it means

Returned by PrivateKey.Validate when Precomputed.fips is non-nil but precomputedIsConsistent() returns false — i.e. the cached FIPS key handle was built for different N/E/D/p/q/dP/dQ/qInv than the key currently carries. This means somebody mutated the struct fields after Precompute (or after NewPrivateKey), invalidating the cached CRT values. The library treats this as a hard error rather than silently using stale CRT data.

Source

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

// Validate performs basic sanity checks on the key.
// It returns nil if the key is valid, or else an error describing a problem.
//
// It runs faster on valid keys if run after [PrivateKey.Precompute].
func (priv *PrivateKey) Validate() error {
	// We can operate on keys based on d alone, but they can't be encoded with
	// [crypto/x509.MarshalPKCS1PrivateKey], which unfortunately doesn't return
	// an error, so we need to reject them here.
	if len(priv.Primes) < 2 {
		return errors.New("crypto/rsa: missing primes")
	}
	// If Precomputed.fips is set and consistent, then the key has been
	// validated by [rsa.NewPrivateKey] or [rsa.NewPrivateKeyWithoutCRT].
	if priv.precomputedIsConsistent() {
		return nil
	}
	if priv.Precomputed.fips != nil {
		return errors.New("crypto/rsa: precomputed values are inconsistent with the key")
	}
	_, err := priv.precompute()
	return err
}

func (priv *PrivateKey) precomputedIsConsistent() bool {
	if priv.Precomputed.fips == nil {
		return false
	}
	N, e, d, P, Q, dP, dQ, qInv := priv.Precomputed.fips.Export()
	if !bigIntEqualToBytes(priv.N, N) || priv.E != e || !bigIntEqualToBytes(priv.D, d) {
		return false
	}
	if len(priv.Primes) != 2 {
		return P == nil && Q == nil && dP == nil && dQ == nil && qInv == nil
	}
	return bigIntEqualToBytes(priv.Primes[0], P) &&
		bigIntEqualToBytes(priv.Primes[1], Q) &&

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Treat *rsa.PrivateKey as immutable after construction — build a fresh struct for a new key.
  2. If you must mutate, zero out priv.Precomputed (set Precomputed to PrecomputedValues{}) and call Precompute() again before Validate().
  3. Re-construct the key via rsa.NewPrivateKey / x509.ParsePKCS1PrivateKey to get a clean Precomputed handle.

Example fix

// before: mutate in place
priv.N = newN
err := priv.Validate() // err: precomputed values are inconsistent

// after: reset and recompute, or rebuild
priv.Precomputed = rsa.PrecomputedValues{}
priv.Precompute()
err := priv.Validate()
Defensive patterns

Strategy: validation

Validate before calling

// Reset cached precomputation before mutating a key, then recompute.
func safeMutate(priv *rsa.PrivateKey) {
    priv.Precomputed = rsa.PrecomputedValues{}
    // ... apply field changes ...
    priv.Precompute()
}

Prevention

When it happens

Trigger: Mutate priv.N, priv.E, priv.D, priv.Primes[i], or priv.Precomputed.Dp/Dq/Qinv after Precompute()/NewPrivateKey(); unmarshal a key on top of an already-precomputed one without resetting Precomputed.

Common situations: Reusing a *rsa.PrivateKey variable across keys to avoid allocation; a deserializer that fills fields in two passes (first N/E, then primes) after the struct was already Precomputed; test fixtures that mutate keys in place.

Related errors


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