golang/go · error

ecdsa: invalid private key length

Error message

ecdsa: invalid private key length

What it means

Thrown by fips140/ecdsa.NewPrivateKey when len(D) != c.N.Size(). D must be the fixed-length big-endian encoding of the private scalar with exactly the byte-width of the curve order (e.g. 32 for P-256, 48 for P-384, 66 for P-521). After this length check the scalar is parsed with SetBytes and additionally rejected if zero.

Source

Thrown at src/crypto/internal/fips140/ecdsa/ecdsa.go:170

	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa,
	0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f, 0x96, 0x6b,
	0x7f, 0xcc, 0x01, 0x48, 0xf7, 0x09, 0xa5, 0xd0,
	0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c, 0x47, 0xae,
	0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x09}

// NewPrivateKey creates a new ECDSA private key from the given D and Q byte
// slices. D must be the fixed-length big-endian encoding of the private scalar,
// and Q must be the compressed or uncompressed encoding of the public point.
func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
	fips140.RecordApproved()
	pub, err := NewPublicKey(c, Q)
	if err != nil {
		return nil, err
	}
	if len(D) != c.N.Size() {
		return nil, errors.New("ecdsa: invalid private key length")
	}
	d, err := bigmod.NewNat().SetBytes(D, c.N)
	if err != nil {
		return nil, err
	}
	if d.IsZero() == 1 {
		return nil, errors.New("ecdsa: private key is zero")
	}
	priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
	return priv, nil
}

// NewPublicKey creates a new ECDSA public key from the given Q byte slice.
// Q must be the compressed or uncompressed encoding of the public point.
func NewPublicKey[P Point[P]](c *Curve[P], Q []byte) (*PublicKey, error) {
	// SetBytes checks that Q is a valid point on the curve, and that its
	// coordinates are reduced modulo p, fulfilling the requirements of SP
	// 800-89, Section 5.3.2.

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Left-pad (or trim) D to exactly c.N.Size() bytes in big-endian before calling NewPrivateKey.
  2. If D came from math/big, use the fixed-size FillBytes(buf) with a buffer of length c.N.Size() rather than Bytes().
  3. Verify the curve constant matches the key's intended curve.

Example fix

// before
priv, err := ecdsa.NewPrivateKey(curve, bigInt.Bytes()) // variable length

// after: fixed-width big-endian
buf := make([]byte, curve.N.Size())
bigInt.FillBytes(buf)
priv, err := ecdsa.NewPrivateKey(curve, buf)
Defensive patterns

Strategy: validation

Validate before calling

// Require exactly c.N.Size() bytes for the scalar.
if len(D) != curveN.Size() {
    return fmt.Errorf("private scalar must be %d bytes", curveN.Size())
}
return ecdsa.NewPrivateKey(curve, D, Q)

Type guard

func validScalarLen(D []byte, order *bigmod.Modulus) bool {
    return len(D) == order.Size()
}

Prevention

When it happens

Trigger: Constructing an ECDSA private key whose D slice is shorter or longer than the curve order byte size, including a value with a stripped or extra leading byte.

Common situations: Importing a scalar encoded with a variable-length big.Int that dropped leading zeros or added a sign byte; cross-curve key reuse; truncated key material.

Related errors


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