golang/go · error
ECDSA verification failure
Error message
ECDSA verification failure
What it means
Thrown by tls verifyHandshakeSignature when ecdsa.VerifyASN1 returns false for an ECDSA signature over the handshake transcript. The public key type matched (it is *ecdsa.PublicKey), but the signature is mathematically invalid for the given signed data. This is a TLS-level signature check on the peer's signed handshake message.
Source
Thrown at src/crypto/tls/auth.go:39
// verifyHandshakeSignature verifies a signature against unhashed handshake contents.
func verifyHandshakeSignature(sigType uint8, pubkey crypto.PublicKey, hashFunc crypto.Hash, signed, sig []byte) error {
if hashFunc != directSigning {
if !hashFunc.Available() {
return fmt.Errorf("hash function unavailable: %v", hashFunc)
}
h := hashFunc.New()
h.Write(signed)
signed = h.Sum(nil)
}
switch sigType {
case signatureECDSA:
pubKey, ok := pubkey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ECDSA public key, got %T", pubkey)
}
if !ecdsa.VerifyASN1(pubKey, signed, sig) {
return errors.New("ECDSA verification failure")
}
case signatureEd25519:
pubKey, ok := pubkey.(ed25519.PublicKey)
if !ok {
return fmt.Errorf("expected an Ed25519 public key, got %T", pubkey)
}
if !ed25519.Verify(pubKey, signed, sig) {
return errors.New("Ed25519 verification failure")
}
case signatureMLDSA:
pubKey, ok := pubkey.(*mldsa.PublicKey)
if !ok {
return fmt.Errorf("expected an ML-DSA public key, got %T", pubkey)
}
if err := mldsa.Verify(pubKey, signed, sig, nil); err != nil {
return fmt.Errorf("ML-DSA verification failure: %w", err)
}
case signaturePKCS1v15:View on GitHub (pinned to b6b368adc5)
Solutions
- Verify the server certificate is valid and unmodified (full chain, correct curve).
- Capture the handshake with a TLS tracer to confirm the signed bytes and signature match what the peer sent.
- Ensure client and server agree on signature algorithm (curve + hash) per the signature_algorithms extension.
- If writing the signer, confirm ecdsa.SignASN1 and VerifyASN1 use the same digest and curve.
Example fix
// before (buggy signer hashes twice)
sig, _ := ecdsa.SignASN1(rand, priv, alreadyHashed)
// peer verifies over the raw transcript -> mismatch
// after
sig, err := ecdsa.SignASN1(rand, priv, transcript)
if err != nil { return err }
// verifier hashes per the negotiated hash; pass the right input Defensive patterns
Strategy: try-catch
Type guard
func isECDSAPubKey(k any) bool {
_, ok := k.(*ecdsa.PublicKey)
return ok
} Try / catch
// During TLS verification, surface ECDSA failures distinctly.
if _, err := tls.Dial("tcp", addr, cfg); err != nil {
var verErr *tls.RecordHeaderError
if strings.Contains(err.Error(), "ECDSA verification failure") {
log.Printf("ECDSA handshake failed (cert/sig/curve mismatch): %v", err)
}
return err
} Prevention
- Ensure signer and verifier use the same curve and hash.
- Verify the full certificate chain before trusting an ECDSA signature.
- Log signature failures with the negotiated curve/hash to diagnose mismatches.
- Test signing/verifying round-trips with crypto/ecdsa directly before TLS integration.
When it happens
Trigger: During a TLS handshake, the peer signs handshake data with ECDSA and the local side's ecdsa.VerifyASN1(pub, signed, sig) returns false. Reached via signatureECDSA in verifyHandshakeSignature, used by TLS 1.2 and 1.3.
Common situations: Mismatched curve between cert and signature; corrupted signature over the wire; wrong transcript bytes due to a MITM or buggy peer; key reuse across contexts; ASN.1 encoding produced by a non-conformant signer.
Related errors
- ecdsa: invalid signature: r is zero
- ecdsa: invalid signature: s is zero
- ecdsa: signature did not verify
- Ed25519 verification failure
- client doesn't support certificate curve
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/e903bcd3b35272b6.
Report an issue: GitHub.