golang/go · error

ed25519: bad public key

Error message

ed25519: bad public key

What it means

Returned by NewPublicKey after SetBytes fails — the 32 bytes decoded to a point that is not on the Ed25519 curve (or is one of the excluded low-order points). Length is correct but the bytes do not represent a valid group element.

Source

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

	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
	// uint8-length prefixed context.
	domPrefixCtx = "SigEd25519 no Ed25519 collisions\x00"
)

View on GitHub (pinned to b6b368adc5)

Solutions

  1. Confirm the bytes were produced by an Ed25519 key generation, not X25519 or another curve.
  2. Re-fetch the public key from the authoritative source to rule out corruption.
  3. Surface the error to the user as 'untrusted public key is malformed' — do not fall back to a default key.
Defensive patterns

Strategy: try-catch

Try / catch

pub, err := ed25519.NewPublicKey(b)
if err != nil {
    if strings.Contains(err.Error(), "bad public key") && !strings.Contains(err.Error(), "length") {
        // length OK but point off-curve
        return nil, ErrKeyNotOnCurve
    }
    return nil, err
}

Prevention

When it happens

Trigger: Calling NewPublicKey with 32 bytes that are random, corrupted, or a valid 32-byte blob from a different curve (e.g. X25519 public key, or an Ed448-truncated key).

Common situations: Mixing up X25519 (ECDH) and Ed25519 (signing) public keys — both 32 bytes; corrupted key material from storage/network; feeding an identity element that the encoding explicitly rejects; typos in pasted keys.

Related errors


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