blob: b455ef13cccdde451580b544f54b4dd4a98e5fd8 [file]
// Copyright 2018 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 <openssl/bytestring.h>
#include <openssl/err.h>
#include <algorithm>
#include <optional>
#include "../crypto/internal.h"
#include "internal.h"
BSSL_NAMESPACE_BEGIN
static const CBS_ASN1_TAG kFeatureTagALPS = CBS_ASN1_CONTEXT_SPECIFIC | 0;
// early_data_t represents the state of early data in a more compact way than
// the 3 bits used by the implementation.
enum early_data_t {
early_data_not_offered = 0,
early_data_accepted = 1,
early_data_rejected_hrr = 2,
early_data_skipped = 3,
early_data_max_value = early_data_skipped,
};
// serialize_features adds a description of features supported by this binary to
// `out`. Returns true on success and false on error.
static bool serialize_features(CBB *out) {
CBB ciphers;
if (!CBB_add_asn1(out, &ciphers, CBS_ASN1_OCTETSTRING)) {
return false;
}
Span<const SSL_CIPHER> all_ciphers = AllCiphers();
for (const SSL_CIPHER &cipher : all_ciphers) {
if (!CBB_add_u16(&ciphers, cipher.protocol_id)) {
return false;
}
}
CBB groups;
if (!CBB_add_asn1(out, &groups, CBS_ASN1_OCTETSTRING)) {
return false;
}
for (const NamedGroup &g : NamedGroups()) {
if (!CBB_add_u16(&groups, g.group_id)) {
return false;
}
}
// ALPS is a draft protocol and may change over time. The features structure
// contains a [0] IMPLICIT OCTET STRING OPTIONAL, containing a list of u16
// ALPS versions that the binary supports. For now we name them by codepoint.
// Once ALPS is finalized and past the support horizon, this field can be
// removed.
CBB alps;
if (!CBB_add_asn1(out, &alps, kFeatureTagALPS) ||
!CBB_add_u16(&alps, TLSEXT_TYPE_application_settings_old) ||
!CBB_add_u16(&alps, TLSEXT_TYPE_application_settings)) {
return false;
}
return CBB_flush(out);
}
// apply_remote_features reads a list of supported features from `in` and
// (possibly) reconfigures `ssl` to disallow the negotiation of features whose
// support has not been indicated. (This prevents the hints from being generated
// based on features that are not supported on the actual server.)
static bool apply_remote_features(SSLImpl *ssl, CBS *in) {
CBS ciphers;
if (!CBS_get_asn1(in, &ciphers, CBS_ASN1_OCTETSTRING)) {
return false;
}
bssl::UniquePtr<STACK_OF(SSL_CIPHER)> supported(sk_SSL_CIPHER_new_null());
if (!supported) {
return false;
}
while (CBS_len(&ciphers)) {
uint16_t id;
if (!CBS_get_u16(&ciphers, &id)) {
return false;
}
const SSL_CIPHER *cipher = SSL_get_cipher_by_value(id);
if (!cipher) {
continue;
}
if (!sk_SSL_CIPHER_push(supported.get(), cipher)) {
return false;
}
}
const STACK_OF(SSL_CIPHER) *configured =
ssl->config->cipher_list ? ssl->config->cipher_list->ciphers()
: ssl->ctx->cipher_list->ciphers();
bssl::UniquePtr<STACK_OF(SSL_CIPHER)> unsupported(sk_SSL_CIPHER_new_null());
if (!unsupported) {
return false;
}
for (const SSL_CIPHER *configured_cipher : configured) {
if (sk_SSL_CIPHER_find(supported.get(), nullptr, configured_cipher)) {
continue;
}
if (!sk_SSL_CIPHER_push(unsupported.get(), configured_cipher)) {
return false;
}
}
if (sk_SSL_CIPHER_num(unsupported.get()) && !ssl->config->cipher_list) {
ssl->config->cipher_list = bssl::MakeUnique<SSLCipherPreferenceList>();
if (!ssl->config->cipher_list ||
!ssl->config->cipher_list->CopyFrom(*ssl->ctx->cipher_list)) {
return false;
}
}
for (const SSL_CIPHER *unsupported_cipher : unsupported.get()) {
ssl->config->cipher_list->Remove(unsupported_cipher);
}
if (sk_SSL_CIPHER_num(SSL_get_ciphers(ssl)) == 0) {
return false;
}
CBS groups;
if (!CBS_get_asn1(in, &groups, CBS_ASN1_OCTETSTRING)) {
return false;
}
Array<uint16_t> supported_groups;
if (!supported_groups.InitForOverwrite(CBS_len(&groups) / 2)) {
return false;
}
size_t idx = 0;
while (CBS_len(&groups)) {
uint16_t group;
if (!CBS_get_u16(&groups, &group)) {
return false;
}
supported_groups[idx++] = group;
}
Span<const uint16_t> configured_groups =
ssl->s3->hs->config->supported_group_list;
Span<const uint32_t> configured_groups_flags =
ssl->s3->hs->config->supported_group_list_flags;
Array<uint16_t> new_configured_groups;
// Temporary scratch space to keep track of consecutive runs of groups of
// equal preference, so they can be relabeled after filtering for groups
// present in the remote features and copying to the new configuration.
Array<size_t> new_groups_preference_ranks;
Array<uint32_t> new_groups_flags;
if (!new_configured_groups.InitForOverwrite(configured_groups.size()) ||
!new_groups_preference_ranks.Init(configured_groups.size()) ||
!new_groups_flags.Init(configured_groups.size())) {
return false;
}
// Populate temporary array containing "preference rank" of each group.
for (size_t i = 1; i < configured_groups.size(); ++i) {
new_groups_preference_ranks[i] = new_groups_preference_ranks[i - 1];
if ((configured_groups_flags[i - 1] &
SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT) == 0) {
++new_groups_preference_ranks[i];
}
}
idx = 0;
std::optional<size_t> last_rank;
for (size_t i = 0u; i < configured_groups.size(); ++i) {
uint16_t configured_group = configured_groups[i];
if (std::find(supported_groups.begin(), supported_groups.end(),
configured_group) == supported_groups.end()) {
continue;
}
new_configured_groups[idx] = configured_group;
// Set the "equal preference with next" flag on the previous group, if
// appropriate.
if (last_rank.has_value() && new_groups_preference_ranks[i] == *last_rank) {
new_groups_flags[idx - 1] |= SSL_GROUP_FLAG_EQUAL_PREFERENCE_WITH_NEXT;
}
last_rank = new_groups_preference_ranks[i];
++idx;
}
if (idx == 0) {
return false;
}
new_configured_groups.Shrink(idx);
new_groups_flags.Shrink(idx);
ssl->config->supported_group_list = std::move(new_configured_groups);
ssl->config->supported_group_list_flags = std::move(new_groups_flags);
CBS alps;
CBS_init(&alps, nullptr, 0);
if (!CBS_get_optional_asn1(in, &alps, /*out_present=*/nullptr,
kFeatureTagALPS)) {
return false;
}
bool supports_alps = false;
while (CBS_len(&alps) != 0) {
uint16_t id;
if (!CBS_get_u16(&alps, &id)) {
return false;
}
// For now, we support two ALPS codepoints, so we need to extract both
// codepoints, and then filter what the handshaker might try to send.
if ((id == TLSEXT_TYPE_application_settings &&
ssl->config->alps_use_new_codepoint) ||
(id == TLSEXT_TYPE_application_settings_old &&
!ssl->config->alps_use_new_codepoint)) {
supports_alps = true;
break;
}
}
if (!supports_alps) {
ssl->config->alps_configs.clear();
}
return true;
}
BSSL_NAMESPACE_END
using namespace bssl;
int SSL_serialize_capabilities(const SSL *ssl, CBB *out) {
CBB seq;
if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
!serialize_features(&seq) || //
!CBB_flush(out)) {
return 0;
}
return 1;
}
int SSL_request_handshake_hints(SSL *ssl, const uint8_t *client_hello,
size_t client_hello_len,
const uint8_t *capabilities,
size_t capabilities_len) {
auto *ssl_impl = FromOpaque(ssl);
if (SSL_is_dtls(ssl_impl) || ssl_impl->s3->hs == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
CBS cbs, seq;
CBS_init(&cbs, capabilities, capabilities_len);
UniquePtr<SSL_HANDSHAKE_HINTS> pending_hints =
MakeUnique<SSL_HANDSHAKE_HINTS>();
if (pending_hints == nullptr ||
!CBS_get_asn1(&cbs, &seq, CBS_ASN1_SEQUENCE) ||
!apply_remote_features(ssl_impl, &seq)) {
return 0;
}
SSL3_STATE *const s3 = ssl_impl->s3;
s3->v2_hello_done = true;
s3->has_message = true;
Array<uint8_t> client_hello_msg;
ScopedCBB client_hello_cbb;
CBB client_hello_body;
if (!ssl_impl->method->init_message(ssl_impl, client_hello_cbb.get(),
&client_hello_body,
SSL3_MT_CLIENT_HELLO) ||
!CBB_add_bytes(&client_hello_body, client_hello, client_hello_len) ||
!ssl_impl->method->finish_message(ssl_impl, client_hello_cbb.get(),
&client_hello_msg)) {
return 0;
}
s3->hs_buf.reset(BUF_MEM_new());
if (!s3->hs_buf || !BUF_MEM_append(s3->hs_buf.get(), client_hello_msg.data(),
client_hello_msg.size())) {
return 0;
}
s3->hs->pending_hints = std::move(pending_hints);
return 1;
}
// `SSL_HANDSHAKE_HINTS` is serialized as the following ASN.1 structure. We use
// implicit tagging to make it a little more compact.
//
// HandshakeHints ::= SEQUENCE {
// serverRandomTLS13 [0] IMPLICIT OCTET STRING OPTIONAL,
// keyShareHint [1] IMPLICIT KeyShareHint OPTIONAL,
// signatureHint [2] IMPLICIT SignatureHint OPTIONAL,
// -- At most one of decryptedPSKHint or ignorePSKHint may be present. It
// -- corresponds to the first entry in pre_shared_keys. TLS 1.2 session
// -- tickets use a separate hint, to ensure the caller does not apply the
// -- hint to the wrong field.
// decryptedPSKHint [3] IMPLICIT OCTET STRING OPTIONAL,
// ignorePSKHint [4] IMPLICIT NULL OPTIONAL,
// -- Due to a historical typo, compressCertificateHint's tag is primitive
// -- instead of constructed.
// compressCertificateHint [5 PRIMITIVE] IMPLICIT CompressCertificateHint
// OPTIONAL,
// -- TLS 1.2 and 1.3 use different server random hints because one contains
// -- a timestamp while the other doesn't. If the hint was generated
// -- assuming TLS 1.3 but we actually negotiate TLS 1.2, mixing the two
// -- will break this.
// serverRandomTLS12 [6] IMPLICIT OCTET STRING OPTIONAL,
// -- Due to a historical typo, ecdheHint tag has universal class instead of
// -- context-specific.
// ecdheHint [UNIVERSAL 7] IMPLICIT ECDHEHint OPTIONAL
// -- At most one of decryptedTicketHint or ignoreTicketHint may be present.
// -- renewTicketHint requires decryptedTicketHint.
// decryptedTicketHint [8] IMPLICIT OCTET STRING OPTIONAL,
// renewTicketHint [9] IMPLICIT NULL OPTIONAL,
// ignoreTicketHint [10] IMPLICIT NULL OPTIONAL,
// -- Unlike a usual ASN.1 structure, trailing data is ignored.
// }
//
// KeyShareHint ::= SEQUENCE {
// groupId INTEGER,
// ciphertext OCTET STRING,
// secret OCTET STRING,
// }
//
// SignatureHint ::= SEQUENCE {
// algorithm INTEGER,
// input OCTET STRING,
// subjectPublicKeyInfo OCTET STRING,
// signature OCTET STRING,
// }
//
// CompressCertificateHint ::= SEQUENCE {
// algorithm INTEGER,
// input OCTET STRING,
// compressed OCTET STRING,
// }
//
// ECDHEHint ::= SEQUENCE {
// groupId INTEGER,
// publicKey OCTET STRING,
// privateKey OCTET STRING,
// }
// HandshakeHints tags.
static const CBS_ASN1_TAG kServerRandomTLS13Tag = CBS_ASN1_CONTEXT_SPECIFIC | 0;
static const CBS_ASN1_TAG kKeyShareHintTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 1;
static const CBS_ASN1_TAG kSignatureHintTag =
CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC | 2;
static const CBS_ASN1_TAG kDecryptedPSKTag = CBS_ASN1_CONTEXT_SPECIFIC | 3;
static const CBS_ASN1_TAG kIgnorePSKTag = CBS_ASN1_CONTEXT_SPECIFIC | 4;
static const CBS_ASN1_TAG kCompressCertificateTag =
CBS_ASN1_CONTEXT_SPECIFIC | 5;
static const CBS_ASN1_TAG kServerRandomTLS12Tag = CBS_ASN1_CONTEXT_SPECIFIC | 6;
static const CBS_ASN1_TAG kECDHEHintTag = CBS_ASN1_CONSTRUCTED | 7;
static const CBS_ASN1_TAG kDecryptedTicketTag = CBS_ASN1_CONTEXT_SPECIFIC | 8;
static const CBS_ASN1_TAG kRenewTicketTag = CBS_ASN1_CONTEXT_SPECIFIC | 9;
static const CBS_ASN1_TAG kIgnoreTicketTag = CBS_ASN1_CONTEXT_SPECIFIC | 10;
int SSL_serialize_handshake_hints(const SSL *ssl, CBB *out) {
const auto *ssl_impl = FromOpaque(ssl);
const SSL_HANDSHAKE *hs = ssl_impl->s3->hs.get();
if (!ssl_impl->server || hs == nullptr || hs->pending_hints == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
const SSL_HANDSHAKE_HINTS *const hints = hs->pending_hints.get();
CBB seq, child;
if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE)) {
return 0;
}
if (!hints->server_random_tls13.empty()) {
if (!CBB_add_asn1_element(&seq, kServerRandomTLS13Tag,
hints->server_random_tls13.data(),
hints->server_random_tls13.size())) {
return 0;
}
}
if (hints->key_share_group_id != 0 && !hints->key_share_ciphertext.empty() &&
!hints->key_share_secret.empty()) {
if (!CBB_add_asn1(&seq, &child, kKeyShareHintTag) ||
!CBB_add_asn1_uint64(&child, hints->key_share_group_id) ||
!CBB_add_asn1_octet_string(&child, hints->key_share_ciphertext.data(),
hints->key_share_ciphertext.size()) ||
!CBB_add_asn1_octet_string(&child, hints->key_share_secret.data(),
hints->key_share_secret.size())) {
return 0;
}
}
if (hints->signature_algorithm != 0 && !hints->signature_input.empty() &&
!hints->signature.empty()) {
if (!CBB_add_asn1(&seq, &child, kSignatureHintTag) ||
!CBB_add_asn1_uint64(&child, hints->signature_algorithm) ||
!CBB_add_asn1_octet_string(&child, hints->signature_input.data(),
hints->signature_input.size()) ||
!CBB_add_asn1_octet_string(&child, hints->signature_spki.data(),
hints->signature_spki.size()) ||
!CBB_add_asn1_octet_string(&child, hints->signature.data(),
hints->signature.size())) {
return 0;
}
}
if (!hints->decrypted_psk.empty()) {
if (!CBB_add_asn1_element(&seq, kDecryptedPSKTag,
hints->decrypted_psk.data(),
hints->decrypted_psk.size())) {
return 0;
}
}
if (hints->ignore_psk && //
!CBB_add_asn1(&seq, &child, kIgnorePSKTag)) {
return 0;
}
if (hints->cert_compression_alg_id != 0 &&
!hints->cert_compression_input.empty() &&
!hints->cert_compression_output.empty()) {
if (!CBB_add_asn1(&seq, &child, kCompressCertificateTag) ||
!CBB_add_asn1_uint64(&child, hints->cert_compression_alg_id) ||
!CBB_add_asn1_octet_string(&child, hints->cert_compression_input.data(),
hints->cert_compression_input.size()) ||
!CBB_add_asn1_octet_string(&child,
hints->cert_compression_output.data(),
hints->cert_compression_output.size())) {
return 0;
}
}
if (!hints->server_random_tls12.empty()) {
if (!CBB_add_asn1_element(&seq, kServerRandomTLS12Tag,
hints->server_random_tls12.data(),
hints->server_random_tls12.size())) {
return 0;
}
}
if (hints->ecdhe_group_id != 0 && !hints->ecdhe_public_key.empty() &&
!hints->ecdhe_private_key.empty()) {
if (!CBB_add_asn1(&seq, &child, kECDHEHintTag) ||
!CBB_add_asn1_uint64(&child, hints->ecdhe_group_id) ||
!CBB_add_asn1_octet_string(&child, hints->ecdhe_public_key.data(),
hints->ecdhe_public_key.size()) ||
!CBB_add_asn1_octet_string(&child, hints->ecdhe_private_key.data(),
hints->ecdhe_private_key.size())) {
return 0;
}
}
if (!hints->decrypted_ticket.empty()) {
if (!CBB_add_asn1_element(&seq, kDecryptedTicketTag,
hints->decrypted_ticket.data(),
hints->decrypted_ticket.size())) {
return 0;
}
}
if (hints->renew_ticket && //
!CBB_add_asn1(&seq, &child, kRenewTicketTag)) {
return 0;
}
if (hints->ignore_ticket && //
!CBB_add_asn1(&seq, &child, kIgnoreTicketTag)) {
return 0;
}
return CBB_flush(out);
}
static bool get_optional_implicit_null(CBS *cbs, bool *out_present,
CBS_ASN1_TAG tag) {
CBS value;
int present;
if (!CBS_get_optional_asn1(cbs, &value, &present, tag) ||
(present && CBS_len(&value) != 0)) {
return false;
}
*out_present = present;
return true;
}
int SSL_set_handshake_hints(SSL *ssl, const uint8_t *hints, size_t hints_len) {
auto *ssl_impl = FromOpaque(ssl);
if (SSL_is_dtls(ssl_impl) || ssl_impl->s3->hs == nullptr) {
OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
UniquePtr<SSL_HANDSHAKE_HINTS> hints_obj = MakeUnique<SSL_HANDSHAKE_HINTS>();
if (hints_obj == nullptr) {
return 0;
}
CBS cbs, seq, server_random_tls13, key_share, signature_hint, psk,
cert_compression, server_random_tls12, ecdhe, ticket;
int has_server_random_tls13, has_key_share, has_signature_hint, has_psk,
has_cert_compression, has_server_random_tls12, has_ecdhe, has_ticket;
CBS_init(&cbs, hints, hints_len);
if (!CBS_get_asn1(&cbs, &seq, CBS_ASN1_SEQUENCE) ||
!CBS_get_optional_asn1(&seq, &server_random_tls13,
&has_server_random_tls13, kServerRandomTLS13Tag) ||
!CBS_get_optional_asn1(&seq, &key_share, &has_key_share,
kKeyShareHintTag) ||
!CBS_get_optional_asn1(&seq, &signature_hint, &has_signature_hint,
kSignatureHintTag) ||
!CBS_get_optional_asn1(&seq, &psk, &has_psk, kDecryptedPSKTag) ||
!get_optional_implicit_null(&seq, &hints_obj->ignore_psk,
kIgnorePSKTag) ||
!CBS_get_optional_asn1(&seq, &cert_compression, &has_cert_compression,
kCompressCertificateTag) ||
!CBS_get_optional_asn1(&seq, &server_random_tls12,
&has_server_random_tls12, kServerRandomTLS12Tag) ||
!CBS_get_optional_asn1(&seq, &ecdhe, &has_ecdhe, kECDHEHintTag) ||
!CBS_get_optional_asn1(&seq, &ticket, &has_ticket, kDecryptedTicketTag) ||
!get_optional_implicit_null(&seq, &hints_obj->renew_ticket,
kRenewTicketTag) ||
!get_optional_implicit_null(&seq, &hints_obj->ignore_ticket,
kIgnoreTicketTag)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
if (has_server_random_tls13 &&
!hints_obj->server_random_tls13.CopyFrom(server_random_tls13)) {
return 0;
}
if (has_key_share) {
uint64_t group_id;
CBS ciphertext, secret;
if (!CBS_get_asn1_uint64(&key_share, &group_id) || //
group_id == 0 || group_id > 0xffff ||
!CBS_get_asn1(&key_share, &ciphertext, CBS_ASN1_OCTETSTRING) ||
!hints_obj->key_share_ciphertext.CopyFrom(ciphertext) ||
!CBS_get_asn1(&key_share, &secret, CBS_ASN1_OCTETSTRING) ||
!hints_obj->key_share_secret.CopyFrom(secret)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
hints_obj->key_share_group_id = static_cast<uint16_t>(group_id);
}
if (has_signature_hint) {
uint64_t sig_alg;
CBS input, spki, signature;
if (!CBS_get_asn1_uint64(&signature_hint, &sig_alg) || //
sig_alg == 0 || sig_alg > 0xffff ||
!CBS_get_asn1(&signature_hint, &input, CBS_ASN1_OCTETSTRING) ||
!hints_obj->signature_input.CopyFrom(input) ||
!CBS_get_asn1(&signature_hint, &spki, CBS_ASN1_OCTETSTRING) ||
!hints_obj->signature_spki.CopyFrom(spki) ||
!CBS_get_asn1(&signature_hint, &signature, CBS_ASN1_OCTETSTRING) ||
!hints_obj->signature.CopyFrom(signature)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
hints_obj->signature_algorithm = static_cast<uint16_t>(sig_alg);
}
if (has_psk && !hints_obj->decrypted_psk.CopyFrom(psk)) {
return 0;
}
if (has_psk && hints_obj->ignore_psk) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
if (has_cert_compression) {
uint64_t alg;
CBS input, output;
if (!CBS_get_asn1_uint64(&cert_compression, &alg) || //
alg == 0 || alg > 0xffff ||
!CBS_get_asn1(&cert_compression, &input, CBS_ASN1_OCTETSTRING) ||
!hints_obj->cert_compression_input.CopyFrom(input) ||
!CBS_get_asn1(&cert_compression, &output, CBS_ASN1_OCTETSTRING) ||
!hints_obj->cert_compression_output.CopyFrom(output)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
hints_obj->cert_compression_alg_id = static_cast<uint16_t>(alg);
}
if (has_server_random_tls12 &&
!hints_obj->server_random_tls12.CopyFrom(server_random_tls12)) {
return 0;
}
if (has_ecdhe) {
uint64_t group_id;
CBS public_key, private_key;
if (!CBS_get_asn1_uint64(&ecdhe, &group_id) || //
group_id == 0 || group_id > 0xffff ||
!CBS_get_asn1(&ecdhe, &public_key, CBS_ASN1_OCTETSTRING) ||
!hints_obj->ecdhe_public_key.CopyFrom(public_key) ||
!CBS_get_asn1(&ecdhe, &private_key, CBS_ASN1_OCTETSTRING) ||
!hints_obj->ecdhe_private_key.CopyFrom(private_key)) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
hints_obj->ecdhe_group_id = static_cast<uint16_t>(group_id);
}
if (has_ticket && !hints_obj->decrypted_ticket.CopyFrom(ticket)) {
return 0;
}
if (has_ticket && hints_obj->ignore_ticket) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
if (!has_ticket && hints_obj->renew_ticket) {
OPENSSL_PUT_ERROR(SSL, SSL_R_COULD_NOT_PARSE_HINTS);
return 0;
}
ssl_impl->s3->hs->provided_hints = std::move(hints_obj);
return 1;
}