mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00
Reland: 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. No-Try: True Bug: webrtc:11410 Change-Id: I86ddb361b94ad85b15ebb8743490de83632ca53f Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/196941 Commit-Queue: Mirko Bonadei <mbonadei@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org> Cr-Commit-Position: refs/heads/master@{#32818}
This commit is contained in:
parent
c1ad1ff178
commit
165c618bb9
24 changed files with 1620 additions and 279 deletions
|
@ -83,7 +83,7 @@ static const rtc::RTCCertificatePEM kRsaPems[] = {
|
||||||
|
|
||||||
// ECDSA with EC_NIST_P256.
|
// ECDSA with EC_NIST_P256.
|
||||||
// These PEM strings were created by generating an identity with
|
// 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->PublicKeyToPEMString()| and
|
||||||
// |identity->certificate().ToPEMString()|.
|
// |identity->certificate().ToPEMString()|.
|
||||||
static const rtc::RTCCertificatePEM kEcdsaPems[] = {
|
static const rtc::RTCCertificatePEM kEcdsaPems[] = {
|
||||||
|
|
|
@ -911,12 +911,10 @@ rtc_library("rtc_base") {
|
||||||
"openssl.h",
|
"openssl.h",
|
||||||
"openssl_adapter.cc",
|
"openssl_adapter.cc",
|
||||||
"openssl_adapter.h",
|
"openssl_adapter.h",
|
||||||
"openssl_certificate.cc",
|
|
||||||
"openssl_certificate.h",
|
|
||||||
"openssl_digest.cc",
|
"openssl_digest.cc",
|
||||||
"openssl_digest.h",
|
"openssl_digest.h",
|
||||||
"openssl_identity.cc",
|
"openssl_key_pair.cc",
|
||||||
"openssl_identity.h",
|
"openssl_key_pair.h",
|
||||||
"openssl_session_cache.cc",
|
"openssl_session_cache.cc",
|
||||||
"openssl_session_cache.h",
|
"openssl_session_cache.h",
|
||||||
"openssl_stream_adapter.cc",
|
"openssl_stream_adapter.cc",
|
||||||
|
@ -962,6 +960,22 @@ rtc_library("rtc_base") {
|
||||||
"unique_id_generator.h",
|
"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) {
|
if (build_with_chromium) {
|
||||||
include_dirs = [ "../../boringssl/src/include" ]
|
include_dirs = [ "../../boringssl/src/include" ]
|
||||||
public_configs += [ ":rtc_base_chromium_config" ]
|
public_configs += [ ":rtc_base_chromium_config" ]
|
||||||
|
|
410
rtc_base/boringssl_certificate.cc
Normal file
410
rtc_base/boringssl_certificate.cc
Normal file
|
@ -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_key_pair.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 SSLIdentity::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
|
80
rtc_base/boringssl_certificate.h
Normal file
80
rtc_base/boringssl_certificate.h
Normal file
|
@ -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_
|
215
rtc_base/boringssl_identity.cc
Normal file
215
rtc_base/boringssl_identity.cc
Normal file
|
@ -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
|
76
rtc_base/boringssl_identity.h
Normal file
76
rtc_base/boringssl_identity.h
Normal file
|
@ -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_
|
|
@ -13,6 +13,9 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include <openssl/pool.h>
|
||||||
|
#endif
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -20,13 +23,24 @@
|
||||||
|
|
||||||
#include <memory>
|
#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 "absl/memory/memory.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/location.h"
|
#include "rtc_base/location.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/numerics/safe_conversions.h"
|
#include "rtc_base/numerics/safe_conversions.h"
|
||||||
#include "rtc_base/openssl.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/openssl_utility.h"
|
||||||
#include "rtc_base/string_encode.h"
|
#include "rtc_base/string_encode.h"
|
||||||
#include "rtc_base/thread.h"
|
#include "rtc_base/thread.h"
|
||||||
|
@ -223,8 +237,13 @@ void OpenSSLAdapter::SetCertVerifier(
|
||||||
|
|
||||||
void OpenSSLAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
|
void OpenSSLAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
|
||||||
RTC_DCHECK(!identity_);
|
RTC_DCHECK(!identity_);
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
identity_ =
|
||||||
|
absl::WrapUnique(static_cast<BoringSSLIdentity*>(identity.release()));
|
||||||
|
#else
|
||||||
identity_ =
|
identity_ =
|
||||||
absl::WrapUnique(static_cast<OpenSSLIdentity*>(identity.release()));
|
absl::WrapUnique(static_cast<OpenSSLIdentity*>(identity.release()));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void OpenSSLAdapter::SetRole(SSLRole role) {
|
void OpenSSLAdapter::SetRole(SSLRole role) {
|
||||||
|
@ -797,7 +816,70 @@ void OpenSSLAdapter::SSLInfoCallback(const SSL* s, int where, int ret) {
|
||||||
|
|
||||||
#endif
|
#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) {
|
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 !defined(NDEBUG)
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
char data[256];
|
char data[256];
|
||||||
|
@ -814,33 +896,40 @@ int OpenSSLAdapter::SSLVerifyCallback(int ok, X509_STORE_CTX* store) {
|
||||||
<< X509_verify_cert_error_string(err);
|
<< X509_verify_cert_error_string(err);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// Get our stream pointer from the store
|
if (ssl_cert_verifier_ == nullptr) {
|
||||||
SSL* ssl = reinterpret_cast<SSL*>(
|
return ok;
|
||||||
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";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Should only be used for debugging and development.
|
RTC_LOG(LS_INFO) << "Invoking SSL Verify Callback.";
|
||||||
if (!ok && stream->ignore_bad_cert_) {
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
RTC_DLOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
|
// Convert X509 to CRYPTO_BUFFER.
|
||||||
ok = 1;
|
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) {
|
int OpenSSLAdapter::NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session) {
|
||||||
OpenSSLAdapter* stream =
|
OpenSSLAdapter* stream =
|
||||||
|
@ -852,8 +941,15 @@ int OpenSSLAdapter::NewSSLSessionCallback(SSL* ssl, SSL_SESSION* session) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX* OpenSSLAdapter::CreateContext(SSLMode mode, bool enable_cache) {
|
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* ctx =
|
||||||
SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
|
SSL_CTX_new(mode == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
|
||||||
|
#endif
|
||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
unsigned long error = ERR_get_error(); // NOLINT: type used by OpenSSL.
|
unsigned long error = ERR_get_error(); // NOLINT: type used by OpenSSL.
|
||||||
RTC_LOG(LS_WARNING) << "SSL_CTX creation failed: " << '"'
|
RTC_LOG(LS_WARNING) << "SSL_CTX creation failed: " << '"'
|
||||||
|
@ -877,8 +973,16 @@ SSL_CTX* OpenSSLAdapter::CreateContext(SSLMode mode, bool enable_cache) {
|
||||||
SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
|
SSL_CTX_set_info_callback(ctx, SSLInfoCallback);
|
||||||
#endif
|
#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(ctx, SSL_VERIFY_PEER, SSLVerifyCallback);
|
||||||
SSL_CTX_set_verify_depth(ctx, 4);
|
SSL_CTX_set_verify_depth(ctx, 4);
|
||||||
|
#endif
|
||||||
// Use defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
|
// Use defaults, but disable HMAC-SHA256 and HMAC-SHA384 ciphers
|
||||||
// (note that SHA256 and SHA384 only select legacy CBC ciphers).
|
// (note that SHA256 and SHA384 only select legacy CBC ciphers).
|
||||||
// Additionally disable HMAC-SHA1 ciphers in ECDSA. These are the remaining
|
// Additionally disable HMAC-SHA1 ciphers in ECDSA. These are the remaining
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#ifndef RTC_BASE_OPENSSL_ADAPTER_H_
|
#ifndef RTC_BASE_OPENSSL_ADAPTER_H_
|
||||||
#define RTC_BASE_OPENSSL_ADAPTER_H_
|
#define RTC_BASE_OPENSSL_ADAPTER_H_
|
||||||
|
|
||||||
|
#include <openssl/ossl_typ.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
|
@ -21,7 +22,11 @@
|
||||||
#include "rtc_base/async_socket.h"
|
#include "rtc_base/async_socket.h"
|
||||||
#include "rtc_base/buffer.h"
|
#include "rtc_base/buffer.h"
|
||||||
#include "rtc_base/message_handler.h"
|
#include "rtc_base/message_handler.h"
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include "rtc_base/boringssl_identity.h"
|
||||||
|
#else
|
||||||
#include "rtc_base/openssl_identity.h"
|
#include "rtc_base/openssl_identity.h"
|
||||||
|
#endif
|
||||||
#include "rtc_base/openssl_session_cache.h"
|
#include "rtc_base/openssl_session_cache.h"
|
||||||
#include "rtc_base/socket.h"
|
#include "rtc_base/socket.h"
|
||||||
#include "rtc_base/socket_address.h"
|
#include "rtc_base/socket_address.h"
|
||||||
|
@ -109,7 +114,16 @@ class OpenSSLAdapter final : public SSLAdapter,
|
||||||
// In debug builds, logs info about the state of the SSL connection.
|
// In debug builds, logs info about the state of the SSL connection.
|
||||||
static void SSLInfoCallback(const SSL* ssl, int where, int ret);
|
static void SSLInfoCallback(const SSL* ssl, int where, int ret);
|
||||||
#endif
|
#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);
|
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_;
|
friend class OpenSSLStreamAdapter; // for custom_verify_callback_;
|
||||||
|
|
||||||
// If the SSL_CTX was created with |enable_cache| set to true, this callback
|
// If the SSL_CTX was created with |enable_cache| set to true, this callback
|
||||||
|
@ -123,7 +137,12 @@ class OpenSSLAdapter final : public SSLAdapter,
|
||||||
SSLCertificateVerifier* ssl_cert_verifier_ = nullptr;
|
SSLCertificateVerifier* ssl_cert_verifier_ = nullptr;
|
||||||
// The current connection state of the (d)TLS connection.
|
// The current connection state of the (d)TLS connection.
|
||||||
SSLState state_;
|
SSLState state_;
|
||||||
|
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
std::unique_ptr<BoringSSLIdentity> identity_;
|
||||||
|
#else
|
||||||
std::unique_ptr<OpenSSLIdentity> identity_;
|
std::unique_ptr<OpenSSLIdentity> identity_;
|
||||||
|
#endif
|
||||||
// Indicates whethere this is a client or a server.
|
// Indicates whethere this is a client or a server.
|
||||||
SSLRole role_;
|
SSLRole role_;
|
||||||
bool ssl_read_needs_write_;
|
bool ssl_read_needs_write_;
|
||||||
|
|
|
@ -20,10 +20,8 @@
|
||||||
#endif // WEBRTC_WIN
|
#endif // WEBRTC_WIN
|
||||||
|
|
||||||
#include <openssl/bio.h>
|
#include <openssl/bio.h>
|
||||||
#include <openssl/bn.h>
|
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include <openssl/rsa.h>
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "absl/memory/memory.h"
|
#include "absl/memory/memory.h"
|
||||||
|
@ -35,160 +33,6 @@
|
||||||
|
|
||||||
namespace rtc {
|
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(
|
OpenSSLIdentity::OpenSSLIdentity(
|
||||||
std::unique_ptr<OpenSSLKeyPair> key_pair,
|
std::unique_ptr<OpenSSLKeyPair> key_pair,
|
||||||
std::unique_ptr<OpenSSLCertificate> certificate)
|
std::unique_ptr<OpenSSLCertificate> certificate)
|
||||||
|
@ -211,8 +55,7 @@ OpenSSLIdentity::~OpenSSLIdentity() = default;
|
||||||
|
|
||||||
std::unique_ptr<OpenSSLIdentity> OpenSSLIdentity::CreateInternal(
|
std::unique_ptr<OpenSSLIdentity> OpenSSLIdentity::CreateInternal(
|
||||||
const SSLIdentityParams& params) {
|
const SSLIdentityParams& params) {
|
||||||
std::unique_ptr<OpenSSLKeyPair> key_pair(
|
auto key_pair = OpenSSLKeyPair::Generate(params.key_params);
|
||||||
OpenSSLKeyPair::Generate(params.key_params));
|
|
||||||
if (key_pair) {
|
if (key_pair) {
|
||||||
std::unique_ptr<OpenSSLCertificate> certificate(
|
std::unique_ptr<OpenSSLCertificate> certificate(
|
||||||
OpenSSLCertificate::Generate(key_pair.get(), params));
|
OpenSSLCertificate::Generate(key_pair.get(), params));
|
||||||
|
@ -221,7 +64,7 @@ std::unique_ptr<OpenSSLIdentity> OpenSSLIdentity::CreateInternal(
|
||||||
new OpenSSLIdentity(std::move(key_pair), std::move(certificate)));
|
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;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,8 +99,7 @@ std::unique_ptr<SSLIdentity> OpenSSLIdentity::CreateFromPEMStrings(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenSSLKeyPair> key_pair(
|
auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
|
||||||
OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
|
|
||||||
if (!key_pair) {
|
if (!key_pair) {
|
||||||
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
|
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -298,8 +140,7 @@ std::unique_ptr<SSLIdentity> OpenSSLIdentity::CreateFromPEMChainStrings(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OpenSSLKeyPair> key_pair(
|
auto key_pair = OpenSSLKeyPair::FromPrivateKeyPEMString(private_key);
|
||||||
OpenSSLKeyPair::FromPrivateKeyPEMString(private_key));
|
|
||||||
if (!key_pair) {
|
if (!key_pair) {
|
||||||
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
|
RTC_LOG(LS_ERROR) << "Failed to create key pair from PEM string.";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -320,8 +161,8 @@ const SSLCertChain& OpenSSLIdentity::cert_chain() const {
|
||||||
std::unique_ptr<SSLIdentity> OpenSSLIdentity::CloneInternal() const {
|
std::unique_ptr<SSLIdentity> OpenSSLIdentity::CloneInternal() const {
|
||||||
// We cannot use std::make_unique here because the referenced OpenSSLIdentity
|
// We cannot use std::make_unique here because the referenced OpenSSLIdentity
|
||||||
// constructor is private.
|
// constructor is private.
|
||||||
return absl::WrapUnique(new OpenSSLIdentity(
|
return absl::WrapUnique(
|
||||||
absl::WrapUnique(key_pair_->GetReference()), cert_chain_->Clone()));
|
new OpenSSLIdentity(key_pair_->Clone(), cert_chain_->Clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
|
bool OpenSSLIdentity::ConfigureIdentity(SSL_CTX* ctx) {
|
||||||
|
|
|
@ -17,45 +17,14 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "rtc_base/checks.h"
|
|
||||||
#include "rtc_base/constructor_magic.h"
|
#include "rtc_base/constructor_magic.h"
|
||||||
#include "rtc_base/openssl_certificate.h"
|
#include "rtc_base/openssl_certificate.h"
|
||||||
|
#include "rtc_base/openssl_key_pair.h"
|
||||||
#include "rtc_base/ssl_certificate.h"
|
#include "rtc_base/ssl_certificate.h"
|
||||||
#include "rtc_base/ssl_identity.h"
|
#include "rtc_base/ssl_identity.h"
|
||||||
|
|
||||||
namespace rtc {
|
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
|
// Holds a keypair and certificate together, and a method to generate
|
||||||
// them consistently.
|
// them consistently.
|
||||||
class OpenSSLIdentity final : public SSLIdentity {
|
class OpenSSLIdentity final : public SSLIdentity {
|
||||||
|
|
192
rtc_base/openssl_key_pair.cc
Normal file
192
rtc_base/openssl_key_pair.cc
Normal file
|
@ -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
|
60
rtc_base/openssl_key_pair.h
Normal file
60
rtc_base/openssl_key_pair.h
Normal file
|
@ -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_
|
|
@ -19,10 +19,28 @@
|
||||||
#include "rtc_base/gunit.h"
|
#include "rtc_base/gunit.h"
|
||||||
#include "rtc_base/openssl.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 {
|
namespace rtc {
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) {
|
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);
|
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
|
||||||
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_DTLS);
|
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_DTLS);
|
||||||
|
@ -31,7 +49,7 @@ TEST(OpenSSLSessionCache, DTLSModeSetCorrectly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, TLSModeSetCorrectly) {
|
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);
|
OpenSSLSessionCache session_cache(SSL_MODE_TLS, ssl_ctx);
|
||||||
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_TLS);
|
EXPECT_EQ(session_cache.GetSSLMode(), SSL_MODE_TLS);
|
||||||
|
@ -40,7 +58,7 @@ TEST(OpenSSLSessionCache, TLSModeSetCorrectly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, SSLContextSetCorrectly) {
|
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);
|
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
|
||||||
EXPECT_EQ(session_cache.GetSSLContext(), ssl_ctx);
|
EXPECT_EQ(session_cache.GetSSLContext(), ssl_ctx);
|
||||||
|
@ -49,7 +67,7 @@ TEST(OpenSSLSessionCache, SSLContextSetCorrectly) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, InvalidLookupReturnsNullptr) {
|
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);
|
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
|
||||||
EXPECT_EQ(session_cache.LookupSession("Invalid"), nullptr);
|
EXPECT_EQ(session_cache.LookupSession("Invalid"), nullptr);
|
||||||
|
@ -60,7 +78,7 @@ TEST(OpenSSLSessionCache, InvalidLookupReturnsNullptr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, SimpleValidSessionLookup) {
|
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);
|
SSL_SESSION* ssl_session = SSL_SESSION_new(ssl_ctx);
|
||||||
|
|
||||||
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
|
OpenSSLSessionCache session_cache(SSL_MODE_DTLS, ssl_ctx);
|
||||||
|
@ -71,7 +89,7 @@ TEST(OpenSSLSessionCache, SimpleValidSessionLookup) {
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(OpenSSLSessionCache, AddToExistingReplacesPrevious) {
|
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_1 = SSL_SESSION_new(ssl_ctx);
|
||||||
SSL_SESSION* ssl_session_2 = SSL_SESSION_new(ssl_ctx);
|
SSL_SESSION* ssl_session_2 = SSL_SESSION_new(ssl_ctx);
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,12 @@
|
||||||
#include "rtc_base/openssl.h"
|
#include "rtc_base/openssl.h"
|
||||||
#include "rtc_base/openssl_adapter.h"
|
#include "rtc_base/openssl_adapter.h"
|
||||||
#include "rtc_base/openssl_digest.h"
|
#include "rtc_base/openssl_digest.h"
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include "rtc_base/boringssl_identity.h"
|
||||||
|
#else
|
||||||
#include "rtc_base/openssl_identity.h"
|
#include "rtc_base/openssl_identity.h"
|
||||||
|
#endif
|
||||||
|
#include "rtc_base/openssl_utility.h"
|
||||||
#include "rtc_base/ssl_certificate.h"
|
#include "rtc_base/ssl_certificate.h"
|
||||||
#include "rtc_base/stream.h"
|
#include "rtc_base/stream.h"
|
||||||
#include "rtc_base/task_utils/to_queued_task.h"
|
#include "rtc_base/task_utils/to_queued_task.h"
|
||||||
|
@ -304,10 +309,14 @@ OpenSSLStreamAdapter::~OpenSSLStreamAdapter() {
|
||||||
|
|
||||||
void OpenSSLStreamAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
|
void OpenSSLStreamAdapter::SetIdentity(std::unique_ptr<SSLIdentity> identity) {
|
||||||
RTC_DCHECK(!identity_);
|
RTC_DCHECK(!identity_);
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
identity_.reset(static_cast<BoringSSLIdentity*>(identity.release()));
|
||||||
|
#else
|
||||||
identity_.reset(static_cast<OpenSSLIdentity*>(identity.release()));
|
identity_.reset(static_cast<OpenSSLIdentity*>(identity.release()));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
OpenSSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
|
SSLIdentity* OpenSSLStreamAdapter::GetIdentityForTesting() const {
|
||||||
return identity_.get();
|
return identity_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -994,8 +1003,16 @@ void OpenSSLStreamAdapter::Cleanup(uint8_t alert) {
|
||||||
}
|
}
|
||||||
|
|
||||||
SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
|
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* ctx =
|
||||||
SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
|
SSL_CTX_new(ssl_mode_ == SSL_MODE_DTLS ? DTLS_method() : TLS_method());
|
||||||
|
#endif
|
||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -1033,6 +1050,7 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
|
||||||
if (g_use_time_callback_for_testing) {
|
if (g_use_time_callback_for_testing) {
|
||||||
SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
|
SSL_CTX_set_current_time_cb(ctx, &TimeCallbackForTesting);
|
||||||
}
|
}
|
||||||
|
SSL_CTX_set0_buffer_pool(ctx, openssl::GetBufferPool());
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (identity_ && !identity_->ConfigureIdentity(ctx)) {
|
if (identity_ && !identity_->ConfigureIdentity(ctx)) {
|
||||||
|
@ -1053,11 +1071,16 @@ SSL_CTX* OpenSSLStreamAdapter::SetupSSLContext() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Configure a custom certificate verification callback to check the peer
|
// Configure a custom certificate verification callback to check the peer
|
||||||
// certificate digest. Note the second argument to SSL_CTX_set_verify is to
|
// certificate digest.
|
||||||
// override individual errors in the default verification logic, which is not
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
// what we want here.
|
// 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_verify(ctx, mode, nullptr);
|
||||||
SSL_CTX_set_cert_verify_callback(ctx, SSLVerifyCallback, nullptr);
|
SSL_CTX_set_cert_verify_callback(ctx, SSLVerifyCallback, nullptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Select list of available ciphers. Note that !SHA256 and !SHA384 only
|
// Select list of available ciphers. Note that !SHA256 and !SHA384 only
|
||||||
// remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
|
// remove HMAC-SHA256 and HMAC-SHA384 cipher suites, not GCM cipher suites
|
||||||
|
@ -1082,14 +1105,12 @@ bool OpenSSLStreamAdapter::VerifyPeerCertificate() {
|
||||||
RTC_LOG(LS_WARNING) << "Missing digest or peer certificate.";
|
RTC_LOG(LS_WARNING) << "Missing digest or peer certificate.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const OpenSSLCertificate* leaf_cert =
|
|
||||||
static_cast<const OpenSSLCertificate*>(&peer_cert_chain_->Get(0));
|
|
||||||
|
|
||||||
unsigned char digest[EVP_MAX_MD_SIZE];
|
unsigned char digest[EVP_MAX_MD_SIZE];
|
||||||
size_t digest_length;
|
size_t digest_length;
|
||||||
if (!OpenSSLCertificate::ComputeDigest(
|
if (!peer_cert_chain_->Get(0).ComputeDigest(
|
||||||
leaf_cert->x509(), peer_certificate_digest_algorithm_, digest,
|
peer_certificate_digest_algorithm_, digest, sizeof(digest),
|
||||||
sizeof(digest), &digest_length)) {
|
&digest_length)) {
|
||||||
RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest.";
|
RTC_LOG(LS_WARNING) << "Failed to compute peer cert digest.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1113,6 +1134,36 @@ std::unique_ptr<SSLCertChain> OpenSSLStreamAdapter::GetPeerSSLCertChain()
|
||||||
return peer_cert_chain_ ? peer_cert_chain_->Clone() : nullptr;
|
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) {
|
int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
|
||||||
// Get our SSL structure and OpenSSLStreamAdapter from the store.
|
// Get our SSL structure and OpenSSLStreamAdapter from the store.
|
||||||
SSL* ssl = reinterpret_cast<SSL*>(
|
SSL* ssl = reinterpret_cast<SSL*>(
|
||||||
|
@ -1120,20 +1171,10 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
|
||||||
OpenSSLStreamAdapter* stream =
|
OpenSSLStreamAdapter* stream =
|
||||||
reinterpret_cast<OpenSSLStreamAdapter*>(SSL_get_app_data(ssl));
|
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.
|
// Record the peer's certificate.
|
||||||
X509* cert = X509_STORE_CTX_get0_cert(store);
|
X509* cert = X509_STORE_CTX_get0_cert(store);
|
||||||
stream->peer_cert_chain_.reset(
|
stream->peer_cert_chain_.reset(
|
||||||
new SSLCertChain(std::make_unique<OpenSSLCertificate>(cert)));
|
new SSLCertChain(std::make_unique<OpenSSLCertificate>(cert)));
|
||||||
#endif
|
|
||||||
|
|
||||||
// If the peer certificate digest isn't known yet, we'll wait to verify
|
// 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.
|
// until it's known, and for now just return a success status.
|
||||||
|
@ -1149,6 +1190,7 @@ int OpenSSLStreamAdapter::SSLVerifyCallback(X509_STORE_CTX* store, void* arg) {
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
#endif // !OPENSSL_IS_BORINGSSL
|
||||||
|
|
||||||
bool OpenSSLStreamAdapter::IsBoringSsl() {
|
bool OpenSSLStreamAdapter::IsBoringSsl() {
|
||||||
#ifdef OPENSSL_IS_BORINGSSL
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
|
|
@ -21,7 +21,11 @@
|
||||||
|
|
||||||
#include "absl/types/optional.h"
|
#include "absl/types/optional.h"
|
||||||
#include "rtc_base/buffer.h"
|
#include "rtc_base/buffer.h"
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include "rtc_base/boringssl_identity.h"
|
||||||
|
#else
|
||||||
#include "rtc_base/openssl_identity.h"
|
#include "rtc_base/openssl_identity.h"
|
||||||
|
#endif
|
||||||
#include "rtc_base/ssl_identity.h"
|
#include "rtc_base/ssl_identity.h"
|
||||||
#include "rtc_base/ssl_stream_adapter.h"
|
#include "rtc_base/ssl_stream_adapter.h"
|
||||||
#include "rtc_base/stream.h"
|
#include "rtc_base/stream.h"
|
||||||
|
@ -71,7 +75,7 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter {
|
||||||
~OpenSSLStreamAdapter() override;
|
~OpenSSLStreamAdapter() override;
|
||||||
|
|
||||||
void SetIdentity(std::unique_ptr<SSLIdentity> identity) override;
|
void SetIdentity(std::unique_ptr<SSLIdentity> identity) override;
|
||||||
OpenSSLIdentity* GetIdentityForTesting() const override;
|
SSLIdentity* GetIdentityForTesting() const override;
|
||||||
|
|
||||||
// Default argument is for compatibility
|
// Default argument is for compatibility
|
||||||
void SetServerRole(SSLRole role = SSL_SERVER) override;
|
void SetServerRole(SSLRole role = SSL_SERVER) override;
|
||||||
|
@ -179,9 +183,16 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter {
|
||||||
SSL_CTX* SetupSSLContext();
|
SSL_CTX* SetupSSLContext();
|
||||||
// Verify the peer certificate matches the signaled digest.
|
// Verify the peer certificate matches the signaled digest.
|
||||||
bool VerifyPeerCertificate();
|
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 certificate verification callback. See
|
||||||
// SSL_CTX_set_cert_verify_callback.
|
// SSL_CTX_set_cert_verify_callback.
|
||||||
static int SSLVerifyCallback(X509_STORE_CTX* store, void* arg);
|
static int SSLVerifyCallback(X509_STORE_CTX* store, void* arg);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool WaitingToVerifyPeerCertificate() const {
|
bool WaitingToVerifyPeerCertificate() const {
|
||||||
return GetClientAuthEnabled() && !peer_certificate_verified_;
|
return GetClientAuthEnabled() && !peer_certificate_verified_;
|
||||||
|
@ -208,7 +219,11 @@ class OpenSSLStreamAdapter final : public SSLStreamAdapter {
|
||||||
SSL_CTX* ssl_ctx_;
|
SSL_CTX* ssl_ctx_;
|
||||||
|
|
||||||
// Our key and certificate.
|
// Our key and certificate.
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
std::unique_ptr<BoringSSLIdentity> identity_;
|
||||||
|
#else
|
||||||
std::unique_ptr<OpenSSLIdentity> identity_;
|
std::unique_ptr<OpenSSLIdentity> identity_;
|
||||||
|
#endif
|
||||||
// The certificate chain that the peer presented. Initially null, until the
|
// The certificate chain that the peer presented. Initially null, until the
|
||||||
// connection is established.
|
// connection is established.
|
||||||
std::unique_ptr<SSLCertChain> peer_cert_chain_;
|
std::unique_ptr<SSLCertChain> peer_cert_chain_;
|
||||||
|
|
|
@ -14,6 +14,9 @@
|
||||||
#include "rtc_base/win32.h" // NOLINT
|
#include "rtc_base/win32.h" // NOLINT
|
||||||
#endif // WEBRTC_WIN
|
#endif // WEBRTC_WIN
|
||||||
|
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include <openssl/pool.h>
|
||||||
|
#endif
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
|
@ -23,7 +26,7 @@
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/numerics/safe_conversions.h"
|
#include "rtc_base/numerics/safe_conversions.h"
|
||||||
#include "rtc_base/openssl.h"
|
#include "rtc_base/openssl.h"
|
||||||
#include "rtc_base/openssl_certificate.h"
|
#include "rtc_base/ssl_identity.h"
|
||||||
#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
#ifndef WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
||||||
#include "rtc_base/ssl_roots.h"
|
#include "rtc_base/ssl_roots.h"
|
||||||
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
||||||
|
@ -33,6 +36,10 @@ namespace openssl {
|
||||||
|
|
||||||
// Holds various helper methods.
|
// Holds various helper methods.
|
||||||
namespace {
|
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) {
|
void LogCertificates(SSL* ssl, X509* certificate) {
|
||||||
// Logging certificates is extremely verbose. So it is disabled by default.
|
// Logging certificates is extremely verbose. So it is disabled by default.
|
||||||
#ifdef LOG_CERTIFICATES
|
#ifdef LOG_CERTIFICATES
|
||||||
|
@ -65,6 +72,118 @@ void LogCertificates(SSL* ssl, X509* certificate) {
|
||||||
}
|
}
|
||||||
} // namespace
|
} // 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) {
|
bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
|
||||||
if (host.empty()) {
|
if (host.empty()) {
|
||||||
RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate.";
|
RTC_DLOG(LS_ERROR) << "Hostname is empty. Cannot verify peer certificate.";
|
||||||
|
@ -76,9 +195,28 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
|
||||||
return false;
|
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);
|
X509* certificate = SSL_get_peer_certificate(ssl);
|
||||||
if (certificate == nullptr) {
|
if (certificate == nullptr) {
|
||||||
RTC_DLOG(LS_ERROR)
|
RTC_LOG(LS_ERROR)
|
||||||
<< "SSL_get_peer_certificate failed. This should never happen.";
|
<< "SSL_get_peer_certificate failed. This should never happen.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -89,6 +227,7 @@ bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host) {
|
||||||
X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1;
|
X509_check_host(certificate, host.c_str(), host.size(), 0, nullptr) == 1;
|
||||||
X509_free(certificate);
|
X509_free(certificate);
|
||||||
return is_valid_cert_name;
|
return is_valid_cert_name;
|
||||||
|
#endif // !defined(OPENSSL_IS_BORINGSSL)
|
||||||
}
|
}
|
||||||
|
|
||||||
void LogSSLErrors(const std::string& prefix) {
|
void LogSSLErrors(const std::string& prefix) {
|
||||||
|
@ -123,5 +262,12 @@ bool LoadBuiltinSSLRootCertificates(SSL_CTX* ctx) {
|
||||||
}
|
}
|
||||||
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
#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 openssl
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
|
@ -20,8 +20,21 @@ namespace rtc {
|
||||||
// to OpenSSL that are commonly used and don't require global state should be
|
// to OpenSSL that are commonly used and don't require global state should be
|
||||||
// placed here.
|
// placed here.
|
||||||
namespace openssl {
|
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
|
// Verifies that the hostname provided matches that in the peer certificate
|
||||||
// attached to this SSL state.
|
// 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);
|
bool VerifyPeerCertMatchesHost(SSL* ssl, const std::string& host);
|
||||||
|
|
||||||
// Logs all the errors in the OpenSSL errror queue from the current thread. A
|
// Logs all the errors in the OpenSSL errror queue from the current thread. A
|
||||||
|
@ -35,6 +48,10 @@ void LogSSLErrors(const std::string& prefix);
|
||||||
bool LoadBuiltinSSLRootCertificates(SSL_CTX* ssl_ctx);
|
bool LoadBuiltinSSLRootCertificates(SSL_CTX* ssl_ctx);
|
||||||
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
#endif // WEBRTC_EXCLUDE_BUILT_IN_SSL_ROOT_CERTS
|
||||||
|
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
CRYPTO_BUFFER_POOL* GetBufferPool();
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace openssl
|
} // namespace openssl
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,12 @@
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
#include <openssl/evp.h>
|
#include <openssl/evp.h>
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include <openssl/pool.h>
|
||||||
|
#else
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/x509v3.h>
|
#include <openssl/x509v3.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "rtc_base/arraysize.h"
|
#include "rtc_base/arraysize.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
|
@ -169,14 +173,17 @@ const unsigned char kFakeSSLCertificateLegacy[] = {
|
||||||
0x84, 0x0b, 0xc7, 0x15, 0x86, 0xc3, 0xfc, 0x48, 0x55, 0xb5, 0x81, 0x94,
|
0x84, 0x0b, 0xc7, 0x15, 0x86, 0xc3, 0xfc, 0x48, 0x55, 0xb5, 0x81, 0x94,
|
||||||
0x73, 0xbd, 0x18, 0xcd, 0x9d, 0x92, 0x47, 0xaa, 0xfd, 0x18};
|
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
|
// Creates a client SSL that has completed handshaking with a server that uses
|
||||||
// the specified certificate (which must have private key kFakeSSLPrivateKey).
|
// the specified certificate (which must have private key kFakeSSLPrivateKey).
|
||||||
// The server is deallocated. This client will have a peer certificate available
|
// The server is deallocated. This client will have a peer certificate available
|
||||||
// and is thus suitable for testing VerifyPeerCertMatchesHost.
|
// and is thus suitable for testing VerifyPeerCertMatchesHost.
|
||||||
SSL* CreateSSLWithPeerCertificate(const unsigned char* cert, size_t cert_len) {
|
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;
|
const unsigned char* key_ptr = kFakeSSLPrivateKey;
|
||||||
EVP_PKEY* key = d2i_PrivateKey(
|
EVP_PKEY* key = d2i_PrivateKey(
|
||||||
|
@ -184,14 +191,33 @@ SSL* CreateSSLWithPeerCertificate(const unsigned char* cert, size_t cert_len) {
|
||||||
checked_cast<long>(arraysize(kFakeSSLPrivateKey))); // NOLINT
|
checked_cast<long>(arraysize(kFakeSSLPrivateKey))); // NOLINT
|
||||||
RTC_CHECK(key);
|
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* client = SSL_new(ctx);
|
||||||
SSL* server = SSL_new(ctx);
|
SSL* server = SSL_new(ctx);
|
||||||
SSL_set_connect_state(client);
|
SSL_set_connect_state(client);
|
||||||
SSL_set_accept_state(server);
|
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_certificate(server, x509));
|
||||||
RTC_CHECK(SSL_use_PrivateKey(server, key));
|
RTC_CHECK(SSL_use_PrivateKey(server, key));
|
||||||
|
#endif
|
||||||
|
|
||||||
BIO* bio1;
|
BIO* bio1;
|
||||||
BIO* bio2;
|
BIO* bio2;
|
||||||
|
@ -221,13 +247,19 @@ SSL* CreateSSLWithPeerCertificate(const unsigned char* cert, size_t cert_len) {
|
||||||
SSL_free(server);
|
SSL_free(server);
|
||||||
SSL_CTX_free(ctx);
|
SSL_CTX_free(ctx);
|
||||||
EVP_PKEY_free(key);
|
EVP_PKEY_free(key);
|
||||||
|
#ifndef OPENSSL_IS_BORINGSSL
|
||||||
X509_free(x509);
|
X509_free(x509);
|
||||||
|
#endif
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(OpenSSLUtilityTest, VerifyPeerCertMatchesHostFailsOnNoPeerCertificate) {
|
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);
|
SSL* ssl = SSL_new(ssl_ctx);
|
||||||
|
|
||||||
EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
|
EXPECT_FALSE(openssl::VerifyPeerCertMatchesHost(ssl, "webrtc.org"));
|
||||||
|
|
|
@ -51,7 +51,7 @@ scoped_refptr<RTCCertificate> RTCCertificateGenerator::GenerateCertificate(
|
||||||
expires_s = std::min(expires_s, kYearInSeconds);
|
expires_s = std::min(expires_s, kYearInSeconds);
|
||||||
// TODO(torbjorng): Stop using |time_t|, its type is unspecified. It it safe
|
// 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
|
// 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.
|
// See bugs.webrtc.org/5720.
|
||||||
time_t cert_lifetime_s = static_cast<time_t>(expires_s);
|
time_t cert_lifetime_s = static_cast<time_t>(expires_s);
|
||||||
identity = SSLIdentity::Create(kIdentityName, key_params, cert_lifetime_s);
|
identity = SSLIdentity::Create(kIdentityName, key_params, cert_lifetime_s);
|
||||||
|
|
|
@ -16,7 +16,12 @@
|
||||||
|
|
||||||
#include "absl/algorithm/container.h"
|
#include "absl/algorithm/container.h"
|
||||||
#include "rtc_base/checks.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/ssl_fingerprint.h"
|
||||||
#include "rtc_base/third_party/base64/base64.h"
|
#include "rtc_base/third_party/base64/base64.h"
|
||||||
|
|
||||||
|
@ -117,7 +122,11 @@ std::unique_ptr<SSLCertificateStats> SSLCertChain::GetStats() const {
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<SSLCertificate> SSLCertificate::FromPEMString(
|
std::unique_ptr<SSLCertificate> SSLCertificate::FromPEMString(
|
||||||
const std::string& pem_string) {
|
const std::string& pem_string) {
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
return BoringSSLCertificate::FromPEMString(pem_string);
|
||||||
|
#else
|
||||||
return OpenSSLCertificate::FromPEMString(pem_string);
|
return OpenSSLCertificate::FromPEMString(pem_string);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|
|
@ -11,12 +11,16 @@
|
||||||
// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
|
// Handling of certificates and keypairs for SSLStreamAdapter's peer mode.
|
||||||
#include "rtc_base/ssl_identity.h"
|
#include "rtc_base/ssl_identity.h"
|
||||||
|
|
||||||
|
#include <openssl/ossl_typ.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
#include "rtc_base/boringssl_identity.h"
|
||||||
|
#else
|
||||||
#include "rtc_base/openssl_identity.h"
|
#include "rtc_base/openssl_identity.h"
|
||||||
|
#endif
|
||||||
#include "rtc_base/ssl_certificate.h"
|
#include "rtc_base/ssl_certificate.h"
|
||||||
#include "rtc_base/strings/string_builder.h"
|
#include "rtc_base/strings/string_builder.h"
|
||||||
#include "rtc_base/third_party/base64/base64.h"
|
#include "rtc_base/third_party/base64/base64.h"
|
||||||
|
@ -213,28 +217,36 @@ std::string SSLIdentity::DerToPem(const std::string& pem_type,
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
||||||
const KeyParams& key_param,
|
const KeyParams& key_param,
|
||||||
time_t certificate_lifetime) {
|
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,
|
return OpenSSLIdentity::CreateWithExpiration(common_name, key_param,
|
||||||
certificate_lifetime);
|
certificate_lifetime);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
||||||
const KeyParams& key_param) {
|
const KeyParams& key_param) {
|
||||||
return OpenSSLIdentity::CreateWithExpiration(
|
return Create(common_name, key_param, kDefaultCertificateLifetimeInSeconds);
|
||||||
common_name, key_param, kDefaultCertificateLifetimeInSeconds);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
std::unique_ptr<SSLIdentity> SSLIdentity::Create(const std::string& common_name,
|
||||||
KeyType key_type) {
|
KeyType key_type) {
|
||||||
return OpenSSLIdentity::CreateWithExpiration(
|
return Create(common_name, KeyParams(key_type),
|
||||||
common_name, KeyParams(key_type), kDefaultCertificateLifetimeInSeconds);
|
kDefaultCertificateLifetimeInSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::CreateForTest(
|
std::unique_ptr<SSLIdentity> SSLIdentity::CreateForTest(
|
||||||
const SSLIdentityParams& params) {
|
const SSLIdentityParams& params) {
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
return BoringSSLIdentity::CreateForTest(params);
|
||||||
|
#else
|
||||||
return OpenSSLIdentity::CreateForTest(params);
|
return OpenSSLIdentity::CreateForTest(params);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct an identity from a private key and a certificate.
|
// Construct an identity from a private key and a certificate.
|
||||||
|
@ -242,7 +254,11 @@ std::unique_ptr<SSLIdentity> SSLIdentity::CreateForTest(
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMStrings(
|
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMStrings(
|
||||||
const std::string& private_key,
|
const std::string& private_key,
|
||||||
const std::string& certificate) {
|
const std::string& certificate) {
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
return BoringSSLIdentity::CreateFromPEMStrings(private_key, certificate);
|
||||||
|
#else
|
||||||
return OpenSSLIdentity::CreateFromPEMStrings(private_key, certificate);
|
return OpenSSLIdentity::CreateFromPEMStrings(private_key, certificate);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Construct an identity from a private key and a certificate chain.
|
// Construct an identity from a private key and a certificate chain.
|
||||||
|
@ -250,13 +266,23 @@ std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMStrings(
|
||||||
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMChainStrings(
|
std::unique_ptr<SSLIdentity> SSLIdentity::CreateFromPEMChainStrings(
|
||||||
const std::string& private_key,
|
const std::string& private_key,
|
||||||
const std::string& certificate_chain) {
|
const std::string& certificate_chain) {
|
||||||
|
#ifdef OPENSSL_IS_BORINGSSL
|
||||||
|
return BoringSSLIdentity::CreateFromPEMChainStrings(private_key,
|
||||||
|
certificate_chain);
|
||||||
|
#else
|
||||||
return OpenSSLIdentity::CreateFromPEMChainStrings(private_key,
|
return OpenSSLIdentity::CreateFromPEMChainStrings(private_key,
|
||||||
certificate_chain);
|
certificate_chain);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const SSLIdentity& a, const SSLIdentity& b) {
|
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) ==
|
return static_cast<const OpenSSLIdentity&>(a) ==
|
||||||
static_cast<const OpenSSLIdentity&>(b);
|
static_cast<const OpenSSLIdentity&>(b);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
bool operator!=(const SSLIdentity& a, const SSLIdentity& b) {
|
bool operator!=(const SSLIdentity& a, const SSLIdentity& b) {
|
||||||
return !(a == b);
|
return !(a == b);
|
||||||
|
|
|
@ -65,7 +65,7 @@ const unsigned char kTestCertSha512[] = {
|
||||||
0x35, 0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6};
|
0x35, 0xce, 0x26, 0x58, 0x4a, 0x33, 0x6d, 0xbc, 0xb6};
|
||||||
|
|
||||||
// These PEM strings were created by generating an identity with
|
// 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->PublicKeyToPEMString()| and
|
||||||
// |identity->certificate().ToPEMString()|. If the crypto library is updated,
|
// |identity->certificate().ToPEMString()|. If the crypto library is updated,
|
||||||
// and the update changes the string form of the keys, these will have to be
|
// and the update changes the string form of the keys, these will have to be
|
||||||
|
@ -406,6 +406,21 @@ TEST_F(SSLIdentityTest, FromPEMStringsEC) {
|
||||||
EXPECT_EQ(kECDSA_CERT_PEM, identity->certificate().ToPEMString());
|
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) {
|
TEST_F(SSLIdentityTest, CloneIdentityRSA) {
|
||||||
TestCloningIdentity(*identity_rsa1_);
|
TestCloningIdentity(*identity_rsa1_);
|
||||||
TestCloningIdentity(*identity_rsa2_);
|
TestCloningIdentity(*identity_rsa2_);
|
||||||
|
|
|
@ -508,8 +508,9 @@ class SSLStreamAdapterTestBase : public ::testing::Test,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This tests that the handshake can complete before the identity is
|
// This tests that the handshake can complete before the identity is verified,
|
||||||
// verified, and the identity will be verified after the fact.
|
// 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) {
|
void TestHandshakeWithDelayedIdentity(bool valid_identity) {
|
||||||
server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
|
server_ssl_->SetMode(dtls_ ? rtc::SSL_MODE_DTLS : rtc::SSL_MODE_TLS);
|
||||||
client_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 @@ class SSLStreamAdapterTestBase : public ::testing::Test,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the handshake
|
// Start the handshake
|
||||||
int rv;
|
|
||||||
|
|
||||||
server_ssl_->SetServerRole();
|
server_ssl_->SetServerRole();
|
||||||
rv = server_ssl_->StartSSL();
|
ASSERT_EQ(0, server_ssl_->StartSSL());
|
||||||
ASSERT_EQ(0, rv);
|
ASSERT_EQ(0, client_ssl_->StartSSL());
|
||||||
|
|
||||||
rv = client_ssl_->StartSSL();
|
|
||||||
ASSERT_EQ(0, rv);
|
|
||||||
|
|
||||||
// Now run the handshake.
|
// Now run the handshake.
|
||||||
EXPECT_TRUE_WAIT(
|
EXPECT_TRUE_WAIT(
|
||||||
|
@ -547,16 +543,57 @@ class SSLStreamAdapterTestBase : public ::testing::Test,
|
||||||
EXPECT_EQ(rtc::SR_BLOCK, client_ssl_->Write(&packet, 1, &sent, 0));
|
EXPECT_EQ(rtc::SR_BLOCK, client_ssl_->Write(&packet, 1, &sent, 0));
|
||||||
EXPECT_EQ(rtc::SR_BLOCK, server_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
|
// Collect both of the certificate digests; needs to be done before calling
|
||||||
// should return false.
|
// SetPeerCertificateDigest as that may reset the identity.
|
||||||
SetPeerIdentitiesByDigest(valid_identity, valid_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
|
// State should then transition to SS_OPEN or SS_CLOSED based on validation
|
||||||
// of the identity.
|
// of the identity.
|
||||||
if (valid_identity) {
|
if (valid_identity) {
|
||||||
EXPECT_EQ(rtc::SS_OPEN, client_ssl_->GetState());
|
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 {
|
} else {
|
||||||
EXPECT_EQ(rtc::SS_CLOSED, client_ssl_->GetState());
|
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());
|
EXPECT_EQ(rtc::SS_CLOSED, server_ssl_->GetState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,6 +227,10 @@ declare_args() {
|
||||||
rtc_libvpx_build_vp9 = !build_with_mozilla
|
rtc_libvpx_build_vp9 = !build_with_mozilla
|
||||||
rtc_build_opus = !build_with_mozilla
|
rtc_build_opus = !build_with_mozilla
|
||||||
rtc_build_ssl = !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
|
rtc_build_usrsctp = !build_with_mozilla
|
||||||
|
|
||||||
# Enable libevent task queues on platforms that support it.
|
# Enable libevent task queues on platforms that support it.
|
||||||
|
|
Loading…
Reference in a new issue