Rewrite ECDSA signature parsing code
There are now 2 encoding formats supported: 64-byte "compact" and DER. The latter is strict: the data has to be exact DER, though the values inside don't need to be valid.
This commit is contained in:
301
src/tests.c
301
src/tests.c
@@ -2495,6 +2495,306 @@ void run_ecdsa_end_to_end(void) {
|
||||
}
|
||||
}
|
||||
|
||||
int test_ecdsa_der_parse(const unsigned char *sig, size_t siglen, int certainly_der, int certainly_not_der) {
|
||||
static const unsigned char zeroes[32] = {0};
|
||||
static const unsigned char max_scalar[32] = {
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe,
|
||||
0xba, 0xae, 0xdc, 0xe6, 0xaf, 0x48, 0xa0, 0x3b,
|
||||
0xbf, 0xd2, 0x5e, 0x8c, 0xd0, 0x36, 0x41, 0x40
|
||||
};
|
||||
|
||||
int ret = 0;
|
||||
|
||||
secp256k1_ecdsa_signature sig_der;
|
||||
unsigned char roundtrip_der[2048];
|
||||
unsigned char compact_der[64];
|
||||
size_t len_der = 2048;
|
||||
int parsed_der = 0, valid_der = 0, roundtrips_der = 0;
|
||||
|
||||
#ifdef ENABLE_OPENSSL_TESTS
|
||||
ECDSA_SIG *sig_openssl;
|
||||
const unsigned char *sigptr;
|
||||
unsigned char roundtrip_openssl[2048];
|
||||
int len_openssl = 2048;
|
||||
int parsed_openssl, valid_openssl = 0, roundtrips_openssl = 0;
|
||||
#endif
|
||||
|
||||
parsed_der = secp256k1_ecdsa_signature_parse_der(ctx, &sig_der, sig, siglen);
|
||||
if (parsed_der) {
|
||||
ret |= (!secp256k1_ecdsa_signature_serialize_compact(ctx, compact_der, &sig_der)) << 0;
|
||||
valid_der = (memcmp(compact_der, zeroes, 32) != 0) && (memcmp(compact_der + 32, zeroes, 32) != 0);
|
||||
}
|
||||
if (valid_der) {
|
||||
ret |= (!secp256k1_ecdsa_signature_serialize_der(ctx, roundtrip_der, &len_der, &sig_der)) << 1;
|
||||
roundtrips_der = (len_der == siglen) && memcmp(roundtrip_der, sig, siglen) == 0;
|
||||
}
|
||||
|
||||
if (certainly_der) {
|
||||
ret |= (!parsed_der) << 2;
|
||||
}
|
||||
if (certainly_not_der) {
|
||||
ret |= (parsed_der) << 17;
|
||||
}
|
||||
if (valid_der) {
|
||||
ret |= (!roundtrips_der) << 3;
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENSSL_TESTS
|
||||
sig_openssl = ECDSA_SIG_new();
|
||||
sigptr = sig;
|
||||
parsed_openssl = (d2i_ECDSA_SIG(&sig_openssl, &sigptr, siglen) != NULL);
|
||||
if (parsed_openssl) {
|
||||
valid_openssl = !BN_is_negative(sig_openssl->r) && !BN_is_negative(sig_openssl->s) && BN_num_bits(sig_openssl->r) > 0 && BN_num_bits(sig_openssl->r) <= 256 && BN_num_bits(sig_openssl->s) > 0 && BN_num_bits(sig_openssl->s) <= 256;
|
||||
if (valid_openssl) {
|
||||
unsigned char tmp[32] = {0};
|
||||
BN_bn2bin(sig_openssl->r, tmp + 32 - BN_num_bytes(sig_openssl->r));
|
||||
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
|
||||
}
|
||||
if (valid_openssl) {
|
||||
unsigned char tmp[32] = {0};
|
||||
BN_bn2bin(sig_openssl->s, tmp + 32 - BN_num_bytes(sig_openssl->s));
|
||||
valid_openssl = memcmp(tmp, max_scalar, 32) < 0;
|
||||
}
|
||||
}
|
||||
len_openssl = i2d_ECDSA_SIG(sig_openssl, NULL);
|
||||
if (len_openssl <= 2048) {
|
||||
unsigned char *ptr = roundtrip_openssl;
|
||||
CHECK(i2d_ECDSA_SIG(sig_openssl, &ptr) == len_openssl);
|
||||
roundtrips_openssl = valid_openssl && ((size_t)len_openssl == siglen) && (memcmp(roundtrip_openssl, sig, siglen) == 0);
|
||||
} else {
|
||||
len_openssl = 0;
|
||||
}
|
||||
ECDSA_SIG_free(sig_openssl);
|
||||
|
||||
ret |= (parsed_der && !parsed_openssl) << 4;
|
||||
ret |= (valid_der && !valid_openssl) << 5;
|
||||
ret |= (roundtrips_openssl && !parsed_der) << 6;
|
||||
ret |= (roundtrips_der != roundtrips_openssl) << 7;
|
||||
if (roundtrips_openssl) {
|
||||
ret |= (len_der != (size_t)len_openssl) << 8;
|
||||
ret |= (memcmp(roundtrip_der, roundtrip_openssl, len_der) != 0) << 9;
|
||||
}
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void assign_big_endian(unsigned char *ptr, size_t ptrlen, uint32_t val) {
|
||||
size_t i;
|
||||
for (i = 0; i < ptrlen; i++) {
|
||||
int shift = ptrlen - 1 - i;
|
||||
if (shift >= 4) {
|
||||
ptr[i] = 0;
|
||||
} else {
|
||||
ptr[i] = (val >> shift) & 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void damage_array(unsigned char *sig, size_t *len) {
|
||||
int pos;
|
||||
int action = secp256k1_rand_bits(3);
|
||||
if (action < 1) {
|
||||
/* Delete a byte. */
|
||||
pos = secp256k1_rand_int(*len);
|
||||
memmove(sig + pos, sig + pos + 1, *len - pos - 1);
|
||||
(*len)--;
|
||||
return;
|
||||
} else if (action < 2) {
|
||||
/* Insert a byte. */
|
||||
pos = secp256k1_rand_int(1 + *len);
|
||||
memmove(sig + pos + 1, sig + pos, *len - pos);
|
||||
sig[pos] = secp256k1_rand_bits(8);
|
||||
(*len)++;
|
||||
return;
|
||||
} else if (action < 4) {
|
||||
/* Modify a byte. */
|
||||
sig[secp256k1_rand_int(*len)] += 1 + secp256k1_rand_int(255);
|
||||
return;
|
||||
} else { /* action < 8 */
|
||||
/* Modify a bit. */
|
||||
sig[secp256k1_rand_int(*len)] ^= 1 << secp256k1_rand_bits(3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void random_ber_signature(unsigned char *sig, size_t *len, int* certainly_der, int* certainly_not_der) {
|
||||
int der;
|
||||
int nlow[2], nlen[2], nlenlen[2], nhbit[2], nhbyte[2], nzlen[2];
|
||||
size_t tlen, elen, glen;
|
||||
int indet;
|
||||
int n;
|
||||
|
||||
*len = 0;
|
||||
der = secp256k1_rand_bits(2) == 0;
|
||||
*certainly_der = der;
|
||||
*certainly_not_der = 0;
|
||||
indet = der ? 0 : secp256k1_rand_int(10) == 0;
|
||||
|
||||
for (n = 0; n < 2; n++) {
|
||||
/* We generate two classes of numbers: nlow==1 "low" ones (up to 32 bytes), nlow==0 "high" ones (32 bytes with 129 top bits set, or larger than 32 bytes) */
|
||||
nlow[n] = der ? 1 : (secp256k1_rand_bits(3) != 0);
|
||||
/* The length of the number in bytes (the first byte of which will always be nonzero) */
|
||||
nlen[n] = nlow[n] ? secp256k1_rand_int(33) : 32 + secp256k1_rand_int(200) * secp256k1_rand_int(8) / 8;
|
||||
CHECK(nlen[n] <= 232);
|
||||
/* The top bit of the number. */
|
||||
nhbit[n] = (nlow[n] == 0 && nlen[n] == 32) ? 1 : (nlen[n] == 0 ? 0 : secp256k1_rand_bits(1));
|
||||
/* The top byte of the number (after the potential hardcoded 16 0xFF characters for "high" 32 bytes numbers) */
|
||||
nhbyte[n] = nlen[n] == 0 ? 0 : (nhbit[n] ? 128 + secp256k1_rand_bits(7) : 1 + secp256k1_rand_int(127));
|
||||
/* The number of zero bytes in front of the number (which is 0 or 1 in case of DER, otherwise we extend up to 300 bytes) */
|
||||
nzlen[n] = der ? ((nlen[n] == 0 || nhbit[n]) ? 1 : 0) : (nlow[n] ? secp256k1_rand_int(3) : secp256k1_rand_int(300 - nlen[n]) * secp256k1_rand_int(8) / 8);
|
||||
if (nzlen[n] > ((nlen[n] == 0 || nhbit[n]) ? 1 : 0)) {
|
||||
*certainly_not_der = 1;
|
||||
}
|
||||
CHECK(nlen[n] + nzlen[n] <= 300);
|
||||
/* The length of the length descriptor for the number. 0 means short encoding, anything else is long encoding. */
|
||||
nlenlen[n] = nlen[n] + nzlen[n] < 128 ? 0 : (nlen[n] + nzlen[n] < 256 ? 1 : 2);
|
||||
if (!der) {
|
||||
/* nlenlen[n] max 127 bytes */
|
||||
int add = secp256k1_rand_int(127 - nlenlen[n]) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
|
||||
nlenlen[n] += add;
|
||||
if (add != 0) {
|
||||
*certainly_not_der = 1;
|
||||
}
|
||||
}
|
||||
CHECK(nlen[n] + nzlen[n] + nlenlen[n] <= 427);
|
||||
}
|
||||
|
||||
/* The total length of the data to go, so far */
|
||||
tlen = 2 + nlenlen[0] + nlen[0] + nzlen[0] + 2 + nlenlen[1] + nlen[1] + nzlen[1];
|
||||
CHECK(tlen <= 856);
|
||||
|
||||
/* The length of the garbage inside the tuple. */
|
||||
elen = (der || indet) ? 0 : secp256k1_rand_int(980 - tlen) * secp256k1_rand_int(8) / 8;
|
||||
if (elen != 0) {
|
||||
*certainly_not_der = 1;
|
||||
}
|
||||
tlen += elen;
|
||||
CHECK(tlen <= 980);
|
||||
|
||||
/* The length of the garbage after the end of the tuple. */
|
||||
glen = der ? 0 : secp256k1_rand_int(990 - tlen) * secp256k1_rand_int(8) / 8;
|
||||
if (glen != 0) {
|
||||
*certainly_not_der = 1;
|
||||
}
|
||||
CHECK(tlen + glen <= 990);
|
||||
|
||||
/* Write the tuple header. */
|
||||
sig[(*len)++] = 0x30;
|
||||
if (indet) {
|
||||
/* Indeterminate length */
|
||||
sig[(*len)++] = 0x80;
|
||||
*certainly_not_der = 1;
|
||||
} else {
|
||||
int tlenlen = tlen < 128 ? 0 : (tlen < 256 ? 1 : 2);
|
||||
if (!der) {
|
||||
int add = secp256k1_rand_int(127 - tlenlen) * secp256k1_rand_int(16) * secp256k1_rand_int(16) / 256;
|
||||
tlenlen += add;
|
||||
if (add != 0) {
|
||||
*certainly_not_der = 1;
|
||||
}
|
||||
}
|
||||
if (tlenlen == 0) {
|
||||
/* Short length notation */
|
||||
sig[(*len)++] = tlen;
|
||||
} else {
|
||||
/* Long length notation */
|
||||
sig[(*len)++] = 128 + tlenlen;
|
||||
assign_big_endian(sig + *len, tlenlen, tlen);
|
||||
*len += tlenlen;
|
||||
}
|
||||
tlen += tlenlen;
|
||||
}
|
||||
tlen += 2;
|
||||
CHECK(tlen + glen <= 1119);
|
||||
|
||||
for (n = 0; n < 2; n++) {
|
||||
/* Write the integer header. */
|
||||
sig[(*len)++] = 0x02;
|
||||
if (nlenlen[n] == 0) {
|
||||
/* Short length notation */
|
||||
sig[(*len)++] = nlen[n] + nzlen[n];
|
||||
} else {
|
||||
/* Long length notation. */
|
||||
sig[(*len)++] = 128 + nlenlen[n];
|
||||
assign_big_endian(sig + *len, nlenlen[n], nlen[n] + nzlen[n]);
|
||||
*len += nlenlen[n];
|
||||
}
|
||||
/* Write zero padding */
|
||||
while (nzlen[n] > 0) {
|
||||
sig[(*len)++] = 0x00;
|
||||
nzlen[n]--;
|
||||
}
|
||||
if (nlen[n] == 32 && !nlow[n]) {
|
||||
/* Special extra 16 0xFF bytes in "high" 32-byte numbers */
|
||||
int i;
|
||||
for (i = 0; i < 16; i++) {
|
||||
sig[(*len)++] = 0xFF;
|
||||
}
|
||||
nlen[n] -= 16;
|
||||
}
|
||||
/* Write first byte of number */
|
||||
if (nlen[n] > 0) {
|
||||
sig[(*len)++] = nhbyte[n];
|
||||
nlen[n]--;
|
||||
}
|
||||
/* Generate remaining random bytes of number */
|
||||
secp256k1_rand_bytes_test(sig + *len, nlen[n]);
|
||||
*len += nlen[n];
|
||||
nlen[n] = 0;
|
||||
}
|
||||
|
||||
/* Generate random garbage inside tuple. */
|
||||
secp256k1_rand_bytes_test(sig + *len, elen);
|
||||
*len += elen;
|
||||
|
||||
/* Generate end-of-contents bytes. */
|
||||
if (indet) {
|
||||
sig[(*len)++] = 0;
|
||||
sig[(*len)++] = 0;
|
||||
tlen += 2;
|
||||
}
|
||||
CHECK(tlen + glen <= 1121);
|
||||
|
||||
/* Generate random garbage outside tuple. */
|
||||
secp256k1_rand_bytes_test(sig + *len, glen);
|
||||
*len += glen;
|
||||
tlen += glen;
|
||||
CHECK(tlen <= 1121);
|
||||
CHECK(tlen == *len);
|
||||
}
|
||||
|
||||
void run_ecdsa_der_parse(void) {
|
||||
int i,j;
|
||||
for (i = 0; i < 200 * count; i++) {
|
||||
unsigned char buffer[2048];
|
||||
size_t buflen = 0;
|
||||
int certainly_der = 0;
|
||||
int certainly_not_der = 0;
|
||||
random_ber_signature(buffer, &buflen, &certainly_der, &certainly_not_der);
|
||||
for (j = 0; j < 16; j++) {
|
||||
int ret = 0;
|
||||
if (j > 0) {
|
||||
damage_array(buffer, &buflen);
|
||||
/* We don't know anything anymore about the DERness of the result */
|
||||
certainly_der = 0;
|
||||
certainly_not_der = 0;
|
||||
}
|
||||
ret = test_ecdsa_der_parse(buffer, buflen, certainly_der, certainly_not_der);
|
||||
if (ret != 0) {
|
||||
size_t k;
|
||||
fprintf(stderr, "Failure %x on ", ret);
|
||||
for (k = 0; k < buflen; k++) {
|
||||
fprintf(stderr, "%02x ", buffer[k]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
CHECK(ret == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* Tests several edge cases. */
|
||||
void test_ecdsa_edge_cases(void) {
|
||||
int t;
|
||||
@@ -2803,6 +3103,7 @@ int main(int argc, char **argv) {
|
||||
|
||||
/* ecdsa tests */
|
||||
run_random_pubkeys();
|
||||
run_ecdsa_der_parse();
|
||||
run_ecdsa_sign_verify();
|
||||
run_ecdsa_end_to_end();
|
||||
run_ecdsa_edge_cases();
|
||||
|
||||
Reference in New Issue
Block a user