Add CTLOG_STORE_add0_log() to add CTLOGs to a store programmatically

Reviewed-by: Tomas Mraz <tomas@openssl.foundation>
Reviewed-by: Eugene Syromiatnikov <esyr@openssl.org>
MergeDate: Thu Mar 19 20:45:34 2026
(Merged from https://github.com/openssl/openssl/pull/30427)
This commit is contained in:
Tim Perry
2026-03-15 17:00:48 +01:00
committed by Eugene Syromiatnikov
parent 10b0340fec
commit fe26a8fc90
6 changed files with 174 additions and 6 deletions
+4
View File
@@ -41,6 +41,10 @@ OpenSSL Releases
*Paul Louvel*
* Added `CTLOG_STORE_add0_log()` to add individual CT logs to a `CTLOG_STORE`.
*Tim Perry*
* Dropped `no-ecdsa` and `no-ecdh` options from `Configure` as these options
did not really disable the implementations. Use `no-ec` to disable the
elliptic curve support.
+14
View File
@@ -315,6 +315,20 @@ EVP_PKEY *CTLOG_get0_public_key(const CTLOG *log)
return log->public_key;
}
int CTLOG_STORE_add0_log(CTLOG_STORE *store, CTLOG *log)
{
if (store == NULL || log == NULL) {
ERR_raise(ERR_LIB_CT, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (!sk_CTLOG_push(store->logs, log)) {
ERR_raise(ERR_LIB_CT, ERR_R_CRYPTO_LIB);
return 0;
}
return 1;
}
/*
* Given a log ID, finds the matching log.
* Returns NULL if no match found.
+19 -6
View File
@@ -4,6 +4,7 @@
CTLOG_STORE_new_ex,
CTLOG_STORE_new, CTLOG_STORE_free,
CTLOG_STORE_add0_log,
CTLOG_STORE_load_default_file, CTLOG_STORE_load_file -
Create and populate a Certificate Transparency log list
@@ -15,14 +16,17 @@ Create and populate a Certificate Transparency log list
CTLOG_STORE *CTLOG_STORE_new(void);
void CTLOG_STORE_free(CTLOG_STORE *store);
int CTLOG_STORE_add0_log(CTLOG_STORE *store, CTLOG *log);
int CTLOG_STORE_load_default_file(CTLOG_STORE *store);
int CTLOG_STORE_load_file(CTLOG_STORE *store, const char *file);
=head1 DESCRIPTION
A CTLOG_STORE is a container for a list of CTLOGs (Certificate Transparency
logs). The list can be loaded from one or more files and then searched by LogID
(see RFC 6962, Section 3.2, for the definition of a LogID).
logs). The list can be loaded from one or more files, or populated
programmatically, and then searched by LogID (see RFC 6962, Section 3.2, for
the definition of a LogID).
CTLOG_STORE_new_ex() creates an empty list of CT logs associated with
the library context I<libctx> and the property query string I<propq>.
@@ -30,8 +34,10 @@ the library context I<libctx> and the property query string I<propq>.
CTLOG_STORE_new() does the same thing as CTLOG_STORE_new_ex() but with
the default library context and property query string.
The CTLOG_STORE is then populated by CTLOG_STORE_load_default_file() or
CTLOG_STORE_load_file(). CTLOG_STORE_load_default_file() loads from the default
The CTLOG_STORE is then populated by CTLOG_STORE_load_default_file(),
CTLOG_STORE_load_file(), or CTLOG_STORE_add0_log().
CTLOG_STORE_load_default_file() loads from the default
file, which is named F<ct_log_list.cnf> in OPENSSLDIR (see the output of
L<openssl-version(1)>). This can be overridden using an environment variable
named B<CTLOG_FILE>. CTLOG_STORE_load_file() loads from a caller-specified file
@@ -50,6 +56,11 @@ The expected format of the file is:
description = Log 2
key = <base64-encoded DER SubjectPublicKeyInfo here>
CTLOG_STORE_add0_log() adds a single CTLOG (see L<CTLOG_new(3)>) to the store.
On success, the store takes ownership of I<log> and the caller must not free
it. On failure, the caller retains ownership and is responsible for freeing
I<log>.
Once a CTLOG_STORE is no longer required, it should be passed to
CTLOG_STORE_free(). This will delete all of the CTLOGs stored within, along
with the CTLOG_STORE itself. If the argument is NULL, nothing is done.
@@ -62,6 +73,8 @@ invalid if it is missing a "key" or "description" field.
=head1 RETURN VALUES
B<CTLOG_STORE_add0_log> returns 1 on success, 0 on failure.
Both B<CTLOG_STORE_load_default_file> and B<CTLOG_STORE_load_file> return 1 if
all CT logs in the file are successfully parsed and loaded, 0 otherwise.
@@ -73,8 +86,8 @@ L<SSL_CTX_set_ctlog_list_file(3)>
=head1 HISTORY
CTLOG_STORE_new_ex was added in OpenSSL 3.0. All other functions were
added in OpenSSL 1.1.0.
CTLOG_STORE_add0_log was added in OpenSSL 4.1. CTLOG_STORE_new_ex was added in
OpenSSL 3.0. All other functions were added in OpenSSL 1.1.0.
=head1 COPYRIGHT
+8
View File
@@ -499,6 +499,14 @@ CTLOG_STORE *CTLOG_STORE_new(void);
*/
void CTLOG_STORE_free(CTLOG_STORE *store);
/*
* Adds a CT log to a CTLOG_STORE.
* Takes ownership of the CTLOG on success - the caller must not free it after
* a successful call. On failure, the caller retains ownership.
* Returns 1 on success, 0 on failure.
*/
__owur int CTLOG_STORE_add0_log(CTLOG_STORE *store, CTLOG *log);
/*
* Finds a CT log in the store based on its log ID.
* Returns the CT log, or NULL if no match is found.
+128
View File
@@ -508,6 +508,131 @@ static int test_ctlog_from_base64(void)
return 0;
return 1;
}
static int test_ctlog_store_add0_log(void)
{
CTLOG_STORE *store = NULL;
CTLOG *log = NULL;
const CTLOG *found = NULL;
const uint8_t *log_id = NULL;
size_t log_id_len = 0;
int result = 0;
const char pkey_base64[] = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U"
"yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==";
const char name[] = "test log";
if (!TEST_ptr(store = CTLOG_STORE_new()))
goto end;
if (!TEST_true(CTLOG_new_from_base64(&log, pkey_base64, name)))
goto end;
CTLOG_get0_log_id(log, &log_id, &log_id_len);
if (!TEST_size_t_eq(log_id_len, CT_V1_HASHLEN))
goto end;
if (!TEST_ptr_null(CTLOG_STORE_get0_log_by_id(store, log_id, log_id_len)))
goto end;
if (!TEST_true(CTLOG_STORE_add0_log(store, log)))
goto end;
log = NULL;
found = CTLOG_STORE_get0_log_by_id(store, log_id, log_id_len);
if (!TEST_ptr(found))
goto end;
if (!TEST_str_eq(CTLOG_get0_name(found), name))
goto end;
result = 1;
end:
CTLOG_STORE_free(store);
CTLOG_free(log);
return result;
}
static int test_ctlog_store_add0_log_validates_sct(void)
{
CTLOG_STORE *store = NULL;
CTLOG *log = NULL;
CT_POLICY_EVAL_CTX *ct_policy_ctx = NULL;
X509 *cert = NULL, *issuer = NULL;
STACK_OF(SCT) *scts = NULL;
const X509_EXTENSION *sct_extension = NULL;
int result = 0;
const char pkey_base64[] = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U"
"yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==";
if (!TEST_ptr(store = CTLOG_STORE_new()))
goto end;
if (!TEST_true(CTLOG_new_from_base64(&log, pkey_base64, "test")))
goto end;
if (!TEST_true(CTLOG_STORE_add0_log(store, log)))
goto end;
log = NULL;
if (!TEST_ptr(ct_policy_ctx = CT_POLICY_EVAL_CTX_new()))
goto end;
CT_POLICY_EVAL_CTX_set_shared_CTLOG_STORE(ct_policy_ctx, store);
CT_POLICY_EVAL_CTX_set_time(ct_policy_ctx, 1580335307000ULL);
if (!TEST_ptr(cert = load_pem_cert(certs_dir, "embeddedSCTs1.pem")))
goto end;
if (!TEST_ptr(issuer = load_pem_cert(certs_dir, "embeddedSCTs1_issuer.pem")))
goto end;
CT_POLICY_EVAL_CTX_set1_cert(ct_policy_ctx, cert);
CT_POLICY_EVAL_CTX_set1_issuer(ct_policy_ctx, issuer);
sct_extension = X509_get_ext(cert,
X509_get_ext_by_NID(cert, NID_ct_precert_scts, -1));
if (!TEST_ptr(sct_extension))
goto end;
if (!TEST_ptr(scts = X509V3_EXT_d2i(sct_extension)))
goto end;
if (!TEST_int_eq(sk_SCT_num(scts), 1))
goto end;
if (!TEST_int_ge(SCT_LIST_validate(scts, ct_policy_ctx), 0))
goto end;
if (!TEST_int_eq(SCT_get_validation_status(sk_SCT_value(scts, 0)),
SCT_VALIDATION_STATUS_VALID))
goto end;
result = 1;
end:
CTLOG_STORE_free(store);
CTLOG_free(log);
CT_POLICY_EVAL_CTX_free(ct_policy_ctx);
X509_free(cert);
X509_free(issuer);
SCT_LIST_free(scts);
return result;
}
static int test_ctlog_store_add0_log_null(void)
{
CTLOG_STORE *store = NULL;
CTLOG *log = NULL;
int result = 0;
const char pkey_base64[] = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmXg8sUUzwBYaWrRb+V0IopzQ6o3U"
"yEJ04r5ZrRXGdpYM8K+hB0pXrGRLI0eeWz+3skXrS0IO83AhA3GpRL6s6w==";
if (!TEST_ptr(store = CTLOG_STORE_new()))
goto end;
if (!TEST_false(CTLOG_STORE_add0_log(store, NULL)))
goto end;
if (!TEST_true(CTLOG_new_from_base64(&log, pkey_base64, "test")))
goto end;
if (!TEST_false(CTLOG_STORE_add0_log(NULL, log)))
goto end;
result = 1;
end:
CTLOG_STORE_free(store);
CTLOG_free(log);
return result;
}
#endif
int setup_tests(void)
@@ -528,6 +653,9 @@ int setup_tests(void)
ADD_TEST(test_encode_tls_sct);
ADD_TEST(test_default_ct_policy_eval_ctx_time_is_now);
ADD_TEST(test_ctlog_from_base64);
ADD_TEST(test_ctlog_store_add0_log);
ADD_TEST(test_ctlog_store_add0_log_validates_sct);
ADD_TEST(test_ctlog_store_add0_log_null);
#else
printf("No CT support\n");
#endif
+1
View File
@@ -5715,3 +5715,4 @@ OPENSSL_sk_set_cmp_thunks ? 4_0_0 EXIST::FUNCTION:
ASN1_BIT_STRING_set1 ? 4_0_0 EXIST::FUNCTION:
OSSL_ESS_check_signing_certs_ex ? 4_0_0 EXIST::FUNCTION:
X509v3_delete_extension ? 4_0_0 EXIST::FUNCTION:
CTLOG_STORE_add0_log ? 4_0_0 EXIST::FUNCTION:CT