golang/go · error
ecdsa: signature did not verify
Error message
ecdsa: signature did not verify
What it means
Thrown by the ECDSA verifier when the recomputed x-coordinate of the point R = [u1]G + [u2]Q does not equal the signature's r (v.Equal(r) != 1). This is the final verdict that the signature is not valid for the given key and hash under FIPS 186-5 §6.4.2. Any earlier structural failure (zero r/s, bad point, curve mismatch) returns a different error; this one means the math checked out but the signature is wrong.
Source
Thrown at src/crypto/internal/fips140/ecdsa/ecdsa.go:514
}
// p₂ = [r * s⁻¹]Q
p2, err := Q.ScalarMult(Q, w.Mul(r, c.N).Bytes(c.N))
if err != nil {
return err
}
// BytesX returns an error for the point at infinity.
Rx, err := p1.Add(p1, p2).BytesX()
if err != nil {
return err
}
v, err := bigmod.NewNat().SetOverflowingBytes(Rx, c.N)
if err != nil {
return err
}
if v.Equal(r) != 1 {
return errors.New("ecdsa: signature did not verify")
}
return nil
}
View on GitHub (pinned to b6b368adc5)
Solutions
- Confirm signer and verifier use the same hash function and curve.
- Confirm the public key is the counterpart of the signing private key.
- Confirm the message bytes hashed at both ends are identical (no re-encoding).
- Treat this as an authentication failure: do not retry blindly; surface it to the caller.
Example fix
// before
if err := ecdsa.Verify(curve, pub, hash, sig); err != nil {
// generic failure
}
// after: distinguish causes before calling Verify
if pub.Curve() != signerCurve { return ErrCurveMismatch }
if !bytes.Equal(hash, expectedDigest) { return ErrHashMismatch }
if err := ecdsa.Verify(curve, pub, hash, sig); err != nil {
return ErrInvalidSignature // genuine verification failure
} Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-check the deterministic causes of verification mismatch.
if pub.Curve() != signerCurve { return ErrCurveMismatch }
if len(hash) == 0 { return ErrEmptyHash }
if allZero(sig.R) || allZero(sig.S) { return ErrMalformedSignature }
// Remaining failures are genuine: wrong key, message, or signature. Type guard
func verifyInputsPlausible(pub *ecdsa.PublicKey, hash []byte, sig *ecdsa.Signature) bool {
return len(hash) > 0 && !allZero(sig.R) && !allZero(sig.S)
} Try / catch
if err := ecdsa.Verify(c, pub, hash, sig); err != nil {
// Distinguish structural errors from the final verdict if needed,
// but for 'signature did not verify' the correct action is to reject.
return ErrAuthenticationFailed
} Prevention
- Pin the hash function and curve at both signer and verifier.
- Bind the public key to the identity that produced the signature.
- Hash the canonical (identically-encoded) message on both sides.
- Never retry verification with mutated inputs to 'make it pass'.
When it happens
Trigger: Verifying a signature produced with a different private key, over a different message/hash, with a tampered r or s, for the wrong curve, or after any byte-level corruption of key/hash/signature.
Common situations: Wrong public key paired with the signature, mismatched hash function between signer and verifier, message alteration, or a deliberately forged signature.
Related errors
- ecdsa: invalid signature: r is zero
- ecdsa: invalid signature: s is zero
- ecdsa: public key does not match curve
- ecdsa: private key scalar is zero or negative
- negative coordinate
AI-assisted analysis of golang/go@b6b368adc5 (2026-08-12).
Data as JSON: /api/errors/55e6c18c4d513bb0.
Report an issue: GitHub.