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:
Pieter Wuille
2015-09-28 17:40:21 +02:00
parent fa57f1bdf1
commit 3bb9c44719
7 changed files with 502 additions and 60 deletions

View File

@@ -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();