Files
boringssl/ssl/ssl_credential.cc
David Benjamin 035e720641 Don't pass parent or ad to CRYPTO_EX_free
We used to pass the parent object to callbacks, but this isn't safe. The
object is in the middle of being destroyed, so the object's
invariants won't hold. We can't remove the parameters, but pass in NULL
instead.

This also has the side effect of making it possible to manage
CRYPTO_EX_DATA lifetime in its destructor, because we won't need to find
some handle back to the parent object. It also means that defensively
clearing fields in BIO destroy callbacks, while harmless, is more
obviously unnecessary.

Update-Note: CRYPTO_EX_free is no longer passed the parent object. We do
not expect any callers to be impacted. Callers that were impacted
probably had some object lifetime bug already.

Fixed: 412707574
Change-Id: Idda889898a9eca3b59e3200ba1d5b4f2e62f2d37
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/79607
Reviewed-by: Adam Langley <agl@google.com>
Auto-Submit: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
2025-06-02 13:27:41 -07:00

686 lines
20 KiB
C++

// Copyright 2024 The BoringSSL Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/ssl.h>
#include <assert.h>
#include <openssl/span.h>
#include "../crypto/internal.h"
#include "../crypto/spake2plus/internal.h"
#include "internal.h"
BSSL_NAMESPACE_BEGIN
// new_leafless_chain returns a fresh stack of buffers set to {nullptr}.
static UniquePtr<STACK_OF(CRYPTO_BUFFER)> new_leafless_chain(void) {
UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain(sk_CRYPTO_BUFFER_new_null());
if (!chain || !sk_CRYPTO_BUFFER_push(chain.get(), nullptr)) {
return nullptr;
}
return chain;
}
bool ssl_get_full_credential_list(SSL_HANDSHAKE *hs,
Array<SSL_CREDENTIAL *> *out) {
CERT *cert = hs->config->cert.get();
// Finish filling in the legacy credential if needed.
if (!cert->x509_method->ssl_auto_chain_if_needed(hs)) {
return false;
}
size_t num_creds = cert->credentials.size();
bool include_legacy = cert->legacy_credential->IsComplete();
if (include_legacy) {
num_creds++;
}
if (!out->InitForOverwrite(num_creds)) {
return false;
}
for (size_t i = 0; i < cert->credentials.size(); i++) {
(*out)[i] = cert->credentials[i].get();
}
if (include_legacy) {
(*out)[num_creds - 1] = cert->legacy_credential.get();
}
return true;
}
bool ssl_credential_matches_requested_issuers(SSL_HANDSHAKE *hs,
const SSL_CREDENTIAL *cred) {
if (!cred->must_match_issuer) {
// This credential does not need to match a requested issuer, so
// it is good to use without a match.
return true;
}
// If we have names sent by the CA extension, and this
// credential matches it, it is good.
if (hs->ca_names != nullptr) {
for (const CRYPTO_BUFFER *ca_name : hs->ca_names.get()) {
if (cred->ChainContainsIssuer(
Span(CRYPTO_BUFFER_data(ca_name), CRYPTO_BUFFER_len(ca_name)))) {
return true;
}
}
}
// If the credential has a trust anchor ID and it matches one sent by the
// peer, it is good.
if (!cred->trust_anchor_id.empty() && hs->peer_requested_trust_anchors) {
CBS cbs = CBS(*hs->peer_requested_trust_anchors), candidate;
while (CBS_len(&cbs) > 0) {
if (!CBS_get_u8_length_prefixed(&cbs, &candidate) ||
CBS_len(&candidate) == 0) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return false;
}
if (candidate == Span(cred->trust_anchor_id)) {
hs->matched_peer_trust_anchor = true;
return true;
}
}
}
OPENSSL_PUT_ERROR(SSL, SSL_R_NO_MATCHING_ISSUER);
return false;
}
BSSL_NAMESPACE_END
using namespace bssl;
static CRYPTO_EX_DATA_CLASS g_ex_data_class = CRYPTO_EX_DATA_CLASS_INIT;
ssl_credential_st::ssl_credential_st(SSLCredentialType type_arg)
: RefCounted(CheckSubClass()), type(type_arg) {
CRYPTO_new_ex_data(&ex_data);
}
ssl_credential_st::~ssl_credential_st() {
CRYPTO_free_ex_data(&g_ex_data_class, &ex_data);
}
static CRYPTO_BUFFER *buffer_up_ref(const CRYPTO_BUFFER *buffer) {
CRYPTO_BUFFER_up_ref(const_cast<CRYPTO_BUFFER *>(buffer));
return const_cast<CRYPTO_BUFFER *>(buffer);
}
UniquePtr<SSL_CREDENTIAL> ssl_credential_st::Dup() const {
assert(type == SSLCredentialType::kX509);
UniquePtr<SSL_CREDENTIAL> ret = MakeUnique<SSL_CREDENTIAL>(type);
if (ret == nullptr) {
return nullptr;
}
ret->pubkey = UpRef(pubkey);
ret->privkey = UpRef(privkey);
ret->key_method = key_method;
if (!ret->sigalgs.CopyFrom(sigalgs)) {
return nullptr;
}
if (chain) {
ret->chain.reset(sk_CRYPTO_BUFFER_deep_copy(chain.get(), buffer_up_ref,
CRYPTO_BUFFER_free));
if (!ret->chain) {
return nullptr;
}
}
ret->dc = UpRef(dc);
ret->signed_cert_timestamp_list = UpRef(signed_cert_timestamp_list);
ret->ocsp_response = UpRef(ocsp_response);
ret->dc_algorithm = dc_algorithm;
return ret;
}
void ssl_credential_st::ClearCertAndKey() {
pubkey = nullptr;
privkey = nullptr;
key_method = nullptr;
chain = nullptr;
}
bool ssl_credential_st::UsesX509() const {
switch (type) {
case SSLCredentialType::kX509:
case SSLCredentialType::kDelegated:
return true;
case SSLCredentialType::kSPAKE2PlusV1Client:
case SSLCredentialType::kSPAKE2PlusV1Server:
return false;
}
abort();
}
bool ssl_credential_st::UsesPrivateKey() const {
switch (type) {
case SSLCredentialType::kX509:
case SSLCredentialType::kDelegated:
return true;
case SSLCredentialType::kSPAKE2PlusV1Client:
case SSLCredentialType::kSPAKE2PlusV1Server:
return false;
}
abort();
}
bool ssl_credential_st::IsComplete() const {
// APIs like |SSL_use_certificate| and |SSL_set1_chain| configure the leaf and
// other certificates separately. It is possible for |chain| have a null leaf.
if (UsesX509() && (sk_CRYPTO_BUFFER_num(chain.get()) == 0 ||
sk_CRYPTO_BUFFER_value(chain.get(), 0) == nullptr)) {
return false;
}
// We must have successfully extracted a public key from the certificate,
// delegated credential, etc.
if (UsesPrivateKey() && pubkey == nullptr) {
return false;
}
if (UsesPrivateKey() && privkey == nullptr && key_method == nullptr) {
return false;
}
if (type == SSLCredentialType::kDelegated && dc == nullptr) {
return false;
}
return true;
}
bool ssl_credential_st::SetLeafCert(UniquePtr<CRYPTO_BUFFER> leaf,
bool discard_key_on_mismatch) {
if (!UsesX509()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return false;
}
const bool private_key_matches_leaf = type != SSLCredentialType::kDelegated;
CBS cbs;
CRYPTO_BUFFER_init_CBS(leaf.get(), &cbs);
UniquePtr<EVP_PKEY> new_pubkey = ssl_cert_parse_pubkey(&cbs);
if (new_pubkey == nullptr) {
return false;
}
if (!ssl_is_key_type_supported(EVP_PKEY_id(new_pubkey.get()))) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
return false;
}
// An ECC certificate may be usable for ECDH or ECDSA. We only support ECDSA
// certificates, so sanity-check the key usage extension.
if (EVP_PKEY_id(new_pubkey.get()) == EVP_PKEY_EC &&
!ssl_cert_check_key_usage(&cbs, key_usage_digital_signature)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE);
return false;
}
if (private_key_matches_leaf && privkey != nullptr &&
!ssl_compare_public_and_private_key(new_pubkey.get(), privkey.get())) {
if (!discard_key_on_mismatch) {
return false;
}
ERR_clear_error();
privkey = nullptr;
}
if (chain == nullptr) {
chain = new_leafless_chain();
if (chain == nullptr) {
return false;
}
}
CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_value(chain.get(), 0));
sk_CRYPTO_BUFFER_set(chain.get(), 0, leaf.release());
if (private_key_matches_leaf) {
pubkey = std::move(new_pubkey);
}
return true;
}
void ssl_credential_st::ClearIntermediateCerts() {
if (chain == nullptr) {
return;
}
while (sk_CRYPTO_BUFFER_num(chain.get()) > 1) {
CRYPTO_BUFFER_free(sk_CRYPTO_BUFFER_pop(chain.get()));
}
}
bool ssl_credential_st::ChainContainsIssuer(
bssl::Span<const uint8_t> dn) const {
if (UsesX509()) {
// TODO(bbe) This is used for matching a chain by CA name for the CA
// extension. If we require a chain to be present, we could remove any
// remaining parts of the chain after the found issuer, on the assumption
// that the peer sending the CA extension has the issuer in their trust
// store and does not need us to waste bytes on the wire.
CBS dn_cbs;
CBS_init(&dn_cbs, dn.data(), dn.size());
for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain.get()); i++) {
const CRYPTO_BUFFER *cert = sk_CRYPTO_BUFFER_value(chain.get(), i);
CBS cert_cbs;
CRYPTO_BUFFER_init_CBS(cert, &cert_cbs);
if (ssl_cert_matches_issuer(&cert_cbs, &dn_cbs)) {
return true;
}
}
}
return false;
}
bool ssl_credential_st::HasPAKEAttempts() const {
return pake_limit.load() != 0;
}
bool ssl_credential_st::ClaimPAKEAttempt() const {
uint32_t current = pake_limit.load(std::memory_order_relaxed);
for (;;) {
if (current == 0) {
return false;
}
if (pake_limit.compare_exchange_weak(current, current - 1)) {
break;
}
}
return true;
}
void ssl_credential_st::RestorePAKEAttempt() const {
// This should not overflow because it will only be paired with
// ClaimPAKEAttempt.
pake_limit.fetch_add(1);
}
bool ssl_credential_st::AppendIntermediateCert(UniquePtr<CRYPTO_BUFFER> cert) {
if (!UsesX509()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return false;
}
if (chain == nullptr) {
chain = new_leafless_chain();
if (chain == nullptr) {
return false;
}
}
return PushToStack(chain.get(), std::move(cert));
}
SSL_CREDENTIAL *SSL_CREDENTIAL_new_x509(void) {
return New<SSL_CREDENTIAL>(SSLCredentialType::kX509);
}
SSL_CREDENTIAL *SSL_CREDENTIAL_new_delegated(void) {
return New<SSL_CREDENTIAL>(SSLCredentialType::kDelegated);
}
void SSL_CREDENTIAL_up_ref(SSL_CREDENTIAL *cred) { cred->UpRefInternal(); }
void SSL_CREDENTIAL_free(SSL_CREDENTIAL *cred) {
if (cred != nullptr) {
cred->DecRefInternal();
}
}
int SSL_CREDENTIAL_set1_private_key(SSL_CREDENTIAL *cred, EVP_PKEY *key) {
if (!cred->UsesPrivateKey()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
// If the public half has been configured, check |key| matches. |pubkey| will
// have been extracted from the certificate, delegated credential, etc.
if (cred->pubkey != nullptr &&
!ssl_compare_public_and_private_key(cred->pubkey.get(), key)) {
return false;
}
cred->privkey = UpRef(key);
cred->key_method = nullptr;
return 1;
}
int SSL_CREDENTIAL_set_private_key_method(
SSL_CREDENTIAL *cred, const SSL_PRIVATE_KEY_METHOD *key_method) {
if (!cred->UsesPrivateKey()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
cred->privkey = nullptr;
cred->key_method = key_method;
return 1;
}
int SSL_CREDENTIAL_set1_cert_chain(SSL_CREDENTIAL *cred,
CRYPTO_BUFFER *const *certs,
size_t num_certs) {
if (!cred->UsesX509() || num_certs == 0) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
if (!cred->SetLeafCert(UpRef(certs[0]), /*discard_key_on_mismatch=*/false)) {
return 0;
}
cred->ClearIntermediateCerts();
for (size_t i = 1; i < num_certs; i++) {
if (!cred->AppendIntermediateCert(UpRef(certs[i]))) {
return 0;
}
}
return 1;
}
int SSL_CREDENTIAL_set1_delegated_credential(SSL_CREDENTIAL *cred,
CRYPTO_BUFFER *dc) {
if (cred->type != SSLCredentialType::kDelegated) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
// Parse the delegated credential to check for validity, and extract a few
// fields from it. See RFC 9345, section 4.
CBS cbs, spki, sig;
uint32_t valid_time;
uint16_t dc_cert_verify_algorithm, algorithm;
CRYPTO_BUFFER_init_CBS(dc, &cbs);
if (!CBS_get_u32(&cbs, &valid_time) ||
!CBS_get_u16(&cbs, &dc_cert_verify_algorithm) ||
!CBS_get_u24_length_prefixed(&cbs, &spki) ||
!CBS_get_u16(&cbs, &algorithm) ||
!CBS_get_u16_length_prefixed(&cbs, &sig) || //
CBS_len(&sig) == 0 || //
CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return 0;
}
// RFC 9345 forbids algorithms that use the rsaEncryption OID. As the
// RSASSA-PSS OID is unusably complicated, this effectively means we will not
// support RSA delegated credentials.
if (SSL_get_signature_algorithm_key_type(dc_cert_verify_algorithm) ==
EVP_PKEY_RSA) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SIGNATURE_ALGORITHM);
return 0;
}
UniquePtr<EVP_PKEY> pubkey(EVP_parse_public_key(&spki));
if (pubkey == nullptr || CBS_len(&spki) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return 0;
}
if (!cred->sigalgs.CopyFrom(Span(&dc_cert_verify_algorithm, 1))) {
return 0;
}
if (cred->privkey != nullptr &&
!ssl_compare_public_and_private_key(pubkey.get(), cred->privkey.get())) {
return 0;
}
cred->dc = UpRef(dc);
cred->pubkey = std::move(pubkey);
cred->dc_algorithm = algorithm;
return 1;
}
int SSL_CREDENTIAL_set1_ocsp_response(SSL_CREDENTIAL *cred,
CRYPTO_BUFFER *ocsp) {
if (!cred->UsesX509()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
cred->ocsp_response = UpRef(ocsp);
return 1;
}
int SSL_CREDENTIAL_set1_signed_cert_timestamp_list(SSL_CREDENTIAL *cred,
CRYPTO_BUFFER *sct_list) {
if (!cred->UsesX509()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
CBS cbs;
CRYPTO_BUFFER_init_CBS(sct_list, &cbs);
if (!ssl_is_sct_list_valid(&cbs)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SCT_LIST);
return 0;
}
cred->signed_cert_timestamp_list = UpRef(sct_list);
return 1;
}
int SSL_spake2plusv1_register(uint8_t out_w0[32], uint8_t out_w1[32],
uint8_t out_registration_record[65],
const uint8_t *password, size_t password_len,
const uint8_t *client_identity,
size_t client_identity_len,
const uint8_t *server_identity,
size_t server_identity_len) {
return spake2plus::Register(
Span(out_w0, 32), Span(out_w1, 32), Span(out_registration_record, 65),
Span(password, password_len), Span(client_identity, client_identity_len),
Span(server_identity, server_identity_len));
}
static UniquePtr<SSL_CREDENTIAL> ssl_credential_new_spake2plusv1(
SSLCredentialType type, Span<const uint8_t> context,
Span<const uint8_t> client_identity, Span<const uint8_t> server_identity,
uint32_t limit) {
assert(type == SSLCredentialType::kSPAKE2PlusV1Client ||
type == SSLCredentialType::kSPAKE2PlusV1Server);
auto cred = MakeUnique<SSL_CREDENTIAL>(type);
if (cred == nullptr) {
return nullptr;
}
if (!cred->pake_context.CopyFrom(context) ||
!cred->client_identity.CopyFrom(client_identity) ||
!cred->server_identity.CopyFrom(server_identity)) {
return nullptr;
}
cred->pake_limit.store(limit);
return cred;
}
SSL_CREDENTIAL *SSL_CREDENTIAL_new_spake2plusv1_client(
const uint8_t *context, size_t context_len, const uint8_t *client_identity,
size_t client_identity_len, const uint8_t *server_identity,
size_t server_identity_len, uint32_t error_limit, const uint8_t *w0,
size_t w0_len, const uint8_t *w1, size_t w1_len) {
if (w0_len != spake2plus::kVerifierSize ||
w1_len != spake2plus::kVerifierSize ||
(context == nullptr && context_len != 0)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_SPAKE2PLUSV1_VALUE);
return nullptr;
}
UniquePtr<SSL_CREDENTIAL> cred = ssl_credential_new_spake2plusv1(
SSLCredentialType::kSPAKE2PlusV1Client, Span(context, context_len),
Span(client_identity, client_identity_len),
Span(server_identity, server_identity_len), error_limit);
if (!cred) {
return nullptr;
}
if (!cred->password_verifier_w0.CopyFrom(Span(w0, w0_len)) ||
!cred->password_verifier_w1.CopyFrom(Span(w1, w1_len))) {
return nullptr;
}
return cred.release();
}
SSL_CREDENTIAL *SSL_CREDENTIAL_new_spake2plusv1_server(
const uint8_t *context, size_t context_len, const uint8_t *client_identity,
size_t client_identity_len, const uint8_t *server_identity,
size_t server_identity_len, uint32_t rate_limit, const uint8_t *w0,
size_t w0_len, const uint8_t *registration_record,
size_t registration_record_len) {
if (w0_len != spake2plus::kVerifierSize ||
registration_record_len != spake2plus::kRegistrationRecordSize ||
(context == nullptr && context_len != 0)) {
return nullptr;
}
UniquePtr<SSL_CREDENTIAL> cred = ssl_credential_new_spake2plusv1(
SSLCredentialType::kSPAKE2PlusV1Server, Span(context, context_len),
Span(client_identity, client_identity_len),
Span(server_identity, server_identity_len), rate_limit);
if (!cred) {
return nullptr;
}
if (!cred->password_verifier_w0.CopyFrom(Span(w0, w0_len)) ||
!cred->registration_record.CopyFrom(
Span(registration_record, registration_record_len))) {
return nullptr;
}
return cred.release();
}
int SSL_CTX_add1_credential(SSL_CTX *ctx, SSL_CREDENTIAL *cred) {
if (!cred->IsComplete()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
return ctx->cert->credentials.Push(UpRef(cred));
}
int SSL_add1_credential(SSL *ssl, SSL_CREDENTIAL *cred) {
if (ssl->config == nullptr) {
return 0;
}
if (!cred->IsComplete()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
return ssl->config->cert->credentials.Push(UpRef(cred));
}
const SSL_CREDENTIAL *SSL_get0_selected_credential(const SSL *ssl) {
if (ssl->s3->hs == nullptr) {
return nullptr;
}
return ssl->s3->hs->credential.get();
}
int SSL_CREDENTIAL_get_ex_new_index(long argl, void *argp,
CRYPTO_EX_unused *unused,
CRYPTO_EX_dup *dup_unused,
CRYPTO_EX_free *free_func) {
return CRYPTO_get_ex_new_index_ex(&g_ex_data_class, argl, argp, free_func);
}
int SSL_CREDENTIAL_set_ex_data(SSL_CREDENTIAL *cred, int idx, void *arg) {
return CRYPTO_set_ex_data(&cred->ex_data, idx, arg);
}
void *SSL_CREDENTIAL_get_ex_data(const SSL_CREDENTIAL *cred, int idx) {
return CRYPTO_get_ex_data(&cred->ex_data, idx);
}
void SSL_CREDENTIAL_set_must_match_issuer(SSL_CREDENTIAL *cred, int match) {
cred->must_match_issuer = !!match;
}
int SSL_CREDENTIAL_set1_trust_anchor_id(SSL_CREDENTIAL *cred, const uint8_t *id,
size_t id_len) {
// For now, this is only valid for X.509.
if (!cred->UsesX509()) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
if (!cred->trust_anchor_id.CopyFrom(Span(id, id_len))) {
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
return 0;
}
return 1;
}
int SSL_CREDENTIAL_set1_certificate_properties(
SSL_CREDENTIAL *cred, CRYPTO_BUFFER *cert_property_list) {
std::optional<CBS> trust_anchor;
CBS cbs, cpl;
CRYPTO_BUFFER_init_CBS(cert_property_list, &cbs);
if (!CBS_get_u16_length_prefixed(&cbs, &cpl)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST);
return 0;
}
while (CBS_len(&cpl) != 0) {
uint16_t cp_type;
CBS cp_data;
if (!CBS_get_u16(&cpl, &cp_type) ||
!CBS_get_u16_length_prefixed(&cpl, &cp_data)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST);
return 0;
}
switch (cp_type) {
case 0: // trust anchor identifier.
if (trust_anchor.has_value()) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST);
return 0;
}
trust_anchor = cp_data;
break;
default:
break;
}
}
if (CBS_len(&cbs) != 0) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_CERTIFICATE_PROPERTY_LIST);
return 0;
}
// Certificate property list has parsed correctly.
// We do not currently retain |cert_property_list|, but if we define another
// property with larger fields (e.g. stapled SCTs), it may make sense for
// those fields to retain |cert_property_list| and alias into it.
if (trust_anchor.has_value()) {
if (!CBS_len(&trust_anchor.value())) {
OPENSSL_PUT_ERROR(SSL, SSL_R_INVALID_TRUST_ANCHOR_LIST);
return 0;
}
if (!SSL_CREDENTIAL_set1_trust_anchor_id(cred,
CBS_data(&trust_anchor.value()),
CBS_len(&trust_anchor.value()))) {
return 0;
}
}
return 1;
}