Files
openssl/test/x509_internal_test.c
Daniel Kubec fed15f076f ASN1: Reject negative BIGNUM components
In the ASN.1 structures we define the BIGNUM as positive and enforce
this during parsing. If the encoded value is negative, we raise an error
and reject the material.

Fixes #29210
Fixes #27407

Reviewed-by: Richard Levitte <levitte@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/29370)
2025-12-12 15:02:06 +01:00

538 lines
18 KiB
C

/*
* Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/* Internal tests for the x509 and x509v3 modules */
#include <stdio.h>
#include <string.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/x509_vfy.h>
#include "testutil.h"
#include "internal/nelem.h"
#include "crypto/x509.h"
/**********************************************************************
*
* Test of x509v3
*
***/
#include "../crypto/x509/ext_dat.h"
#include "../crypto/x509/standard_exts.h"
static int test_standard_exts(void)
{
size_t i;
int prev = -1, good = 1;
const X509V3_EXT_METHOD *const *tmp;
tmp = standard_exts;
for (i = 0; i < OSSL_NELEM(standard_exts); i++, tmp++) {
if ((*tmp)->ext_nid < prev)
good = 0;
prev = (*tmp)->ext_nid;
}
if (!good) {
tmp = standard_exts;
TEST_error("Extensions out of order!");
for (i = 0; i < STANDARD_EXTENSION_COUNT; i++, tmp++)
TEST_note("%d : %s", (*tmp)->ext_nid, OBJ_nid2sn((*tmp)->ext_nid));
}
return good;
}
typedef struct {
const char *ipasc;
const char *data;
int length;
} IP_TESTDATA;
static IP_TESTDATA a2i_ipaddress_tests[] = {
{ "127.0.0.1", "\x7f\x00\x00\x01", 4 },
{ "1.2.3.4", "\x01\x02\x03\x04", 4 },
{ "1.2.3.255", "\x01\x02\x03\xff", 4 },
{ "255.255.255.255", "\xff\xff\xff\xff", 4 },
{ "::", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16 },
{ "::1", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16 },
{ "::01", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16 },
{ "::0001", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16 },
{ "ffff::", "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16 },
{ "ffff::1", "\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01", 16 },
{ "1::2", "\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02", 16 },
{ "1:1:1:1:1:1:1:1", "\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01\x00\x01", 16 },
{ "2001:db8::ff00:42:8329", "\x20\x01\x0d\xb8\x00\x00\x00\x00\x00\x00\xff\x00\x00\x42\x83\x29", 16 },
{ "::1.2.3.4", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x02\x03\x04", 16 },
{ "ffff:ffff:ffff:ffff:ffff:ffff:1.2.3.4", "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x02\x03\x04", 16 },
{ "1:1:1:1:1:1:1:1.test", NULL, 0 },
{ ":::1", NULL, 0 },
{ "2001::123g", NULL, 0 },
/* Too few IPv4 components. */
{ "1", NULL, 0 },
{ "1.", NULL, 0 },
{ "1.2", NULL, 0 },
{ "1.2.", NULL, 0 },
{ "1.2.3", NULL, 0 },
{ "1.2.3.", NULL, 0 },
/* Invalid embedded IPv4 address. */
{ "::1.2.3", NULL, 0 },
/* IPv4 literals take the place of two IPv6 components. */
{ "1:2:3:4:5:6:7:1.2.3.4", NULL, 0 },
/* '::' should have fewer than 16 components or it is redundant. */
{ "1:2:3:4:5:6:7::8", NULL, 0 },
/* Embedded IPv4 addresses must be at the end. */
{ "::1.2.3.4:1", NULL, 0 },
/* Too many components. */
{ "1.2.3.4.5", NULL, 0 },
{ "1:2:3:4:5:6:7:8:9", NULL, 0 },
{ "1:2:3:4:5::6:7:8:9", NULL, 0 },
/* Stray whitespace or other invalid characters. */
{ "1.2.3.4 ", NULL, 0 },
{ "1.2.3 .4", NULL, 0 },
{ "1.2.3. 4", NULL, 0 },
{ " 1.2.3.4", NULL, 0 },
{ "1.2.3.4.", NULL, 0 },
{ "1.2.3.+4", NULL, 0 },
{ "1.2.3.-4", NULL, 0 },
{ "1.2.3.4.example.test", NULL, 0 },
{ "::1 ", NULL, 0 },
{ " ::1", NULL, 0 },
{ ":: 1", NULL, 0 },
{ ": :1", NULL, 0 },
{ "1.2.3.nope", NULL, 0 },
{ "::nope", NULL, 0 },
/* Components too large. */
{ "1.2.3.256", NULL, 0 }, /* Overflows when adding */
{ "1.2.3.260", NULL, 0 }, /* Overflows when multiplying by 10 */
{ "1.2.3.999999999999999999999999999999999999999999", NULL, 0 },
{ "::fffff", NULL, 0 },
/* Although not an overflow, more than four hex digits is an error. */
{ "::00000", NULL, 0 },
/* Too many colons. */
{ ":::", NULL, 0 },
{ "1:::", NULL, 0 },
{ ":::2", NULL, 0 },
{ "1:::2", NULL, 0 },
/* Only one group of zeros may be elided. */
{ "1::2::3", NULL, 0 },
/* We only support decimal. */
{ "1.2.3.01", NULL, 0 },
{ "1.2.3.0x1", NULL, 0 },
/* Random garbage. */
{ "example.test", NULL, 0 },
{ "", NULL, 0 },
{ " 1.2.3.4", NULL, 0 },
{ " 1.2.3.4 ", NULL, 0 },
{ "1.2.3.4.example.test", NULL, 0 },
};
static int test_a2i_ipaddress(int idx)
{
int good = 1;
ASN1_OCTET_STRING *ip;
int len = a2i_ipaddress_tests[idx].length;
ip = a2i_IPADDRESS(a2i_ipaddress_tests[idx].ipasc);
if (len == 0) {
if (!TEST_ptr_null(ip)) {
good = 0;
TEST_note("'%s' should not be parsed as IP address", a2i_ipaddress_tests[idx].ipasc);
}
} else {
if (!TEST_ptr(ip)
|| !TEST_int_eq(ASN1_STRING_length(ip), len)
|| !TEST_mem_eq(ASN1_STRING_get0_data(ip), len,
a2i_ipaddress_tests[idx].data, len)) {
good = 0;
}
}
ASN1_OCTET_STRING_free(ip);
return good;
}
static int ck_purp(ossl_unused const X509_PURPOSE *purpose,
ossl_unused const X509 *x, int ca)
{
return 1;
}
static int tests_X509_PURPOSE(void)
{
OSSL_LIB_CTX *libctx = NULL;
int id, idx, *p;
X509_PURPOSE *xp;
#undef LN
#define LN "LN_test"
#undef SN
#define SN "SN_test"
#undef ARGS
#define ARGS(id, sn) id, X509_TRUST_MAX, 0, ck_purp, LN, sn, NULL
return TEST_int_gt((id = X509_PURPOSE_get_unused_id(libctx)), X509_PURPOSE_MAX)
&& TEST_int_eq(X509_PURPOSE_get_count() + 1, id)
&& TEST_int_eq(X509_PURPOSE_get_by_id(id), -1)
&& TEST_int_eq(X509_PURPOSE_get_by_sname(SN), -1)
/* add new entry with fresh id and fresh sname: */
&& TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN)), 1)
&& TEST_int_ne((idx = X509_PURPOSE_get_by_sname(SN)), -1)
&& TEST_int_eq(X509_PURPOSE_get_by_id(id), idx)
/* overwrite same entry, should be idempotent: */
&& TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN)), 1)
&& TEST_int_eq(X509_PURPOSE_get_by_sname(SN), idx)
&& TEST_int_eq(X509_PURPOSE_get_by_id(id), idx)
/* fail adding entry with same sname but existing conflicting id: */
&& TEST_int_eq(X509_PURPOSE_add(ARGS(X509_PURPOSE_MAX, SN)), 0)
/* fail adding entry with same existing id but conflicting sname: */
&& TEST_int_eq(X509_PURPOSE_add(ARGS(id, SN "_different")), 0)
&& TEST_ptr((xp = X509_PURPOSE_get0(idx)))
&& TEST_int_eq(X509_PURPOSE_get_id(xp), id)
&& TEST_str_eq(X509_PURPOSE_get0_name(xp), LN)
&& TEST_str_eq(X509_PURPOSE_get0_sname(xp), SN)
&& TEST_int_eq(X509_PURPOSE_get_trust(xp), X509_TRUST_MAX)
&& TEST_int_eq(*(p = &xp->purpose), id)
&& TEST_int_eq(X509_PURPOSE_set(p, X509_PURPOSE_DEFAULT_ANY), 1)
&& TEST_int_eq(X509_PURPOSE_get_id(xp), X509_PURPOSE_DEFAULT_ANY);
}
/* 0000-01-01 00:00:00 UTC */
#define MIN_CERT_TIME INT64_C(-62167219200)
/* 9999-12-31 23:59:59 UTC */
#define MAX_CERT_TIME INT64_C(253402300799)
/* 1950-01-01 00:00:00 UTC */
#define MIN_UTC_TIME INT64_C(-631152000)
/* 2049-12-31 23:59:59 UTC */
#define MAX_UTC_TIME INT64_C(2524607999)
typedef struct {
int64_t NotBefore;
int64_t NotAfter;
} CERT_TEST_DATA;
/* clang-format off */
static CERT_TEST_DATA cert_test_data[] = {
{ 0, 0 },
{ 0, 1 },
{ 1, 1 },
{ -1, 0 },
{ -1, 1 },
{ 1442939232, 1443004020 },
{ 0, INT32_MAX },
{ INT32_MIN, 0 },
{ INT32_MIN, INT32_MAX },
{ 0, UINT32_MAX },
{ MIN_UTC_TIME, 0 },
{ MIN_UTC_TIME - 1, 0 },
{ 0, MAX_UTC_TIME },
{ 0, MAX_UTC_TIME + 1 },
{ MIN_UTC_TIME, MAX_UTC_TIME},
{ MIN_UTC_TIME - 1, MAX_UTC_TIME + 1 },
{ MIN_CERT_TIME, MAX_CERT_TIME },
{ MIN_CERT_TIME, MAX_CERT_TIME - 1 },
{ MIN_CERT_TIME + 1, MAX_CERT_TIME },
{ MIN_CERT_TIME + 1, MAX_CERT_TIME - 1 },
{ 0, MAX_CERT_TIME },
{ 0, MAX_CERT_TIME - 1 }
};
/* clang-format on */
/* Returns 0 for success, 1 if failed */
static int test_a_time(X509_STORE_CTX *ctx, X509 *x509,
X509_CRL *crl,
const int64_t test_time,
int64_t notBefore, int64_t notAfter,
const char *file, const int line)
{
int expected_value, expected_crl_value, error, expected_error;
X509_VERIFY_PARAM *vpm;
expected_value = notBefore <= test_time;
if (expected_value)
expected_value = notAfter == MAX_CERT_TIME || notAfter >= test_time;
expected_crl_value = notBefore <= test_time;
if (expected_crl_value)
expected_crl_value = notAfter >= test_time;
if (notBefore > test_time)
expected_error = X509_V_ERR_CERT_NOT_YET_VALID;
else if (notAfter < test_time && notAfter != MAX_CERT_TIME)
expected_error = X509_V_ERR_CERT_HAS_EXPIRED;
else
expected_error = 0;
vpm = X509_STORE_CTX_get0_param(ctx);
ossl_x509_verify_param_set_time_posix(vpm, test_time);
if (ossl_x509_check_cert_time(ctx, x509, 0) != expected_value) {
TEST_info("%s:%d - ossl_X509_check_cert_time %s unexpectedly when "
"verifying notBefore %lld, notAfter %lld at time %lld\n",
file, line,
expected_value ? "failed" : "succeeded",
(long long)notBefore, (long long)notAfter,
(long long)test_time);
return 1;
}
if (ossl_x509_check_crl_time(ctx, crl, 0) != expected_crl_value) {
TEST_info("%s:%d - ossl_X509_check_crl_time %s unexpectedly when "
"verifying lastUpdate %lld, nextUpdate %lld at time %lld\n",
file, line,
expected_value ? "failed" : "succeeded",
(long long)notBefore, (long long)notAfter,
(long long)test_time);
return 1;
}
error = 0;
if (ossl_x509_check_certificate_times(vpm, x509, &error) != expected_value) {
TEST_info("%s:%d - ossl_X509_check_certificate_times %s unexpectedly "
"when verifying notBefore %lld, notAfter %lld at time %lld\n",
file, line,
expected_value ? "failed" : "succeeded",
(long long)notBefore, (long long)notAfter,
(long long)test_time);
return 1;
}
if (error != expected_error) {
TEST_info("%s:%d - ossl_X509_check_certificate_times error return was "
"%d, expected %d when verifying notBefore %lld, notAfter "
"%lld at time %lld\n",
file, line,
error, expected_error,
(long long)notBefore, (long long)notAfter,
(long long)test_time);
return 1;
}
return 0;
}
static int do_x509_time_tests(CERT_TEST_DATA *tests, size_t ntests)
{
int ret = 0;
int failures = 0;
X509 *x509 = NULL;
X509_CRL *crl = NULL;
X509_STORE_CTX *ctx = NULL;
X509_VERIFY_PARAM *vpm = NULL;
ASN1_TIME *nb = NULL, *na = NULL;
size_t i;
if (!TEST_ptr(x509 = X509_new())) {
TEST_info("Malloc failed");
goto err;
}
if (!TEST_ptr(crl = X509_CRL_new())) {
TEST_info("Malloc failed");
goto err;
}
if (!TEST_ptr(ctx = X509_STORE_CTX_new())) {
TEST_info("Malloc failed");
goto err;
}
X509_STORE_CTX_init(ctx, NULL, NULL, NULL);
if (!TEST_ptr(vpm = X509_VERIFY_PARAM_new())) {
TEST_info("Malloc failed");
goto err;
}
X509_STORE_CTX_set0_param(ctx, vpm);
if (!TEST_ptr(nb = ASN1_TIME_new())) {
TEST_info("Malloc failed");
goto err;
}
if (!TEST_ptr(na = ASN1_TIME_new())) {
TEST_info("Malloc failed");
goto err;
}
for (i = 0; i < ntests; i++) {
int64_t test_time;
if (!TEST_true(ossl_posix_to_asn1_time(tests[i].NotBefore, &nb))) {
TEST_info("Could not create NotBefore for time %lld\n", (long long)tests[i].NotBefore);
goto err;
}
if (!TEST_true(ossl_posix_to_asn1_time(tests[i].NotAfter, &na))) {
TEST_info("Could not create NotAfter for time %lld\n", (long long)tests[i].NotBefore);
goto err;
}
/* Forcibly jam the times into the X509 */
if (!TEST_true(X509_set1_notBefore(x509, nb)))
goto err;
if (!TEST_true(X509_set1_notAfter(x509, na)))
TEST_info("X509_set1_notAftere failed");
/* Forcibly jam the times into the CRL */
if (!TEST_true(X509_CRL_set1_lastUpdate(crl, nb)))
goto err;
if (!TEST_true(X509_CRL_set1_nextUpdate(crl, na)))
goto err;
/* Test boundaries of NotBefore */
test_time = tests[i].NotBefore - 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = tests[i].NotBefore;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = tests[i].NotBefore + 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
/* Test boundaries of NotAfter */
test_time = tests[i].NotAfter - 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = tests[i].NotAfter;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = tests[i].NotAfter + 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = 1442939232;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = 1443004020;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = MIN_UTC_TIME;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = MIN_UTC_TIME - 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = MAX_UTC_TIME;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = MAX_UTC_TIME + 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
/* Test integer value boundaries */
test_time = INT64_MIN;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = INT32_MIN;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = -1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = 0;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = 1;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = INT32_MAX;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = UINT32_MAX;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
test_time = INT64_MAX;
failures += test_a_time(ctx, x509, crl, test_time, tests[i].NotBefore,
tests[i].NotAfter,
__FILE__, __LINE__);
}
ret = (failures == 0);
err:
X509_STORE_CTX_free(ctx);
X509_free(x509);
X509_CRL_free(crl);
ASN1_STRING_free(nb);
ASN1_STRING_free(na);
return ret;
}
static int tests_X509_check_time(void)
{
return do_x509_time_tests(cert_test_data, sizeof(cert_test_data) / sizeof(CERT_TEST_DATA));
}
static const char *kRSAModulusNeg[] = {
"-----BEGIN CERTIFICATE-----\n",
"MIIByjCCAXSgAwIBAgIQBjdsAKoAZIoRz7jUqlw19DANBgkqhkiG9w0BAQQFADAW\n",
"MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw05NjA1MjgyMjAyNTlaFw0zOTEyMzEy\n",
"MzU5NTlaMBYxFDASBgNVBAMTC1Jvb3QgQWdlbmN5MFswDQYJKoZIhvcNAQEBBQAD\n",
"SgAwRwJAgVUiuYqkb+3W59lmD1W8183VvE5AAiGisfeHMIVe0vJEudybdbb7Rl9C\n",
"tp0jNgveVA/NvR+ZKhBYEctAy7WnQQIDAQABo4GeMIGbMFAGA1UEAwRJE0dGb3Ig\n",
"VGVzdGluZyBQdXJwb3NlcyBPbmx5IFNhbXBsZSBTb2Z0d2FyZSBQdWJsaXNoaW5n\n",
"IENyZWRlbnRpYWxzIEFnZW5jeTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRj\n",
"oRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJ\n",
"KoZIhvcNAQEEBQADQQAtLj57iUKJP6ghF/rw9cOV22JpW8ncwbP68MRvb2Savecb\n",
"JWhyg2e9VrCNAb0q98xLvYeluocgTEIRQa0QFzuM\n",
"-----END CERTIFICATE-----\n",
NULL
};
static int tests_X509_check_crypto(void)
{
X509 *rsa_n_neg = NULL;
EVP_PKEY *pub = NULL;
int test;
test = TEST_ptr((rsa_n_neg = X509_from_strings(kRSAModulusNeg)))
&& TEST_ptr_null((pub = X509_get_pubkey(rsa_n_neg)))
&& TEST_true(err_chk(ERR_LIB_EVP, EVP_R_DECODE_ERROR));
EVP_PKEY_free(pub);
X509_free(rsa_n_neg);
return test;
}
int setup_tests(void)
{
ADD_TEST(test_standard_exts);
ADD_ALL_TESTS(test_a2i_ipaddress, OSSL_NELEM(a2i_ipaddress_tests));
ADD_TEST(tests_X509_PURPOSE);
ADD_TEST(tests_X509_check_time);
ADD_TEST(tests_X509_check_crypto);
return 1;
}