diff --git a/lib/vtls/x509asn1.c b/lib/vtls/x509asn1.c index 57f5f09a91..b430c74704 100644 --- a/lib/vtls/x509asn1.c +++ b/lib/vtls/x509asn1.c @@ -51,41 +51,6 @@ #define CURL_ASN1_MAX ((size_t)0x40000) /* 256K */ #ifdef WANT_EXTRACT_CERTINFO -/* ASN.1 classes. */ -/* #define CURL_ASN1_UNIVERSAL 0 */ -/* #define CURL_ASN1_APPLICATION 1 */ -/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ -/* #define CURL_ASN1_PRIVATE 3 */ - -/* ASN.1 types. */ -#define CURL_ASN1_BOOLEAN 1 -#define CURL_ASN1_INTEGER 2 -#define CURL_ASN1_BIT_STRING 3 -#define CURL_ASN1_OCTET_STRING 4 -#define CURL_ASN1_NULL 5 -#define CURL_ASN1_OBJECT_IDENTIFIER 6 -/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ -/* #define CURL_ASN1_INSTANCE_OF 8 */ -/* #define CURL_ASN1_REAL 9 */ -#define CURL_ASN1_ENUMERATED 10 -/* #define CURL_ASN1_EMBEDDED 11 */ -#define CURL_ASN1_UTF8_STRING 12 -/* #define CURL_ASN1_RELATIVE_OID 13 */ -/* #define CURL_ASN1_SEQUENCE 16 */ -/* #define CURL_ASN1_SET 17 */ -#define CURL_ASN1_NUMERIC_STRING 18 -#define CURL_ASN1_PRINTABLE_STRING 19 -#define CURL_ASN1_TELETEX_STRING 20 -/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ -#define CURL_ASN1_IA5_STRING 22 -#define CURL_ASN1_UTC_TIME 23 -#define CURL_ASN1_GENERALIZED_TIME 24 -/* #define CURL_ASN1_GRAPHIC_STRING 25 */ -#define CURL_ASN1_VISIBLE_STRING 26 -/* #define CURL_ASN1_GENERAL_STRING 27 */ -#define CURL_ASN1_UNIVERSAL_STRING 28 -/* #define CURL_ASN1_CHARACTER_STRING 29 */ -#define CURL_ASN1_BMP_STRING 30 /* ASN.1 OID table entry. */ struct Curl_OID { @@ -265,9 +230,13 @@ static const struct Curl_OID *searchOID(const char *oid) static CURLcode bool2str(struct dynbuf *store, const char *beg, const char *end) { + uint8_t val; if(end - beg != 1) return CURLE_BAD_FUNCTION_ARGUMENT; - return curlx_dyn_add(store, *beg ? "TRUE" : "FALSE"); + val = (uint8_t)*beg; + if((val != 0xff) && (val != 0x00)) + return CURLE_BAD_FUNCTION_ARGUMENT; + return curlx_dyn_add(store, val ? "TRUE" : "FALSE"); } /* @@ -290,7 +259,12 @@ static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end) { /* Convert an ASN.1 bit string to a printable string. */ - if(++beg > end) + if(beg < end) { + uint8_t unused_bits = (uint8_t)*beg++; + if(unused_bits > 7) + return CURLE_BAD_FUNCTION_ARGUMENT; + } + else return CURLE_BAD_FUNCTION_ARGUMENT; return octet2str(store, beg, end); } @@ -302,8 +276,11 @@ static CURLcode bit2str(struct dynbuf *store, const char *beg, const char *end) */ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end) { - unsigned int val = 0; + uint32_t uval = 0; + int32_t sval = 0; size_t n = end - beg; + size_t i; + const unsigned char *p = (const unsigned char *)beg; if(!n) return CURLE_BAD_FUNCTION_ARGUMENT; @@ -311,14 +288,20 @@ static CURLcode int2str(struct dynbuf *store, const char *beg, const char *end) if(n > 4) return octet2str(store, beg, end); - /* Represent integers <= 32-bit as a single value. */ - if(*beg & 0x80) - val = ~val; + if(p[0] & 0x80) + uval = ~0U; - do - val = (val << 8) | *(const unsigned char *)beg++; - while(beg < end); - return curlx_dyn_addf(store, "%s%x", val >= 10 ? "0x" : "", val); + for(i = 0; i < n; i++) + uval = (uval << 8) | p[i]; + + sval = (int32_t)uval; + + /* small values are easier to read as decimals */ + if(sval > -10000 && sval < 10000) + return curlx_dyn_addf(store, "%d", sval); + + /* represent larger values as hex */ + return curlx_dyn_addf(store, "0x%x", uval); } /* @@ -618,8 +601,6 @@ static CURLcode UTime2str(struct dynbuf *store, tzp = "GMT"; end = tzp + 3; } - else - tzp++; tzl = end - tzp; return curlx_dyn_addf(store, "%u%.2s-%.2s-%.2s %.2s:%.2s:%.2s %.*s", @@ -633,16 +614,16 @@ static CURLcode UTime2str(struct dynbuf *store, * * Return error */ -static CURLcode ASN1tostr(struct dynbuf *store, - struct Curl_asn1Element *elem, int type) +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem); +UNITTEST CURLcode ASN1tostr(struct dynbuf *store, + struct Curl_asn1Element *elem) { CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT; + int type = elem->tag; if(elem->constructed) return result; /* No conversion of structured elements. */ - if(!type) - type = elem->tag; /* Type not forced: use element tag as type. */ - switch(type) { case CURL_ASN1_BOOLEAN: result = bool2str(store, elem->beg, elem->end); @@ -726,7 +707,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) goto error; } curlx_dyn_reset(&temp); - result = ASN1tostr(&temp, &oid, 0); + result = ASN1tostr(&temp, &oid); if(result) goto error; @@ -761,7 +742,7 @@ static CURLcode encodeDN(struct dynbuf *store, struct Curl_asn1Element *dn) goto error; /* Generate value. */ - result = ASN1tostr(store, &value, 0); + result = ASN1tostr(store, &value); if(result) goto error; curlx_dyn_reset(&temp); @@ -970,7 +951,7 @@ static CURLcode do_pubkey_field(struct Curl_easy *data, int certnum, /* Generate a certificate information record for the public key. */ - result = ASN1tostr(&out, elem, 0); + result = ASN1tostr(&out, elem); if(!result) { if(data->set.ssl.certinfo) result = ssl_push_certinfo_dyn(data, certnum, label, &out); @@ -1158,7 +1139,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, } /* Serial number. */ - result = ASN1tostr(&out, &cert.serialNumber, 0); + result = ASN1tostr(&out, &cert.serialNumber); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1181,7 +1162,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Start Date. */ - result = ASN1tostr(&out, &cert.notBefore, 0); + result = ASN1tostr(&out, &cert.notBefore); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1192,7 +1173,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Expire Date. */ - result = ASN1tostr(&out, &cert.notAfter, 0); + result = ASN1tostr(&out, &cert.notAfter); if(result) goto done; if(data->set.ssl.certinfo) { @@ -1223,7 +1204,7 @@ CURLcode Curl_extract_certinfo(struct Curl_easy *data, curlx_dyn_reset(&out); /* Signature. */ - result = ASN1tostr(&out, &cert.signature, 0); + result = ASN1tostr(&out, &cert.signature); if(result) goto done; if(data->set.ssl.certinfo) { diff --git a/lib/vtls/x509asn1.h b/lib/vtls/x509asn1.h index 61ea452bcd..dda55ac77f 100644 --- a/lib/vtls/x509asn1.h +++ b/lib/vtls/x509asn1.h @@ -26,6 +26,7 @@ ***************************************************************************/ #include "curl_setup.h" +/* forward-declare to make it visible in all builds */ struct Curl_asn1Element; #if defined(USE_GNUTLS) || defined(USE_WOLFSSL) || defined(USE_SCHANNEL) || \ @@ -34,6 +35,42 @@ struct Curl_asn1Element; #include "cfilters.h" #include "urldata.h" +/* ASN.1 classes. */ +/* #define CURL_ASN1_UNIVERSAL 0 */ +/* #define CURL_ASN1_APPLICATION 1 */ +/* #define CURL_ASN1_CONTEXT_SPECIFIC 2 */ +/* #define CURL_ASN1_PRIVATE 3 */ + +/* ASN.1 types. */ +#define CURL_ASN1_BOOLEAN 1 +#define CURL_ASN1_INTEGER 2 +#define CURL_ASN1_BIT_STRING 3 +#define CURL_ASN1_OCTET_STRING 4 +#define CURL_ASN1_NULL 5 +#define CURL_ASN1_OBJECT_IDENTIFIER 6 +/* #define CURL_ASN1_OBJECT_DESCRIPTOR 7 */ +/* #define CURL_ASN1_INSTANCE_OF 8 */ +/* #define CURL_ASN1_REAL 9 */ +#define CURL_ASN1_ENUMERATED 10 +/* #define CURL_ASN1_EMBEDDED 11 */ +#define CURL_ASN1_UTF8_STRING 12 +/* #define CURL_ASN1_RELATIVE_OID 13 */ +/* #define CURL_ASN1_SEQUENCE 16 */ +/* #define CURL_ASN1_SET 17 */ +#define CURL_ASN1_NUMERIC_STRING 18 +#define CURL_ASN1_PRINTABLE_STRING 19 +#define CURL_ASN1_TELETEX_STRING 20 +/* #define CURL_ASN1_VIDEOTEX_STRING 21 */ +#define CURL_ASN1_IA5_STRING 22 +#define CURL_ASN1_UTC_TIME 23 +#define CURL_ASN1_GENERALIZED_TIME 24 +/* #define CURL_ASN1_GRAPHIC_STRING 25 */ +#define CURL_ASN1_VISIBLE_STRING 26 +/* #define CURL_ASN1_GENERAL_STRING 27 */ +#define CURL_ASN1_UNIVERSAL_STRING 28 +/* #define CURL_ASN1_CHARACTER_STRING 29 */ +#define CURL_ASN1_BMP_STRING 30 + /* * Types. */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index ffb1d38250..d54f108631 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -222,7 +222,7 @@ test1640 test1641 test1642 test1643 \ \ test1650 test1651 test1652 test1653 test1654 test1655 test1656 test1657 \ test1658 test1659 test1660 test1661 test1662 test1663 test1664 test1665 \ -test1666 \ +test1666 test1667 \ \ test1670 test1671 \ \ diff --git a/tests/data/test1667 b/tests/data/test1667 new file mode 100644 index 0000000000..76a1daa5a9 --- /dev/null +++ b/tests/data/test1667 @@ -0,0 +1,20 @@ + + + + +unittest +ASN1tostr +x509asn1 + + + +# Client-side + + +unittest + + +ASN1tostr unit tests + + + diff --git a/tests/unit/Makefile.inc b/tests/unit/Makefile.inc index c03658261c..91b380a037 100644 --- a/tests/unit/Makefile.inc +++ b/tests/unit/Makefile.inc @@ -41,7 +41,7 @@ TESTS_C = \ unit1636.c \ unit1650.c unit1651.c unit1652.c unit1653.c unit1654.c unit1655.c unit1656.c \ unit1657.c unit1658.c unit1660.c unit1661.c unit1663.c unit1664.c \ - unit1666.c \ + unit1666.c unit1667.c \ unit1979.c unit1980.c \ unit2600.c unit2601.c unit2602.c unit2603.c unit2604.c unit2605.c \ unit3200.c unit3205.c \ diff --git a/tests/unit/unit1667.c b/tests/unit/unit1667.c new file mode 100644 index 0000000000..070b5f75de --- /dev/null +++ b/tests/unit/unit1667.c @@ -0,0 +1,339 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at https://curl.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * SPDX-License-Identifier: curl + * + ***************************************************************************/ +#include "unitcheck.h" + +#if defined(USE_GNUTLS) || defined(USE_SCHANNEL) || defined(USE_MBEDTLS) || \ + defined(USE_RUSTLS) + +#include "vtls/x509asn1.h" +#include "vtls/vtls.h" + +struct test_1667 { + unsigned char tag; + const char *asn1; + const size_t size; + const char *out; + CURLcode result_exp; +}; + +/* the size of the object needs to deduct the null terminator */ +#define OID(x) x, sizeof(x) - 1 + +static bool test1667(const struct test_1667 *spec, size_t i, + struct dynbuf *dbuf) +{ + CURLcode result; + bool ok = TRUE; + struct Curl_asn1Element elem; + + curlx_dyn_reset(dbuf); + + /* setup private struct for this invoke */ + elem.tag = spec->tag; + elem.header = NULL; + elem.beg = spec->asn1; + elem.end = elem.beg + spec->size; + elem.eclass = 0; + elem.constructed = 0; + + result = ASN1tostr(dbuf, &elem); + if(result != spec->result_exp) { + curl_mfprintf(stderr, "test %zu (type %u): expect result %d, got %d\n", + i, spec->tag, spec->result_exp, result); + if(!spec->result_exp) { + curl_mfprintf(stderr, "test %zu: expected output '%s'\n", + i, spec->out); + } + ok = FALSE; + } + else if(!result) { + /* use strlen on the pointer instead of curlx_dyn_len() because for some + of these type, the code explicitly adds a null terminator which is then + counted as buffer size. */ + size_t actual_len = strlen(curlx_dyn_ptr(dbuf)); + if(strlen(spec->out) != actual_len) { + curl_mfprintf(stderr, + "test %zu (type %u): " + "unexpected length. Got %zu, expected %zu)\n", + i, spec->tag, actual_len, strlen(spec->out)); + ok = FALSE; + } + if(strcmp(spec->out, curlx_dyn_ptr(dbuf))) { + size_t loop; + const uint8_t *data = curlx_dyn_uptr(dbuf); + const size_t len = curlx_dyn_len(dbuf); + curl_mfprintf(stderr, + "test %zu (type %u): " + "expected output '%s', got '%s' (length %zu)\n", + i, spec->tag, spec->out, data, len); + for(loop = 0; loop < len; loop++) + curl_mfprintf(stderr, "test %zu: index %zu byte %02x\n", + i, loop, data[loop]); + ok = FALSE; + } + } + + return ok; +} + +static CURLcode test_unit1667(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + + static const struct test_1667 test_specs[] = { + /* unsupported type > CURL_ASN1_BMP_STRING (30) */ + { 31, "abcde", 5, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { 99, "abcde", 5, "", CURLE_BAD_FUNCTION_ARGUMENT }, + + /* + (many different) strings: + + CURL_ASN1_UTF8_STRING + CURL_ASN1_NUMERIC_STRING + CURL_ASN1_PRINTABLE_STRING + CURL_ASN1_TELETEX_STRING + CURL_ASN1_IA5_STRING + CURL_ASN1_VISIBLE_STRING + CURL_ASN1_UNIVERSAL_STRING + CURL_ASN1_BMP_STRING + */ + { CURL_ASN1_UTF8_STRING, "abcde", 5, "abcde", CURLE_OK }, + /* a with ring, a with umlaut, o with umlaut in UTF-8 encoding */ + { CURL_ASN1_UTF8_STRING, "\xc3\xa5\xc3\xa4\xc3\xb6", 6, + "\xc3\xa5\xc3\xa4\xc3\xb6", CURLE_OK }, + { CURL_ASN1_NUMERIC_STRING, "abcde", 5, "abcde", CURLE_OK }, + { CURL_ASN1_PRINTABLE_STRING, "abcde", 5, "abcde", CURLE_OK }, + { CURL_ASN1_TELETEX_STRING, "abcde", 5, "abcde", CURLE_OK }, + { CURL_ASN1_IA5_STRING, "abcde", 5, "abcde", CURLE_OK }, + { CURL_ASN1_VISIBLE_STRING, "abcde", 5, "abcde", CURLE_OK }, + { CURL_ASN1_UNIVERSAL_STRING, "abcde", 5, "abcde", + CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_UNIVERSAL_STRING, "abcd", 4, "abcd", + CURLE_WEIRD_SERVER_REPLY }, + { CURL_ASN1_UNIVERSAL_STRING, "\x00\x00\x12\x34", 4, "\xe1\x88\xb4", + CURLE_OK }, + { CURL_ASN1_BMP_STRING, "abcde", 5, "abcde", CURLE_BAD_FUNCTION_ARGUMENT }, + + /* Generalized Time */ + /* GTime2str() is tested separately in unit test 1656 */ + { CURL_ASN1_GENERALIZED_TIME, "19851106210627.3", 16, + "1985-11-06 21:06:27.3", CURLE_OK }, + { CURL_ASN1_GENERALIZED_TIME, "19851106210627.3Z", 17, + "1985-11-06 21:06:27.3 GMT", CURLE_OK }, + { CURL_ASN1_GENERALIZED_TIME, "19851106210627.3-0500", 21, + "1985-11-06 21:06:27.3 UTC-0500", CURLE_OK }, + /* Generalized Time: Invalid month (13). Still fine! */ + { CURL_ASN1_GENERALIZED_TIME, "20231301000000Z", 15, + "2023-13-01 00:00:00 GMT", CURLE_OK }, + /* Generalized Time: Valid millisecond precision */ + { CURL_ASN1_GENERALIZED_TIME, "20230101000000.123Z", 19, + "2023-01-01 00:00:00.123 GMT", CURLE_OK }, + + /* UTC Time */ + { CURL_ASN1_UTC_TIME, "991231235959Z", 13, "1999-12-31 23:59:59 GMT", + CURLE_OK }, + { CURL_ASN1_UTC_TIME, "991231235959+0200", 17, "1999-12-31 23:59:59 +0200", + CURLE_OK }, + { CURL_ASN1_UTC_TIME, "991231235959-0200", 17, "1999-12-31 23:59:59 -0200", + CURLE_OK }, + { CURL_ASN1_UTC_TIME, "991231235959+999", 16, "1999-12-31 23:59:59 +999", + CURLE_OK }, + /* Leap year check (Feb 29, 2024) */ + { CURL_ASN1_UTC_TIME, "240229120000Z", 13, "2024-02-29 12:00:00 GMT", + CURLE_OK }, + /* Century roll-over (00-49 is 20xx, 50-99 is 19xx) */ + { CURL_ASN1_UTC_TIME, "491231235959Z", 13, "2049-12-31 23:59:59 GMT", + CURLE_OK }, + { CURL_ASN1_UTC_TIME, "500101000000Z", 13, "1950-01-01 00:00:00 GMT", + CURLE_OK }, + + /* Object Identifier (encodeOID() is tested in unit test 1666) */ + + /* 1.2.840.10040.4.1 */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x2A\x86\x48\xCE\x38\x04\x01", 7, + "dsa", CURLE_OK }, + /* 1.2.840.113549.1.1.13 */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x2A\x86\x48\x86\xF7\x0D\x01\x01\x0D", 9, + "sha512WithRSAEncryption", CURLE_OK }, + /* 1.2.840.10040.4.2 */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x2A\x86\x48\xCE\x38\x04\x02", 7, + "1.2.840.10040.4.2", CURLE_OK }, + /* 2.16.840.1.101.3.4.2.3 */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x60\x86\x48\x01\x65\x03\x04\x02\x03", 9, + "sha512", CURLE_OK }, + /* 2.999 -> (2*40) + 999 = 1079. 1079 in VLQ is 0x88 0x37 */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x88\x37\x03", 3, "2.999.3", + CURLE_OK }, + /* 1.0 (Minimum possible OID length/value) + (1*40) + 0 = 40 (0x28) */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x28", 1, "1.0", CURLE_OK }, + /* Malformed (Incomplete multi-byte SID, MSB set but no following byte) */ + { CURL_ASN1_OBJECT_IDENTIFIER, "\x88", 1, "", + CURLE_BAD_FUNCTION_ARGUMENT }, + + /* NULL */ + { CURL_ASN1_NULL, "", 0, "", CURLE_OK }, + { CURL_ASN1_NULL, "a", 1, "", CURLE_OK }, + { CURL_ASN1_NULL, "aa", 2, "", CURLE_OK }, + + /* Octet string */ + { CURL_ASN1_OCTET_STRING, "\x00\x00\x00\x04\x05", 5, "00:00:00:04:05:", + CURLE_OK }, + { CURL_ASN1_OCTET_STRING, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff", 14, "", CURLE_TOO_LARGE}, + + /* Bit string. The first byte is number of unused bits. */ + { CURL_ASN1_BIT_STRING, "\x00\x55", 2, "55:", CURLE_OK }, + /* Invalid number of unused bits (> 7) */ + { CURL_ASN1_BIT_STRING, "\x08\x55", 2, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_BIT_STRING, "\x00\xaa\x55", 3, "aa:55:", CURLE_OK }, + { CURL_ASN1_BIT_STRING, "\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff", 15, "", CURLE_TOO_LARGE}, + /* 3 unused bits, data 0xF0 (11110000) + The '0' at the end of 11110... are the unused bits. */ + { CURL_ASN1_BIT_STRING, "\x03\xf0", 2, "f0:", CURLE_OK }, + + /* Integer */ + { CURL_ASN1_INTEGER, "\x00", 1, "0", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x01", 1, "1", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x09", 1, "9", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x0a", 1, "10", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xb", 1, "11", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x11", 1, "17", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x7f", 1, "127", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x80", 1, "-128", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x81", 1, "-127", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x82", 1, "-126", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff", 1, "-1", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x27\x0f", 2, "9999", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x27\x10", 2, "0x2710", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x7f\x81", 2, "0x7f81", CURLE_OK }, + + /* sign-extended, would be -32385 in decimal */ + { CURL_ASN1_INTEGER, "\x81\x7f", 2, "0xffff817f", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\xff", 2, "-1", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x00\x00", 2, "0", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x01\x02\x03", 3, "0x10203", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\x02\xff", 3, "0xffff02ff", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x00\xff\x00", 3, "0xff00", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x01\x02\x03\x04", 4, "0x1020304", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\x02\x03\x04", 4, "0xff020304", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x00\x00\x00\x04", 4, "4", CURLE_OK }, + { CURL_ASN1_INTEGER, "\x00\x00\x00\x04\x05", 5, "00:00:00:04:05:", + CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\x00\x00\x04\x05", 5, "ff:00:00:04:05:", + CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", + 11, "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff", 12, "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:", CURLE_OK }, + { CURL_ASN1_INTEGER, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff", 14, "", CURLE_TOO_LARGE}, + { CURL_ASN1_INTEGER, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff", 15, "", CURLE_TOO_LARGE }, + { CURL_ASN1_INTEGER, "", 0, "", CURLE_BAD_FUNCTION_ARGUMENT }, + /* Leading zero required if the MSB of the next byte is 1 (to keep it + positive) */ + { CURL_ASN1_INTEGER, "\x00\x80", 2, "128", CURLE_OK }, + + /* Enumerated works the same as Integer */ + { CURL_ASN1_ENUMERATED, "\x00", 1, "0", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x01", 1, "1", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x09", 1, "9", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x0a", 1, "10", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xb", 1, "11", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x11", 1, "17", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x7f", 1, "127", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x80", 1, "-128", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff", 1, "-1", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x7f\x81", 2, "0x7f81", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\xff", 2, "-1", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x00\x00", 2, "0", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x01\x02\x03", 3, "0x10203", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\x02\xff", 3, "0xffff02ff", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x00\xff\x00", 3, "0xff00", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x01\x02\x03\x04", 4, "0x1020304", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\x02\x03\x04", 4, "0xff020304", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x00\x00\x00\x04", 4, "4", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\x00\x00\x00\x04\x05", 5, "00:00:00:04:05:", + CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\x00\x00\x04\x05", 5, "ff:00:00:04:05:", + CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", + 11, "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff", 12, "ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:ff:", CURLE_OK }, + { CURL_ASN1_ENUMERATED, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff", 14, "", CURLE_TOO_LARGE}, + { CURL_ASN1_ENUMERATED, "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff" + "\xff\xff\xff", 15, "", CURLE_TOO_LARGE }, + { CURL_ASN1_ENUMERATED, "", 0, "", CURLE_BAD_FUNCTION_ARGUMENT }, + + /* Boolean */ + { CURL_ASN1_BOOLEAN, "\xff", 1, "TRUE", CURLE_OK }, + { CURL_ASN1_BOOLEAN, "\x01", 1, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_BOOLEAN, "\x02", 1, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_BOOLEAN, "\x00", 1, "FALSE", CURLE_OK }, + { CURL_ASN1_BOOLEAN, "\x01\x01", 2, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_BOOLEAN, "\x00\x01", 2, "", CURLE_BAD_FUNCTION_ARGUMENT }, + { CURL_ASN1_BOOLEAN, "", 0, "", CURLE_BAD_FUNCTION_ARGUMENT }, + }; + + size_t i; + struct dynbuf dbuf; + bool all_ok = TRUE; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + curl_mfprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + /* the real code uses CURL_X509_STR_MAX for maximum size, but we set a + smaller one here so that we can test running into the limit a little + easier */ + curlx_dyn_init(&dbuf, 40); + for(i = 0; i < CURL_ARRAYSIZE(test_specs); ++i) { + if(!test1667(&test_specs[i], i, &dbuf)) + all_ok = FALSE; + } + fail_unless(all_ok, "some tests of ASN1tostr() failed"); + + curlx_dyn_free(&dbuf); + curl_global_cleanup(); + + UNITTEST_END_SIMPLE +} + +#undef OID + +#else + +static CURLcode test_unit1667(const char *arg) +{ + UNITTEST_BEGIN_SIMPLE + puts("not tested since ASN1tostr() is not built in"); + UNITTEST_END_SIMPLE +} + +#endif