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

  1. Confirm signer and verifier use the same hash function and curve.
  2. Confirm the public key is the counterpart of the signing private key.
  3. Confirm the message bytes hashed at both ends are identical (no re-encoding).
  4. 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

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


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