diff --git a/src/headers/tomcrypt_pk.h b/src/headers/tomcrypt_pk.h index dcf714d7d..3a07c591d 100644 --- a/src/headers/tomcrypt_pk.h +++ b/src/headers/tomcrypt_pk.h @@ -691,9 +691,17 @@ enum ltc_der_seq { LTC_DER_SEQ_RELAXED = LTC_DER_SEQ_ZERO, LTC_DER_SEQ_STRICT = 0x2u, + /** Bit2 - [0]=Relaxed Length Check + * [1]=Strict Length Check */ + LTC_DER_SEQ_LEN_RELAXED = LTC_DER_SEQ_ZERO, + LTC_DER_SEQ_LEN_STRICT = 0x4u, + /** Alternative naming */ LTC_DER_SEQ_SET = LTC_DER_SEQ_UNORDERED, LTC_DER_SEQ_SEQUENCE = LTC_DER_SEQ_ORDERED, + + LTC_DER_SEQ_ALL_STRICT = LTC_DER_SEQ_STRICT | LTC_DER_SEQ_LEN_STRICT, + }; int der_decode_sequence_ex(const unsigned char *in, unsigned long inlen, diff --git a/src/headers/tomcrypt_private.h b/src/headers/tomcrypt_private.h index ecf90c1d3..43a90943e 100644 --- a/src/headers/tomcrypt_private.h +++ b/src/headers/tomcrypt_private.h @@ -546,7 +546,8 @@ int der_decode_asn1_identifier(const unsigned char *in, unsigned long *inlen, lt int der_length_asn1_identifier(const ltc_asn1_list *id, unsigned long *idlen); int der_encode_asn1_length(unsigned long len, unsigned char* out, unsigned long* outlen); -int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsigned long *outlen); +int der_decode_asn1_length_ex(const unsigned char *in, unsigned long *inlen, unsigned long *outlen, unsigned int flags); +#define der_decode_asn1_length(i, il, ol) der_decode_asn1_length_ex(i, il, ol, 0) int der_length_asn1_length(unsigned long len, unsigned long *outlen); int der_length_sequence_ex(const ltc_asn1_list *list, unsigned long inlen, diff --git a/src/pk/asn1/der/custom_type/der_decode_custom_type.c b/src/pk/asn1/der/custom_type/der_decode_custom_type.c index 4c4d52d35..60f5054c7 100644 --- a/src/pk/asn1/der/custom_type/der_decode_custom_type.c +++ b/src/pk/asn1/der/custom_type/der_decode_custom_type.c @@ -47,6 +47,7 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen, int err, seq_err, i, ordered; ltc_asn1_type type; ltc_asn1_list ident; + unsigned int f; unsigned long size, x, y, z, blksize; unsigned char* in_new = NULL; void *data; @@ -63,7 +64,8 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen, LTC_ARGCHK(list != NULL); /* sequence type? We allow 0x30 SEQUENCE and 0x31 SET since fundamentally they're the same structure */ - if (in[x] != 0x30 && in[x] != 0x31) { + f = flags & ~(LTC_DER_SEQ_ALL_STRICT); + if (((f == LTC_DER_SEQ_SEQUENCE) && (in[x] != 0x30)) || (((f == LTC_DER_SEQ_SET) && (in[x] != 0x31)))) { return CRYPT_INVALID_PACKET; } ++x; @@ -116,7 +118,7 @@ int der_decode_custom_type_ex(const unsigned char *in, unsigned long inlen, } else { y = inlen - x; - if ((err = der_decode_asn1_length(&in[x], &y, &blksize)) != CRYPT_OK) { + if ((err = der_decode_asn1_length_ex(&in[x], &y, &blksize, flags)) != CRYPT_OK) { goto LBL_ERR; } x += y; diff --git a/src/pk/asn1/der/general/der_decode_asn1_length.c b/src/pk/asn1/der/general/der_decode_asn1_length.c index 0e5a3939d..c46e02d66 100644 --- a/src/pk/asn1/der/general/der_decode_asn1_length.c +++ b/src/pk/asn1/der/general/der_decode_asn1_length.c @@ -15,7 +15,7 @@ @param outlen [out] The decoded ASN.1 length @return CRYPT_OK if successful */ -int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsigned long *outlen) +int der_decode_asn1_length_ex(const unsigned char *in, unsigned long *inlen, unsigned long *outlen, unsigned int flags) { unsigned long real_len, decoded_len, offset, i; @@ -42,10 +42,17 @@ int der_decode_asn1_length(const unsigned char *in, unsigned long *inlen, unsign if (real_len > (*inlen - 1)) { return CRYPT_BUFFER_OVERFLOW; } + flags &= LTC_DER_SEQ_LEN_STRICT; decoded_len = 0; offset = 1 + real_len; for (i = 0; i < real_len; i++) { decoded_len = (decoded_len << 8) | in[1 + i]; + if ((flags == LTC_DER_SEQ_LEN_STRICT) && (decoded_len == 0)) { + return CRYPT_PK_ASN1_ERROR; + } + } + if ((flags == LTC_DER_SEQ_LEN_STRICT) && (real_len == 1) && (decoded_len < 128)) { + return CRYPT_PK_ASN1_ERROR; } } diff --git a/src/pk/ecc/ecc_verify_hash.c b/src/pk/ecc/ecc_verify_hash.c index 7f5730d5c..3607ae65d 100644 --- a/src/pk/ecc/ecc_verify_hash.c +++ b/src/pk/ecc/ecc_verify_hash.c @@ -63,7 +63,7 @@ int ecc_verify_hash_ex(const unsigned char *sig, unsigned long siglen, if (sigformat == LTC_ECCSIG_ANSIX962) { /* ANSI X9.62 format - ASN.1 encoded SEQUENCE{ INTEGER(r), INTEGER(s) } */ - if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_STRICT, + if ((err = der_decode_sequence_multi_ex(sig, siglen, LTC_DER_SEQ_SEQUENCE | LTC_DER_SEQ_ALL_STRICT, LTC_ASN1_INTEGER, 1UL, r, LTC_ASN1_INTEGER, 1UL, s, LTC_ASN1_EOL, 0UL, LTC_NULL)) != CRYPT_OK) { goto error; } diff --git a/tests/ecc_test.c b/tests/ecc_test.c index 0a7a449e8..49f7cc9c0 100644 --- a/tests/ecc_test.c +++ b/tests/ecc_test.c @@ -332,6 +332,170 @@ static int s_ecc_issue443_447(void) return CRYPT_OK; } +/* https://github.com/libtom/libtomcrypt/issues/446 */ +static int s_ecc_issue446(void) +{ + const ltc_ecc_curve* cu; + ecc_key key1, key2, key3, key4; + int stat = 0; + unsigned char hash[64]; + unsigned long hashlen; + const unsigned char msg1[] = { 0x31,0x32,0x33,0x34,0x30,0x30 }; + const unsigned char msg2[] = { 0x36,0x39,0x38,0x31,0x39 }; + const unsigned char msg3[] = { 0x4d,0x73,0x67 }; + const unsigned char msg4[] = { 0x54,0x65,0x73,0x74 }; + const unsigned char pub1[] = { + 0x04, /* secp256r1 */ + 0x29,0x27,0xb1,0x05,0x12,0xba,0xe3,0xed,0xdc,0xfe,0x46,0x78,0x28,0x12,0x8b,0xad, + 0x29,0x03,0x26,0x99,0x19,0xf7,0x08,0x60,0x69,0xc8,0xc4,0xdf,0x6c,0x73,0x28,0x38, + 0xc7,0x78,0x79,0x64,0xea,0xac,0x00,0xe5,0x92,0x1f,0xb1,0x49,0x8a,0x60,0xf4,0x60, + 0x67,0x66,0xb3,0xd9,0x68,0x50,0x01,0x55,0x8d,0x1a,0x97,0x4e,0x73,0x41,0x51,0x3e + }; + const unsigned char pub2[] = { + 0x04, /* secp224r1 */ + 0x53,0x88,0x35,0x38,0xd8,0x66,0x09,0x21,0x7a,0x4d,0x9c,0x27,0x99,0x2e, + 0x2b,0xd7,0x04,0xa4,0xbb,0x12,0xfb,0x91,0x25,0x3c,0xd4,0xf4,0x96,0xfa, + 0x00,0xb3,0x2f,0x3a,0x2d,0x15,0x3a,0x52,0xbb,0x57,0x76,0x50,0xcd,0xf1, + 0xb7,0x3a,0x34,0x05,0x35,0xc7,0xe7,0x3d,0xeb,0x59,0x9a,0xa9,0x50,0x88 + }; + const unsigned char pub3[] = { + 0x04, /* brainpoolP256r1 */ + 0x30,0x29,0x1b,0xca,0x4e,0xb9,0x3c,0x41,0x99,0x1d,0xae,0xc0,0xfd,0x2e,0x87,0xc2, + 0x77,0x8d,0x95,0x8d,0x9a,0x8e,0x2f,0xa4,0xe6,0xd3,0x83,0x5d,0x48,0x5b,0xc2,0x3e, + 0x42,0x17,0xf6,0x84,0xcf,0xdd,0x83,0x6d,0x7c,0xf6,0x09,0x9a,0x34,0xc6,0x82,0x61, + 0xe5,0xa0,0x94,0xad,0xb5,0xb3,0xe4,0xa9,0x26,0x2f,0xf5,0x95,0x7c,0x1b,0x1b,0xa8 + }; + const unsigned char pub4[] = { + 0x04, /* brainpoolP256t1 */ + 0x44,0x24,0xf0,0x9e,0x4f,0x86,0x92,0x31,0xe4,0x7b,0x1e,0x34,0x26,0xec,0x26,0x9e, + 0x5b,0xef,0x19,0xec,0x64,0xcc,0x96,0xda,0x5b,0x2e,0xfd,0xd2,0x11,0x6b,0x83,0xf0, + 0x7a,0x86,0x3d,0x25,0x74,0x3b,0xb2,0x60,0xa6,0x38,0xde,0x64,0x97,0x96,0xc6,0x76, + 0xe1,0x38,0x05,0x86,0x5c,0x8b,0xb9,0xda,0xc3,0x8e,0x16,0xfa,0xe5,0x4b,0x0c,0xef + }; + /* msg1+pub1+sig1 test vector is from wycheproof/ecdsa_test VALID */ + const unsigned char sig1[] = { + 0x30,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19,0x0a, + 0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e,0x18, + 0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1,0xf5, + 0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb + }; + /* msg1+pub1+sig2 test vector is from wycheproof/ecdsa_test INVALID (changing tag value of sequence) */ + const unsigned char sig2[] = { + 0x31,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19,0x0a, + 0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e,0x18, + 0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1,0xf5, + 0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb + }; + /* msg2+pub1+sig3 test vector is from wycheproof/ecdsa_test VALID (Edge case for Shamir multiplication) */ + const unsigned char sig3[] = { + 0x30,0x44,0x02,0x20,0x64,0xa1,0xaa,0xb5,0x00,0x0d,0x0e,0x80,0x4f,0x3e,0x2f,0xc0,0x2b,0xde, + 0xe9,0xbe,0x8f,0xf3,0x12,0x33,0x4e,0x2b,0xa1,0x6d,0x11,0x54,0x7c,0x97,0x71,0x1c,0x89,0x8e, + 0x02,0x20,0x6a,0xf0,0x15,0x97,0x1c,0xc3,0x0b,0xe6,0xd1,0xa2,0x06,0xd4,0xe0,0x13,0xe0,0x99, + 0x77,0x72,0xa2,0xf9,0x1d,0x73,0x28,0x6f,0xfd,0x68,0x3b,0x9b,0xb2,0xcf,0x4f,0x1b + }; + /* msg1+pub1+sig4 test vector is from wycheproof/ecdsa_test INVALID (long form encoding of length) */ + const unsigned char sig4[] = { + 0x30,0x81,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1,0x19, + 0x0a,0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c,0x2e, + 0x18,0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93,0xb1, + 0xf5,0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb + }; + /* msg1+pub1+sig5 test vector is from wycheproof/ecdsa_test INVALID (length contains leading 0) */ + const unsigned char sig5[] = { + 0x30,0x82,0x00,0x45,0x02,0x20,0x2b,0xa3,0xa8,0xbe,0x6b,0x94,0xd5,0xec,0x80,0xa6,0xd9,0xd1, + 0x19,0x0a,0x43,0x6e,0xff,0xe5,0x0d,0x85,0xa1,0xee,0xe8,0x59,0xb8,0xcc,0x6a,0xf9,0xbd,0x5c, + 0x2e,0x18,0x02,0x21,0x00,0xb3,0x29,0xf4,0x79,0xa2,0xbb,0xd0,0xa5,0xc3,0x84,0xee,0x14,0x93, + 0xb1,0xf5,0x18,0x6a,0x87,0x13,0x9c,0xac,0x5d,0xf4,0x08,0x7c,0x13,0x4b,0x49,0x15,0x68,0x47,0xdb + }; + /* msg3+pub2+sig6 test vector is from wycheproof/ecdsa_test VALID (extreme value for k and edgecase s) */ + const unsigned char sig6[] = { + 0x30,0x3c,0x02,0x1c,0x70,0x6a,0x46,0xdc,0x76,0xdc,0xb7,0x67,0x98,0xe6,0x0e,0x6d,0x89,0x47,0x47,0x88,0xd1, + 0x6d,0xc1,0x80,0x32,0xd2,0x68,0xfd,0x1a,0x70,0x4f,0xa6,0x02,0x1c,0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55, + 0x55,0x55,0x55,0x55,0x55,0x55,0x07,0x8b,0xa0,0x3d,0xa5,0x6a,0x06,0x9f,0x0d,0xc1,0xc9,0x74,0x0e,0x14 + }; + /* msg4+pub3+sig7 test vector is from wycheproof/ecdsa_test VALID (extreme value for k) */ + const unsigned char sig7[] = { + 0x30,0x44,0x02,0x20,0x74,0x3c,0xf1,0xb8,0xb5,0xcd,0x4f,0x2e,0xb5,0x5f,0x8a,0xa3,0x69,0x59, + 0x3a,0xc4,0x36,0xef,0x04,0x41,0x66,0x69,0x9e,0x37,0xd5,0x1a,0x14,0xc2,0xce,0x13,0xea,0x0e, + 0x02,0x20,0x77,0x47,0x2d,0x9a,0x28,0xb4,0xec,0xe7,0x1c,0xf4,0x13,0xa6,0x8e,0xac,0x0e,0xb4, + 0x23,0xa1,0x6f,0xb4,0x62,0xb1,0xf4,0x87,0x06,0xfe,0xd4,0x8c,0xa4,0x37,0xbd,0x2d + }; + /* msg4+pub4+sig8 test vector is from wycheproof/ecdsa_test VALID (extreme value for k and s^-1) */ + const unsigned char sig8[] = { + 0x30,0x45,0x02,0x21,0x00,0x83,0x38,0x42,0x7c,0x7c,0xf4,0xd1,0x1c,0xb9,0x81,0xd9,0xb1,0x87, + 0x93,0xe3,0x77,0x9c,0x49,0x4c,0x50,0x2c,0x75,0xbd,0x73,0x9e,0x57,0x8d,0xe2,0xa7,0x00,0x57, + 0x8d,0x02,0x20,0x30,0x90,0xf4,0x87,0xe5,0x1f,0x9e,0x35,0xc8,0xaf,0x70,0xbb,0x9a,0xb7,0xdf, + 0x45,0x03,0x7e,0x23,0x0a,0x33,0xd2,0xc1,0xfd,0x96,0xe4,0x04,0x25,0x4f,0xcb,0x86,0x79 + }; + + DO(ecc_find_curve("secp256r1", &cu)); + DO(ecc_set_curve(cu, &key1)); + DO(ecc_set_key(pub1, sizeof(pub1), PK_PUBLIC, &key1)); + + DO(ecc_find_curve("secp224r1", &cu)); + DO(ecc_set_curve(cu, &key2)); + DO(ecc_set_key(pub2, sizeof(pub2), PK_PUBLIC, &key2)); + + DO(ecc_find_curve("brainpoolP256r1", &cu)); + DO(ecc_set_curve(cu, &key3)); + DO(ecc_set_key(pub3, sizeof(pub3), PK_PUBLIC, &key3)); + + DO(ecc_find_curve("brainpoolP256t1", &cu)); + DO(ecc_set_curve(cu, &key4)); + DO(ecc_set_key(pub4, sizeof(pub4), PK_PUBLIC, &key4)); + + hashlen = sizeof(hash); + DO(hash_memory(find_hash("sha256"), msg1, sizeof(msg1), hash, &hashlen)); + + DO(ecc_verify_hash(sig1, sizeof(sig1), hash, hashlen, &stat, &key1)); + ENSUREX(stat == 1, "sig1 - wycheproof / Valid"); + + SHOULD_FAIL(ecc_verify_hash(sig2, sizeof(sig2), hash, hashlen, &stat, &key1)); + ENSUREX(stat == 0, "sig2 - wycheproof / changing tag value of sequence"); + + SHOULD_FAIL(ecc_verify_hash(sig4, sizeof(sig4), hash, hashlen, &stat, &key1)); + ENSUREX(stat == 0, "sig4 - wycheproof / long form encoding of length"); + + SHOULD_FAIL(ecc_verify_hash(sig5, sizeof(sig5), hash, hashlen, &stat, &key1)); + ENSUREX(stat == 0, "sig5 - wycheproof / length contains leading 0"); + + hashlen = sizeof(hash); + DO(hash_memory(find_hash("sha256"), msg2, sizeof(msg2), hash, &hashlen)); + + DO(ecc_verify_hash(sig3, sizeof(sig3), hash, hashlen, &stat, &key1)); + if (stat != 1) { + fprintf(stderr, "XXX-TODO should be valid - wycheproof / Edge case for Shamir multiplication\n"); + /* return CRYPT_FAIL_TESTVECTOR; / * expected result: VALID */ + } + + hashlen = sizeof(hash); + DO(hash_memory(find_hash("sha224"), msg3, sizeof(msg3), hash, &hashlen)); + DO(ecc_verify_hash(sig6, sizeof(sig6), hash, hashlen, &stat, &key2)); + if (stat != 1) { + fprintf(stderr, "XXX-TODO should be valid - wycheproof / extreme value for k and edgecase s\n"); + /* return CRYPT_FAIL_TESTVECTOR; / * expected result: VALID */ + } + + hashlen = sizeof(hash); + DO(hash_memory(find_hash("sha256"), msg4, sizeof(msg4), hash, &hashlen)); + DO(ecc_verify_hash(sig7, sizeof(sig7), hash, hashlen, &stat, &key3)); + if (stat != 1) { + fprintf(stderr, "XXX-TODO should be valid - wycheproof / extreme value for k\n"); + /* return CRYPT_FAIL_TESTVECTOR; / * expected result: VALID */ + } + DO(ecc_verify_hash(sig8, sizeof(sig8), hash, hashlen, &stat, &key4)); + if (stat != 1) { + fprintf(stderr, "XXX-TODO should be valid - wycheproof / extreme value for k and s^-1\n"); + /* return CRYPT_FAIL_TESTVECTOR; / * expected result: VALID */ + } + + ecc_free(&key1); + ecc_free(&key2); + ecc_free(&key3); + ecc_free(&key4); + return CRYPT_OK; +} + static int s_ecc_test_mp(void) { void *a, *modulus, *order; @@ -1630,6 +1794,7 @@ int ecc_test(void) DO(s_ecc_issue108()); DO(s_ecc_issue443_447()); DO(s_ecc_issue630()); + DO(s_ecc_issue446()); #ifdef LTC_ECC_SHAMIR DO(s_ecc_test_shamir()); DO(s_ecc_test_recovery());