/* Copyright (c) 2015, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "internal.h" #include "../crypto/internal.h" #include "../crypto/fipsmodule/ec/internal.h" #include "../crypto/fipsmodule/ml_kem/ml_kem.h" #include "../crypto/kyber/kem_kyber.h" BSSL_NAMESPACE_BEGIN namespace { class ECKeyShare : public SSLKeyShare { public: ECKeyShare(const EC_GROUP *group, uint16_t group_id) : group_(group), group_id_(group_id) {} uint16_t GroupID() const override { return group_id_; } bool Offer(CBB *out) override { assert(!private_key_); // Generate a private key. private_key_.reset(BN_new()); if (!private_key_ || !BN_rand_range_ex(private_key_.get(), 1, EC_GROUP_get0_order(group_))) { return false; } // Compute the corresponding public key and serialize it. UniquePtr public_key(EC_POINT_new(group_)); if (!public_key || !EC_POINT_mul(group_, public_key.get(), private_key_.get(), nullptr, nullptr, /*ctx=*/nullptr) || !EC_POINT_point2cbb(out, group_, public_key.get(), POINT_CONVERSION_UNCOMPRESSED, /*ctx=*/nullptr)) { return false; } return true; } bool Finish(Array *out_secret, uint8_t *out_alert, Span peer_key) override { assert(group_); assert(private_key_); *out_alert = SSL_AD_INTERNAL_ERROR; UniquePtr peer_point(EC_POINT_new(group_)); UniquePtr result(EC_POINT_new(group_)); UniquePtr x(BN_new()); if (!peer_point || !result || !x) { return false; } if (peer_key.empty() || peer_key[0] != POINT_CONVERSION_UNCOMPRESSED || !EC_POINT_oct2point(group_, peer_point.get(), peer_key.data(), peer_key.size(), /*ctx=*/nullptr)) { OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); *out_alert = SSL_AD_DECODE_ERROR; return false; } // Compute the x-coordinate of |peer_key| * |private_key_|. if (!EC_POINT_mul(group_, result.get(), nullptr, peer_point.get(), private_key_.get(), /*ctx=*/nullptr) || !EC_POINT_get_affine_coordinates_GFp(group_, result.get(), x.get(), nullptr, /*ctx=*/nullptr)) { return false; } // Encode the x-coordinate left-padded with zeros. Array secret; if (!secret.Init((EC_GROUP_get_degree(group_) + 7) / 8) || !BN_bn2bin_padded(secret.data(), secret.size(), x.get())) { return false; } *out_secret = std::move(secret); return true; } bool SerializePrivateKey(CBB *out) override { assert(group_); assert(private_key_); // Padding is added to avoid leaking the length. size_t len = BN_num_bytes(EC_GROUP_get0_order(group_)); return BN_bn2cbb_padded(out, len, private_key_.get()); } bool DeserializePrivateKey(CBS *in) override { assert(!private_key_); private_key_.reset(BN_bin2bn(CBS_data(in), CBS_len(in), nullptr)); return private_key_ != nullptr; } private: UniquePtr private_key_; const EC_GROUP *const group_ = nullptr; uint16_t group_id_; }; class X25519KeyShare : public SSLKeyShare { public: X25519KeyShare() {} uint16_t GroupID() const override { return SSL_GROUP_X25519; } bool Offer(CBB *out) override { uint8_t public_key[32]; X25519_keypair(public_key, private_key_); return !!CBB_add_bytes(out, public_key, sizeof(public_key)); } bool Finish(Array *out_secret, uint8_t *out_alert, Span peer_key) override { *out_alert = SSL_AD_INTERNAL_ERROR; Array secret; if (!secret.Init(32)) { return false; } if (peer_key.size() != 32 || !X25519(secret.data(), private_key_, peer_key.data())) { *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT); return false; } *out_secret = std::move(secret); return true; } bool SerializePrivateKey(CBB *out) override { return CBB_add_bytes(out, private_key_, sizeof(private_key_)); } bool DeserializePrivateKey(CBS *in) override { if (CBS_len(in) != sizeof(private_key_) || !CBS_copy_bytes(in, private_key_, sizeof(private_key_))) { return false; } return true; } private: uint8_t private_key_[32]; }; class KEMKeyShare : public SSLKeyShare { public: KEMKeyShare(int nid, uint16_t group_id) : nid_(nid), group_id_(group_id) {} uint16_t GroupID() const override { return group_id_; } bool Offer(CBB *out) override { // Ensure |out| is valid, has no children, and has been initialized. // We check that |out| has no children because otherwise CBB_data() // will produce a fatal error by way of assert(cbb->child == NULL). if (!out || out->child || !CBB_data(out)) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // ctx_ should not have been initialized (by e.g. a previous call // to Offer()). if (ctx_) { OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return false; } // Initialize the KEM ctx ctx_.reset(EVP_PKEY_CTX_new_id(EVP_PKEY_KEM, nullptr)); if (!ctx_ || !EVP_PKEY_CTX_kem_set_params(ctx_.get(), nid_)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Generate a KEM keypair and retrieve the length of the public key size_t public_key_len = 0; EVP_PKEY *raw_key = nullptr; if (!EVP_PKEY_keygen_init(ctx_.get()) || !EVP_PKEY_keygen(ctx_.get(), &raw_key) || !raw_key || !EVP_PKEY_get_raw_public_key(raw_key, nullptr, &public_key_len)) { EVP_PKEY_free(raw_key); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Retain the secret key for decapsulation UniquePtr pkey(raw_key); ctx_.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr)); if (!ctx_) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } uint8_t *public_key = NULL; size_t public_key_bytes_written = public_key_len; if (!CBB_add_space(out, &public_key, public_key_len) || !EVP_PKEY_get_raw_public_key(pkey.get(), public_key, &public_key_bytes_written) || public_key_bytes_written != public_key_len) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } return true; } // Because this is a KEM key share, |out_public_key| will be the ciphertext // resulting from the encapsulation of the shared secret under the // received public key, |peek_key|. bool Accept(CBB *out_public_key, Array *out_secret, uint8_t *out_alert, Span peer_key) override { // Basic nullptr checks if (!out_alert) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Set alert to internal error by default *out_alert = SSL_AD_INTERNAL_ERROR; if (!out_secret) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Ensure |out_public_key| is valid, has no children, and has been // initialized. We check that |out_public_key| has no children because // otherwise CBB_data() will produce a fatal error by way of // assert(cbb->child == NULL). if (!out_public_key || out_public_key->child || !CBB_data(out_public_key)) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // ctx_ should not have been initialized (by e.g. a call to Offer()). if (ctx_) { OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return false; } // Ensure that peer_key is valid if (!peer_key.data() || peer_key.empty()) { *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Initialize pkey from the received public key UniquePtr pkey( EVP_PKEY_kem_new_raw_public_key(nid_, peer_key.begin(), peer_key.size())); if (!pkey) { *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Initialize the context with the pkey ctx_.reset(EVP_PKEY_CTX_new(pkey.get(), nullptr)); if (!ctx_) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Retrieve the lengths of the ciphertext and shared secret size_t ciphertext_len = 0; size_t secret_len = 0; if (!EVP_PKEY_encapsulate(ctx_.get(), nullptr, &ciphertext_len, nullptr, &secret_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // EVP_PKEY_encapsulate() will generate a shared secret, then encapsulate it // with the peer's public key. We send the resultant ciphertext to the // peer by writing it to |out_public_key|, then... Array shared_secret; if (!shared_secret.Init(secret_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return false; } uint8_t *ciphertext = NULL; size_t ciphertext_bytes_written = ciphertext_len; size_t secret_bytes_written = secret_len; if (!CBB_add_space(out_public_key, &ciphertext, ciphertext_len) || !EVP_PKEY_encapsulate(ctx_.get(), ciphertext, &ciphertext_bytes_written, shared_secret.data(), &secret_bytes_written) || ciphertext_bytes_written != ciphertext_len || secret_bytes_written != secret_len) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // ...we retain the shared secret for our own use. *out_secret = std::move(shared_secret); // Success; clear the alert *out_alert = 0; return true; } // Because this is a KEM key share, |peer_key| is actually the ciphertext // resulting from the peer encapsulating the shared secret under our public key. // In Finish(), we use our previously generated secret key to decrypt // that ciphertext and obtain the shared secret. bool Finish(Array *out_secret, uint8_t *out_alert, Span peer_key) override { // Basic nullptr checks if (!out_alert) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Set alert to internal error by default *out_alert = SSL_AD_INTERNAL_ERROR; if (!out_secret) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // ctx_ should have been initialized by a previous call to Offer() if (!ctx_) { OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return false; } // Retrieve the length of the shared secret size_t secret_len = 0; if (!EVP_PKEY_decapsulate(ctx_.get(), nullptr, &secret_len, nullptr, peer_key.size())) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // |peer_key| is the ciphertext, encapsulating the shared secret, // that the peer generated using our public key. We use our secret // key to decapsulate it, and retain the shared secret by writing // it to |out_secret|. uint8_t *ciphertext = (uint8_t *)peer_key.begin(); Array shared_secret; if (!shared_secret.Init(secret_len)) { OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE); return false; } size_t secret_bytes_written = secret_len; if (!EVP_PKEY_decapsulate(ctx_.get(), shared_secret.data(), &secret_bytes_written, ciphertext, peer_key.size()) || secret_bytes_written != secret_len) { OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_KEM_CIPHERTEXT); return false; } *out_secret = std::move(shared_secret); // Success; clear the alert *out_alert = 0; return true; } private: int nid_; uint16_t group_id_; UniquePtr ctx_; }; // A HybridKeyShare consists of key shares from two or more component groups, // all of which are used to generate a hybrid shared secret. // See https://datatracker.ietf.org/doc/html/draft-ietf-tls-hybrid-design. class HybridKeyShare : public SSLKeyShare { public: HybridKeyShare(uint16_t group_id) : group_id_(group_id), exchange_performed(false), hybrid_group_(nullptr) { for (const HybridGroup &hybrid_group : HybridGroups()) { if (group_id_ == hybrid_group.group_id) { hybrid_group_ = &hybrid_group; for (size_t i = 0; i < NUM_HYBRID_COMPONENTS; i++) { key_shares_[i] = SSLKeyShare::Create(hybrid_group.component_group_ids[i]); } return; } } hybrid_group_ = nullptr; } uint16_t GroupID() const override { return group_id_; } bool Offer(CBB *out) override { // Ensure |out| is valid, has no children, and has been initialized. // We check that |out| has no children because otherwise CBB_data() // will produce a fatal error by way of assert(cbb->child == NULL); if (!out || out->child || !CBB_data(out)) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } if (!hybrid_group_ || this->exchange_performed) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Iterate through the component groups and Offer() each of their key // shares. If one of the calls to a component Offer() fails, // OPENSSL_PUT_ERROR will be set appropriately in that component. for (const UniquePtr &key_share : key_shares_) { if (!key_share || !key_share->Offer(out)) { return false; } } this->exchange_performed = true; return true; } bool Accept(CBB *out_public_key, Array *out_secret, uint8_t *out_alert, Span peer_key) override { if (!out_alert) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Set alert to internal error by default *out_alert = SSL_AD_INTERNAL_ERROR; if (!out_secret|| !peer_key.data()) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Ensure |out_public_key| is valid, has no children, and has been // initialized. We check that |out_public_key| has no children because // otherwise CBB_data() will produce a fatal error by way of // assert(cbb->child == NULL); if (!out_public_key || out_public_key->child || !CBB_data(out_public_key)) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } if (!hybrid_group_ || this->exchange_performed) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // A hybrid shared secret with two components should be 64 bytes. // If it happens to be larger, the CBB will grow accordingly. CBB hybrid_shared_secret; if (!CBB_init(&hybrid_shared_secret, 64)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Accept() each component key share. Each component's Accept() function // will generate a shared secret and a public key to be sent back to // the peer. The hybrid public key is the concatenation of all component // public keys; the hybrid shared secret is the concatenation of all // component shared secrets. size_t peer_key_read_index = 0; for (size_t i = 0; i < NUM_HYBRID_COMPONENTS; i++) { size_t component_key_size = 0; if (!get_component_offer_key_share_size(&component_key_size, hybrid_group_->component_group_ids[i])) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Verify that |peer_key| contains enough data if (peer_key_read_index + component_key_size > peer_key.size()) { CBB_cleanup(&hybrid_shared_secret); *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_HYBRID_KEYSHARE); return false; } Span component_key = peer_key.subspan(peer_key_read_index, component_key_size); Array component_secret; if (!key_shares_[i] || !key_shares_[i]->Accept(out_public_key, &component_secret, out_alert, component_key) || !CBB_add_bytes(&hybrid_shared_secret, component_secret.data(), component_secret.size())) { CBB_cleanup(&hybrid_shared_secret); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } peer_key_read_index += component_key_size; } // Final validation that |peer_key| was the correct size if (peer_key_read_index != peer_key.size()) { CBB_cleanup(&hybrid_shared_secret); *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Retain the hybrid shared secret for our use. if (!CBBFinishArray(&hybrid_shared_secret, out_secret)) { CBB_cleanup(&hybrid_shared_secret); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Success; clear the alert *out_alert = 0; this->exchange_performed = true; return true; } bool Finish(Array *out_secret, uint8_t *out_alert, Span peer_key) override { if (!out_alert) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } // Set alert to internal error by default *out_alert = SSL_AD_INTERNAL_ERROR; if (!out_secret || !peer_key.data()) { OPENSSL_PUT_ERROR(SSL, ERR_R_PASSED_NULL_PARAMETER); return false; } if (!hybrid_group_ || !this->exchange_performed) { OPENSSL_PUT_ERROR(SSL, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED); return false; } // A hybrid shared secret with two components should be 64 bytes. // If it happens to be larger, the CBB will grow accordingly. CBB hybrid_shared_secret; if (!CBB_init(&hybrid_shared_secret, 64)) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Finish() each component key share. Each component's Finish() function // will generate a shared secret. The hybrid shared secret is the // concatenation of all component shared secrets. size_t peer_key_index = 0; for (size_t i = 0; i < NUM_HYBRID_COMPONENTS; i++) { size_t component_key_size = 0; if (!get_component_accept_key_share_size(&component_key_size, hybrid_group_->component_group_ids[i])) { OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Verify that |peer_key| contains enough data if (peer_key_index + component_key_size > peer_key.size()) { CBB_cleanup(&hybrid_shared_secret); *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } Span component_key = peer_key.subspan(peer_key_index, component_key_size); Array component_secret; if (!key_shares_[i] || !key_shares_[i]->Finish(&component_secret, out_alert, component_key) || !CBB_add_bytes(&hybrid_shared_secret, component_secret.data(), component_secret.size())) { CBB_cleanup(&hybrid_shared_secret); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } peer_key_index += component_key_size; } // Final validation that |peer_key| was the correct size if (peer_key_index != peer_key.size()) { CBB_cleanup(&hybrid_shared_secret); *out_alert = SSL_AD_DECODE_ERROR; OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Retain the hybrid shared secret for our use. if (!CBBFinishArray(&hybrid_shared_secret, out_secret)) { CBB_cleanup(&hybrid_shared_secret); OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR); return false; } // Success; clear the alert *out_alert = 0; return true; } private: // Only need to support SSL Groups that are a component of a supported HybridKeyShare bool get_component_offer_key_share_size(size_t *out, uint16_t component_group_id) { switch (component_group_id) { case SSL_GROUP_SECP256R1: *out = 1 + (2 * EC_P256R1_FIELD_ELEM_BYTES); return true; case SSL_GROUP_KYBER768_R3: *out = KYBER768_R3_PUBLIC_KEY_BYTES; return true; case SSL_GROUP_MLKEM768: *out = MLKEM768_PUBLIC_KEY_BYTES; return true; case SSL_GROUP_X25519: *out = 32; return true; default: return false; } } // Only need to support SSL Groups that are a component of a supported HybridKeyShare bool get_component_accept_key_share_size(size_t *out, uint16_t component_group_id) { switch (component_group_id) { case SSL_GROUP_SECP256R1: *out = 1 + (2 * EC_P256R1_FIELD_ELEM_BYTES); return true; case SSL_GROUP_KYBER768_R3: *out = KYBER768_R3_CIPHERTEXT_BYTES; return true; case SSL_GROUP_MLKEM768: *out = MLKEM768_CIPHERTEXT_BYTES; return true; case SSL_GROUP_X25519: *out = 32; return true; default: return false; } } uint16_t group_id_; bool exchange_performed; const HybridGroup *hybrid_group_; UniquePtr key_shares_[NUM_HYBRID_COMPONENTS]; }; CONSTEXPR_ARRAY NamedGroup kNamedGroups[] = { {NID_secp224r1, SSL_GROUP_SECP224R1, "P-224", "secp224r1"}, {NID_X9_62_prime256v1, SSL_GROUP_SECP256R1, "P-256", "prime256v1"}, {NID_secp384r1, SSL_GROUP_SECP384R1, "P-384", "secp384r1"}, {NID_secp521r1, SSL_GROUP_SECP521R1, "P-521", "secp521r1"}, {NID_X25519, SSL_GROUP_X25519, "X25519", "x25519"}, {NID_SecP256r1Kyber768Draft00, SSL_GROUP_SECP256R1_KYBER768_DRAFT00, "SecP256r1Kyber768Draft00", ""}, {NID_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00, "X25519Kyber768Draft00", ""}, {NID_SecP256r1MLKEM768, SSL_GROUP_SECP256R1_MLKEM768, "SecP256r1MLKEM768", ""}, {NID_X25519MLKEM768, SSL_GROUP_X25519_MLKEM768, "X25519MLKEM768", ""}, }; CONSTEXPR_ARRAY uint16_t kPQGroups[] = { SSL_GROUP_KYBER512_R3, SSL_GROUP_KYBER768_R3, SSL_GROUP_KYBER1024_R3, SSL_GROUP_MLKEM768, SSL_GROUP_MLKEM1024, SSL_GROUP_SECP256R1_KYBER768_DRAFT00, SSL_GROUP_X25519_KYBER768_DRAFT00, SSL_GROUP_SECP256R1_MLKEM768, SSL_GROUP_X25519_MLKEM768 }; CONSTEXPR_ARRAY HybridGroup kHybridGroups[] = { { SSL_GROUP_SECP256R1_KYBER768_DRAFT00, // group_id { SSL_GROUP_SECP256R1, // component_group_ids[0] SSL_GROUP_KYBER768_R3, // component_group_ids[1] }, }, { SSL_GROUP_X25519_KYBER768_DRAFT00, // group_id { SSL_GROUP_X25519, // component_group_ids[0] SSL_GROUP_KYBER768_R3, // component_group_ids[1] }, }, { SSL_GROUP_SECP256R1_MLKEM768, // group_id { SSL_GROUP_SECP256R1, // component_group_ids[0] SSL_GROUP_MLKEM768, // component_group_ids[1] }, }, { SSL_GROUP_X25519_MLKEM768, // group_id { // Note: MLKEM768 is sent first due to FIPS requirements. // For more details, see https://datatracker.ietf.org/doc/html/draft-kwiatkowski-tls-ecdhe-mlkem.html#section-3 SSL_GROUP_MLKEM768, // component_group_ids[0] SSL_GROUP_X25519, // component_group_ids[1] }, } }; } // namespace Span NamedGroups() { return MakeConstSpan(kNamedGroups, OPENSSL_ARRAY_SIZE(kNamedGroups)); } Span HybridGroups() { return MakeConstSpan(kHybridGroups, OPENSSL_ARRAY_SIZE(kHybridGroups)); } Span PQGroups() { return MakeConstSpan(kPQGroups, OPENSSL_ARRAY_SIZE(kPQGroups)); } UniquePtr SSLKeyShare::Create(uint16_t group_id) { switch (group_id) { case SSL_GROUP_SECP224R1: return MakeUnique(EC_group_p224(), SSL_GROUP_SECP224R1); case SSL_GROUP_SECP256R1: return MakeUnique(EC_group_p256(), SSL_GROUP_SECP256R1); case SSL_GROUP_SECP384R1: return MakeUnique(EC_group_p384(), SSL_GROUP_SECP384R1); case SSL_GROUP_SECP521R1: return MakeUnique(EC_group_p521(), SSL_GROUP_SECP521R1); case SSL_GROUP_X25519: return MakeUnique(); case SSL_GROUP_KYBER768_R3: // Kyber, as a standalone group, is not a NamedGroup; however, we // need to create Kyber key shares as part of hybrid groups. return MakeUnique(NID_KYBER768_R3, SSL_GROUP_KYBER768_R3); case SSL_GROUP_SECP256R1_KYBER768_DRAFT00: return MakeUnique(SSL_GROUP_SECP256R1_KYBER768_DRAFT00); case SSL_GROUP_X25519_KYBER768_DRAFT00: return MakeUnique(SSL_GROUP_X25519_KYBER768_DRAFT00); case SSL_GROUP_MLKEM768: // MLKEM768, as a standalone group, is not a NamedGroup; however, we // need to create MLKEM768 key shares as part of hybrid groups. return MakeUnique(NID_MLKEM768, SSL_GROUP_MLKEM768); case SSL_GROUP_SECP256R1_MLKEM768: return MakeUnique(SSL_GROUP_SECP256R1_MLKEM768); case SSL_GROUP_X25519_MLKEM768: return MakeUnique(SSL_GROUP_X25519_MLKEM768); default: return nullptr; } } bool SSLKeyShare::Accept(CBB *out_public_key, Array *out_secret, uint8_t *out_alert, Span peer_key) { *out_alert = SSL_AD_INTERNAL_ERROR; return Offer(out_public_key) && Finish(out_secret, out_alert, peer_key); } bool ssl_nid_to_group_id(uint16_t *out_group_id, int nid) { for (const auto &group : kNamedGroups) { if (group.nid == nid) { *out_group_id = group.group_id; return true; } } return false; } bool ssl_name_to_group_id(uint16_t *out_group_id, const char *name, size_t len) { for (const auto &group : kNamedGroups) { if (len == strlen(group.name) && !strncmp(group.name, name, len)) { *out_group_id = group.group_id; return true; } if ((strlen(group.alias) > 0) && len == strlen(group.alias) && !strncmp(group.alias, name, len)) { *out_group_id = group.group_id; return true; } } return false; } BSSL_NAMESPACE_END using namespace bssl; const char* SSL_get_group_name(uint16_t group_id) { for (const auto &group : kNamedGroups) { if (group.group_id == group_id) { return group.name; } } return nullptr; } size_t SSL_get_all_group_names(const char **out, size_t max_out) { return GetAllNames(out, max_out, Span(), &NamedGroup::name, MakeConstSpan(kNamedGroups)); }