tests: add ECDSA verify case where r + n overflows p
What changed, and why it matters
This commit adds a new test case to the secp256k1 cryptographic library's test suite. It checks that ECDSA signature verification correctly rejects a crafted edge case where a mathematical value (r + n) overflows the prime field boundary. The change only adds tests; it does not modify the verification logic itself, so it is not a fix for a known vulnerability but rather a regression test for a subtle boundary condition.
No immediate action required. Treat as normal test-coverage improvement. If auditing the ECDSA verifier, confirm the existing implementation already handles this boundary correctly, which this passing test implies.
Security signals we found
ECDSA signature verification edge-case test
r + n overflow modulo p boundary condition
Regression test only; no functional code change
Evidence from the diff
The commit adds one test block in src/tests.c within run_ecdsa_edge_cases(). It constructs a scalar r = p - n + 1 (where p is the field prime and n is the curve order), sets s = 1 and message = 0, derives a public key such that the resulting R has x-coordinate 1, and asserts that secp256k1_ecdsa_sig_verify returns 0 (failure). This exercises the case where r + n exceeds p and could theoretically be reduced modulo p to a valid-looking x-coordinate, ensuring the verifier rejects it. No library code is changed.
Changed components
src/tests.cECDSA verification test coverageInspect captured patch +28 / −0
### src/tests.c
@@ -7729,6 +7729,34 @@ static void run_ecdsa_edge_cases(void) {
CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0);
}
+ /* Verify signature where r + n overflows p fails. */
+ {
+ /* Scalar r as chars: r = p - n + 1, so that (r + n) mod p = 1 */
+ const unsigned char csr[32] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
+ 0x45, 0x51, 0x23, 0x19, 0x50, 0xb7, 0x5f, 0xc4,
+ 0x40, 0x2d, 0xa1, 0x72, 0x2f, 0xc9, 0xba, 0xef
+ };
+ /* With s = 1 and msg = 0, verification computes R = r * pubkey.
+ * pubkey = r^-1 * (1, y), so x(R) = 1. */
+ const unsigned char pubkey[33] = {
+ 0x02, 0x57, 0xad, 0x61, 0xc8, 0x68, 0x3f, 0xcf,
+ 0x06, 0x99, 0x19, 0x11, 0x8c, 0x0f, 0x99, 0xb9,
+ 0x38, 0x9f, 0x65, 0x05, 0x9b, 0xa0, 0x71, 0xba,
+ 0xbe, 0xa6, 0x32, 0x05, 0x34, 0x14, 0x45, 0xda,
+ 0xe8
+ };
+ secp256k1_ge key;
+ secp256k1_scalar msg;
+ secp256k1_scalar sr, ss;
+ secp256k1_scalar_set_int(&ss, 1);
+ secp256k1_scalar_set_int(&msg, 0);
+ secp256k1_scalar_set_b32(&sr, csr, NULL);
+ CHECK(secp256k1_ge_parse33(&key, pubkey));
+ CHECK(secp256k1_ecdsa_sig_verify(&sr, &ss, &key, &msg) == 0);
+ }
+
/* Signature where s would be zero. */
{
secp256k1_pubkey pubkey;Why this scored 20/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.