We use the standard Apache 2.0 file header, described in "APPENDIX: How to apply the Apache License to your work." This was primarily automated by running: git ls-tree -r --name-only HEAD | xargs go run ./util/relicense.go See go/boringssl-relicensing-triage for the results of triaging the output of the tool. As part of this, switch from taking fiat-crypto under MIT license to Apache 2.0. (It is licensed under MIT OR Apache-2.0 OR BSD-1-Clause.) The copyright_summary tool can also be used to confirm we didn't accidentally drop any copyright lines: # Run before the CL git grep -l Copyright | xargs go run ./util/copyright_summary.go -out /tmp/old.json # Run after the CL git grep -l Copyright | xargs go run ./util/copyright_summary.go -compare /tmp/old.json Bug: 364634028 Change-Id: I17c50e761e9d077a1f92e25969e50ed35e320c59 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/75852 Reviewed-by: Bob Beck <bbe@google.com> Commit-Queue: David Benjamin <davidben@google.com> Reviewed-by: Adam Langley <agl@google.com>
225 lines
7.3 KiB
C++
225 lines
7.3 KiB
C++
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
|
|
// Copyright 2005 Nokia. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// https://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <string_view>
|
|
|
|
#include <openssl/buf.h>
|
|
#include <openssl/digest.h>
|
|
#include <openssl/err.h>
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
BSSL_NAMESPACE_BEGIN
|
|
|
|
SSLTranscript::SSLTranscript(bool is_dtls) : is_dtls_(is_dtls) {}
|
|
|
|
SSLTranscript::~SSLTranscript() {}
|
|
|
|
bool SSLTranscript::Init() {
|
|
buffer_.reset(BUF_MEM_new());
|
|
if (!buffer_) {
|
|
return false;
|
|
}
|
|
|
|
hash_.Reset();
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::InitHash(uint16_t version, const SSL_CIPHER *cipher) {
|
|
version_ = version;
|
|
const EVP_MD *md = ssl_get_handshake_digest(version, cipher);
|
|
if (Digest() == md) {
|
|
// No need to re-hash the buffer.
|
|
return true;
|
|
}
|
|
if (!HashBuffer(hash_.get(), md)) {
|
|
return false;
|
|
}
|
|
if (is_dtls_ && version_ >= TLS1_3_VERSION) {
|
|
// In DTLS 1.3, prior to the call to InitHash, the message (if present) in
|
|
// the buffer has the DTLS 1.2 header. After the call to InitHash, the TLS
|
|
// 1.3 header is written by SSLTranscript::Update. If the buffer isn't freed
|
|
// here, it would have a mix of different header formats and using it would
|
|
// yield wrong results. However, there's no need for the buffer once the
|
|
// version and the digest for the cipher suite are known, so the buffer is
|
|
// freed here to avoid potential misuse of the SSLTranscript object.
|
|
FreeBuffer();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::HashBuffer(EVP_MD_CTX *ctx, const EVP_MD *digest) const {
|
|
if (!EVP_DigestInit_ex(ctx, digest, nullptr)) {
|
|
return false;
|
|
}
|
|
if (!is_dtls_ || version_ < TLS1_3_VERSION) {
|
|
return EVP_DigestUpdate(ctx, buffer_->data, buffer_->length);
|
|
}
|
|
|
|
// If the version is DTLS 1.3 and we still have a buffer, then there should be
|
|
// at most a single DTLSHandshake message in the buffer, for the ClientHello.
|
|
// On the server side, the version (DTLS 1.3) and cipher suite are chosen in
|
|
// response to the first ClientHello, and InitHash is called before that
|
|
// ClientHello is added to the SSLTranscript, so the buffer is empty if this
|
|
// SSLTranscript is on the server.
|
|
if (buffer_->length == 0) {
|
|
return true;
|
|
}
|
|
|
|
// On the client side, we can receive either a ServerHello or
|
|
// HelloRetryRequest in response to the ClientHello. Regardless of which
|
|
// message we receive, the client code calls InitHash before updating the
|
|
// transcript with that message, so the ClientHello is the only message in the
|
|
// buffer. In DTLS 1.3, we need to skip the message_seq, fragment_offset, and
|
|
// fragment_length fields from the DTLSHandshake message in the buffer. The
|
|
// structure of a DTLSHandshake message is as follows (RFC 9147, section 5.2):
|
|
//
|
|
// struct {
|
|
// HandshakeType msg_type; /* handshake type */
|
|
// uint24 length; /* bytes in message */
|
|
// uint16 message_seq; /* DTLS-required field */
|
|
// uint24 fragment_offset; /* DTLS-required field */
|
|
// uint24 fragment_length; /* DTLS-required field */
|
|
// select (msg_type) {
|
|
// /* omitted for brevity */
|
|
// } body;
|
|
// } DTLSHandshake;
|
|
CBS buf, header;
|
|
CBS_init(&buf, reinterpret_cast<uint8_t *>(buffer_->data), buffer_->length);
|
|
if (!CBS_get_bytes(&buf, &header, 4) || //
|
|
!CBS_skip(&buf, 8) || //
|
|
!EVP_DigestUpdate(ctx, CBS_data(&header), CBS_len(&header)) || //
|
|
!EVP_DigestUpdate(ctx, CBS_data(&buf), CBS_len(&buf))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SSLTranscript::FreeBuffer() { buffer_.reset(); }
|
|
|
|
size_t SSLTranscript::DigestLen() const { return EVP_MD_size(Digest()); }
|
|
|
|
const EVP_MD *SSLTranscript::Digest() const {
|
|
return EVP_MD_CTX_get0_md(hash_.get());
|
|
}
|
|
|
|
bool SSLTranscript::UpdateForHelloRetryRequest() {
|
|
if (buffer_) {
|
|
buffer_->length = 0;
|
|
}
|
|
|
|
uint8_t old_hash[EVP_MAX_MD_SIZE];
|
|
size_t hash_len;
|
|
if (!GetHash(old_hash, &hash_len)) {
|
|
return false;
|
|
}
|
|
const uint8_t header[4] = {SSL3_MT_MESSAGE_HASH, 0, 0,
|
|
static_cast<uint8_t>(hash_len)};
|
|
if (!EVP_DigestInit_ex(hash_.get(), Digest(), nullptr) ||
|
|
!AddToBufferOrHash(header) ||
|
|
!AddToBufferOrHash(Span(old_hash, hash_len))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::CopyToHashContext(EVP_MD_CTX *ctx,
|
|
const EVP_MD *digest) const {
|
|
const EVP_MD *transcript_digest = Digest();
|
|
if (transcript_digest != nullptr &&
|
|
EVP_MD_type(transcript_digest) == EVP_MD_type(digest)) {
|
|
return EVP_MD_CTX_copy_ex(ctx, hash_.get());
|
|
}
|
|
|
|
if (buffer_) {
|
|
return HashBuffer(ctx, digest);
|
|
}
|
|
|
|
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
bool SSLTranscript::Update(Span<const uint8_t> in) {
|
|
if (!is_dtls_ || version_ < TLS1_3_VERSION) {
|
|
return AddToBufferOrHash(in);
|
|
}
|
|
if (in.size() < DTLS1_HM_HEADER_LENGTH) {
|
|
return false;
|
|
}
|
|
// The message passed into Update is the whole Handshake or DTLSHandshake
|
|
// message, including the msg_type and length. In DTLS, the DTLSHandshake
|
|
// message also has message_seq, fragment_offset, and fragment_length
|
|
// fields. In DTLS 1.3, those fields are omitted so that the same
|
|
// transcript format as TLS 1.3 is used. This means we write the 1-byte
|
|
// msg_type, 3-byte length, then skip 2+3+3 bytes for the DTLS-specific
|
|
// fields that get omitted.
|
|
if (!AddToBufferOrHash(in.subspan(0, 4)) ||
|
|
!AddToBufferOrHash(in.subspan(12))) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::AddToBufferOrHash(Span<const uint8_t> in) {
|
|
// Depending on the state of the handshake, either the handshake buffer may be
|
|
// active, the rolling hash, or both.
|
|
if (buffer_ && //
|
|
!BUF_MEM_append(buffer_.get(), in.data(), in.size())) {
|
|
return false;
|
|
}
|
|
|
|
if (EVP_MD_CTX_md(hash_.get()) != NULL) {
|
|
EVP_DigestUpdate(hash_.get(), in.data(), in.size());
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::GetHash(uint8_t *out, size_t *out_len) const {
|
|
ScopedEVP_MD_CTX ctx;
|
|
unsigned len;
|
|
if (!EVP_MD_CTX_copy_ex(ctx.get(), hash_.get()) ||
|
|
!EVP_DigestFinal_ex(ctx.get(), out, &len)) {
|
|
return false;
|
|
}
|
|
*out_len = len;
|
|
return true;
|
|
}
|
|
|
|
bool SSLTranscript::GetFinishedMAC(uint8_t *out, size_t *out_len,
|
|
const SSL_SESSION *session,
|
|
bool from_server) const {
|
|
uint8_t digest[EVP_MAX_MD_SIZE];
|
|
size_t digest_len;
|
|
if (!GetHash(digest, &digest_len)) {
|
|
return false;
|
|
}
|
|
|
|
std::string_view label = from_server ? "server finished" : "client finished";
|
|
static const size_t kFinishedLen = 12;
|
|
if (!tls1_prf(Digest(), Span(out, kFinishedLen), session->secret, label,
|
|
Span(digest, digest_len), {})) {
|
|
return false;
|
|
}
|
|
|
|
*out_len = kFinishedLen;
|
|
return true;
|
|
}
|
|
|
|
BSSL_NAMESPACE_END
|