golang/go · error

ed25519: bad public key length: {l}

Error message

ed25519: bad public key length: {l}

What it means

Returned by NewPublicKey when pubBytes is not exactly publicKeySize (32) bytes. Ed25519 public keys are the y-coordinate plus a sign bit packed into 32 bytes per RFC 8032.

Source

Thrown at src/crypto/internal/fips140/ed25519/ed25519.go:143

	}
	// Note that we are not decompressing the public key point here,
	// because it takes > 20% of the time of a signature generation.
	// Signing doesn't use it as a point anyway.
	copy(priv.pub[:], privBytes[32:])

	copy(priv.prefix[:], h[32:])

	return priv, nil
}

func NewPublicKey(pub []byte) (*PublicKey, error) {
	p := &PublicKey{}
	return newPublicKey(p, pub)
}

func newPublicKey(pub *PublicKey, pubBytes []byte) (*PublicKey, error) {
	if l := len(pubBytes); l != publicKeySize {
		return nil, errors.New("ed25519: bad public key length: " + strconv.Itoa(l))
	}
	// SetBytes checks that the point is on the curve.
	if _, err := pub.a.SetBytes(pubBytes); err != nil {
		return nil, errors.New("ed25519: bad public key")
	}
	copy(pub.aBytes[:], pubBytes)
	return pub, nil
}

// Domain separation prefixes used to disambiguate Ed25519/Ed25519ph/Ed25519ctx.
// See RFC 8032, Section 2 and Section 5.1.
const (
	// domPrefixPure is empty for pure Ed25519.
	domPrefixPure = ""
	// domPrefixPh is dom2(phflag=1) for Ed25519ph. It must be followed by the
	// uint8-length prefixed context.
	domPrefixPh = "SigEd25519 no Ed25519 collisions\x01"
	// domPrefixCtx is dom2(phflag=0) for Ed25519ctx. It must be followed by the

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Supply exactly 32 raw bytes representing the Ed25519 public key.
  2. Decode hex/base64 before calling and verify length==32.
  3. Ensure the bytes are from an Ed25519 keypair, not X25519 — they will pass length but may fail the next check (bad public key).

Example fix

// before
pub, err := ed25519.NewPublicKey([]byte(pubHex)) // hex string as bytes

// after
b, err := hex.DecodeString(pubHex)
if err != nil { return err }
if len(b) != 32 { return fmt.Errorf("ed25519 pubkey must be 32 bytes") }
pub, err := ed25519.NewPublicKey(b)
Defensive patterns

Strategy: validation

Validate before calling

const ed25519PubSize = 32
if len(b) != ed25519PubSize {
    return nil, fmt.Errorf("ed25519 public key must be %d bytes, got %d", ed25519PubSize, len(b))
}
return ed25519.NewPublicKey(b)

Try / catch

pub, err := ed25519.NewPublicKey(b)
if err != nil {
    if strings.Contains(err.Error(), "bad public key length") {
        return nil, ErrInvalidPublicKeyEncoding
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling fips140/ed25519.NewPublicKey(pub) with a slice whose length is not 32 (e.g. 64 bytes that are actually a private key, 33-byte compressed point from another curve, hex string).

Common situations: Passing a hex/base64 string instead of decoded bytes; passing a 64-byte private key where only the public key was expected; confusing an X25519 (Curve25519) public key with an Ed25519 one — both are 32 bytes but only the latter decodes.

Related errors


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