Use CRYPTO_BUFFER APIs instead of X509 when building with BoringSSL.
Using CRYPTO_BUFFERs instead of legacy X509 objects offers memory and
security gains, and will provide binary size improvements as well once
the default list of built-in certificates can be removed; the code
dealing with them still depends on the X509 API.
Implemented by splitting openssl_identity and openssl_certificate
into BoringSSL and vanilla OpenSSL implementations.
Bug: webrtc:11410
Change-Id: Idc043462faac5e4ab1b75bedab2057197f80aba6
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/174120
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: David Benjamin <davidben@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Taylor <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32811}
diff --git a/pc/test/fake_rtc_certificate_generator.h b/pc/test/fake_rtc_certificate_generator.h
index b726a4c..b591c4c 100644
--- a/pc/test/fake_rtc_certificate_generator.h
+++ b/pc/test/fake_rtc_certificate_generator.h
@@ -83,7 +83,7 @@
// ECDSA with EC_NIST_P256.
// These PEM strings were created by generating an identity with
-// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|,
+// |SSLIdentity::Create| and invoking |identity->PrivateKeyToPEMString()|,
// |identity->PublicKeyToPEMString()| and
// |identity->certificate().ToPEMString()|.
static const rtc::RTCCertificatePEM kEcdsaPems[] = {
diff --git a/rtc_base/BUILD.gn b/rtc_base/BUILD.gn
index 8b92090..8762bfb 100644
--- a/rtc_base/BUILD.gn
+++ b/rtc_base/BUILD.gn
@@ -911,12 +911,10 @@
"openssl.h",
"openssl_adapter.cc",
"openssl_adapter.h",
- "openssl_certificate.cc",
- "openssl_certificate.h",
"openssl_digest.cc",
"openssl_digest.h",
- "openssl_identity.cc",
- "openssl_identity.h",
+ "openssl_key_pair.cc",
+ "openssl_key_pair.h",
"openssl_session_cache.cc",
"openssl_session_cache.h",
"openssl_stream_adapter.cc",
@@ -962,6 +960,22 @@
"unique_id_generator.h",
]
+ if (rtc_openssl_is_boringssl) {
+ sources += [
+ "boringssl_certificate.cc",
+ "boringssl_certificate.h",
+ "boringssl_identity.cc",
+ "boringssl_identity.h",
+ ]
+ } else {
+ sources += [
+ "openssl_certificate.cc",
+ "openssl_certificate.h",
+ "openssl_identity.cc",
+ "openssl_identity.h",
+ ]
+ }
+
if (build_with_chromium) {
include_dirs = [ "../../boringssl/src/include" ]
public_configs += [ ":rtc_base_chromium_config" ]
diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc
new file mode 100644
index 0000000..c9713c2
--- /dev/null
+++ b/rtc_base/boringssl_certificate.cc
@@ -0,0 +1,410 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/boringssl_certificate.h"
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h" // NOLINT
+#endif // WEBRTC_WIN
+
+#include <openssl/asn1.h>
+#include <openssl/bytestring.h>
+#include <openssl/digest.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/pool.h>
+#include <openssl/rand.h>
+#include <time.h>
+
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/helpers.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/message_digest.h"
+#include "rtc_base/openssl_digest.h"
+#include "rtc_base/openssl_identity.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+namespace {
+
+// List of OIDs of signature algorithms accepted by WebRTC.
+// Taken from openssl/nid.h.
+static const uint8_t kMD5WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x03};
+static const uint8_t kMD5WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x04};
+static const uint8_t kECDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x01};
+static const uint8_t kDSAWithSHA1[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x38, 0x04, 0x03};
+static const uint8_t kDSAWithSHA1_2[] = {0x2b, 0x0e, 0x03, 0x02, 0x1b};
+static const uint8_t kSHA1WithRSA[] = {0x2b, 0x0e, 0x03, 0x02, 0x1d};
+static const uint8_t kSHA1WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x05};
+static const uint8_t kECDSAWithSHA224[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x01};
+static const uint8_t kSHA224WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0e};
+static const uint8_t kDSAWithSHA224[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x03, 0x01};
+static const uint8_t kECDSAWithSHA256[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x02};
+static const uint8_t kSHA256WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0b};
+static const uint8_t kDSAWithSHA256[] = {0x60, 0x86, 0x48, 0x01, 0x65,
+ 0x03, 0x04, 0x03, 0x028};
+static const uint8_t kECDSAWithSHA384[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x03};
+static const uint8_t kSHA384WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0c};
+static const uint8_t kECDSAWithSHA512[] = {0x2a, 0x86, 0x48, 0xce,
+ 0x3d, 0x04, 0x03, 0x04};
+static const uint8_t kSHA512WithRSAEncryption[] = {0x2a, 0x86, 0x48, 0x86, 0xf7,
+ 0x0d, 0x01, 0x01, 0x0d};
+
+#if !defined(NDEBUG)
+// Print a certificate to the log, for debugging.
+static void PrintCert(BoringSSLCertificate* cert) {
+ // Since we're using CRYPTO_BUFFER, we can't use X509_print_ex, so we'll just
+ // print the PEM string.
+ RTC_DLOG(LS_VERBOSE) << "PEM representation of certificate:\n"
+ << cert->ToPEMString();
+}
+#endif
+
+bool AddSHA256SignatureAlgorithm(CBB* cbb, KeyType key_type) {
+ // An AlgorithmIdentifier is described in RFC 5280, 4.1.1.2.
+ CBB sequence, oid, params;
+ if (!CBB_add_asn1(cbb, &sequence, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&sequence, &oid, CBS_ASN1_OBJECT)) {
+ return false;
+ }
+
+ switch (key_type) {
+ case KT_RSA:
+ if (!CBB_add_bytes(&oid, kSHA256WithRSAEncryption,
+ sizeof(kSHA256WithRSAEncryption)) ||
+ !CBB_add_asn1(&sequence, ¶ms, CBS_ASN1_NULL)) {
+ return false;
+ }
+ break;
+ case KT_ECDSA:
+ if (!CBB_add_bytes(&oid, kECDSAWithSHA256, sizeof(kECDSAWithSHA256))) {
+ return false;
+ }
+ break;
+ default:
+ RTC_NOTREACHED();
+ return false;
+ }
+ if (!CBB_flush(cbb)) {
+ return false;
+ }
+ return true;
+}
+
+// Adds an X.509 Common Name to |cbb|.
+bool AddCommonName(CBB* cbb, const std::string& common_name) {
+ // See RFC 4519.
+ static const uint8_t kCommonName[] = {0x55, 0x04, 0x03};
+
+ if (common_name.empty()) {
+ RTC_LOG(LS_ERROR) << "Common name cannot be empty.";
+ return false;
+ }
+
+ // See RFC 5280, section 4.1.2.4.
+ CBB rdns;
+ if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+
+ CBB rdn, attr, type, value;
+ if (!CBB_add_asn1(&rdns, &rdn, CBS_ASN1_SET) ||
+ !CBB_add_asn1(&rdn, &attr, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&attr, &type, CBS_ASN1_OBJECT) ||
+ !CBB_add_bytes(&type, kCommonName, sizeof(kCommonName)) ||
+ !CBB_add_asn1(&attr, &value, CBS_ASN1_UTF8STRING) ||
+ !CBB_add_bytes(&value,
+ reinterpret_cast<const uint8_t*>(common_name.c_str()),
+ common_name.size()) ||
+ !CBB_flush(cbb)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool AddTime(CBB* cbb, time_t time) {
+ bssl::UniquePtr<ASN1_TIME> asn1_time(ASN1_TIME_new());
+ if (!asn1_time) {
+ return false;
+ }
+
+ if (!ASN1_TIME_set(asn1_time.get(), time)) {
+ return false;
+ }
+
+ unsigned tag;
+ switch (asn1_time->type) {
+ case V_ASN1_UTCTIME:
+ tag = CBS_ASN1_UTCTIME;
+ break;
+ case V_ASN1_GENERALIZEDTIME:
+ tag = CBS_ASN1_GENERALIZEDTIME;
+ break;
+ default:
+ return false;
+ }
+
+ CBB child;
+ if (!CBB_add_asn1(cbb, &child, tag) ||
+ !CBB_add_bytes(&child, asn1_time->data, asn1_time->length) ||
+ !CBB_flush(cbb)) {
+ return false;
+ }
+
+ return true;
+}
+
+// Generate a self-signed certificate, with the public key from the
+// given key pair. Caller is responsible for freeing the returned object.
+static bssl::UniquePtr<CRYPTO_BUFFER> MakeCertificate(
+ EVP_PKEY* pkey,
+ const SSLIdentityParams& params) {
+ RTC_LOG(LS_INFO) << "Making certificate for " << params.common_name;
+
+ // See RFC 5280, section 4.1. First, construct the TBSCertificate.
+ bssl::ScopedCBB cbb;
+ CBB tbs_cert, version, validity;
+ uint8_t* tbs_cert_bytes;
+ size_t tbs_cert_len;
+ uint64_t serial_number;
+ if (!CBB_init(cbb.get(), 64) ||
+ !CBB_add_asn1(cbb.get(), &tbs_cert, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_asn1(&tbs_cert, &version,
+ CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
+ !CBB_add_asn1_uint64(&version, 2) ||
+ !RAND_bytes(reinterpret_cast<uint8_t*>(&serial_number),
+ sizeof(serial_number)) ||
+ !CBB_add_asn1_uint64(&tbs_cert, serial_number) ||
+ !AddSHA256SignatureAlgorithm(&tbs_cert, params.key_params.type()) ||
+ !AddCommonName(&tbs_cert, params.common_name) || // issuer
+ !CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) ||
+ !AddTime(&validity, params.not_before) ||
+ !AddTime(&validity, params.not_after) ||
+ !AddCommonName(&tbs_cert, params.common_name) || // subject
+ !EVP_marshal_public_key(&tbs_cert, pkey) || // subjectPublicKeyInfo
+ !CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) {
+ return nullptr;
+ }
+
+ bssl::UniquePtr<uint8_t> delete_tbs_cert_bytes(tbs_cert_bytes);
+
+ // Sign the TBSCertificate and write the entire certificate.
+ CBB cert, signature;
+ bssl::ScopedEVP_MD_CTX ctx;
+ uint8_t* sig_out;
+ size_t sig_len;
+ uint8_t* cert_bytes;
+ size_t cert_len;
+ if (!CBB_init(cbb.get(), tbs_cert_len) ||
+ !CBB_add_asn1(cbb.get(), &cert, CBS_ASN1_SEQUENCE) ||
+ !CBB_add_bytes(&cert, tbs_cert_bytes, tbs_cert_len) ||
+ !AddSHA256SignatureAlgorithm(&cert, params.key_params.type()) ||
+ !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) ||
+ !CBB_add_u8(&signature, 0 /* no unused bits */) ||
+ !EVP_DigestSignInit(ctx.get(), nullptr, EVP_sha256(), nullptr, pkey) ||
+ // Compute the maximum signature length.
+ !EVP_DigestSign(ctx.get(), nullptr, &sig_len, tbs_cert_bytes,
+ tbs_cert_len) ||
+ !CBB_reserve(&signature, &sig_out, sig_len) ||
+ // Actually sign the TBSCertificate.
+ !EVP_DigestSign(ctx.get(), sig_out, &sig_len, tbs_cert_bytes,
+ tbs_cert_len) ||
+ !CBB_did_write(&signature, sig_len) ||
+ !CBB_finish(cbb.get(), &cert_bytes, &cert_len)) {
+ return nullptr;
+ }
+ bssl::UniquePtr<uint8_t> delete_cert_bytes(cert_bytes);
+
+ RTC_LOG(LS_INFO) << "Returning certificate";
+ return bssl::UniquePtr<CRYPTO_BUFFER>(
+ CRYPTO_BUFFER_new(cert_bytes, cert_len, openssl::GetBufferPool()));
+}
+
+} // namespace
+
+BoringSSLCertificate::BoringSSLCertificate(
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer)
+ : cert_buffer_(std::move(cert_buffer)) {
+ RTC_DCHECK(cert_buffer_ != nullptr);
+}
+
+std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::Generate(
+ OpenSSLKeyPair* key_pair,
+ const SSLIdentityParams& params) {
+ SSLIdentityParams actual_params(params);
+ if (actual_params.common_name.empty()) {
+ // Use a random string, arbitrarily 8 chars long.
+ actual_params.common_name = CreateRandomString(8);
+ }
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer =
+ MakeCertificate(key_pair->pkey(), actual_params);
+ if (!cert_buffer) {
+ openssl::LogSSLErrors("Generating certificate");
+ return nullptr;
+ }
+ auto ret = std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
+#if !defined(NDEBUG)
+ PrintCert(ret.get());
+#endif
+ return ret;
+}
+
+std::unique_ptr<BoringSSLCertificate> BoringSSLCertificate::FromPEMString(
+ const std::string& pem_string) {
+ std::string der;
+ if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der)) {
+ return nullptr;
+ }
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer(
+ CRYPTO_BUFFER_new(reinterpret_cast<const uint8_t*>(der.c_str()),
+ der.length(), openssl::GetBufferPool()));
+ if (!cert_buffer) {
+ return nullptr;
+ }
+ return std::make_unique<BoringSSLCertificate>(std::move(cert_buffer));
+}
+
+#define OID_MATCHES(oid, oid_other) \
+ (CBS_len(&oid) == sizeof(oid_other) && \
+ 0 == memcmp(CBS_data(&oid), oid_other, sizeof(oid_other)))
+
+bool BoringSSLCertificate::GetSignatureDigestAlgorithm(
+ std::string* algorithm) const {
+ CBS oid;
+ if (!openssl::ParseCertificate(cert_buffer_.get(), &oid, nullptr)) {
+ RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
+ return false;
+ }
+ if (OID_MATCHES(oid, kMD5WithRSA) ||
+ OID_MATCHES(oid, kMD5WithRSAEncryption)) {
+ *algorithm = DIGEST_MD5;
+ return true;
+ }
+ if (OID_MATCHES(oid, kECDSAWithSHA1) || OID_MATCHES(oid, kDSAWithSHA1) ||
+ OID_MATCHES(oid, kDSAWithSHA1_2) || OID_MATCHES(oid, kSHA1WithRSA) ||
+ OID_MATCHES(oid, kSHA1WithRSAEncryption)) {
+ *algorithm = DIGEST_SHA_1;
+ return true;
+ }
+ if (OID_MATCHES(oid, kECDSAWithSHA224) ||
+ OID_MATCHES(oid, kSHA224WithRSAEncryption) ||
+ OID_MATCHES(oid, kDSAWithSHA224)) {
+ *algorithm = DIGEST_SHA_224;
+ return true;
+ }
+ if (OID_MATCHES(oid, kECDSAWithSHA256) ||
+ OID_MATCHES(oid, kSHA256WithRSAEncryption) ||
+ OID_MATCHES(oid, kDSAWithSHA256)) {
+ *algorithm = DIGEST_SHA_256;
+ return true;
+ }
+ if (OID_MATCHES(oid, kECDSAWithSHA384) ||
+ OID_MATCHES(oid, kSHA384WithRSAEncryption)) {
+ *algorithm = DIGEST_SHA_384;
+ return true;
+ }
+ if (OID_MATCHES(oid, kECDSAWithSHA512) ||
+ OID_MATCHES(oid, kSHA512WithRSAEncryption)) {
+ *algorithm = DIGEST_SHA_512;
+ return true;
+ }
+ // Unknown algorithm. There are several unhandled options that are less
+ // common and more complex.
+ RTC_LOG(LS_ERROR) << "Unknown signature algorithm.";
+ algorithm->clear();
+ return false;
+}
+
+bool BoringSSLCertificate::ComputeDigest(const std::string& algorithm,
+ unsigned char* digest,
+ size_t size,
+ size_t* length) const {
+ return ComputeDigest(cert_buffer_.get(), algorithm, digest, size, length);
+}
+
+bool BoringSSLCertificate::ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
+ const std::string& algorithm,
+ unsigned char* digest,
+ size_t size,
+ size_t* length) {
+ const EVP_MD* md = nullptr;
+ unsigned int n = 0;
+ if (!OpenSSLDigest::GetDigestEVP(algorithm, &md)) {
+ return false;
+ }
+ if (size < static_cast<size_t>(EVP_MD_size(md))) {
+ return false;
+ }
+ if (!EVP_Digest(CRYPTO_BUFFER_data(cert_buffer),
+ CRYPTO_BUFFER_len(cert_buffer), digest, &n, md, nullptr)) {
+ return false;
+ }
+ *length = n;
+ return true;
+}
+
+BoringSSLCertificate::~BoringSSLCertificate() {}
+
+std::unique_ptr<SSLCertificate> BoringSSLCertificate::Clone() const {
+ return std::make_unique<BoringSSLCertificate>(
+ bssl::UpRef(cert_buffer_.get()));
+}
+
+std::string BoringSSLCertificate::ToPEMString() const {
+ return OpenSSLIdentity::DerToPem(kPemTypeCertificate,
+ CRYPTO_BUFFER_data(cert_buffer_.get()),
+ CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+void BoringSSLCertificate::ToDER(Buffer* der_buffer) const {
+ der_buffer->SetData(CRYPTO_BUFFER_data(cert_buffer_.get()),
+ CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+bool BoringSSLCertificate::operator==(const BoringSSLCertificate& other) const {
+ return CRYPTO_BUFFER_len(cert_buffer_.get()) ==
+ CRYPTO_BUFFER_len(other.cert_buffer_.get()) &&
+ 0 == memcmp(CRYPTO_BUFFER_data(cert_buffer_.get()),
+ CRYPTO_BUFFER_data(other.cert_buffer_.get()),
+ CRYPTO_BUFFER_len(cert_buffer_.get()));
+}
+
+bool BoringSSLCertificate::operator!=(const BoringSSLCertificate& other) const {
+ return !(*this == other);
+}
+
+int64_t BoringSSLCertificate::CertificateExpirationTime() const {
+ int64_t ret;
+ if (!openssl::ParseCertificate(cert_buffer_.get(), nullptr, &ret)) {
+ RTC_LOG(LS_ERROR) << "Failed to parse certificate.";
+ return -1;
+ }
+ return ret;
+}
+
+} // namespace rtc
diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h
new file mode 100644
index 0000000..740763d
--- /dev/null
+++ b/rtc_base/boringssl_certificate.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_BORINGSSL_CERTIFICATE_H_
+#define RTC_BASE_BORINGSSL_CERTIFICATE_H_
+
+#include <openssl/ossl_typ.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+
+#include "rtc_base/buffer.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/ssl_certificate.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+class OpenSSLKeyPair;
+
+// BoringSSLCertificate encapsulates a BoringSSL CRYPTO_BUFFER object holding a
+// certificate, which is also reference counted inside the BoringSSL library.
+// This offers binary size and memory improvements over the OpenSSL X509
+// object.
+class BoringSSLCertificate final : public SSLCertificate {
+ public:
+ explicit BoringSSLCertificate(bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer);
+
+ static std::unique_ptr<BoringSSLCertificate> Generate(
+ OpenSSLKeyPair* key_pair,
+ const SSLIdentityParams& params);
+ static std::unique_ptr<BoringSSLCertificate> FromPEMString(
+ const std::string& pem_string);
+
+ ~BoringSSLCertificate() override;
+
+ std::unique_ptr<SSLCertificate> Clone() const override;
+
+ CRYPTO_BUFFER* cert_buffer() const { return cert_buffer_.get(); }
+
+ std::string ToPEMString() const override;
+ void ToDER(Buffer* der_buffer) const override;
+ bool operator==(const BoringSSLCertificate& other) const;
+ bool operator!=(const BoringSSLCertificate& other) const;
+
+ // Compute the digest of the certificate given |algorithm|.
+ bool ComputeDigest(const std::string& algorithm,
+ unsigned char* digest,
+ size_t size,
+ size_t* length) const override;
+
+ // Compute the digest of a certificate as a CRYPTO_BUFFER.
+ static bool ComputeDigest(const CRYPTO_BUFFER* cert_buffer,
+ const std::string& algorithm,
+ unsigned char* digest,
+ size_t size,
+ size_t* length);
+
+ bool GetSignatureDigestAlgorithm(std::string* algorithm) const override;
+
+ int64_t CertificateExpirationTime() const override;
+
+ private:
+ // A handle to the DER encoded certificate data.
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer_;
+ RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLCertificate);
+};
+
+} // namespace rtc
+
+#endif // RTC_BASE_BORINGSSL_CERTIFICATE_H_
diff --git a/rtc_base/boringssl_identity.cc b/rtc_base/boringssl_identity.cc
new file mode 100644
index 0000000..d22c8ce
--- /dev/null
+++ b/rtc_base/boringssl_identity.cc
@@ -0,0 +1,215 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/boringssl_identity.h"
+
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+#include <openssl/pool.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+
+BoringSSLIdentity::BoringSSLIdentity(
+ std::unique_ptr<OpenSSLKeyPair> key_pair,
+ std::unique_ptr<BoringSSLCertificate> certificate)
+ : key_pair_(std::move(key_pair)) {
+ RTC_DCHECK(key_pair_ != nullptr);
+ RTC_DCHECK(certificate != nullptr);
+ std::vector<std::unique_ptr<SSLCertificate>> certs;
+ certs.push_back(std::move(certificate));
+ cert_chain_.reset(new SSLCertChain(std::move(certs)));
+}
+
+BoringSSLIdentity::BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+ std::unique_ptr<SSLCertChain> cert_chain)
+ : key_pair_(std::move(key_pair)), cert_chain_(std::move(cert_chain)) {
+ RTC_DCHECK(key_pair_ != nullptr);
+ RTC_DCHECK(cert_chain_ != nullptr);
+}
+
+BoringSSLIdentity::~BoringSSLIdentity() = default;
+
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateInternal(
+ const SSLIdentityParams& params) {
+ auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
+ if (key_pair) {
+ std::unique_ptr<BoringSSLCertificate> certificate(
+ BoringSSLCertificate::Generate(key_pair.get(), params));
+ if (certificate != nullptr) {
+ return absl::WrapUnique(
+ new BoringSSLIdentity(std::move(key_pair), std::move(certificate)));
+ }
+ }
+ RTC_LOG(LS_ERROR) << "Identity generation failed.";
+ return nullptr;
+}
+
+// static
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateWithExpiration(
+ const std::string& common_name,
+ const KeyParams& key_params,
+ time_t certificate_lifetime) {
+ SSLIdentityParams params;
+ params.key_params = key_params;
+ params.common_name = common_name;
+ time_t now = time(nullptr);
+ params.not_before = now + kCertificateWindowInSeconds;
+ params.not_after = now + certificate_lifetime;
+ if (params.not_before > params.not_after)
+ return nullptr;
+ return CreateInternal(params);
+}
+
+std::unique_ptr<BoringSSLIdentity> BoringSSLIdentity::CreateForTest(
+ const SSLIdentityParams& params) {
+ return CreateInternal(params);
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMStrings(
+ const std::string& private_key,
+ const std::string& certificate) {
+ std::unique_ptr<BoringSSLCertificate> cert(
+ BoringSSLCertificate::FromPEMString(certificate));
+ if (!cert) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to create BoringSSLCertificate from PEM string.";
+ return nullptr;
+ }
+
+ auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
+ if (!key_pair) {
+ RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
+ return nullptr;
+ }
+
+ return absl::WrapUnique(
+ new BoringSSLIdentity(std::move(key_pair), std::move(cert)));
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CreateFromPEMChainStrings(
+ const std::string& private_key,
+ const std::string& certificate_chain) {
+ bssl::UniquePtr<BIO> bio(
+ BIO_new_mem_buf(certificate_chain.data(),
+ rtc::dchecked_cast<int>(certificate_chain.size())));
+ if (!bio) {
+ return nullptr;
+ }
+ BIO_set_mem_eof_return(bio.get(), 0);
+ std::vector<std::unique_ptr<SSLCertificate>> certs;
+ while (true) {
+ char* name;
+ char* header;
+ unsigned char* data;
+ long len; // NOLINT
+ int ret = PEM_read_bio(bio.get(), &name, &header, &data, &len);
+ if (ret == 0) {
+ uint32_t err = ERR_peek_error();
+ if (ERR_GET_LIB(err) == ERR_LIB_PEM &&
+ ERR_GET_REASON(err) == PEM_R_NO_START_LINE) {
+ break;
+ }
+ RTC_LOG(LS_ERROR) << "Failed to parse certificate from PEM string.";
+ return nullptr;
+ }
+ bssl::UniquePtr<char> owned_name(name);
+ bssl::UniquePtr<char> owned_header(header);
+ bssl::UniquePtr<unsigned char> owned_data(data);
+ if (strcmp(owned_name.get(), PEM_STRING_X509) != 0) {
+ RTC_LOG(LS_ERROR)
+ << "Non-certificate found while parsing certificate chain: "
+ << owned_name.get();
+ return nullptr;
+ }
+ bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer(
+ CRYPTO_BUFFER_new(data, len, openssl::GetBufferPool()));
+ if (!crypto_buffer) {
+ return nullptr;
+ }
+ certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer)));
+ }
+ if (certs.empty()) {
+ RTC_LOG(LS_ERROR) << "Found no certificates in PEM string.";
+ return nullptr;
+ }
+
+ auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
+ if (!key_pair) {
+ RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
+ return nullptr;
+ }
+
+ return absl::WrapUnique(new BoringSSLIdentity(
+ std::move(key_pair), std::make_unique<SSLCertChain>(std::move(certs))));
+}
+
+const BoringSSLCertificate& BoringSSLIdentity::certificate() const {
+ return *static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(0));
+}
+
+const SSLCertChain& BoringSSLIdentity::cert_chain() const {
+ return *cert_chain_.get();
+}
+
+std::unique_ptr<SSLIdentity> BoringSSLIdentity::CloneInternal() const {
+ // We cannot use std::make_unique here because the referenced
+ // BoringSSLIdentity constructor is private.
+ return absl::WrapUnique(
+ new BoringSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
+}
+
+bool BoringSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
+ std::vector<CRYPTO_BUFFER*> cert_buffers;
+ for (size_t i = 0; i < cert_chain_->GetSize(); ++i) {
+ cert_buffers.push_back(
+ static_cast<const BoringSSLCertificate*>(&cert_chain_->Get(i))
+ ->cert_buffer());
+ }
+ // 1 is the documented success return code.
+ if (1 != SSL_CTX_set_chain_and_key(ctx, &cert_buffers[0], cert_buffers.size(),
+ key_pair_->pkey(), nullptr)) {
+ openssl::LogSSLErrors("Configuring key and certificate");
+ return false;
+ }
+ return true;
+}
+
+std::string BoringSSLIdentity::PrivateKeyToPEMString() const {
+ return key_pair_->PrivateKeyToPEMString();
+}
+
+std::string BoringSSLIdentity::PublicKeyToPEMString() const {
+ return key_pair_->PublicKeyToPEMString();
+}
+
+bool BoringSSLIdentity::operator==(const BoringSSLIdentity& other) const {
+ return *this->key_pair_ == *other.key_pair_ &&
+ this->certificate() == other.certificate();
+}
+
+bool BoringSSLIdentity::operator!=(const BoringSSLIdentity& other) const {
+ return !(*this == other);
+}
+
+} // namespace rtc
diff --git a/rtc_base/boringssl_identity.h b/rtc_base/boringssl_identity.h
new file mode 100644
index 0000000..71b29b4
--- /dev/null
+++ b/rtc_base/boringssl_identity.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_BORINGSSL_IDENTITY_H_
+#define RTC_BASE_BORINGSSL_IDENTITY_H_
+
+#include <openssl/ossl_typ.h>
+
+#include <ctime>
+#include <memory>
+#include <string>
+
+#include "rtc_base/boringssl_certificate.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/openssl_key_pair.h"
+#include "rtc_base/ssl_certificate.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+// Holds a keypair and certificate together, and a method to generate them
+// consistently. Uses CRYPTO_BUFFER instead of X509, which offers binary size
+// and memory improvements.
+class BoringSSLIdentity final : public SSLIdentity {
+ public:
+ static std::unique_ptr<BoringSSLIdentity> CreateWithExpiration(
+ const std::string& common_name,
+ const KeyParams& key_params,
+ time_t certificate_lifetime);
+ static std::unique_ptr<BoringSSLIdentity> CreateForTest(
+ const SSLIdentityParams& params);
+ static std::unique_ptr<SSLIdentity> CreateFromPEMStrings(
+ const std::string& private_key,
+ const std::string& certificate);
+ static std::unique_ptr<SSLIdentity> CreateFromPEMChainStrings(
+ const std::string& private_key,
+ const std::string& certificate_chain);
+ ~BoringSSLIdentity() override;
+
+ const BoringSSLCertificate& certificate() const override;
+ const SSLCertChain& cert_chain() const override;
+
+ // Configure an SSL context object to use our key and certificate.
+ bool ConfigureIdentity(SSL_CTX* ctx);
+
+ std::string PrivateKeyToPEMString() const override;
+ std::string PublicKeyToPEMString() const override;
+ bool operator==(const BoringSSLIdentity& other) const;
+ bool operator!=(const BoringSSLIdentity& other) const;
+
+ private:
+ BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+ std::unique_ptr<BoringSSLCertificate> certificate);
+ BoringSSLIdentity(std::unique_ptr<OpenSSLKeyPair> key_pair,
+ std::unique_ptr<SSLCertChain> cert_chain);
+ std::unique_ptr<SSLIdentity> CloneInternal() const override;
+
+ static std::unique_ptr<BoringSSLIdentity> CreateInternal(
+ const SSLIdentityParams& params);
+
+ std::unique_ptr<OpenSSLKeyPair> key_pair_;
+ std::unique_ptr<SSLCertChain> cert_chain_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(BoringSSLIdentity);
+};
+
+} // namespace rtc
+
+#endif // RTC_BASE_BORINGSSL_IDENTITY_H_
diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc
index 8fd882c..e5c2c42 100644
--- a/rtc_base/openssl_adapter.cc
+++ b/rtc_base/openssl_adapter.cc
@@ -13,6 +13,9 @@
#include <errno.h>
#include <openssl/bio.h>
#include <openssl/err.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#endif
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <string.h>
@@ -20,13 +23,24 @@
#include <memory>
+// Use CRYPTO_BUFFER APIs if available and we have no dependency on X509
+// objects.
+#if defined(OPENSSL_IS_BORINGSSL) && \
+ defined(WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS)
+#define WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+#endif
+
#include "absl/memory/memory.h"
#include "rtc_base/checks.h"
#include "rtc_base/location.h"
#include "rtc_base/logging.h"
#include "rtc_base/numerics/safe_conversions.h"
#include "rtc_base/openssl.h"
-#include "rtc_base/openssl_certificate.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
+#include "rtc_base/openssl_identity.h"
+#endif
#include "rtc_base/openssl_utility.h"
#include "rtc_base/string_encode.h"
#include "rtc_base/thread.h"
@@ -223,8 +237,13 @@
void OpenSSLAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
RTC_DCHECK(!identity_);
+#ifdef OPENSSL_IS_BORINGSSL
+ identity_ =
+ absl::WrapUnique(static_cast<BoringSSLIdentity*>(identity.release()));
+#else
identity_ =
absl::WrapUnique(static_cast<OpenSSLIdentity*>(identity.release()));
+#endif
}
void OpenSSLAdapter::SetRole(SSLRole role) {
@@ -797,7 +816,70 @@
#endif
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+// static
+enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyCallback(SSL* ssl,
+ uint8_t* out_alert) {
+ // Get our stream pointer from the SSL context.
+ OpenSSLAdapter* stream =
+ reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+
+ ssl_verify_result_t ret = stream->SSLVerifyInternal(ssl, out_alert);
+
+ // Should only be used for debugging and development.
+ if (ret != ssl_verify_ok && stream->ignore_bad_cert_) {
+ RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+ return ssl_verify_ok;
+ }
+
+ return ret;
+}
+
+enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyInternal(SSL* ssl,
+ uint8_t* out_alert) {
+ if (ssl_cert_verifier_ == nullptr) {
+ RTC_LOG(LS_WARNING) << "Built-in trusted root certificates disabled but no "
+ "SSL verify callback provided.";
+ return ssl_verify_invalid;
+ }
+
+ RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
+ const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+ if (sk_CRYPTO_BUFFER_num(chain) == 0) {
+ RTC_LOG(LS_ERROR) << "Peer certificate chain empty?";
+ return ssl_verify_invalid;
+ }
+
+ BoringSSLCertificate cert(bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, 0)));
+ if (!ssl_cert_verifier_->Verify(cert)) {
+ RTC_LOG(LS_WARNING) << "Failed to verify certificate using custom callback";
+ return ssl_verify_invalid;
+ }
+
+ custom_cert_verifier_status_ = true;
+ RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
+ return ssl_verify_ok;
+}
+#else // WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
int OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
+ // Get our stream pointer from the store
+ SSL* ssl = reinterpret_cast<SSL*>(
+ X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
+
+ OpenSSLAdapter* stream =
+ reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
+ ok = stream->SSLVerifyInternal(ok, ssl, store);
+
+ // Should only be used for debugging and development.
+ if (!ok && stream->ignore_bad_cert_) {
+ RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
+ return 1;
+ }
+
+ return ok;
+}
+
+int OpenSSLAdapter::SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store) {
#if !defined(NDEBUG)
if (!ok) {
char data[256];
@@ -814,33 +896,40 @@
<< X509_verify_cert_error_string(err);
}
#endif
- // Get our stream pointer from the store
- SSL* ssl = reinterpret_cast<SSL*>(
- X509_STORE_CTX_get_ex_data(store, SSL_get_ex_data_X509_STORE_CTX_idx()));
-
- OpenSSLAdapter* stream =
- reinterpret_cast<OpenSSLAdapter*>(SSL_get_app_data(ssl));
-
- if (!ok && stream->ssl_cert_verifier_ != nullptr) {
- RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
- const OpenSSLCertificate cert(X509_STORE_CTX_get_current_cert(store));
- if (stream->ssl_cert_verifier_->Verify(cert)) {
- stream->custom_cert_verifier_status_ = true;
- RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
- ok = true;
- } else {
- RTC_LOG(LS_INFO) << "Failed to verify certificate using custom callback";
- }
+ if (ssl_cert_verifier_ == nullptr) {
+ return ok;
}
- // Should only be used for debugging and development.
- if (!ok && stream->ignore_bad_cert_) {
- RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
- ok = 1;
+ RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
+#ifdef OPENSSL_IS_BORINGSSL
+ // Convert X509 to CRYPTO_BUFFER.
+ uint8_t* data = nullptr;
+ int length = i2d_X509(X509_STORE_CTX_get_current_cert(store), &data);
+ if (length < 0) {
+ RTC_LOG(LS_ERROR) << "Failed to encode X509.";
+ return ok;
+ }
+ bssl::UniquePtr<uint8_t> owned_data(data);
+ bssl::UniquePtr<CRYPTO_BUFFER> crypto_buffer(
+ CRYPTO_BUFFER_new(data, length, openssl::GetBufferPool()));
+ if (!crypto_buffer) {
+ RTC_LOG(LS_ERROR) << "Failed to allocate CRYPTO_BUFFER.";
+ return ok;
+ }
+ const BoringSSLCertificate cert(std::move(crypto_buffer));
+#else
+ const OpenSSLCertificate cert(X509_STORE_CTX_get_current_cert(store));
+#endif
+ if (!ssl_cert_verifier_->Verify(cert)) {
+ RTC_LOG(LS_INFO) << "Failed to verify certificate using custom callback";
+ return ok;
}
- return ok;
+ custom_cert_verifier_status_ = true;
+ RTC_LOG(LS_INFO) << "Validated certificate using custom callback";
+ return 1;
}
+#endif // !defined(WEBRTC_USE_CRYPTO_BUFFER_CALLBACK)
int OpenSSLAdapter::NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session) {
OpenSSLAdapter* stream =
@@ -852,8 +941,15 @@
}
SSL_CTX* OpenSSLAdapter::CreateContext(SSLMode mode, bool enable_cache) {
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+ // If X509 objects aren't used, we can use these methods to avoid
+ // linking the sizable crypto/x509 code.
+ SSL_CTX* ctx = SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_with_buffers_method()
+ : TLS_with_buffers_method());
+#else
SSL_CTX* ctx =
SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
+#endif
if (ctx == nullptr) {
unsigned long error = ERR_get_error(); // NOLINT: type used by OpenSSL.
RTC_LOG(LS_WARNING) << "SSL_CTX creation failed: " << '"'
@@ -877,8 +973,16 @@
SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
#endif
+#ifdef OPENSSL_IS_BORINGSSL
+ SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
+#endif
+
+#ifdef WEBRTC_USE_CRYPTO_BUFFER_CALLBACK
+ SSL_CTX_set_custom_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
+#else
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
SSL_CTX_set_verify_depth(ctx, 4);
+#endif
// Use defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
// (note that SHA256 and SHA384 only select legacy CBC ciphers).
// Additionally disable HMAC-SHA1 ciphers in ECDSA. These are the remaining
diff --git a/rtc_base/openssl_adapter.h b/rtc_base/openssl_adapter.h
index 6f1f7dc..76b003a 100644
--- a/rtc_base/openssl_adapter.h
+++ b/rtc_base/openssl_adapter.h
@@ -11,6 +11,7 @@
#ifndef RTC_BASE_OPENSSL_ADAPTER_H_
#define RTC_BASE_OPENSSL_ADAPTER_H_
+#include <openssl/ossl_typ.h>
#include <stddef.h>
#include <stdint.h>
@@ -21,7 +22,11 @@
#include "rtc_base/async_socket.h"
#include "rtc_base/buffer.h"
#include "rtc_base/message_handler.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
#include "rtc_base/openssl_identity.h"
+#endif
#include "rtc_base/openssl_session_cache.h"
#include "rtc_base/socket.h"
#include "rtc_base/socket_address.h"
@@ -109,7 +114,16 @@
// In debug builds, logs info about the state of the SSL connection.
static void SSLInfoCallback(const SSL* ssl, int where, int ret);
#endif
+
+#if defined(OPENSSL_IS_BORINGSSL) && \
+ defined(WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS)
+ static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl,
+ uint8_t* out_alert);
+ enum ssl_verify_result_t SSLVerifyInternal(SSL* ssl, uint8_t* out_alert);
+#else
static int SSLVerifyCallback(int ok, X509_STORE_CTX* store);
+ int SSLVerifyInternal(int ok, SSL* ssl, X509_STORE_CTX* store);
+#endif
friend class OpenSSLStreamAdapter; // for custom_verify_callback_;
// If the SSL_CTX was created with |enable_cache| set to true, this callback
@@ -123,7 +137,12 @@
SSLCertificateVerifier* ssl_cert_verifier_ = nullptr;
// The current connection state of the (d)TLS connection.
SSLState state_;
+
+#ifdef OPENSSL_IS_BORINGSSL
+ std::unique_ptr<BoringSSLIdentity> identity_;
+#else
std::unique_ptr<OpenSSLIdentity> identity_;
+#endif
// Indicates whethere this is a client or a server.
SSLRole role_;
bool ssl_read_needs_write_;
diff --git a/rtc_base/openssl_identity.cc b/rtc_base/openssl_identity.cc
index c94df40..3794d98 100644
--- a/rtc_base/openssl_identity.cc
+++ b/rtc_base/openssl_identity.cc
@@ -20,10 +20,8 @@
#endif // WEBRTC_WIN
#include <openssl/bio.h>
-#include <openssl/bn.h>
#include <openssl/err.h>
#include <openssl/pem.h>
-#include <openssl/rsa.h>
#include <stdint.h>
#include "absl/memory/memory.h"
@@ -35,160 +33,6 @@
namespace rtc {
-// We could have exposed a myriad of parameters for the crypto stuff,
-// but keeping it simple seems best.
-
-// Generate a key pair. Caller is responsible for freeing the returned object.
-static EVP_PKEY* MakeKey(const KeyParams& key_params) {
- RTC_LOG(LS_INFO) << "Making key pair";
- EVP_PKEY* pkey = EVP_PKEY_new();
- if (key_params.type() == KT_RSA) {
- int key_length = key_params.rsa_params().mod_size;
- BIGNUM* exponent = BN_new();
- RSA* rsa = RSA_new();
- if (!pkey || !exponent || !rsa ||
- !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
- !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
- !EVP_PKEY_assign_RSA(pkey, rsa)) {
- EVP_PKEY_free(pkey);
- BN_free(exponent);
- RSA_free(rsa);
- RTC_LOG(LS_ERROR) << "Failed to make RSA key pair";
- return nullptr;
- }
- // ownership of rsa struct was assigned, don't free it.
- BN_free(exponent);
- } else if (key_params.type() == KT_ECDSA) {
- if (key_params.ec_curve() == EC_NIST_P256) {
- EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
-
- // Ensure curve name is included when EC key is serialized.
- // Without this call, OpenSSL versions before 1.1.0 will create
- // certificates that don't work for TLS.
- // This is a no-op for BoringSSL and OpenSSL 1.1.0+
- EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
-
- if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
- !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
- EVP_PKEY_free(pkey);
- EC_KEY_free(ec_key);
- RTC_LOG(LS_ERROR) << "Failed to make EC key pair";
- return nullptr;
- }
- // ownership of ec_key struct was assigned, don't free it.
- } else {
- // Add generation of any other curves here.
- EVP_PKEY_free(pkey);
- RTC_LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
- return nullptr;
- }
- } else {
- EVP_PKEY_free(pkey);
- RTC_LOG(LS_ERROR) << "Key type requested not understood";
- return nullptr;
- }
-
- RTC_LOG(LS_INFO) << "Returning key pair";
- return pkey;
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::Generate(const KeyParams& key_params) {
- EVP_PKEY* pkey = MakeKey(key_params);
- if (!pkey) {
- openssl::LogSSLErrors("Generating key pair");
- return nullptr;
- }
- return new OpenSSLKeyPair(pkey);
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::FromPrivateKeyPEMString(
- const std::string& pem_string) {
- BIO* bio = BIO_new_mem_buf(const_cast<char*>(pem_string.c_str()), -1);
- if (!bio) {
- RTC_LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
- return nullptr;
- }
- BIO_set_mem_eof_return(bio, 0);
- EVP_PKEY* pkey =
- PEM_read_bio_PrivateKey(bio, nullptr, nullptr, const_cast<char*>("\0"));
- BIO_free(bio); // Frees the BIO, but not the pointed-to string.
- if (!pkey) {
- RTC_LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
- return nullptr;
- }
- if (EVP_PKEY_missing_parameters(pkey) != 0) {
- RTC_LOG(LS_ERROR)
- << "The resulting key pair is missing public key parameters.";
- EVP_PKEY_free(pkey);
- return nullptr;
- }
- return new OpenSSLKeyPair(pkey);
-}
-
-OpenSSLKeyPair::~OpenSSLKeyPair() {
- EVP_PKEY_free(pkey_);
-}
-
-OpenSSLKeyPair* OpenSSLKeyPair::GetReference() {
- AddReference();
- return new OpenSSLKeyPair(pkey_);
-}
-
-void OpenSSLKeyPair::AddReference() {
- EVP_PKEY_up_ref(pkey_);
-}
-
-std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
- BIO* temp_memory_bio = BIO_new(BIO_s_mem());
- if (!temp_memory_bio) {
- RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
- RTC_NOTREACHED();
- return "";
- }
- if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0,
- nullptr, nullptr)) {
- RTC_LOG_F(LS_ERROR) << "Failed to write private key";
- BIO_free(temp_memory_bio);
- RTC_NOTREACHED();
- return "";
- }
- BIO_write(temp_memory_bio, "\0", 1);
- char* buffer;
- BIO_get_mem_data(temp_memory_bio, &buffer);
- std::string priv_key_str = buffer;
- BIO_free(temp_memory_bio);
- return priv_key_str;
-}
-
-std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
- BIO* temp_memory_bio = BIO_new(BIO_s_mem());
- if (!temp_memory_bio) {
- RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
- RTC_NOTREACHED();
- return "";
- }
- if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
- RTC_LOG_F(LS_ERROR) << "Failed to write public key";
- BIO_free(temp_memory_bio);
- RTC_NOTREACHED();
- return "";
- }
- BIO_write(temp_memory_bio, "\0", 1);
- char* buffer;
- BIO_get_mem_data(temp_memory_bio, &buffer);
- std::string pub_key_str = buffer;
- BIO_free(temp_memory_bio);
- return pub_key_str;
-}
-
-bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
- return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
-}
-
-bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
- return !(*this == other);
-}
-
OpenSSLIdentity::OpenSSLIdentity(
std::unique_ptr<OpenSSLKeyPair> key_pair,
std::unique_ptr<OpenSSLCertificate> certificate)
@@ -211,8 +55,7 @@
std::unique_ptr<OpenSSLIdentity> OpenSSLIdentity::CreateInternal(
const SSLIdentityParams& params) {
- std::unique_ptr<OpenSSLKeyPair> key_pair(
- OpenSSLKeyPair::Generate(params.key_params));
+ auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
if (key_pair) {
std::unique_ptr<OpenSSLCertificate> certificate(
OpenSSLCertificate::Generate(key_pair.get(), params));
@@ -221,7 +64,7 @@
new OpenSSLIdentity(std::move(key_pair), std::move(certificate)));
}
}
- RTC_LOG(LS_INFO) << "Identity generation failed";
+ RTC_LOG(LS_ERROR) << "Identity generation failed";
return nullptr;
}
@@ -256,8 +99,7 @@
return nullptr;
}
- std::unique_ptr<OpenSSLKeyPair> key_pair(
- OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
+ auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
if (!key_pair) {
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
return nullptr;
@@ -298,8 +140,7 @@
return nullptr;
}
- std::unique_ptr<OpenSSLKeyPair> key_pair(
- OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
+ auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
if (!key_pair) {
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
return nullptr;
@@ -320,8 +161,8 @@
std::unique_ptr<SSLIdentity> OpenSSLIdentity::CloneInternal() const {
// We cannot use std::make_unique here because the referenced OpenSSLIdentity
// constructor is private.
- return absl::WrapUnique(new OpenSSLIdentity(
- absl::WrapUnique(key_pair_->GetReference()), cert_chain_->Clone()));
+ return absl::WrapUnique(
+ new OpenSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
}
bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
diff --git a/rtc_base/openssl_identity.h b/rtc_base/openssl_identity.h
index a2ac87c..00d6c74 100644
--- a/rtc_base/openssl_identity.h
+++ b/rtc_base/openssl_identity.h
@@ -17,45 +17,14 @@
#include <memory>
#include <string>
-#include "rtc_base/checks.h"
#include "rtc_base/constructor_magic.h"
#include "rtc_base/openssl_certificate.h"
+#include "rtc_base/openssl_key_pair.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/ssl_identity.h"
namespace rtc {
-// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
-// which is reference counted inside the OpenSSL library.
-class OpenSSLKeyPair final {
- public:
- explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
- RTC_DCHECK(pkey_ != nullptr);
- }
-
- static OpenSSLKeyPair* Generate(const KeyParams& key_params);
- // Constructs a key pair from the private key PEM string. This must not result
- // in missing public key parameters. Returns null on error.
- static OpenSSLKeyPair* FromPrivateKeyPEMString(const std::string& pem_string);
-
- virtual ~OpenSSLKeyPair();
-
- virtual OpenSSLKeyPair* GetReference();
-
- EVP_PKEY* pkey() const { return pkey_; }
- std::string PrivateKeyToPEMString() const;
- std::string PublicKeyToPEMString() const;
- bool operator==(const OpenSSLKeyPair& other) const;
- bool operator!=(const OpenSSLKeyPair& other) const;
-
- private:
- void AddReference();
-
- EVP_PKEY* pkey_;
-
- RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair);
-};
-
// Holds a keypair and certificate together, and a method to generate
// them consistently.
class OpenSSLIdentity final : public SSLIdentity {
diff --git a/rtc_base/openssl_key_pair.cc b/rtc_base/openssl_key_pair.cc
new file mode 100644
index 0000000..911a751
--- /dev/null
+++ b/rtc_base/openssl_key_pair.cc
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2004 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "rtc_base/openssl_key_pair.h"
+
+#include <memory>
+#include <utility>
+
+#if defined(WEBRTC_WIN)
+// Must be included first before openssl headers.
+#include "rtc_base/win32.h" // NOLINT
+#endif // WEBRTC_WIN
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/openssl.h"
+#include "rtc_base/openssl_utility.h"
+
+namespace rtc {
+
+// We could have exposed a myriad of parameters for the crypto stuff,
+// but keeping it simple seems best.
+
+// Generate a key pair. Caller is responsible for freeing the returned object.
+static EVP_PKEY* MakeKey(const KeyParams& key_params) {
+ RTC_LOG(LS_INFO) << "Making key pair";
+ EVP_PKEY* pkey = EVP_PKEY_new();
+ if (key_params.type() == KT_RSA) {
+ int key_length = key_params.rsa_params().mod_size;
+ BIGNUM* exponent = BN_new();
+ RSA* rsa = RSA_new();
+ if (!pkey || !exponent || !rsa ||
+ !BN_set_word(exponent, key_params.rsa_params().pub_exp) ||
+ !RSA_generate_key_ex(rsa, key_length, exponent, nullptr) ||
+ !EVP_PKEY_assign_RSA(pkey, rsa)) {
+ EVP_PKEY_free(pkey);
+ BN_free(exponent);
+ RSA_free(rsa);
+ RTC_LOG(LS_ERROR) << "Failed to make RSA key pair";
+ return nullptr;
+ }
+ // ownership of rsa struct was assigned, don't free it.
+ BN_free(exponent);
+ } else if (key_params.type() == KT_ECDSA) {
+ if (key_params.ec_curve() == EC_NIST_P256) {
+ EC_KEY* ec_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
+ if (!ec_key) {
+ EVP_PKEY_free(pkey);
+ RTC_LOG(LS_ERROR) << "Failed to allocate EC key";
+ return nullptr;
+ }
+
+ // Ensure curve name is included when EC key is serialized.
+ // Without this call, OpenSSL versions before 1.1.0 will create
+ // certificates that don't work for TLS.
+ // This is a no-op for BoringSSL and OpenSSL 1.1.0+
+ EC_KEY_set_asn1_flag(ec_key, OPENSSL_EC_NAMED_CURVE);
+
+ if (!pkey || !ec_key || !EC_KEY_generate_key(ec_key) ||
+ !EVP_PKEY_assign_EC_KEY(pkey, ec_key)) {
+ EVP_PKEY_free(pkey);
+ EC_KEY_free(ec_key);
+ RTC_LOG(LS_ERROR) << "Failed to make EC key pair";
+ return nullptr;
+ }
+ // ownership of ec_key struct was assigned, don't free it.
+ } else {
+ // Add generation of any other curves here.
+ EVP_PKEY_free(pkey);
+ RTC_LOG(LS_ERROR) << "ECDSA key requested for unknown curve";
+ return nullptr;
+ }
+ } else {
+ EVP_PKEY_free(pkey);
+ RTC_LOG(LS_ERROR) << "Key type requested not understood";
+ return nullptr;
+ }
+
+ RTC_LOG(LS_INFO) << "Returning key pair";
+ return pkey;
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Generate(
+ const KeyParams& key_params) {
+ EVP_PKEY* pkey = MakeKey(key_params);
+ if (!pkey) {
+ openssl::LogSSLErrors("Generating key pair");
+ return nullptr;
+ }
+ return std::make_unique<OpenSSLKeyPair>(pkey);
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::FromPrivateKeyPEMString(
+ const std::string& pem_string) {
+ BIO* bio =
+ BIO_new_mem_buf(const_cast<char*>(pem_string.data()), pem_string.size());
+ if (!bio) {
+ RTC_LOG(LS_ERROR) << "Failed to create a new BIO buffer.";
+ return nullptr;
+ }
+ BIO_set_mem_eof_return(bio, 0);
+ EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
+ BIO_free(bio); // Frees the BIO, but not the pointed-to string.
+ if (!pkey) {
+ RTC_LOG(LS_ERROR) << "Failed to create the private key from PEM string.";
+ return nullptr;
+ }
+ if (EVP_PKEY_missing_parameters(pkey) != 0) {
+ RTC_LOG(LS_ERROR)
+ << "The resulting key pair is missing public key parameters.";
+ EVP_PKEY_free(pkey);
+ return nullptr;
+ }
+ return std::make_unique<OpenSSLKeyPair>(pkey);
+}
+
+OpenSSLKeyPair::~OpenSSLKeyPair() {
+ EVP_PKEY_free(pkey_);
+}
+
+std::unique_ptr<OpenSSLKeyPair> OpenSSLKeyPair::Clone() {
+ AddReference();
+ return std::make_unique<OpenSSLKeyPair>(pkey_);
+}
+
+void OpenSSLKeyPair::AddReference() {
+ EVP_PKEY_up_ref(pkey_);
+}
+
+std::string OpenSSLKeyPair::PrivateKeyToPEMString() const {
+ BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+ if (!temp_memory_bio) {
+ RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+ RTC_NOTREACHED();
+ return "";
+ }
+ if (!PEM_write_bio_PrivateKey(temp_memory_bio, pkey_, nullptr, nullptr, 0,
+ nullptr, nullptr)) {
+ RTC_LOG_F(LS_ERROR) << "Failed to write private key";
+ BIO_free(temp_memory_bio);
+ RTC_NOTREACHED();
+ return "";
+ }
+ char* buffer;
+ size_t len = BIO_get_mem_data(temp_memory_bio, &buffer);
+ std::string priv_key_str(buffer, len);
+ BIO_free(temp_memory_bio);
+ return priv_key_str;
+}
+
+std::string OpenSSLKeyPair::PublicKeyToPEMString() const {
+ BIO* temp_memory_bio = BIO_new(BIO_s_mem());
+ if (!temp_memory_bio) {
+ RTC_LOG_F(LS_ERROR) << "Failed to allocate temporary memory bio";
+ RTC_NOTREACHED();
+ return "";
+ }
+ if (!PEM_write_bio_PUBKEY(temp_memory_bio, pkey_)) {
+ RTC_LOG_F(LS_ERROR) << "Failed to write public key";
+ BIO_free(temp_memory_bio);
+ RTC_NOTREACHED();
+ return "";
+ }
+ BIO_write(temp_memory_bio, "\0", 1);
+ char* buffer;
+ BIO_get_mem_data(temp_memory_bio, &buffer);
+ std::string pub_key_str = buffer;
+ BIO_free(temp_memory_bio);
+ return pub_key_str;
+}
+
+bool OpenSSLKeyPair::operator==(const OpenSSLKeyPair& other) const {
+ return EVP_PKEY_cmp(this->pkey_, other.pkey_) == 1;
+}
+
+bool OpenSSLKeyPair::operator!=(const OpenSSLKeyPair& other) const {
+ return !(*this == other);
+}
+
+} // namespace rtc
diff --git a/rtc_base/openssl_key_pair.h b/rtc_base/openssl_key_pair.h
new file mode 100644
index 0000000..a84c43b
--- /dev/null
+++ b/rtc_base/openssl_key_pair.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2020 The WebRTC Project Authors. All rights reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef RTC_BASE_OPENSSL_KEY_PAIR_H_
+#define RTC_BASE_OPENSSL_KEY_PAIR_H_
+
+#include <openssl/ossl_typ.h>
+
+#include <memory>
+#include <string>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/constructor_magic.h"
+#include "rtc_base/ssl_identity.h"
+
+namespace rtc {
+
+// OpenSSLKeyPair encapsulates an OpenSSL EVP_PKEY* keypair object,
+// which is reference counted inside the OpenSSL library.
+class OpenSSLKeyPair final {
+ public:
+ // Takes ownership of the key.
+ explicit OpenSSLKeyPair(EVP_PKEY* pkey) : pkey_(pkey) {
+ RTC_DCHECK(pkey_ != nullptr);
+ }
+
+ static std::unique_ptr<OpenSSLKeyPair> Generate(const KeyParams& key_params);
+ // Constructs a key pair from the private key PEM string. This must not result
+ // in missing public key parameters. Returns null on error.
+ static std::unique_ptr<OpenSSLKeyPair> FromPrivateKeyPEMString(
+ const std::string& pem_string);
+
+ ~OpenSSLKeyPair();
+
+ std::unique_ptr<OpenSSLKeyPair> Clone();
+
+ EVP_PKEY* pkey() const { return pkey_; }
+ std::string PrivateKeyToPEMString() const;
+ std::string PublicKeyToPEMString() const;
+ bool operator==(const OpenSSLKeyPair& other) const;
+ bool operator!=(const OpenSSLKeyPair& other) const;
+
+ private:
+ void AddReference();
+
+ EVP_PKEY* pkey_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(OpenSSLKeyPair);
+};
+
+} // namespace rtc
+
+#endif // RTC_BASE_OPENSSL_KEY_PAIR_H_
diff --git a/rtc_base/openssl_session_cache_unittest.cc b/rtc_base/openssl_session_cache_unittest.cc
index 1d3084b..0441d5c 100644
--- a/rtc_base/openssl_session_cache_unittest.cc
+++ b/rtc_base/openssl_session_cache_unittest.cc
@@ -19,10 +19,28 @@
#include "rtc_base/gunit.h"
#include "rtc_base/openssl.h"
+namespace {
+// Use methods that avoid X509 objects if possible.
+SSL_CTX* NewDtlsContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+ return SSL_CTX_new(DTLS_with_buffers_method());
+#else
+ return SSL_CTX_new(DTLS_method());
+#endif
+}
+SSL_CTX* NewTlsContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+ return SSL_CTX_new(TLS_with_buffers_method());
+#else
+ return SSL_CTX_new(TLS_method());
+#endif
+}
+} // namespace
+
namespace rtc {
TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewDtlsContext();
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_DTLS);
@@ -31,7 +49,7 @@
}
TEST(OpenSSLSessionCache, TLSModeSetCorrectly) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(TLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewTlsContext();
OpenSSLSessionCache session_cache(SSL_MODE_TLS, ssl_ctx);
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_TLS);
@@ -40,7 +58,7 @@
}
TEST(OpenSSLSessionCache, SSLContextSetCorrectly) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewDtlsContext();
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
EXPECT_EQ(session_cache.GetSSLContext(), ssl_ctx);
@@ -49,7 +67,7 @@
}
TEST(OpenSSLSessionCache, InvalidLookupReturnsNullptr) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewDtlsContext();
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
EXPECT_EQ(session_cache.LookupSession("Invalid"), nullptr);
@@ -60,7 +78,7 @@
}
TEST(OpenSSLSessionCache, SimpleValidSessionLookup) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewDtlsContext();
SSL_SESSION* ssl_session = SSL_SESSION_new(ssl_ctx);
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
@@ -71,7 +89,7 @@
}
TEST(OpenSSLSessionCache, AddToExistingReplacesPrevious) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+ SSL_CTX* ssl_ctx = NewDtlsContext();
SSL_SESSION* ssl_session_1 = SSL_SESSION_new(ssl_ctx);
SSL_SESSION* ssl_session_2 = SSL_SESSION_new(ssl_ctx);
diff --git a/rtc_base/openssl_stream_adapter.cc b/rtc_base/openssl_stream_adapter.cc
index f59b4ed..63b8069 100644
--- a/rtc_base/openssl_stream_adapter.cc
+++ b/rtc_base/openssl_stream_adapter.cc
@@ -32,7 +32,12 @@
#include "rtc_base/openssl.h"
#include "rtc_base/openssl_adapter.h"
#include "rtc_base/openssl_digest.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
#include "rtc_base/openssl_identity.h"
+#endif
+#include "rtc_base/openssl_utility.h"
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/stream.h"
#include "rtc_base/task_utils/to_queued_task.h"
@@ -304,10 +309,14 @@
void OpenSSLStreamAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
RTC_DCHECK(!identity_);
+#ifdef OPENSSL_IS_BORINGSSL
+ identity_.reset(static_cast<BoringSSLIdentity*>(identity.release()));
+#else
identity_.reset(static_cast<OpenSSLIdentity*>(identity.release()));
+#endif
}
-OpenSSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
+SSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
return identity_.get();
}
@@ -994,8 +1003,16 @@
}
SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
+#ifdef OPENSSL_IS_BORINGSSL
+ // If X509 objects aren't used, we can use these methods to avoid
+ // linking the sizable crypto/x509 code, using CRYPTO_BUFFER instead.
+ SSL_CTX* ctx =
+ SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_with_buffers_method()
+ : TLS_with_buffers_method());
+#else
SSL_CTX* ctx =
SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
+#endif
if (ctx == nullptr) {
return nullptr;
}
@@ -1033,6 +1050,7 @@
if (g_use_time_callback_for_testing) {
SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
}
+ SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
#endif
if (identity_ && !identity_->ConfigureIdentity(ctx)) {
@@ -1053,11 +1071,16 @@
}
// Configure a custom certificate verification callback to check the peer
- // certificate digest. Note the second argument to SSL_CTX_set_verify is to
- // override individual errors in the default verification logic, which is not
- // what we want here.
+ // certificate digest.
+#ifdef OPENSSL_IS_BORINGSSL
+ // Use CRYPTO_BUFFER version of the callback if building with BoringSSL.
+ SSL_CTX_set_custom_verify(ctx, mode, SSLVerifyCallback);
+#else
+ // Note the second argument to SSL_CTX_set_verify is to override individual
+ // errors in the default verification logic, which is not what we want here.
SSL_CTX_set_verify(ctx, mode, nullptr);
SSL_CTX_set_cert_verify_callback(ctx, SSLVerifyCallback, nullptr);
+#endif
// Select list of available ciphers. Note that !SHA256 and !SHA384 only
// remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
@@ -1082,14 +1105,12 @@
RTC_LOG(LS_WARNING) << "Missing digest or peer certificate.";
return false;
}
- const OpenSSLCertificate* leaf_cert =
- static_cast<const OpenSSLCertificate*>(&peer_cert_chain_->Get(0));
unsigned char digest[EVP_MAX_MD_SIZE];
size_t digest_length;
- if (!OpenSSLCertificate::ComputeDigest(
- leaf_cert->x509(), peer_certificate_digest_algorithm_, digest,
- sizeof(digest), &digest_length)) {
+ if (!peer_cert_chain_->Get(0).ComputeDigest(
+ peer_certificate_digest_algorithm_, digest, sizeof(digest),
+ &digest_length)) {
RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest.";
return false;
}
@@ -1113,6 +1134,36 @@
return peer_cert_chain_ ? peer_cert_chain_->Clone() : nullptr;
}
+#ifdef OPENSSL_IS_BORINGSSL
+enum ssl_verify_result_t OpenSSLStreamAdapter::SSLVerifyCallback(
+ SSL* ssl,
+ uint8_t* out_alert) {
+ // Get our OpenSSLStreamAdapter from the context.
+ OpenSSLStreamAdapter* stream =
+ reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
+ const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+ // Creates certificate chain.
+ std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
+ for (CRYPTO_BUFFER* cert : chain) {
+ cert_chain.emplace_back(new BoringSSLCertificate(bssl::UpRef(cert)));
+ }
+ stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
+
+ // If the peer certificate digest isn't known yet, we'll wait to verify
+ // until it's known, and for now just return a success status.
+ if (stream->peer_certificate_digest_algorithm_.empty()) {
+ RTC_LOG(LS_INFO) << "Waiting to verify certificate until digest is known.";
+ // TODO(deadbeef): Use ssl_verify_retry?
+ return ssl_verify_ok;
+ }
+
+ if (!stream->VerifyPeerCertificate()) {
+ return ssl_verify_invalid;
+ }
+
+ return ssl_verify_ok;
+}
+#else // OPENSSL_IS_BORINGSSL
int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
// Get our SSL structure and OpenSSLStreamAdapter from the store.
SSL* ssl = reinterpret_cast<SSL*>(
@@ -1120,20 +1171,10 @@
OpenSSLStreamAdapter* stream =
reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
-#if defined(OPENSSL_IS_BORINGSSL)
- STACK_OF(X509)* chain = SSL_get_peer_full_cert_chain(ssl);
- // Creates certificate chain.
- std::vector<std::unique_ptr<SSLCertificate>> cert_chain;
- for (X509* cert : chain) {
- cert_chain.emplace_back(new OpenSSLCertificate(cert));
- }
- stream->peer_cert_chain_.reset(new SSLCertChain(std::move(cert_chain)));
-#else
// Record the peer's certificate.
X509* cert = X509_STORE_CTX_get0_cert(store);
stream->peer_cert_chain_.reset(
new SSLCertChain(std::make_unique<OpenSSLCertificate>(cert)));
-#endif
// If the peer certificate digest isn't known yet, we'll wait to verify
// until it's known, and for now just return a success status.
@@ -1149,6 +1190,7 @@
return 1;
}
+#endif // !OPENSSL_IS_BORINGSSL
bool OpenSSLStreamAdapter::IsBoringSsl() {
#ifdef OPENSSL_IS_BORINGSSL
diff --git a/rtc_base/openssl_stream_adapter.h b/rtc_base/openssl_stream_adapter.h
index fbfccd684..a09737c 100644
--- a/rtc_base/openssl_stream_adapter.h
+++ b/rtc_base/openssl_stream_adapter.h
@@ -21,7 +21,11 @@
#include "absl/types/optional.h"
#include "rtc_base/buffer.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
#include "rtc_base/openssl_identity.h"
+#endif
#include "rtc_base/ssl_identity.h"
#include "rtc_base/ssl_stream_adapter.h"
#include "rtc_base/stream.h"
@@ -71,7 +75,7 @@
~OpenSSLStreamAdapter() override;
void SetIdentity(std::unique_ptr<SSLIdentity> identity) override;
- OpenSSLIdentity* GetIdentityForTesting() const override;
+ SSLIdentity* GetIdentityForTesting() const override;
// Default argument is for compatibility
void SetServerRole(SSLRole role = SSL_SERVER) override;
@@ -179,9 +183,16 @@
SSL_CTX* SetupSSLContext();
// Verify the peer certificate matches the signaled digest.
bool VerifyPeerCertificate();
+
+#ifdef OPENSSL_IS_BORINGSSL
+ // SSL certificate verification callback. See SSL_CTX_set_custom_verify.
+ static enum ssl_verify_result_t SSLVerifyCallback(SSL* ssl,
+ uint8_t* out_alert);
+#else
// SSL certificate verification callback. See
// SSL_CTX_set_cert_verify_callback.
static int SSLVerifyCallback(X509_STORE_CTX* store, void* arg);
+#endif
bool WaitingToVerifyPeerCertificate() const {
return GetClientAuthEnabled() && !peer_certificate_verified_;
@@ -208,7 +219,11 @@
SSL_CTX* ssl_ctx_;
// Our key and certificate.
+#ifdef OPENSSL_IS_BORINGSSL
+ std::unique_ptr<BoringSSLIdentity> identity_;
+#else
std::unique_ptr<OpenSSLIdentity> identity_;
+#endif
// The certificate chain that the peer presented. Initially null, until the
// connection is established.
std::unique_ptr<SSLCertChain> peer_cert_chain_;
diff --git a/rtc_base/openssl_utility.cc b/rtc_base/openssl_utility.cc
index 1984eb0..f91e8d9 100644
--- a/rtc_base/openssl_utility.cc
+++ b/rtc_base/openssl_utility.cc
@@ -14,6 +14,9 @@
#include "rtc_base/win32.h" // NOLINT
#endif // WEBRTC_WIN
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#endif
#include <openssl/err.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
@@ -33,6 +36,10 @@
// Holds various helper methods.
namespace {
+
+// TODO(crbug.com/webrtc/11710): When OS certificate verification is available,
+// and we don't need VerifyPeerCertMatchesHost, don't compile this in order to
+// avoid a dependency on OpenSSL X509 objects (see crbug.com/webrtc/11410).
void LogCertificates(SSL* ssl, X509* certificate) {
// Logging certificates is extremely verbose. So it is disabled by default.
#ifdef LOG_CERTIFICATES
@@ -65,6 +72,118 @@
}
} // namespace
+#ifdef OPENSSL_IS_BORINGSSL
+bool ParseCertificate(CRYPTO_BUFFER* cert_buffer,
+ CBS* signature_algorithm_oid,
+ int64_t* expiration_time) {
+ CBS cbs;
+ CRYPTO_BUFFER_init_CBS(cert_buffer, &cbs);
+
+ // Certificate ::= SEQUENCE {
+ CBS certificate;
+ if (!CBS_get_asn1(&cbs, &certificate, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // tbsCertificate TBSCertificate,
+ CBS tbs_certificate;
+ if (!CBS_get_asn1(&certificate, &tbs_certificate, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // signatureAlgorithm AlgorithmIdentifier,
+ CBS signature_algorithm;
+ if (!CBS_get_asn1(&certificate, &signature_algorithm, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ if (!CBS_get_asn1(&signature_algorithm, signature_algorithm_oid,
+ CBS_ASN1_OBJECT)) {
+ return false;
+ }
+ // signatureValue BIT STRING }
+ if (!CBS_get_asn1(&certificate, nullptr, CBS_ASN1_BITSTRING)) {
+ return false;
+ }
+ if (CBS_len(&certificate)) {
+ return false;
+ }
+
+ // Now parse the inner TBSCertificate.
+ // version [0] EXPLICIT Version DEFAULT v1,
+ if (!CBS_get_optional_asn1(
+ &tbs_certificate, nullptr, nullptr,
+ CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) {
+ return false;
+ }
+ // serialNumber CertificateSerialNumber,
+ if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_INTEGER)) {
+ return false;
+ }
+ // signature AlgorithmIdentifier
+ if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // issuer Name,
+ if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // validity Validity,
+ CBS validity;
+ if (!CBS_get_asn1(&tbs_certificate, &validity, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // Skip over notBefore.
+ if (!CBS_get_any_asn1_element(&validity, nullptr, nullptr, nullptr)) {
+ return false;
+ }
+ // Parse notAfter.
+ CBS not_after;
+ unsigned not_after_tag;
+ if (!CBS_get_any_asn1(&validity, ¬_after, ¬_after_tag)) {
+ return false;
+ }
+ bool long_format;
+ if (not_after_tag == CBS_ASN1_UTCTIME) {
+ long_format = false;
+ } else if (not_after_tag == CBS_ASN1_GENERALIZEDTIME) {
+ long_format = true;
+ } else {
+ return false;
+ }
+ if (expiration_time) {
+ *expiration_time =
+ ASN1TimeToSec(CBS_data(¬_after), CBS_len(¬_after), long_format);
+ }
+ // subject Name,
+ if (!CBS_get_asn1_element(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // subjectPublicKeyInfo SubjectPublicKeyInfo,
+ if (!CBS_get_asn1(&tbs_certificate, nullptr, CBS_ASN1_SEQUENCE)) {
+ return false;
+ }
+ // issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL
+ if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr,
+ 0x01 | CBS_ASN1_CONTEXT_SPECIFIC)) {
+ return false;
+ }
+ // subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL
+ if (!CBS_get_optional_asn1(&tbs_certificate, nullptr, nullptr,
+ 0x02 | CBS_ASN1_CONTEXT_SPECIFIC)) {
+ return false;
+ }
+ // extensions [3] EXPLICIT Extensions OPTIONAL
+ if (!CBS_get_optional_asn1(
+ &tbs_certificate, nullptr, nullptr,
+ 0x03 | CBS_ASN1_CONSTRUCTED | CBS_ASN1_CONTEXT_SPECIFIC)) {
+ return false;
+ }
+ if (CBS_len(&tbs_certificate)) {
+ return false;
+ }
+
+ return true;
+}
+#endif // OPENSSL_IS_BORINGSSL
+
bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
if (host.empty()) {
RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate.";
@@ -76,9 +195,28 @@
return false;
}
+#ifdef OPENSSL_IS_BORINGSSL
+ // We can't grab a X509 object directly, as the SSL context may have been
+ // initialized with TLS_with_buffers_method.
+ const STACK_OF(CRYPTO_BUFFER)* chain = SSL_get0_peer_certificates(ssl);
+ if (chain == nullptr || sk_CRYPTO_BUFFER_num(chain) == 0) {
+ RTC_LOG(LS_ERROR)
+ << "SSL_get0_peer_certificates failed. This should never happen.";
+ return false;
+ }
+ CRYPTO_BUFFER* leaf = sk_CRYPTO_BUFFER_value(chain, 0);
+ bssl::UniquePtr<X509> x509(X509_parse_from_buffer(leaf));
+ if (!x509) {
+ RTC_LOG(LS_ERROR) << "Failed to parse certificate to X509 object.";
+ return false;
+ }
+ LogCertificates(ssl, x509.get());
+ return X509_check_host(x509.get(), host.c_str(), host.size(), 0, nullptr) ==
+ 1;
+#else // OPENSSL_IS_BORINGSSL
X509* certificate = SSL_get_peer_certificate(ssl);
if (certificate == nullptr) {
- RTC_DLOG(LS_ERROR)
+ RTC_LOG(LS_ERROR)
<< "SSL_get_peer_certificate failed. This should never happen.";
return false;
}
@@ -89,6 +227,7 @@
X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1;
X509_free(certificate);
return is_valid_cert_name;
+#endif // !defined(OPENSSL_IS_BORINGSSL)
}
void LogSSLErrors(const std::string& prefix) {
@@ -123,5 +262,12 @@
}
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
+#ifdef OPENSSL_IS_BORINGSSL
+CRYPTO_BUFFER_POOL* GetBufferPool() {
+ static CRYPTO_BUFFER_POOL* instance = CRYPTO_BUFFER_POOL_new();
+ return instance;
+}
+#endif
+
} // namespace openssl
} // namespace rtc
diff --git a/rtc_base/openssl_utility.h b/rtc_base/openssl_utility.h
index 022294d..ee29ccd 100644
--- a/rtc_base/openssl_utility.h
+++ b/rtc_base/openssl_utility.h
@@ -20,8 +20,21 @@
// to OpenSSL that are commonly used and don't require global state should be
// placed here.
namespace openssl {
+
+#ifdef OPENSSL_IS_BORINGSSL
+// Does minimal parsing of a certificate (only verifying the presence of major
+// fields), primarily for the purpose of extracting the relevant out
+// parameters. Any that the caller is uninterested in can be null.
+bool ParseCertificate(CRYPTO_BUFFER* cert_buffer,
+ CBS* signature_algorithm_oid,
+ int64_t* expiration_time);
+#endif
+
// Verifies that the hostname provided matches that in the peer certificate
// attached to this SSL state.
+// TODO(crbug.com/webrtc/11710): When OS certificate verification is available,
+// skip compiling this as it adds a dependency on OpenSSL X509 objects, which we
+// are trying to avoid in favor of CRYPTO_BUFFERs (see crbug.com/webrtc/11410).
bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host);
// Logs all the errors in the OpenSSL errror queue from the current thread. A
@@ -35,6 +48,10 @@
bool LoadBuiltinSSLRootCertificates(SSL_CTX* ssl_ctx);
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
+#ifdef OPENSSL_IS_BORINGSSL
+CRYPTO_BUFFER_POOL* GetBufferPool();
+#endif
+
} // namespace openssl
} // namespace rtc
diff --git a/rtc_base/openssl_utility_unittest.cc b/rtc_base/openssl_utility_unittest.cc
index 9c9b971..d090524 100644
--- a/rtc_base/openssl_utility_unittest.cc
+++ b/rtc_base/openssl_utility_unittest.cc
@@ -24,8 +24,12 @@
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/ssl.h>
+#ifdef OPENSSL_IS_BORINGSSL
+#include <openssl/pool.h>
+#else
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+#endif
#include "rtc_base/arraysize.h"
#include "rtc_base/checks.h"
@@ -169,14 +173,17 @@
0x84, 0x0b, 0xc7, 0x15, 0x86, 0xc3, 0xfc, 0x48, 0x55, 0xb5, 0x81, 0x94,
0x73, 0xbd, 0x18, 0xcd, 0x9d, 0x92, 0x47, 0xaa, 0xfd, 0x18};
+#ifdef OPENSSL_IS_BORINGSSL
+enum ssl_verify_result_t DummyVerifyCallback(SSL* ssl, uint8_t* out_alert) {
+ return ssl_verify_ok;
+}
+#endif
+
// Creates a client SSL that has completed handshaking with a server that uses
// the specified certificate (which must have private key kFakeSSLPrivateKey).
// The server is deallocated. This client will have a peer certificate available
// and is thus suitable for testing VerifyPeerCertMatchesHost.
SSL* CreateSSLWithPeerCertificate(const unsigned char* cert, size_t cert_len) {
- X509* x509 =
- d2i_X509(nullptr, &cert, checked_cast<long>(cert_len)); // NOLINT
- RTC_CHECK(x509);
const unsigned char* key_ptr = kFakeSSLPrivateKey;
EVP_PKEY* key = d2i_PrivateKey(
@@ -184,14 +191,33 @@
checked_cast<long>(arraysize(kFakeSSLPrivateKey))); // NOLINT
RTC_CHECK(key);
- SSL_CTX* ctx = SSL_CTX_new(SSLv23_method());
+#ifdef OPENSSL_IS_BORINGSSL
+ SSL_CTX* ctx = SSL_CTX_new(TLS_with_buffers_method());
+#else
+ SSL_CTX* ctx = SSL_CTX_new(TLS_method());
+#endif
SSL* client = SSL_new(ctx);
SSL* server = SSL_new(ctx);
SSL_set_connect_state(client);
SSL_set_accept_state(server);
+#ifdef OPENSSL_IS_BORINGSSL
+ bssl::UniquePtr<CRYPTO_BUFFER> cert_buffer(CRYPTO_BUFFER_new(
+ static_cast<const uint8_t*>(cert), cert_len, openssl::GetBufferPool()));
+ RTC_CHECK(cert_buffer);
+ std::vector<CRYPTO_BUFFER*> cert_buffers;
+ cert_buffers.push_back(cert_buffer.get());
+ RTC_CHECK(1 == SSL_set_chain_and_key(server, cert_buffers.data(),
+ cert_buffers.size(), key, nullptr));
+ // When using crypto buffers we don't get any built-in verification.
+ SSL_set_custom_verify(client, SSL_VERIFY_PEER, DummyVerifyCallback);
+#else
+ X509* x509 =
+ d2i_X509(nullptr, &cert, checked_cast<long>(cert_len)); // NOLINT
+ RTC_CHECK(x509);
RTC_CHECK(SSL_use_certificate(server, x509));
RTC_CHECK(SSL_use_PrivateKey(server, key));
+#endif
BIO* bio1;
BIO* bio2;
@@ -221,13 +247,19 @@
SSL_free(server);
SSL_CTX_free(ctx);
EVP_PKEY_free(key);
+#ifndef OPENSSL_IS_BORINGSSL
X509_free(x509);
+#endif
return client;
}
} // namespace
TEST(OpenSSLUtilityTest, VerifyPeerCertMatchesHostFailsOnNoPeerCertificate) {
- SSL_CTX* ssl_ctx = SSL_CTX_new(DTLSv1_2_client_method());
+#ifdef OPENSSL_IS_BORINGSSL
+ SSL_CTX* ssl_ctx = SSL_CTX_new(DTLS_with_buffers_method());
+#else
+ SSL_CTX* ssl_ctx = SSL_CTX_new(DTLS_method());
+#endif
SSL* ssl = SSL_new(ssl_ctx);
EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
diff --git a/rtc_base/rtc_certificate_generator.cc b/rtc_base/rtc_certificate_generator.cc
index d95b645..5e1fdca 100644
--- a/rtc_base/rtc_certificate_generator.cc
+++ b/rtc_base/rtc_certificate_generator.cc
@@ -51,7 +51,7 @@
expires_s = std::min(expires_s, kYearInSeconds);
// TODO(torbjorng): Stop using |time_t|, its type is unspecified. It it safe
// to assume it can hold up to a year's worth of seconds (and more), but
- // |SSLIdentity::Generate| should stop relying on |time_t|.
+ // |SSLIdentity::Create| should stop relying on |time_t|.
// See bugs.webrtc.org/5720.
time_t cert_lifetime_s = static_cast<time_t>(expires_s);
identity = SSLIdentity::Create(kIdentityName, key_params, cert_lifetime_s);
diff --git a/rtc_base/ssl_certificate.cc b/rtc_base/ssl_certificate.cc
index db9097b..3f7013e 100644
--- a/rtc_base/ssl_certificate.cc
+++ b/rtc_base/ssl_certificate.cc
@@ -16,7 +16,12 @@
#include "absl/algorithm/container.h"
#include "rtc_base/checks.h"
-#include "rtc_base/openssl_certificate.h"
+#include "rtc_base/openssl.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
+#include "rtc_base/openssl_identity.h"
+#endif
#include "rtc_base/ssl_fingerprint.h"
#include "rtc_base/third_party/base64/base64.h"
@@ -117,7 +122,11 @@
// static
std::unique_ptr<SSLCertificate> SSLCertificate::FromPEMString(
const std::string& pem_string) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return BoringSSLCertificate::FromPEMString(pem_string);
+#else
return OpenSSLCertificate::FromPEMString(pem_string);
+#endif
}
} // namespace rtc
diff --git a/rtc_base/ssl_identity.cc b/rtc_base/ssl_identity.cc
index 09d25d2..8d93ecf 100644
--- a/rtc_base/ssl_identity.cc
+++ b/rtc_base/ssl_identity.cc
@@ -11,12 +11,16 @@
// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
#include "rtc_base/ssl_identity.h"
+#include <openssl/ossl_typ.h>
#include <string.h>
#include <time.h>
-#include <string>
#include "rtc_base/checks.h"
+#ifdef OPENSSL_IS_BORINGSSL
+#include "rtc_base/boringssl_identity.h"
+#else
#include "rtc_base/openssl_identity.h"
+#endif
#include "rtc_base/ssl_certificate.h"
#include "rtc_base/strings/string_builder.h"
#include "rtc_base/third_party/base64/base64.h"
@@ -213,28 +217,36 @@
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
const KeyParams& key_param,
time_t certificate_lifetime) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return BoringSSLIdentity::CreateWithExpiration(common_name, key_param,
+ certificate_lifetime);
+#else
return OpenSSLIdentity::CreateWithExpiration(common_name, key_param,
certificate_lifetime);
+#endif
}
// static
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
const KeyParams& key_param) {
- return OpenSSLIdentity::CreateWithExpiration(
- common_name, key_param, kDefaultCertificateLifetimeInSeconds);
+ return Create(common_name, key_param, kDefaultCertificateLifetimeInSeconds);
}
// static
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
KeyType key_type) {
- return OpenSSLIdentity::CreateWithExpiration(
- common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds);
+ return Create(common_name, KeyParams(key_type),
+ kDefaultCertificateLifetimeInSeconds);
}
// static
std::unique_ptr<SSLIdentity> SSLIdentity::CreateForTest(
const SSLIdentityParams& params) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return BoringSSLIdentity::CreateForTest(params);
+#else
return OpenSSLIdentity::CreateForTest(params);
+#endif
}
// Construct an identity from a private key and a certificate.
@@ -242,7 +254,11 @@
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMStrings(
const std::string& private_key,
const std::string& certificate) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return BoringSSLIdentity::CreateFromPEMStrings(private_key, certificate);
+#else
return OpenSSLIdentity::CreateFromPEMStrings(private_key, certificate);
+#endif
}
// Construct an identity from a private key and a certificate chain.
@@ -250,13 +266,23 @@
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMChainStrings(
const std::string& private_key,
const std::string& certificate_chain) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return BoringSSLIdentity::CreateFromPEMChainStrings(private_key,
+ certificate_chain);
+#else
return OpenSSLIdentity::CreateFromPEMChainStrings(private_key,
certificate_chain);
+#endif
}
bool operator==(const SSLIdentity& a, const SSLIdentity& b) {
+#ifdef OPENSSL_IS_BORINGSSL
+ return static_cast<const BoringSSLIdentity&>(a) ==
+ static_cast<const BoringSSLIdentity&>(b);
+#else
return static_cast<const OpenSSLIdentity&>(a) ==
static_cast<const OpenSSLIdentity&>(b);
+#endif
}
bool operator!=(const SSLIdentity& a, const SSLIdentity& b) {
return !(a == b);
diff --git a/rtc_base/ssl_identity_unittest.cc b/rtc_base/ssl_identity_unittest.cc
index 0d9d0fd..a907bfc 100644
--- a/rtc_base/ssl_identity_unittest.cc
+++ b/rtc_base/ssl_identity_unittest.cc
@@ -65,7 +65,7 @@
0x35, 0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6};
// These PEM strings were created by generating an identity with
-// |SSLIdentity::Generate| and invoking |identity->PrivateKeyToPEMString()|,
+// |SSLIdentity::Create| and invoking |identity->PrivateKeyToPEMString()|,
// |identity->PublicKeyToPEMString()| and
// |identity->certificate().ToPEMString()|. If the crypto library is updated,
// and the update changes the string form of the keys, these will have to be
@@ -406,6 +406,21 @@
EXPECT_EQ(kECDSA_CERT_PEM, identity->certificate().ToPEMString());
}
+TEST_F(SSLIdentityTest, FromPEMChainStrings) {
+ // This doesn't form a valid certificate chain, but that doesn't matter for
+ // the purposes of the test
+ std::string chain(kRSA_CERT_PEM);
+ chain.append(kTestCertificate);
+ std::unique_ptr<SSLIdentity> identity(
+ SSLIdentity::CreateFromPEMChainStrings(kRSA_PRIVATE_KEY_PEM, chain));
+ EXPECT_TRUE(identity);
+ EXPECT_EQ(kRSA_PRIVATE_KEY_PEM, identity->PrivateKeyToPEMString());
+ EXPECT_EQ(kRSA_PUBLIC_KEY_PEM, identity->PublicKeyToPEMString());
+ ASSERT_EQ(2u, identity->cert_chain().GetSize());
+ EXPECT_EQ(kRSA_CERT_PEM, identity->cert_chain().Get(0).ToPEMString());
+ EXPECT_EQ(kTestCertificate, identity->cert_chain().Get(1).ToPEMString());
+}
+
TEST_F(SSLIdentityTest, CloneIdentityRSA) {
TestCloningIdentity(*identity_rsa1_);
TestCloningIdentity(*identity_rsa2_);
diff --git a/rtc_base/ssl_stream_adapter_unittest.cc b/rtc_base/ssl_stream_adapter_unittest.cc
index 379acac..c580d83 100644
--- a/rtc_base/ssl_stream_adapter_unittest.cc
+++ b/rtc_base/ssl_stream_adapter_unittest.cc
@@ -508,8 +508,9 @@
}
}
- // This tests that the handshake can complete before the identity is
- // verified, and the identity will be verified after the fact.
+ // This tests that the handshake can complete before the identity is verified,
+ // and the identity will be verified after the fact. It also verifies that
+ // packets can't be read or written before the identity has been verified.
void TestHandshakeWithDelayedIdentity(bool valid_identity) {
server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
client_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
@@ -524,14 +525,9 @@
}
// Start the handshake
- int rv;
-
server_ssl_->SetServerRole();
- rv = server_ssl_->StartSSL();
- ASSERT_EQ(0, rv);
-
- rv = client_ssl_->StartSSL();
- ASSERT_EQ(0, rv);
+ ASSERT_EQ(0, server_ssl_->StartSSL());
+ ASSERT_EQ(0, client_ssl_->StartSSL());
// Now run the handshake.
EXPECT_TRUE_WAIT(
@@ -547,16 +543,57 @@
EXPECT_EQ(rtc::SR_BLOCK, client_ssl_->Write(&packet, 1, &sent, 0));
EXPECT_EQ(rtc::SR_BLOCK, server_ssl_->Write(&packet, 1, &sent, 0));
- // If we set an invalid identity at this point, SetPeerCertificateDigest
- // should return false.
- SetPeerIdentitiesByDigest(valid_identity, valid_identity);
+ // Collect both of the certificate digests; needs to be done before calling
+ // SetPeerCertificateDigest as that may reset the identity.
+ unsigned char server_digest[20];
+ size_t server_digest_len;
+ unsigned char client_digest[20];
+ size_t client_digest_len;
+ bool rv;
+
+ rv = server_identity()->certificate().ComputeDigest(
+ rtc::DIGEST_SHA_1, server_digest, 20, &server_digest_len);
+ ASSERT_TRUE(rv);
+ rv = client_identity()->certificate().ComputeDigest(
+ rtc::DIGEST_SHA_1, client_digest, 20, &client_digest_len);
+ ASSERT_TRUE(rv);
+
+ if (!valid_identity) {
+ RTC_LOG(LS_INFO) << "Setting bogus digest for client/server certs";
+ client_digest[0]++;
+ server_digest[0]++;
+ }
+
+ // Set the peer certificate digest for the client.
+ rtc::SSLPeerCertificateDigestError err;
+ rtc::SSLPeerCertificateDigestError expected_err =
+ valid_identity
+ ? rtc::SSLPeerCertificateDigestError::NONE
+ : rtc::SSLPeerCertificateDigestError::VERIFICATION_FAILED;
+ rv = client_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, server_digest,
+ server_digest_len, &err);
+ EXPECT_EQ(expected_err, err);
+ EXPECT_EQ(valid_identity, rv);
// State should then transition to SS_OPEN or SS_CLOSED based on validation
// of the identity.
if (valid_identity) {
EXPECT_EQ(rtc::SS_OPEN, client_ssl_->GetState());
- EXPECT_EQ(rtc::SS_OPEN, server_ssl_->GetState());
+ // If the client sends a packet while the server still hasn't verified the
+ // client identity, the server should continue to return SR_BLOCK.
+ EXPECT_EQ(rtc::SR_SUCCESS, client_ssl_->Write(&packet, 1, &sent, 0));
+ EXPECT_EQ(rtc::SR_BLOCK, server_ssl_->Read(&packet, 1, 0, 0));
} else {
EXPECT_EQ(rtc::SS_CLOSED, client_ssl_->GetState());
+ }
+
+ // Set the peer certificate digest for the server.
+ rv = server_ssl_->SetPeerCertificateDigest(rtc::DIGEST_SHA_1, client_digest,
+ client_digest_len, &err);
+ EXPECT_EQ(expected_err, err);
+ EXPECT_EQ(valid_identity, rv);
+ if (valid_identity) {
+ EXPECT_EQ(rtc::SS_OPEN, server_ssl_->GetState());
+ } else {
EXPECT_EQ(rtc::SS_CLOSED, server_ssl_->GetState());
}
}
diff --git a/webrtc.gni b/webrtc.gni
index 05a230c..2f6e77d 100644
--- a/webrtc.gni
+++ b/webrtc.gni
@@ -227,6 +227,10 @@
rtc_libvpx_build_vp9 = !build_with_mozilla
rtc_build_opus = !build_with_mozilla
rtc_build_ssl = !build_with_mozilla
+
+ # Can be set to true if rtc_build_ssl is false, but externally provided
+ # openssl library is boringssl, to enable the use of boringssl-specific code.
+ rtc_openssl_is_boringssl = !build_with_mozilla
rtc_build_usrsctp = !build_with_mozilla
# Enable libevent task queues on platforms that support it.