golang/go · error

ecdsa: Sign called with nil random and nil opts

Error message

ecdsa: Sign called with nil random and nil opts

What it means

Thrown by signRFC6979 when opts is nil. This path is reached from PrivateKey.Sign when the random parameter is nil (requesting deterministic RFC 6979 signing). Deterministic signing requires knowing which hash function produced the digest, which comes from opts.HashFunc(). With nil opts, the hash function is undefined, so signing cannot proceed.

Source

Thrown at src/crypto/ecdsa/ecdsa.go:441

		return nil, errors.New("crypto/ecdsa: only crypto/rand.Reader is allowed in FIPS 140-only mode")
	}
	k, err := privateKeyToFIPS(c, priv)
	if err != nil {
		return nil, err
	}
	// Always using SHA-512 instead of the hash that computed hash is
	// technically a violation of draft-irtf-cfrg-det-sigs-with-noise-04 but in
	// our API we don't get to know what it was, and this has no security impact.
	sig, err := ecdsa.Sign(c, sha512.New, k, rand, hash)
	if err != nil {
		return nil, err
	}
	return encodeSignature(sig.R, sig.S)
}

func signRFC6979(priv *PrivateKey, hash []byte, opts crypto.SignerOpts) ([]byte, error) {
	if opts == nil {
		return nil, errors.New("ecdsa: Sign called with nil random and nil opts")
	}
	h := opts.HashFunc()
	switch priv.Curve.Params() {
	case elliptic.P224().Params():
		return signFIPSDeterministic(ecdsa.P224(), h, priv, hash)
	case elliptic.P256().Params():
		return signFIPSDeterministic(ecdsa.P256(), h, priv, hash)
	case elliptic.P384().Params():
		return signFIPSDeterministic(ecdsa.P384(), h, priv, hash)
	case elliptic.P521().Params():
		return signFIPSDeterministic(ecdsa.P521(), h, priv, hash)
	default:
		return nil, errors.New("ecdsa: curve not supported by deterministic signatures")
	}
}

func signFIPSDeterministic[P ecdsa.Point[P]](c *ecdsa.Curve[P], hashFunc crypto.Hash, priv *PrivateKey, hash []byte) ([]byte, error) {
	k, err := privateKeyToFIPS(c, priv)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. When requesting deterministic signing (random=nil), always provide a non-nil crypto.SignerOpts: priv.Sign(nil, digest, crypto.SHA256).
  2. If you want randomized signing, pass a non-nil random reader (e.g., rand.Reader) — then nil opts is acceptable.
  3. Understand the API contract: nil random = deterministic (requires opts), non-nil random = randomized (opts optional).

Example fix

// before
sig, err := priv.Sign(nil, digest, nil) // both nil — error

// after
sig, err := priv.Sign(nil, digest, crypto.SHA256) // deterministic with SHA-256
Defensive patterns

Strategy: validation

Validate before calling

func validateSignParams(random io.Reader, opts crypto.SignerOpts) error {
    if random == nil && opts == nil {
        return errors.New("deterministic signing requires non-nil opts with a hash")
    }
    return nil
}

Type guard

func canSignDeterministically(opts crypto.SignerOpts) bool {
    return opts != nil && opts.HashFunc() != 0
}

Try / catch

sig, err := priv.Sign(random, digest, opts)
if err != nil && strings.Contains(err.Error(), "nil random and nil opts") {
    // provide opts to enable deterministic signing
    sig, err = priv.Sign(nil, digest, crypto.SHA256)
}

Prevention

When it happens

Trigger: Calling priv.Sign(nil, digest, nil) — nil random triggers the deterministic RFC 6979 path, and nil opts means no hash function is specified. The Sign method only validates opts when it's non-nil, so nil opts passes through to signRFC6979 which then rejects it.

Common situations: Code that passes nil for both random and opts expecting some default behavior; misunderstanding the API where nil random means deterministic but forgetting that opts is still required to specify the hash; refactoring that accidentally nullifies opts.

Related errors


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