|
|
|
/* Copyright (c) 2016, 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 <algorithm>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <openssl/aead.h>
|
|
|
|
#include <openssl/bytestring.h>
|
|
|
|
#include <openssl/digest.h>
|
|
|
|
#include <openssl/hkdf.h>
|
|
|
|
#include <openssl/hmac.h>
|
|
|
|
#include <openssl/mem.h>
|
|
|
|
|
|
|
|
#include "../crypto/internal.h"
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
BSSL_NAMESPACE_BEGIN
|
|
|
|
|
|
|
|
static bool init_key_schedule(SSL_HANDSHAKE *hs, uint16_t version,
|
|
|
|
const SSL_CIPHER *cipher) {
|
|
|
|
if (!hs->transcript.InitHash(version, cipher)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialize the secret to the zero key.
|
|
|
|
hs->ResizeSecrets(hs->transcript.DigestLen());
|
|
|
|
OPENSSL_memset(hs->secret().data(), 0, hs->secret().size());
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hkdf_extract_to_secret(SSL_HANDSHAKE *hs, Span<const uint8_t> in) {
|
|
|
|
size_t len;
|
|
|
|
if (!HKDF_extract(hs->secret().data(), &len, hs->transcript.Digest(),
|
|
|
|
in.data(), in.size(), hs->secret().data(),
|
|
|
|
hs->secret().size())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
assert(len == hs->secret().size());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_init_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk) {
|
|
|
|
if (!init_key_schedule(hs, ssl_protocol_version(hs->ssl), hs->new_cipher)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handback includes the whole handshake transcript, so we cannot free the
|
|
|
|
// transcript buffer in the handback case.
|
|
|
|
if (!hs->handback) {
|
|
|
|
hs->transcript.FreeBuffer();
|
|
|
|
}
|
|
|
|
return hkdf_extract_to_secret(hs, psk);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_init_early_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> psk) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
return init_key_schedule(hs, ssl_session_protocol_version(ssl->session.get()),
|
|
|
|
ssl->session->cipher) &&
|
|
|
|
hkdf_extract_to_secret(hs, psk);
|
|
|
|
}
|
|
|
|
|
|
|
|
static Span<const char> label_to_span(const char *label) {
|
|
|
|
return MakeConstSpan(label, strlen(label));
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hkdf_expand_label(Span<uint8_t> out, const EVP_MD *digest,
|
|
|
|
Span<const uint8_t> secret,
|
|
|
|
Span<const char> label,
|
|
|
|
Span<const uint8_t> hash) {
|
|
|
|
Span<const char> protocol_label = label_to_span("tls13 ");
|
|
|
|
ScopedCBB cbb;
|
|
|
|
CBB child;
|
|
|
|
Array<uint8_t> hkdf_label;
|
|
|
|
if (!CBB_init(cbb.get(), 2 + 1 + protocol_label.size() + label.size() + 1 +
|
|
|
|
hash.size()) ||
|
|
|
|
!CBB_add_u16(cbb.get(), out.size()) ||
|
|
|
|
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
|
|
|
|
!CBB_add_bytes(&child,
|
|
|
|
reinterpret_cast<const uint8_t *>(protocol_label.data()),
|
|
|
|
protocol_label.size()) ||
|
|
|
|
!CBB_add_bytes(&child, reinterpret_cast<const uint8_t *>(label.data()),
|
|
|
|
label.size()) ||
|
|
|
|
!CBB_add_u8_length_prefixed(cbb.get(), &child) ||
|
|
|
|
!CBB_add_bytes(&child, hash.data(), hash.size()) ||
|
|
|
|
!CBBFinishArray(cbb.get(), &hkdf_label)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return HKDF_expand(out.data(), out.size(), digest, secret.data(),
|
|
|
|
secret.size(), hkdf_label.data(), hkdf_label.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelDerived[] = "derived";
|
|
|
|
|
|
|
|
bool tls13_advance_key_schedule(SSL_HANDSHAKE *hs, Span<const uint8_t> in) {
|
|
|
|
uint8_t derive_context[EVP_MAX_MD_SIZE];
|
|
|
|
unsigned derive_context_len;
|
|
|
|
return EVP_Digest(nullptr, 0, derive_context, &derive_context_len,
|
|
|
|
hs->transcript.Digest(), nullptr) &&
|
|
|
|
hkdf_expand_label(hs->secret(), hs->transcript.Digest(), hs->secret(),
|
|
|
|
label_to_span(kTLS13LabelDerived),
|
|
|
|
MakeConstSpan(derive_context, derive_context_len)) &&
|
|
|
|
hkdf_extract_to_secret(hs, in);
|
|
|
|
}
|
|
|
|
|
|
|
|
// derive_secret derives a secret of length |out.size()| and writes the result
|
|
|
|
// in |out| with the given label, the current base secret, and the most
|
|
|
|
// recently-saved handshake context. It returns true on success and false on
|
|
|
|
// error.
|
|
|
|
static bool derive_secret(SSL_HANDSHAKE *hs, Span<uint8_t> out,
|
|
|
|
Span<const char> label) {
|
|
|
|
uint8_t context_hash[EVP_MAX_MD_SIZE];
|
|
|
|
size_t context_hash_len;
|
|
|
|
if (!hs->transcript.GetHash(context_hash, &context_hash_len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hkdf_expand_label(out, hs->transcript.Digest(), hs->secret(), label,
|
|
|
|
MakeConstSpan(context_hash, context_hash_len));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_set_traffic_key(SSL *ssl, enum ssl_encryption_level_t level,
|
|
|
|
enum evp_aead_direction_t direction,
|
|
|
|
const SSL_SESSION *session,
|
|
|
|
Span<const uint8_t> traffic_secret) {
|
|
|
|
uint16_t version = ssl_session_protocol_version(session);
|
|
|
|
UniquePtr<SSLAEADContext> traffic_aead;
|
|
|
|
Span<const uint8_t> secret_for_quic;
|
|
|
|
if (ssl->quic_method != nullptr) {
|
|
|
|
// Install a placeholder SSLAEADContext so that SSL accessors work. The
|
|
|
|
// encryption itself will be handled by the SSL_QUIC_METHOD.
|
|
|
|
traffic_aead =
|
|
|
|
SSLAEADContext::CreatePlaceholderForQUIC(version, session->cipher);
|
|
|
|
secret_for_quic = traffic_secret;
|
|
|
|
} else {
|
|
|
|
// Look up cipher suite properties.
|
|
|
|
const EVP_AEAD *aead;
|
|
|
|
size_t discard;
|
|
|
|
if (!ssl_cipher_get_evp_aead(&aead, &discard, &discard, session->cipher,
|
|
|
|
version, SSL_is_dtls(ssl))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const EVP_MD *digest = ssl_session_get_digest(session);
|
|
|
|
|
|
|
|
// Derive the key.
|
|
|
|
size_t key_len = EVP_AEAD_key_length(aead);
|
|
|
|
uint8_t key_buf[EVP_AEAD_MAX_KEY_LENGTH];
|
|
|
|
auto key = MakeSpan(key_buf, key_len);
|
|
|
|
if (!hkdf_expand_label(key, digest, traffic_secret, label_to_span("key"),
|
|
|
|
{})) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Derive the IV.
|
|
|
|
size_t iv_len = EVP_AEAD_nonce_length(aead);
|
|
|
|
uint8_t iv_buf[EVP_AEAD_MAX_NONCE_LENGTH];
|
|
|
|
auto iv = MakeSpan(iv_buf, iv_len);
|
|
|
|
if (!hkdf_expand_label(iv, digest, traffic_secret, label_to_span("iv"),
|
|
|
|
{})) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
traffic_aead = SSLAEADContext::Create(direction, session->ssl_version,
|
|
|
|
SSL_is_dtls(ssl), session->cipher,
|
|
|
|
key, Span<const uint8_t>(), iv);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!traffic_aead) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (traffic_secret.size() >
|
|
|
|
OPENSSL_ARRAY_SIZE(ssl->s3->read_traffic_secret) ||
|
|
|
|
traffic_secret.size() >
|
|
|
|
OPENSSL_ARRAY_SIZE(ssl->s3->write_traffic_secret)) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direction == evp_aead_open) {
|
|
|
|
if (!ssl->method->set_read_state(ssl, level, std::move(traffic_aead),
|
|
|
|
secret_for_quic)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OPENSSL_memmove(ssl->s3->read_traffic_secret, traffic_secret.data(),
|
|
|
|
traffic_secret.size());
|
|
|
|
ssl->s3->read_traffic_secret_len = traffic_secret.size();
|
|
|
|
} else {
|
|
|
|
if (!ssl->method->set_write_state(ssl, level, std::move(traffic_aead),
|
|
|
|
secret_for_quic)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OPENSSL_memmove(ssl->s3->write_traffic_secret, traffic_secret.data(),
|
|
|
|
traffic_secret.size());
|
|
|
|
ssl->s3->write_traffic_secret_len = traffic_secret.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static const char kTLS13LabelExporter[] = "exp master";
|
|
|
|
|
|
|
|
static const char kTLS13LabelClientEarlyTraffic[] = "c e traffic";
|
|
|
|
static const char kTLS13LabelClientHandshakeTraffic[] = "c hs traffic";
|
|
|
|
static const char kTLS13LabelServerHandshakeTraffic[] = "s hs traffic";
|
|
|
|
static const char kTLS13LabelClientApplicationTraffic[] = "c ap traffic";
|
|
|
|
static const char kTLS13LabelServerApplicationTraffic[] = "s ap traffic";
|
|
|
|
|
|
|
|
bool tls13_derive_early_secret(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
if (!derive_secret(hs, hs->early_traffic_secret(),
|
|
|
|
label_to_span(kTLS13LabelClientEarlyTraffic)) ||
|
|
|
|
!ssl_log_secret(ssl, "CLIENT_EARLY_TRAFFIC_SECRET",
|
|
|
|
hs->early_traffic_secret())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_derive_handshake_secrets(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
if (!derive_secret(hs, hs->client_handshake_secret(),
|
|
|
|
label_to_span(kTLS13LabelClientHandshakeTraffic)) ||
|
|
|
|
!ssl_log_secret(ssl, "CLIENT_HANDSHAKE_TRAFFIC_SECRET",
|
|
|
|
hs->client_handshake_secret()) ||
|
|
|
|
!derive_secret(hs, hs->server_handshake_secret(),
|
|
|
|
label_to_span(kTLS13LabelServerHandshakeTraffic)) ||
|
|
|
|
!ssl_log_secret(ssl, "SERVER_HANDSHAKE_TRAFFIC_SECRET",
|
|
|
|
hs->server_handshake_secret())) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_derive_application_secrets(SSL_HANDSHAKE *hs) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
ssl->s3->exporter_secret_len = hs->transcript.DigestLen();
|
|
|
|
if (!derive_secret(hs, hs->client_traffic_secret_0(),
|
|
|
|
label_to_span(kTLS13LabelClientApplicationTraffic)) ||
|
|
|
|
!ssl_log_secret(ssl, "CLIENT_TRAFFIC_SECRET_0",
|
|
|
|
hs->client_traffic_secret_0()) ||
|
|
|
|
!derive_secret(hs, hs->server_traffic_secret_0(),
|
|
|
|
label_to_span(kTLS13LabelServerApplicationTraffic)) ||
|
|
|
|
!ssl_log_secret(ssl, "SERVER_TRAFFIC_SECRET_0",
|
|
|
|
hs->server_traffic_secret_0()) ||
|
|
|
|
!derive_secret(
|
|
|
|
hs, MakeSpan(ssl->s3->exporter_secret, ssl->s3->exporter_secret_len),
|
|
|
|
label_to_span(kTLS13LabelExporter)) ||
|
|
|
|
!ssl_log_secret(ssl, "EXPORTER_SECRET",
|
|
|
|
MakeConstSpan(ssl->s3->exporter_secret,
|
|
|
|
ssl->s3->exporter_secret_len))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelApplicationTraffic[] = "traffic upd";
|
|
|
|
|
|
|
|
bool tls13_rotate_traffic_key(SSL *ssl, enum evp_aead_direction_t direction) {
|
|
|
|
Span<uint8_t> secret;
|
|
|
|
if (direction == evp_aead_open) {
|
|
|
|
secret = MakeSpan(ssl->s3->read_traffic_secret,
|
|
|
|
ssl->s3->read_traffic_secret_len);
|
|
|
|
} else {
|
|
|
|
secret = MakeSpan(ssl->s3->write_traffic_secret,
|
|
|
|
ssl->s3->write_traffic_secret_len);
|
|
|
|
}
|
|
|
|
|
|
|
|
const SSL_SESSION *session = SSL_get_session(ssl);
|
|
|
|
const EVP_MD *digest = ssl_session_get_digest(session);
|
|
|
|
return hkdf_expand_label(secret, digest, secret,
|
|
|
|
label_to_span(kTLS13LabelApplicationTraffic), {}) &&
|
|
|
|
tls13_set_traffic_key(ssl, ssl_encryption_application, direction,
|
|
|
|
session, secret);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelResumption[] = "res master";
|
|
|
|
|
|
|
|
bool tls13_derive_resumption_secret(SSL_HANDSHAKE *hs) {
|
|
|
|
if (hs->transcript.DigestLen() > SSL_MAX_MASTER_KEY_LENGTH) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
Rename the master_key field in SSL_SESSION to secret.
It's not even accurate. The term "master key" dates to SSL 2, which we
do not implement. (Starting SSL 3, "key" was replaced with "secret".)
The field stores, at various points, the TLS 1.2 master secret, the TLS
1.3 resumption master secret, and the TLS 1.3 resumption PSK. Simply
rename the field to 'secret', which is as descriptive of a name as we
can get at this point.
I've left SSL_SESSION_get_master_key alone for now, as it's there for
OpenSSL compatibility, as well as references to the various TLS secrets
since those refer to concepts in the spec. (When the dust settles a bit
on rfc8446bis, we can fix those.)
Change-Id: I3c1007eb7982788789cc5db851de8724c7f35baf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44144
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
hs->new_session->secret_length = hs->transcript.DigestLen();
|
|
|
|
return derive_secret(
|
Rename the master_key field in SSL_SESSION to secret.
It's not even accurate. The term "master key" dates to SSL 2, which we
do not implement. (Starting SSL 3, "key" was replaced with "secret".)
The field stores, at various points, the TLS 1.2 master secret, the TLS
1.3 resumption master secret, and the TLS 1.3 resumption PSK. Simply
rename the field to 'secret', which is as descriptive of a name as we
can get at this point.
I've left SSL_SESSION_get_master_key alone for now, as it's there for
OpenSSL compatibility, as well as references to the various TLS secrets
since those refer to concepts in the spec. (When the dust settles a bit
on rfc8446bis, we can fix those.)
Change-Id: I3c1007eb7982788789cc5db851de8724c7f35baf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44144
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
hs, MakeSpan(hs->new_session->secret, hs->new_session->secret_length),
|
|
|
|
label_to_span(kTLS13LabelResumption));
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelFinished[] = "finished";
|
|
|
|
|
|
|
|
// tls13_verify_data sets |out| to be the HMAC of |context| using a derived
|
|
|
|
// Finished key for both Finished messages and the PSK binder. |out| must have
|
|
|
|
// space available for |EVP_MAX_MD_SIZE| bytes.
|
|
|
|
static bool tls13_verify_data(uint8_t *out, size_t *out_len,
|
|
|
|
const EVP_MD *digest, uint16_t version,
|
|
|
|
Span<const uint8_t> secret,
|
|
|
|
Span<const uint8_t> context) {
|
|
|
|
uint8_t key_buf[EVP_MAX_MD_SIZE];
|
|
|
|
auto key = MakeSpan(key_buf, EVP_MD_size(digest));
|
|
|
|
unsigned len;
|
|
|
|
if (!hkdf_expand_label(key, digest, secret,
|
|
|
|
label_to_span(kTLS13LabelFinished), {}) ||
|
|
|
|
HMAC(digest, key.data(), key.size(), context.data(), context.size(), out,
|
|
|
|
&len) == nullptr) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
*out_len = len;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_finished_mac(SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len,
|
|
|
|
bool is_server) {
|
|
|
|
Span<const uint8_t> traffic_secret =
|
|
|
|
is_server ? hs->server_handshake_secret() : hs->client_handshake_secret();
|
|
|
|
|
|
|
|
uint8_t context_hash[EVP_MAX_MD_SIZE];
|
|
|
|
size_t context_hash_len;
|
|
|
|
if (!hs->transcript.GetHash(context_hash, &context_hash_len) ||
|
|
|
|
!tls13_verify_data(out, out_len, hs->transcript.Digest(),
|
|
|
|
hs->ssl->version, traffic_secret,
|
|
|
|
MakeConstSpan(context_hash, context_hash_len))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelResumptionPSK[] = "resumption";
|
|
|
|
|
|
|
|
bool tls13_derive_session_psk(SSL_SESSION *session, Span<const uint8_t> nonce) {
|
|
|
|
const EVP_MD *digest = ssl_session_get_digest(session);
|
|
|
|
// The session initially stores the resumption_master_secret, which we
|
|
|
|
// override with the PSK.
|
Rename the master_key field in SSL_SESSION to secret.
It's not even accurate. The term "master key" dates to SSL 2, which we
do not implement. (Starting SSL 3, "key" was replaced with "secret".)
The field stores, at various points, the TLS 1.2 master secret, the TLS
1.3 resumption master secret, and the TLS 1.3 resumption PSK. Simply
rename the field to 'secret', which is as descriptive of a name as we
can get at this point.
I've left SSL_SESSION_get_master_key alone for now, as it's there for
OpenSSL compatibility, as well as references to the various TLS secrets
since those refer to concepts in the spec. (When the dust settles a bit
on rfc8446bis, we can fix those.)
Change-Id: I3c1007eb7982788789cc5db851de8724c7f35baf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44144
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
auto session_secret = MakeSpan(session->secret, session->secret_length);
|
|
|
|
return hkdf_expand_label(session_secret, digest, session_secret,
|
|
|
|
label_to_span(kTLS13LabelResumptionPSK), nonce);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelExportKeying[] = "exporter";
|
|
|
|
|
|
|
|
bool tls13_export_keying_material(SSL *ssl, Span<uint8_t> out,
|
|
|
|
Span<const uint8_t> secret,
|
|
|
|
Span<const char> label,
|
|
|
|
Span<const uint8_t> context) {
|
|
|
|
if (secret.empty()) {
|
|
|
|
assert(0);
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
const EVP_MD *digest = ssl_session_get_digest(SSL_get_session(ssl));
|
|
|
|
|
|
|
|
uint8_t hash_buf[EVP_MAX_MD_SIZE];
|
|
|
|
uint8_t export_context_buf[EVP_MAX_MD_SIZE];
|
|
|
|
unsigned hash_len;
|
|
|
|
unsigned export_context_len;
|
|
|
|
if (!EVP_Digest(context.data(), context.size(), hash_buf, &hash_len, digest,
|
|
|
|
nullptr) ||
|
|
|
|
!EVP_Digest(nullptr, 0, export_context_buf, &export_context_len, digest,
|
|
|
|
nullptr)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto hash = MakeConstSpan(hash_buf, hash_len);
|
|
|
|
auto export_context = MakeConstSpan(export_context_buf, export_context_len);
|
|
|
|
uint8_t derived_secret_buf[EVP_MAX_MD_SIZE];
|
|
|
|
auto derived_secret = MakeSpan(derived_secret_buf, EVP_MD_size(digest));
|
|
|
|
return hkdf_expand_label(derived_secret, digest, secret, label,
|
|
|
|
export_context) &&
|
|
|
|
hkdf_expand_label(out, digest, derived_secret,
|
|
|
|
label_to_span(kTLS13LabelExportKeying), hash);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char kTLS13LabelPSKBinder[] = "res binder";
|
|
|
|
|
|
|
|
static bool tls13_psk_binder(uint8_t *out, size_t *out_len, uint16_t version,
|
|
|
|
const EVP_MD *digest, Span<const uint8_t> psk,
|
|
|
|
Span<const uint8_t> context) {
|
|
|
|
uint8_t binder_context[EVP_MAX_MD_SIZE];
|
|
|
|
unsigned binder_context_len;
|
|
|
|
if (!EVP_Digest(NULL, 0, binder_context, &binder_context_len, digest, NULL)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t early_secret[EVP_MAX_MD_SIZE] = {0};
|
|
|
|
size_t early_secret_len;
|
|
|
|
if (!HKDF_extract(early_secret, &early_secret_len, digest, psk.data(),
|
|
|
|
psk.size(), NULL, 0)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t binder_key_buf[EVP_MAX_MD_SIZE] = {0};
|
|
|
|
auto binder_key = MakeSpan(binder_key_buf, EVP_MD_size(digest));
|
|
|
|
if (!hkdf_expand_label(binder_key, digest,
|
|
|
|
MakeConstSpan(early_secret, early_secret_len),
|
|
|
|
label_to_span(kTLS13LabelPSKBinder),
|
|
|
|
MakeConstSpan(binder_context, binder_context_len)) ||
|
|
|
|
!tls13_verify_data(out, out_len, digest, version, binder_key, context)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(*out_len == EVP_MD_size(digest));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool hash_transcript_and_truncated_client_hello(
|
|
|
|
SSL_HANDSHAKE *hs, uint8_t *out, size_t *out_len, const EVP_MD *digest,
|
|
|
|
Span<const uint8_t> client_hello, size_t binders_len) {
|
|
|
|
// Truncate the ClientHello.
|
|
|
|
if (binders_len + 2 < binders_len || client_hello.size() < binders_len + 2) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
client_hello = client_hello.subspan(0, client_hello.size() - binders_len - 2);
|
|
|
|
|
|
|
|
ScopedEVP_MD_CTX ctx;
|
|
|
|
unsigned len;
|
|
|
|
if (!hs->transcript.CopyToHashContext(ctx.get(), digest) ||
|
|
|
|
!EVP_DigestUpdate(ctx.get(), client_hello.data(), client_hello.size()) ||
|
|
|
|
!EVP_DigestFinal_ex(ctx.get(), out, &len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
*out_len = len;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_write_psk_binder(SSL_HANDSHAKE *hs, Span<uint8_t> msg) {
|
|
|
|
SSL *const ssl = hs->ssl;
|
|
|
|
const EVP_MD *digest = ssl_session_get_digest(ssl->session.get());
|
|
|
|
size_t hash_len = EVP_MD_size(digest);
|
|
|
|
|
|
|
|
ScopedEVP_MD_CTX ctx;
|
|
|
|
uint8_t context[EVP_MAX_MD_SIZE];
|
|
|
|
size_t context_len;
|
|
|
|
uint8_t verify_data[EVP_MAX_MD_SIZE];
|
|
|
|
size_t verify_data_len;
|
|
|
|
if (!hash_transcript_and_truncated_client_hello(
|
|
|
|
hs, context, &context_len, digest, msg,
|
|
|
|
1 /* length prefix */ + hash_len) ||
|
Rename the master_key field in SSL_SESSION to secret.
It's not even accurate. The term "master key" dates to SSL 2, which we
do not implement. (Starting SSL 3, "key" was replaced with "secret".)
The field stores, at various points, the TLS 1.2 master secret, the TLS
1.3 resumption master secret, and the TLS 1.3 resumption PSK. Simply
rename the field to 'secret', which is as descriptive of a name as we
can get at this point.
I've left SSL_SESSION_get_master_key alone for now, as it's there for
OpenSSL compatibility, as well as references to the various TLS secrets
since those refer to concepts in the spec. (When the dust settles a bit
on rfc8446bis, we can fix those.)
Change-Id: I3c1007eb7982788789cc5db851de8724c7f35baf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44144
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
!tls13_psk_binder(
|
|
|
|
verify_data, &verify_data_len, ssl->session->ssl_version, digest,
|
|
|
|
MakeConstSpan(ssl->session->secret, ssl->session->secret_length),
|
|
|
|
MakeConstSpan(context, context_len)) ||
|
|
|
|
verify_data_len != hash_len) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
OPENSSL_memcpy(msg.data() + msg.size() - verify_data_len, verify_data,
|
|
|
|
verify_data_len);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_verify_psk_binder(SSL_HANDSHAKE *hs, SSL_SESSION *session,
|
|
|
|
const SSLMessage &msg, CBS *binders) {
|
|
|
|
uint8_t context[EVP_MAX_MD_SIZE];
|
|
|
|
size_t context_len;
|
|
|
|
uint8_t verify_data[EVP_MAX_MD_SIZE];
|
|
|
|
size_t verify_data_len;
|
|
|
|
CBS binder;
|
|
|
|
if (!hash_transcript_and_truncated_client_hello(hs, context, &context_len,
|
|
|
|
hs->transcript.Digest(),
|
|
|
|
msg.raw, CBS_len(binders)) ||
|
Rename the master_key field in SSL_SESSION to secret.
It's not even accurate. The term "master key" dates to SSL 2, which we
do not implement. (Starting SSL 3, "key" was replaced with "secret".)
The field stores, at various points, the TLS 1.2 master secret, the TLS
1.3 resumption master secret, and the TLS 1.3 resumption PSK. Simply
rename the field to 'secret', which is as descriptive of a name as we
can get at this point.
I've left SSL_SESSION_get_master_key alone for now, as it's there for
OpenSSL compatibility, as well as references to the various TLS secrets
since those refer to concepts in the spec. (When the dust settles a bit
on rfc8446bis, we can fix those.)
Change-Id: I3c1007eb7982788789cc5db851de8724c7f35baf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/44144
Reviewed-by: Adam Langley <agl@google.com>
4 years ago
|
|
|
!tls13_psk_binder(verify_data, &verify_data_len, hs->ssl->version,
|
|
|
|
hs->transcript.Digest(),
|
|
|
|
MakeConstSpan(session->secret, session->secret_length),
|
|
|
|
MakeConstSpan(context, context_len)) ||
|
|
|
|
// We only consider the first PSK, so compare against the first binder.
|
|
|
|
!CBS_get_u8_length_prefixed(binders, &binder)) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool binder_ok =
|
|
|
|
CBS_len(&binder) == verify_data_len &&
|
|
|
|
CRYPTO_memcmp(CBS_data(&binder), verify_data, verify_data_len) == 0;
|
|
|
|
#if defined(BORINGSSL_UNSAFE_FUZZER_MODE)
|
|
|
|
binder_ok = true;
|
|
|
|
#endif
|
|
|
|
if (!binder_ok) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, SSL_R_DIGEST_CHECK_FAILED);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool tls13_ech_accept_confirmation(
|
|
|
|
SSL_HANDSHAKE *hs, bssl::Span<uint8_t> out,
|
|
|
|
bssl::Span<const uint8_t> server_hello_ech_conf) {
|
|
|
|
// Compute the hash of the transcript concatenated with
|
|
|
|
// |server_hello_ech_conf| without modifying |hs->transcript|.
|
|
|
|
uint8_t context_hash[EVP_MAX_MD_SIZE];
|
|
|
|
unsigned context_hash_len;
|
|
|
|
ScopedEVP_MD_CTX ctx;
|
|
|
|
if (!hs->transcript.CopyToHashContext(ctx.get(), hs->transcript.Digest()) ||
|
|
|
|
!EVP_DigestUpdate(ctx.get(), server_hello_ech_conf.data(),
|
|
|
|
server_hello_ech_conf.size()) ||
|
|
|
|
!EVP_DigestFinal_ex(ctx.get(), context_hash, &context_hash_len)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Per draft-ietf-tls-esni-09, accept_confirmation is computed with
|
|
|
|
// Derive-Secret, which derives a secret of size Hash.length. That value is
|
|
|
|
// then truncated to the first 8 bytes. Note this differs from deriving an
|
|
|
|
// 8-byte secret because the target length is included in the derivation.
|
|
|
|
uint8_t accept_confirmation_buf[EVP_MAX_MD_SIZE];
|
|
|
|
bssl::Span<uint8_t> accept_confirmation =
|
|
|
|
MakeSpan(accept_confirmation_buf, hs->transcript.DigestLen());
|
|
|
|
if (!hkdf_expand_label(accept_confirmation, hs->transcript.Digest(),
|
|
|
|
hs->secret(), label_to_span("ech accept confirmation"),
|
|
|
|
MakeConstSpan(context_hash, context_hash_len))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out.size() > accept_confirmation.size()) {
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
OPENSSL_memcpy(out.data(), accept_confirmation.data(), out.size());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
BSSL_NAMESPACE_END
|