| @@ -0,0 +1,339 @@ |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 |