|
|
|
/* 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
|
|
|
}
|