Skip to content

Commit 713daee

Browse files
committed
Add some tests covering edge cases in the ECDSA r <=> x comparison
This upstreams some tests from BoringSSL, though I've taken another pass at the generation script to cover all the curves in Wycheproof. (NB: I've only run the test vectors against an implementation for BoringSSL's curves. It is possible I've gotten the obscure curves wrong.) ECDSA verification involves computing some point (x, y) and then checking if x mod n = r. x is only reduced mod p, so this leads to some edge cases right when x does and does not need a reduction. The existing test vectors covered some of these, but add some missing ones, so we catch both sides of the boundary condition. Additionally, there is an optimized variant which leads to another boundary condition. EC points are typically implemented with Jacobian coordinates, (X:Y:Z) => (x, y) = (X/Z², Y/Z³). This avoids expensive inversions in the point addition formulae. Only when extracting the final affine coordinates do you invert a field element, to divide by Z. Naively, ECDSA verification requires one such inversion. However, this inversion can be avoided in ECDSA verification by observing that comparing X/Z² to r is the same as comparing X to r*Z². However, this works mod p instead of mod n and we must deal with this mismatch. In most cases, n < p, where we must check two cases: r == x and r == x - n. 0 <= r < n < p and 0 <= x < p, so the first case is equivalent to checking x = r (mod p), where we can apply our optimization. The second is equivalent to x == r + n, which in turn is equivalent to checking that r + n < p and then x == r + n (mod p), where we can also apply our optimization. This leads to some other edge cases where we don't check the second case when we should, or when we check it when we shouldn't. The latter could happen if we reduce oversized r + n mod p, or forget a carry bit. Add some tests for these, getting as close to the boundary conditions as we can. Tested by simulating some bugs in BoringSSL's implementation of this optimization and ensuring Wycheproof noticed. (A similar optimization works for the less common p < n curves, though it's a bit simpler.)
1 parent fca0d3b commit 713daee

23 files changed

+5093
-23
lines changed

testvectors_v1/ecdsa_brainpoolP224r1_sha224_test.json

Lines changed: 241 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"algorithm": "ECDSA",
33
"schema": "ecdsa_verify_schema_v1.json",
4-
"numberOfTests": 443,
4+
"numberOfTests": 451,
55
"header": [
66
"Test vectors of type EcdsaVerify are meant for the verification",
77
"of ASN encoded ECDSA signatures."
@@ -6486,6 +6486,246 @@
64866486
"result": "invalid"
64876487
}
64886488
]
6489+
},
6490+
{
6491+
"type": "EcdsaVerify",
6492+
"source": {
6493+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6494+
"version": "0.1"
6495+
},
6496+
"publicKey": {
6497+
"type": "EcPublicKey",
6498+
"curve": "brainpoolP224r1",
6499+
"keySize": 224,
6500+
"uncompressed": "048dad1f3183a05e6027303de8da57d45177d2e2b290a9ac255137245605b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6",
6501+
"wx": "8dad1f3183a05e6027303de8da57d45177d2e2b290a9ac2551372456",
6502+
"wy": "05b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6"
6503+
},
6504+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a00048dad1f3183a05e6027303de8da57d45177d2e2b290a9ac255137245605b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6",
6505+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABI2tHzGDoF5gJzA96NpX1FF30uKy\nkKmsJVE3JFYFsHjPDMpBvyrMgkIAuIXPnJr7D0XjHcTEkZK2\n-----END PUBLIC KEY-----\n",
6506+
"sha": "SHA-224",
6507+
"tests": [
6508+
{
6509+
"tcId": 444,
6510+
"comment": "r = 1, x = 1 is valid",
6511+
"flags": [
6512+
"ValidSignature"
6513+
],
6514+
"msg": "68656c6c6f2c20776f726c64",
6515+
"sig": "3022020101021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6516+
"result": "valid"
6517+
}
6518+
]
6519+
},
6520+
{
6521+
"type": "EcdsaVerify",
6522+
"source": {
6523+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6524+
"version": "0.1"
6525+
},
6526+
"publicKey": {
6527+
"type": "EcPublicKey",
6528+
"curve": "brainpoolP224r1",
6529+
"keySize": 224,
6530+
"uncompressed": "040fb57733ac1ce9d808b78dd3a11e1da365b3fc73dcdcaa47856d482b47b95bed058d46af5bd31750be16c0f8a44819b6b66f99356f9b3420",
6531+
"wx": "0fb57733ac1ce9d808b78dd3a11e1da365b3fc73dcdcaa47856d482b",
6532+
"wy": "47b95bed058d46af5bd31750be16c0f8a44819b6b66f99356f9b3420"
6533+
},
6534+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a00040fb57733ac1ce9d808b78dd3a11e1da365b3fc73dcdcaa47856d482b47b95bed058d46af5bd31750be16c0f8a44819b6b66f99356f9b3420",
6535+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABA+1dzOsHOnYCLeN06EeHaNls/xz\n3NyqR4VtSCtHuVvtBY1Gr1vTF1C+FsD4pEgZtrZvmTVvmzQg\n-----END PUBLIC KEY-----\n",
6536+
"sha": "SHA-224",
6537+
"tests": [
6538+
{
6539+
"tcId": 445,
6540+
"comment": "r = 2, x = 1 is invalid",
6541+
"flags": [
6542+
"ArithmeticError"
6543+
],
6544+
"msg": "68656c6c6f2c20776f726c64",
6545+
"sig": "3022020102021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6546+
"result": "invalid"
6547+
}
6548+
]
6549+
},
6550+
{
6551+
"type": "EcdsaVerify",
6552+
"source": {
6553+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6554+
"version": "0.1"
6555+
},
6556+
"publicKey": {
6557+
"type": "EcPublicKey",
6558+
"curve": "brainpoolP224r1",
6559+
"keySize": 224,
6560+
"uncompressed": "048dad1f3183a05e6027303de8da57d45177d2e2b290a9ac255137245605b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6",
6561+
"wx": "8dad1f3183a05e6027303de8da57d45177d2e2b290a9ac2551372456",
6562+
"wy": "05b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6"
6563+
},
6564+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a00048dad1f3183a05e6027303de8da57d45177d2e2b290a9ac255137245605b078cf0cca41bf2acc824200b885cf9c9afb0f45e31dc4c49192b6",
6565+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABI2tHzGDoF5gJzA96NpX1FF30uKy\nkKmsJVE3JFYFsHjPDMpBvyrMgkIAuIXPnJr7D0XjHcTEkZK2\n-----END PUBLIC KEY-----\n",
6566+
"sha": "SHA-224",
6567+
"tests": [
6568+
{
6569+
"tcId": 446,
6570+
"comment": "r = 1 + n, x = 1 is invalid; r was not reduced mod n",
6571+
"flags": [
6572+
"ArithmeticError"
6573+
],
6574+
"msg": "68656c6c6f2c20776f726c64",
6575+
"sig": "303e021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a793a0021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6576+
"result": "invalid"
6577+
}
6578+
]
6579+
},
6580+
{
6581+
"type": "EcdsaVerify",
6582+
"source": {
6583+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6584+
"version": "0.1"
6585+
},
6586+
"publicKey": {
6587+
"type": "EcPublicKey",
6588+
"curve": "brainpoolP224r1",
6589+
"keySize": 224,
6590+
"uncompressed": "0440f5a0e304c2b81540a850fcccda86ed7e2bf6020939baf335323b009a573e6852a67c44d7b2063f1be310f9aebe04b797f9021ed4762a2b",
6591+
"wx": "40f5a0e304c2b81540a850fcccda86ed7e2bf6020939baf335323b00",
6592+
"wy": "9a573e6852a67c44d7b2063f1be310f9aebe04b797f9021ed4762a2b"
6593+
},
6594+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a000440f5a0e304c2b81540a850fcccda86ed7e2bf6020939baf335323b009a573e6852a67c44d7b2063f1be310f9aebe04b797f9021ed4762a2b",
6595+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABED1oOMEwrgVQKhQ/Mzahu1+K/YC\nCTm68zUyOwCaVz5oUqZ8RNeyBj8b4xD5rr4Et5f5Ah7Udior\n-----END PUBLIC KEY-----\n",
6596+
"sha": "SHA-224",
6597+
"tests": [
6598+
{
6599+
"tcId": 447,
6600+
"comment": "r = n - 2, x = n - 1 is invalid",
6601+
"flags": [
6602+
"ArithmeticError"
6603+
],
6604+
"msg": "68656c6c6f2c20776f726c64",
6605+
"sig": "303e021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939d021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6606+
"result": "invalid"
6607+
}
6608+
]
6609+
},
6610+
{
6611+
"type": "EcdsaVerify",
6612+
"source": {
6613+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6614+
"version": "0.1"
6615+
},
6616+
"publicKey": {
6617+
"type": "EcPublicKey",
6618+
"curve": "brainpoolP224r1",
6619+
"keySize": 224,
6620+
"uncompressed": "04c4a178ffc1460ff3aa326b45c11ac2b91c512ae644d2172be9d7b1f74ecc0e657061a0fcc086db6e7af56d3622215b2b4fa0d6fbab8ee1e7",
6621+
"wx": "c4a178ffc1460ff3aa326b45c11ac2b91c512ae644d2172be9d7b1f7",
6622+
"wy": "4ecc0e657061a0fcc086db6e7af56d3622215b2b4fa0d6fbab8ee1e7"
6623+
},
6624+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a0004c4a178ffc1460ff3aa326b45c11ac2b91c512ae644d2172be9d7b1f74ecc0e657061a0fcc086db6e7af56d3622215b2b4fa0d6fbab8ee1e7",
6625+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABMSheP/BRg/zqjJrRcEawrkcUSrm\nRNIXK+nXsfdOzA5lcGGg/MCG22569W02IiFbK0+g1vurjuHn\n-----END PUBLIC KEY-----\n",
6626+
"sha": "SHA-224",
6627+
"tests": [
6628+
{
6629+
"tcId": 448,
6630+
"comment": "r = 1, x = n + 1 is the smallest possible x with a reduction",
6631+
"flags": [
6632+
"ValidSignature"
6633+
],
6634+
"msg": "68656c6c6f2c20776f726c64",
6635+
"sig": "3022020101021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6636+
"result": "valid"
6637+
}
6638+
]
6639+
},
6640+
{
6641+
"type": "EcdsaVerify",
6642+
"source": {
6643+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6644+
"version": "0.1"
6645+
},
6646+
"publicKey": {
6647+
"type": "EcPublicKey",
6648+
"curve": "brainpoolP224r1",
6649+
"keySize": 224,
6650+
"uncompressed": "04345194ec8030ebc38c6b809bcf932a06032d0f810fe2cdabf463638d598beb6e07edba254054cefb2e87ce723872aea5837fc249d6e031e4",
6651+
"wx": "345194ec8030ebc38c6b809bcf932a06032d0f810fe2cdabf463638d",
6652+
"wy": "598beb6e07edba254054cefb2e87ce723872aea5837fc249d6e031e4"
6653+
},
6654+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a0004345194ec8030ebc38c6b809bcf932a06032d0f810fe2cdabf463638d598beb6e07edba254054cefb2e87ce723872aea5837fc249d6e031e4",
6655+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABDRRlOyAMOvDjGuAm8+TKgYDLQ+B\nD+LNq/RjY41Zi+tuB+26JUBUzvsuh85yOHKupYN/wknW4DHk\n-----END PUBLIC KEY-----\n",
6656+
"sha": "SHA-224",
6657+
"tests": [
6658+
{
6659+
"tcId": 449,
6660+
"comment": "r = 2, x = n + 1 is invalid",
6661+
"flags": [
6662+
"ArithmeticError"
6663+
],
6664+
"msg": "68656c6c6f2c20776f726c64",
6665+
"sig": "3022020102021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6666+
"result": "invalid"
6667+
}
6668+
]
6669+
},
6670+
{
6671+
"type": "EcdsaVerify",
6672+
"source": {
6673+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6674+
"version": "0.1"
6675+
},
6676+
"publicKey": {
6677+
"type": "EcPublicKey",
6678+
"curve": "brainpoolP224r1",
6679+
"keySize": 224,
6680+
"uncompressed": "0454d30d85d7fd91cb119f5da7d22e4b7ed486875d25bcbf0a7a9ab1cdbc448f9349e50319560a06edb46e26fdf9b9709169a4eb487c82f854",
6681+
"wx": "54d30d85d7fd91cb119f5da7d22e4b7ed486875d25bcbf0a7a9ab1cd",
6682+
"wy": "bc448f9349e50319560a06edb46e26fdf9b9709169a4eb487c82f854"
6683+
},
6684+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a000454d30d85d7fd91cb119f5da7d22e4b7ed486875d25bcbf0a7a9ab1cdbc448f9349e50319560a06edb46e26fdf9b9709169a4eb487c82f854",
6685+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABFTTDYXX/ZHLEZ9dp9IuS37Uhodd\nJby/Cnqasc28RI+TSeUDGVYKBu20bib9+blwkWmk60h8gvhU\n-----END PUBLIC KEY-----\n",
6686+
"sha": "SHA-224",
6687+
"tests": [
6688+
{
6689+
"tcId": 450,
6690+
"comment": "r = p - n + 1, x = 1 is invalid; r is too large to compare r + n with x",
6691+
"flags": [
6692+
"ArithmeticError"
6693+
],
6694+
"msg": "68656c6c6f2c20776f726c64",
6695+
"sig": "3030020f00dbeedf884b0c29fbcd51d9212d61021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6696+
"result": "invalid"
6697+
}
6698+
]
6699+
},
6700+
{
6701+
"type": "EcdsaVerify",
6702+
"source": {
6703+
"name": "github/davidben/ecdsa-r-s-edge-cases",
6704+
"version": "0.1"
6705+
},
6706+
"publicKey": {
6707+
"type": "EcPublicKey",
6708+
"curve": "brainpoolP224r1",
6709+
"keySize": 224,
6710+
"uncompressed": "04caf06cc0a63a8fa52cbc4e7ef9eb8758f8605221ba4fe29ae74ca1f1c1ac20a1f0224206cb5c48a75d0cd8ef94b38006c082bc000d26d7ff",
6711+
"wx": "caf06cc0a63a8fa52cbc4e7ef9eb8758f8605221ba4fe29ae74ca1f1",
6712+
"wy": "c1ac20a1f0224206cb5c48a75d0cd8ef94b38006c082bc000d26d7ff"
6713+
},
6714+
"publicKeyDer": "3052301406072a8648ce3d020106092b2403030208010105033a0004caf06cc0a63a8fa52cbc4e7ef9eb8758f8605221ba4fe29ae74ca1f1c1ac20a1f0224206cb5c48a75d0cd8ef94b38006c082bc000d26d7ff",
6715+
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMFIwFAYHKoZIzj0CAQYJKyQDAwIIAQEFAzoABMrwbMCmOo+lLLxOfvnrh1j4YFIh\nuk/imudMofHBrCCh8CJCBstcSKddDNjvlLOABsCCvAANJtf/\n-----END PUBLIC KEY-----\n",
6716+
"sha": "SHA-224",
6717+
"tests": [
6718+
{
6719+
"tcId": 451,
6720+
"comment": "r = 2^224 - n + 1, x = 1 is invalid; r + n is too large to compare r + n with x, and overflows 2^224 bits",
6721+
"flags": [
6722+
"ArithmeticError"
6723+
],
6724+
"msg": "68656c6c6f2c20776f726c64",
6725+
"sig": "303d021c283ecb55d9bc9979d5e7cfda8a2f04672ee943b49221435c5a586c62021d00d7c134aa264366862a18302575d0fb98d116bc4b6ddebca3a5a7939c",
6726+
"result": "invalid"
6727+
}
6728+
]
64896729
}
64906730
]
64916731
}

0 commit comments

Comments
 (0)