Mirror of BoringSSL (grpc依赖) https://boringssl.googlesource.com/boringssl
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

372 lines
11 KiB

/* 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 <openssl/ssl.h>
#include <assert.h>
#include <string.h>
#include <utility>
#include <openssl/bn.h>
#include <openssl/bytestring.h>
#include <openssl/curve25519.h>
#include <openssl/ec.h>
#include <openssl/err.h>
#include <openssl/kyber.h>
#include <openssl/hrss.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
Add APIs to query a list of possible strings for TLS features Envoy needs to have the possible cipher, etc., strings predeclared to reduce synchronization needs in the steady state. It currently does this by (1) iterating over SSL_CTX_get_ciphers at SSL_CTX creation time and (2) hard-coding a lists of known TLS 1.3 ciphers, TLS versions, NamedGroups, etc. (1) would work for some applications, but it breaks any applications that configure ciphers on the SSL on a certificate callback, etc. If the callback configures a cipher that wasn't configured on the SSL_CTX (e.g. if the SSL_CTX were left at defaults), Envoy's logging breaks and we hit an ENVOY_BUG assertion. (2) breaks whenever BoringSSL adds a new feature. In principle, we could update Envoy when updating BoringSSL, but this is an unresasonable development overhead for just one of many BoringSSL consumers to impose. Such costs are particularly high when considering needing to coordinate updates to Envoy and BoringSSL across different repositories. Add APIs to enumerate the possible strings these functions can return. These string lists are a superset of those that any one application may care about (e.g. we may have a deprecated cipher that Envoy no longer needs, or an experimental cipher that's not yet ready for Envoy's stability goals), but this is fine provided this is just used to initialize the table. In particular, they are *not* intended to enumerate supported features. Bump BORINGSSL_API_VERSION to aid in patching these into Envoy. Bug: b:280350955 Change-Id: I4d11db980eebed5620d3657778c09dbec004653c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59667 Commit-Queue: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
2 years ago
#include <openssl/span.h>
#include "internal.h"
#include "../crypto/internal.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_; }
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Generate(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<EC_POINT> 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;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
uint8_t *out_alert, Span<const uint8_t> peer_key) override {
// ECDH may be fit into a KEM-like abstraction by using a second keypair's
// public key as the ciphertext.
*out_alert = SSL_AD_INTERNAL_ERROR;
return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
}
bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
Span<const uint8_t> ciphertext) override {
assert(group_);
assert(private_key_);
*out_alert = SSL_AD_INTERNAL_ERROR;
UniquePtr<EC_POINT> peer_point(EC_POINT_new(group_));
UniquePtr<EC_POINT> result(EC_POINT_new(group_));
UniquePtr<BIGNUM> x(BN_new());
if (!peer_point || !result || !x) {
return false;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
if (ciphertext.empty() || ciphertext[0] != POINT_CONVERSION_UNCOMPRESSED ||
!EC_POINT_oct2point(group_, peer_point.get(), ciphertext.data(),
ciphertext.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<uint8_t> 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<BIGNUM> 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; }
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Generate(CBB *out) override {
uint8_t public_key[32];
X25519_keypair(public_key, private_key_);
return !!CBB_add_bytes(out, public_key, sizeof(public_key));
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
uint8_t *out_alert, Span<const uint8_t> peer_key) override {
// X25519 may be fit into a KEM-like abstraction by using a second keypair's
// public key as the ciphertext.
*out_alert = SSL_AD_INTERNAL_ERROR;
return Generate(out_ciphertext) && Decap(out_secret, out_alert, peer_key);
}
bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
Span<const uint8_t> ciphertext) override {
*out_alert = SSL_AD_INTERNAL_ERROR;
Array<uint8_t> secret;
if (!secret.Init(32)) {
return false;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
if (ciphertext.size() != 32 || //
!X25519(secret.data(), private_key_, ciphertext.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 X25519Kyber768KeyShare : public SSLKeyShare {
public:
X25519Kyber768KeyShare() {}
uint16_t GroupID() const override {
return SSL_GROUP_X25519_KYBER768_DRAFT00;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Generate(CBB *out) override {
uint8_t x25519_public_key[32];
X25519_keypair(x25519_public_key, x25519_private_key_);
uint8_t kyber_public_key[KYBER_PUBLIC_KEY_BYTES];
KYBER_generate_key(kyber_public_key, &kyber_private_key_);
if (!CBB_add_bytes(out, x25519_public_key, sizeof(x25519_public_key)) ||
!CBB_add_bytes(out, kyber_public_key, sizeof(kyber_public_key))) {
return false;
}
return true;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Encap(CBB *out_ciphertext, Array<uint8_t> *out_secret,
uint8_t *out_alert, Span<const uint8_t> peer_key) override {
Array<uint8_t> secret;
if (!secret.Init(32 + 32)) {
return false;
}
uint8_t x25519_public_key[32];
X25519_keypair(x25519_public_key, x25519_private_key_);
KYBER_public_key peer_kyber_pub;
CBS peer_key_cbs;
CBS peer_x25519_cbs;
CBS peer_kyber_cbs;
CBS_init(&peer_key_cbs, peer_key.data(), peer_key.size());
if (!CBS_get_bytes(&peer_key_cbs, &peer_x25519_cbs, 32) ||
!CBS_get_bytes(&peer_key_cbs, &peer_kyber_cbs,
KYBER_PUBLIC_KEY_BYTES) ||
CBS_len(&peer_key_cbs) != 0 ||
!X25519(secret.data(), x25519_private_key_,
CBS_data(&peer_x25519_cbs)) ||
!KYBER_parse_public_key(&peer_kyber_pub, &peer_kyber_cbs)) {
*out_alert = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
return false;
}
uint8_t kyber_ciphertext[KYBER_CIPHERTEXT_BYTES];
KYBER_encap(kyber_ciphertext, secret.data() + 32, secret.size() - 32,
&peer_kyber_pub);
if (!CBB_add_bytes(out_ciphertext, x25519_public_key,
sizeof(x25519_public_key)) ||
!CBB_add_bytes(out_ciphertext, kyber_ciphertext,
sizeof(kyber_ciphertext))) {
return false;
}
*out_secret = std::move(secret);
return true;
}
Use KEM terminology in TLS ECDHE and key_share abstractions TLS 1.2 ECDHE and TLS 1.3 key shares were originally designed around Diffie-Hellman-like primitives and use language based on that. Post-quantum replacements do not look like Diffie-Hellman, where each part exchanges a public key, but schemes that work differently can still slot in without protocol changes. We previously came up with our own Offer/Accept/Finish abstraction for early post-quantum experiments, but the NIST constructions are all expressed as KEMs: First, the recipient generates a keypair and sends the public key. Then the sender encapsulates a symmetric secret and sends the ciphertext. Finally, the recipient decapsulates the ciphertext to get the secret. Align our C++ and Go abstractions to this terminology. The functions are now called Generate/Encap/Decap, and the output of Encap is called "ciphertext", which seems to align with what most folks use. (RFC 9180 uses "enc" for "encapsulated key", but they staple a KEM to an AEAD, so "ciphertext" would be ambiguous.) Where variable names refer to parts of the protocol, rather than the the underlying KEM-like construction, I've kept variable names matching the protocol mechanism, so we still talk about "curves" and "key shares", but, when using the post-quantum replacements, the terminology is no longer quite accurate. I've also not yet renamed SSLKeyShare yet, though the name is now inaccurate. Also ideally we'd touch that up so the stateful object is just a KEM private key, for SSLKEMKey. Though at that point, we maybe should just add EVP_KEM and EVP_KEM_KEY APIs to libcrypto. Change-Id: Icbcc1840c5d2dfad210ef4caad2a7c4bf8146553 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/57726 Reviewed-by: Adam Langley <agl@google.com> Commit-Queue: David Benjamin <davidben@google.com>
2 years ago
bool Decap(Array<uint8_t> *out_secret, uint8_t *out_alert,
Span<const uint8_t> ciphertext) override {
*out_alert = SSL_AD_INTERNAL_ERROR;
Array<uint8_t> secret;
if (!secret.Init(32 + 32)) {
return false;
}
if (ciphertext.size() != 32 + KYBER_CIPHERTEXT_BYTES ||
!X25519(secret.data(), x25519_private_key_, ciphertext.data())) {
*out_alert = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_ECPOINT);
return false;
}
KYBER_decap(secret.data() + 32, secret.size() - 32, ciphertext.data() + 32,
&kyber_private_key_);
*out_secret = std::move(secret);
return true;
}
private:
uint8_t x25519_private_key_[32];
KYBER_private_key kyber_private_key_;
};
constexpr 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_X25519Kyber768Draft00, SSL_GROUP_X25519_KYBER768_DRAFT00,
"X25519Kyber768Draft00", ""},
};
} // namespace
Span<const NamedGroup> NamedGroups() {
return MakeConstSpan(kNamedGroups, OPENSSL_ARRAY_SIZE(kNamedGroups));
}
UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
switch (group_id) {
case SSL_GROUP_SECP224R1:
return MakeUnique<ECKeyShare>(EC_group_p224(), SSL_GROUP_SECP224R1);
case SSL_GROUP_SECP256R1:
return MakeUnique<ECKeyShare>(EC_group_p256(), SSL_GROUP_SECP256R1);
case SSL_GROUP_SECP384R1:
return MakeUnique<ECKeyShare>(EC_group_p384(), SSL_GROUP_SECP384R1);
case SSL_GROUP_SECP521R1:
return MakeUnique<ECKeyShare>(EC_group_p521(), SSL_GROUP_SECP521R1);
case SSL_GROUP_X25519:
return MakeUnique<X25519KeyShare>();
case SSL_GROUP_X25519_KYBER768_DRAFT00:
return MakeUnique<X25519Kyber768KeyShare>();
default:
return nullptr;
}
}
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;
}
Align NIDs vs group IDs in TLS group APIs Right now we use NIDs to configure the group list, but group IDs (the TLS codepoints) to return the negotiated group. The NIDs come from OpenSSL, while the group ID was original our API. OpenSSL has since added SSL_get_negotiated_group, but we don't implement it. To add Kyber to QUIC, we'll need to add an API for configuring groups to QUICHE. Carrying over our inconsistency into QUICHE's public API would be unfortunate, so let's use this as the time to align things. We could either align with OpenSSL and say NIDs are now the group representation at the public API, or we could add a parallel group ID API. (Or we could make a whole new SSL_NAMED_GROUP object to pattern after SSL_CIPHER, which isn't wrong, but is even more new APIs.) Aligning with OpenSSL would be fewer APIs, but NIDs aren't a great representation. The numbers are ad-hoc and even diverge a bit between OpenSSL and BoringSSL. The TLS codepoints are better to export out to callers. Also QUICHE has exported the negotiated group using the codepoints, so the natural solution would be to use codepoints on input too. Thus, this CL adds SSL_CTX_set1_group_ids and SSL_set1_group_ids. It also rearranges the API docs slightly to put the group ID ones first, and leaves a little note about the NID representation before introducing those. While I'm here, I've added SSL_get_negotiated_group. NGINX seems to use it when available, so we may as well fill in that unnecessary compatibility hole. Bug: chromium:1442377 Change-Id: I47ca8ae52c274133f28da9893aed7fc70f942bf8 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60208 Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
1 year ago
int ssl_group_id_to_nid(uint16_t group_id) {
for (const auto &group : kNamedGroups) {
if (group.group_id == group_id) {
return group.nid;
}
}
return NID_undef;
}
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;
}
Add APIs to query a list of possible strings for TLS features Envoy needs to have the possible cipher, etc., strings predeclared to reduce synchronization needs in the steady state. It currently does this by (1) iterating over SSL_CTX_get_ciphers at SSL_CTX creation time and (2) hard-coding a lists of known TLS 1.3 ciphers, TLS versions, NamedGroups, etc. (1) would work for some applications, but it breaks any applications that configure ciphers on the SSL on a certificate callback, etc. If the callback configures a cipher that wasn't configured on the SSL_CTX (e.g. if the SSL_CTX were left at defaults), Envoy's logging breaks and we hit an ENVOY_BUG assertion. (2) breaks whenever BoringSSL adds a new feature. In principle, we could update Envoy when updating BoringSSL, but this is an unresasonable development overhead for just one of many BoringSSL consumers to impose. Such costs are particularly high when considering needing to coordinate updates to Envoy and BoringSSL across different repositories. Add APIs to enumerate the possible strings these functions can return. These string lists are a superset of those that any one application may care about (e.g. we may have a deprecated cipher that Envoy no longer needs, or an experimental cipher that's not yet ready for Envoy's stability goals), but this is fine provided this is just used to initialize the table. In particular, they are *not* intended to enumerate supported features. Bump BORINGSSL_API_VERSION to aid in patching these into Envoy. Bug: b:280350955 Change-Id: I4d11db980eebed5620d3657778c09dbec004653c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59667 Commit-Queue: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
2 years ago
size_t SSL_get_all_group_names(const char **out, size_t max_out) {
return GetAllNames(out, max_out, Span<const char *>(), &NamedGroup::name,
MakeConstSpan(kNamedGroups));
Add APIs to query a list of possible strings for TLS features Envoy needs to have the possible cipher, etc., strings predeclared to reduce synchronization needs in the steady state. It currently does this by (1) iterating over SSL_CTX_get_ciphers at SSL_CTX creation time and (2) hard-coding a lists of known TLS 1.3 ciphers, TLS versions, NamedGroups, etc. (1) would work for some applications, but it breaks any applications that configure ciphers on the SSL on a certificate callback, etc. If the callback configures a cipher that wasn't configured on the SSL_CTX (e.g. if the SSL_CTX were left at defaults), Envoy's logging breaks and we hit an ENVOY_BUG assertion. (2) breaks whenever BoringSSL adds a new feature. In principle, we could update Envoy when updating BoringSSL, but this is an unresasonable development overhead for just one of many BoringSSL consumers to impose. Such costs are particularly high when considering needing to coordinate updates to Envoy and BoringSSL across different repositories. Add APIs to enumerate the possible strings these functions can return. These string lists are a superset of those that any one application may care about (e.g. we may have a deprecated cipher that Envoy no longer needs, or an experimental cipher that's not yet ready for Envoy's stability goals), but this is fine provided this is just used to initialize the table. In particular, they are *not* intended to enumerate supported features. Bump BORINGSSL_API_VERSION to aid in patching these into Envoy. Bug: b:280350955 Change-Id: I4d11db980eebed5620d3657778c09dbec004653c Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59667 Commit-Queue: Adam Langley <agl@google.com> Auto-Submit: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
2 years ago
}