|  | // 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 = ssl_parse_peer_subject_public_key_info(spki); | 
|  | if (pubkey == nullptr) { | 
|  | 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; | 
|  | } |