aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuri Wiitala <miu@chromium.org>2019-11-26 16:10:29 -0800
committerCommit Bot <commit-bot@chromium.org>2019-11-27 00:20:32 +0000
commitfddca10f23f5d483e2768dea6e3e920abb28898c (patch)
treef1783cfc7bc65df183156ecd52487048767dc26d
parentf9d1fe4a538ab7003addcdf8592c77519a55b91a (diff)
downloadopenscreen-fddca10f23f5d483e2768dea6e3e920abb28898c.tar.gz
Remove dependencies on openssl from platform/api.
Moves all certificate utilities out of TlsCredentials (in platform/base) to a new util/crypto/certificate_utilities.* library. Then, all remaning boringssl dependencies are removed from platform/api by modifying the TlsConnectionFactory API to provide DER-encoded X509 certificates (i.e., a serialized form) instead of the boringssl X509 struct. Bug: openscreen:89 Change-Id: Iaaeec687d81770bb8e7e2bab4837880c77a37aa9 Reviewed-on: https://chromium-review.googlesource.com/c/openscreen/+/1932181 Reviewed-by: Yuri Wiitala <miu@chromium.org> Reviewed-by: Jordan Bayles <jophba@chromium.org> Commit-Queue: Yuri Wiitala <miu@chromium.org>
-rw-r--r--cast/sender/BUILD.gn2
-rw-r--r--cast/sender/channel/sender_socket_factory.cc17
-rw-r--r--cast/sender/channel/sender_socket_factory.h8
-rw-r--r--platform/BUILD.gn1
-rw-r--r--platform/api/tls_connection_factory.h15
-rw-r--r--platform/base/DEPS4
-rw-r--r--platform/base/tls_credentials.cc151
-rw-r--r--platform/base/tls_credentials.h80
-rw-r--r--platform/base/tls_credentials_unittest.cc89
-rw-r--r--platform/impl/tls_connection_factory_posix.cc46
-rw-r--r--platform/impl/tls_connection_factory_posix.h2
-rw-r--r--util/BUILD.gn3
-rw-r--r--util/crypto/certificate_utils.cc124
-rw-r--r--util/crypto/certificate_utils.h40
-rw-r--r--util/crypto/certificate_utils_unittest.cc70
15 files changed, 326 insertions, 326 deletions
diff --git a/cast/sender/BUILD.gn b/cast/sender/BUILD.gn
index 86d67ade..31c21215 100644
--- a/cast/sender/BUILD.gn
+++ b/cast/sender/BUILD.gn
@@ -13,12 +13,14 @@ source_set("channel") {
]
deps = [
+ "../../util",
"../common/certificate/proto:certificate_proto",
"../common/channel/proto:channel_proto",
]
public_deps = [
"../../platform",
+ "../../third_party/boringssl",
]
}
diff --git a/cast/sender/channel/sender_socket_factory.cc b/cast/sender/channel/sender_socket_factory.cc
index 811f07df..3e75c977 100644
--- a/cast/sender/channel/sender_socket_factory.cc
+++ b/cast/sender/channel/sender_socket_factory.cc
@@ -7,6 +7,7 @@
#include "cast/common/channel/cast_socket.h"
#include "cast/sender/channel/message_util.h"
#include "platform/base/tls_connect_options.h"
+#include "util/crypto/certificate_utils.h"
namespace cast {
namespace channel {
@@ -43,14 +44,14 @@ void SenderSocketFactory::Connect(const IPEndpoint& endpoint,
void SenderSocketFactory::OnAccepted(
TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) {
OSP_NOTREACHED() << "This factory is connect-only.";
}
void SenderSocketFactory::OnConnected(
TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) {
const IPEndpoint& endpoint = connection->GetRemoteEndpoint();
auto it = FindPendingConnection(endpoint);
@@ -63,16 +64,18 @@ void SenderSocketFactory::OnConnected(
CastSocket::Client* client = it->client;
pending_connections_.erase(it);
+ ErrorOr<bssl::UniquePtr<X509>> peer_cert = openscreen::ImportCertificate(
+ der_x509_peer_cert.data(), der_x509_peer_cert.size());
if (!peer_cert) {
- client_->OnError(this, endpoint, Error::Code::kErrCertsMissing);
+ client_->OnError(this, endpoint, peer_cert.error());
return;
}
auto socket = std::make_unique<CastSocket>(std::move(connection), this,
GetNextSocketId());
- pending_auth_.emplace_back(new PendingAuth{endpoint, media_policy,
- std::move(socket), client,
- AuthContext::Create(), peer_cert});
+ pending_auth_.emplace_back(
+ new PendingAuth{endpoint, media_policy, std::move(socket), client,
+ AuthContext::Create(), std::move(peer_cert.value())});
PendingAuth& pending = *pending_auth_.back();
CastMessage auth_challenge = CreateAuthChallengeMessage(pending.auth_context);
@@ -146,7 +149,7 @@ void SenderSocketFactory::OnMessage(CastSocket* socket, CastMessage message) {
}
ErrorOr<CastDeviceCertPolicy> policy_or_error = AuthenticateChallengeReply(
- message, (*it)->peer_cert, (*it)->auth_context);
+ message, (*it)->peer_cert.get(), (*it)->auth_context);
if (policy_or_error.is_error()) {
client_->OnError(this, pending->endpoint, policy_or_error.error());
return;
diff --git a/cast/sender/channel/sender_socket_factory.h b/cast/sender/channel/sender_socket_factory.h
index 62fa6d97..63998674 100644
--- a/cast/sender/channel/sender_socket_factory.h
+++ b/cast/sender/channel/sender_socket_factory.h
@@ -5,6 +5,8 @@
#ifndef CAST_SENDER_CHANNEL_SENDER_SOCKET_FACTORY_H_
#define CAST_SENDER_CHANNEL_SENDER_SOCKET_FACTORY_H_
+#include <openssl/x509.h>
+
#include <set>
#include <utility>
#include <vector>
@@ -57,10 +59,10 @@ class SenderSocketFactory final : public TlsConnectionFactory::Client,
// TlsConnectionFactory::Client overrides.
void OnAccepted(TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) override;
void OnConnected(TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) override;
void OnConnectionFailed(TlsConnectionFactory* factory,
const IPEndpoint& remote_address) override;
@@ -79,7 +81,7 @@ class SenderSocketFactory final : public TlsConnectionFactory::Client,
std::unique_ptr<CastSocket> socket;
CastSocket::Client* client;
AuthContext auth_context;
- X509* peer_cert;
+ bssl::UniquePtr<X509> peer_cert;
};
friend bool operator<(const std::unique_ptr<PendingAuth>& a, uint32_t b);
diff --git a/platform/BUILD.gn b/platform/BUILD.gn
index 12d849fa..66c7f0dc 100644
--- a/platform/BUILD.gn
+++ b/platform/BUILD.gn
@@ -149,7 +149,6 @@ source_set("unittests") {
"base/ip_address_unittest.cc",
"base/location_unittest.cc",
"base/serial_delete_ptr_unittest.cc",
- "base/tls_credentials_unittest.cc",
]
# The unit tests in impl/ assume the standalone implementation is being used.
diff --git a/platform/api/tls_connection_factory.h b/platform/api/tls_connection_factory.h
index e68d2fc6..eed6893f 100644
--- a/platform/api/tls_connection_factory.h
+++ b/platform/api/tls_connection_factory.h
@@ -5,9 +5,10 @@
#ifndef PLATFORM_API_TLS_CONNECTION_FACTORY_H_
#define PLATFORM_API_TLS_CONNECTION_FACTORY_H_
-#include <openssl/crypto.h>
+#include <stdint.h>
#include <memory>
+#include <vector>
#include "platform/base/ip_address.h"
@@ -17,7 +18,7 @@ namespace platform {
class TaskRunner;
class TlsConnection;
struct TlsConnectOptions;
-class TlsCredentials;
+struct TlsCredentials;
struct TlsListenOptions;
// We expect a single factory to be able to handle an arbitrary number of
@@ -27,12 +28,18 @@ class TlsConnectionFactory {
// Client callbacks are ran on the provided TaskRunner.
class Client {
public:
+ // Provides a new |connection| that resulted from listening on the local
+ // socket. |der_x509_peer_cert| is the DER-encoded X509 certificate from the
+ // peer.
virtual void OnAccepted(TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) = 0;
+ // Provides a new |connection| that resulted from connecting to a remote
+ // endpoint. |der_x509_peer_cert| is the DER-encoded X509 certificate from
+ // the peer.
virtual void OnConnected(TlsConnectionFactory* factory,
- X509* peer_cert,
+ std::vector<uint8_t> der_x509_peer_cert,
std::unique_ptr<TlsConnection> connection) = 0;
virtual void OnConnectionFailed(TlsConnectionFactory* factory,
diff --git a/platform/base/DEPS b/platform/base/DEPS
index 70cdf0d0..b8e2da73 100644
--- a/platform/base/DEPS
+++ b/platform/base/DEPS
@@ -19,8 +19,4 @@ specific_include_rules = {
"serial_delete_ptr\.h": [
'+platform/api',
],
- "tls_credentials\.(cc|h)": [
- '+platform/api',
- '+util',
- ],
}
diff --git a/platform/base/tls_credentials.cc b/platform/base/tls_credentials.cc
index 8c7723b8..9493f597 100644
--- a/platform/base/tls_credentials.cc
+++ b/platform/base/tls_credentials.cc
@@ -4,154 +4,19 @@
#include "platform/base/tls_credentials.h"
-#include <openssl/asn1.h>
-#include <openssl/bio.h>
-#include <openssl/evp.h>
-#include <openssl/x509.h>
-#include <time.h>
-
-#include <atomic>
-#include <chrono>
-#include <utility>
-
-#include "absl/strings/str_cat.h"
-#include "util/crypto/openssl_util.h"
-#include "util/crypto/sha2.h"
-
namespace openscreen {
namespace platform {
-namespace {
-
-// Returns whether or not the certificate field successfully was added.
-bool AddCertificateField(X509_NAME* certificate_name,
- absl::string_view field,
- absl::string_view value) {
- return X509_NAME_add_entry_by_txt(
- certificate_name, std::string(field).c_str(), MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>(value.data()),
- value.length(), -1, 0) == 1;
-}
-
-bssl::UniquePtr<ASN1_TIME> ToAsn1Time(const Clock::time_point time) {
- // We don't have access to system_clock::to_time_t.
- const std::time_t timestamp_seconds =
- std::chrono::duration_cast<std::chrono::seconds>(time.time_since_epoch())
- .count();
-
- return bssl::UniquePtr<ASN1_TIME>(ASN1_TIME_set(nullptr, timestamp_seconds));
-}
-
-bssl::UniquePtr<X509> CreateCertificate(
- absl::string_view name,
- std::chrono::seconds certificate_duration,
- EVP_PKEY key_pair,
- const Clock::time_point now_time_point) {
- bssl::UniquePtr<X509> certificate(X509_new());
-
- // Serial numbers must be unique for this session. As a pretend CA, we should
- // not issue certificates with the same serial number in the same session.
- static std::atomic_int serial_number(1);
- if (ASN1_INTEGER_set(X509_get_serialNumber(certificate.get()),
- serial_number++) != 1) {
- return nullptr;
- }
-
- const bssl::UniquePtr<ASN1_TIME> now(ToAsn1Time(now_time_point));
- const bssl::UniquePtr<ASN1_TIME> expiration_time(
- ToAsn1Time(now_time_point + certificate_duration));
-
- if ((X509_set_notBefore(certificate.get(), now.get()) != 1) ||
- (X509_set_notAfter(certificate.get(), expiration_time.get()) != 1)) {
- return nullptr;
- }
-
- X509_NAME* certificate_name = X509_get_subject_name(certificate.get());
- if (!AddCertificateField(certificate_name, "CN", name)) {
- return nullptr;
- }
-
- if ((X509_set_issuer_name(certificate.get(), certificate_name) != 1) ||
- (X509_set_pubkey(certificate.get(), &key_pair) != 1) ||
- // Unlike all of the other BoringSSL methods here, X509_sign returns
- // the size of the signature in bytes.
- (X509_sign(certificate.get(), &key_pair, EVP_sha256()) <= 0) ||
- (X509_verify(certificate.get(), &key_pair) != 1)) {
- return nullptr;
- }
-
- return certificate;
-}
-
-} // namespace
-
-ErrorOr<TlsCredentials> TlsCredentials::Create(
- absl::string_view name,
- std::chrono::seconds certificate_duration,
- ClockNowFunctionPtr now_function,
- EVP_PKEY* key_pair) {
- EnsureOpenSSLInit();
-
- bssl::UniquePtr<X509> certificate =
- CreateCertificate(name, certificate_duration, *key_pair, now_function());
-
- if (!certificate) {
- return Error::Code::kCertificateCreationError;
- }
-
- unsigned char* buffer = nullptr;
- const int len = i2d_X509(certificate.get(), &buffer);
- if (len <= 0) {
- return Error::Code::kCertificateValidationError;
- }
-
- std::vector<uint8_t> raw_der_certificate(buffer, buffer + len);
- // BoringSSL doesn't free the temporary buffer.
- OPENSSL_free(buffer);
-
- ErrorOr<RSAPrivateKey> rsa_private_key =
- RSAPrivateKey::CreateFromKey(key_pair);
- if (rsa_private_key.is_error()) {
- return rsa_private_key.error();
- }
-
- ErrorOr<std::vector<uint8_t>> private_key_export =
- rsa_private_key.value().ExportPrivateKey();
- if (private_key_export.is_error()) {
- return private_key_export.error();
- }
-
- ErrorOr<std::vector<uint8_t>> public_key_export =
- rsa_private_key.value().ExportPublicKey();
- if (public_key_export.is_error()) {
- return public_key_export.error();
- }
-
- std::vector<uint8_t> public_key_hash(SHA256_DIGEST_LENGTH);
- absl::string_view key_view(
- reinterpret_cast<const char*>(public_key_export.value().data()),
- public_key_export.value().size());
- SHA256HashString(key_view, public_key_hash.data());
+TlsCredentials::TlsCredentials() = default;
- return TlsCredentials(
- std::move(certificate), std::move(rsa_private_key.value()),
- std::move(private_key_export.value()),
- std::move(public_key_export.value()), std::move(public_key_hash),
- std::move(raw_der_certificate));
-}
+TlsCredentials::TlsCredentials(std::vector<uint8_t> der_rsa_private_key,
+ std::vector<uint8_t> der_rsa_public_key,
+ std::vector<uint8_t> der_x509_cert)
+ : der_rsa_private_key(std::move(der_rsa_private_key)),
+ der_rsa_public_key(std::move(der_rsa_public_key)),
+ der_x509_cert(std::move(der_x509_cert)) {}
-TlsCredentials::TlsCredentials(bssl::UniquePtr<X509> certificate,
- RSAPrivateKey key_pair,
- std::vector<uint8_t> private_key_base64,
- std::vector<uint8_t> public_key_base64,
- std::vector<uint8_t> public_key_hash,
- std::vector<uint8_t> raw_der_certificate)
- : certificate_(std::move(certificate)),
- key_pair_(std::move(key_pair)),
- private_key_base64_(std::move(private_key_base64)),
- public_key_base64_(std::move(public_key_base64)),
- public_key_hash_(std::move(public_key_hash)),
- raw_der_certificate_(std::move(raw_der_certificate)) {}
+TlsCredentials::~TlsCredentials() = default;
} // namespace platform
} // namespace openscreen
diff --git a/platform/base/tls_credentials.h b/platform/base/tls_credentials.h
index 715c7847..8c5162dc 100644
--- a/platform/base/tls_credentials.h
+++ b/platform/base/tls_credentials.h
@@ -5,82 +5,28 @@
#ifndef PLATFORM_BASE_TLS_CREDENTIALS_H_
#define PLATFORM_BASE_TLS_CREDENTIALS_H_
-#include <openssl/crypto.h>
-#include <openssl/rsa.h>
-#include <openssl/ssl.h>
+#include <stdint.h>
-#include <chrono>
-#include <memory>
-#include <string>
#include <vector>
-#include "absl/strings/string_view.h"
-#include "platform/api/time.h"
-#include "platform/base/error.h"
-#include "platform/base/macros.h"
-#include "util/crypto/rsa_private_key.h"
-
namespace openscreen {
namespace platform {
-class TlsCredentials {
- public:
- // We are move only due to unique pointers.
- TlsCredentials(TlsCredentials&&) noexcept = default;
- TlsCredentials& operator=(TlsCredentials&&) noexcept = default;
-
- // TlsCredentials generates a self signed certificate given (1) the name
- // to use for the certificate, (2) the length of time the certificate will
- // be valid, and (3) a private/public key pair.
- //
- // NOTE: the ownership of EVP_PKEY is automatically managed by OpenSSL using
- // ref counting, even if you store it in a bssl::UniquePtr.
- static ErrorOr<TlsCredentials> Create(
- absl::string_view name,
- std::chrono::seconds certificate_duration,
- ClockNowFunctionPtr now_function,
- EVP_PKEY* key_pair);
-
- // The OpenSSL encoded self signed certificate.
- const X509& certificate() const { return *certificate_; }
-
- // The original key pair provided on construction.
- const RSAPrivateKey& key_pair() const { return key_pair_; }
-
- // A base64 encoded version of the private key provided on construction.
- const std::vector<uint8_t>& private_key_base64() const {
- return private_key_base64_;
- }
-
- // A base64 encoded version of the public key provided on construction.
- const std::vector<uint8_t>& public_key_base64() const {
- return public_key_base64_;
- }
-
- // A SHA-256 digest of the public key provided on construction.
- const std::vector<uint8_t>& public_key_hash() const {
- return public_key_hash_;
- }
+struct TlsCredentials {
+ TlsCredentials();
+ TlsCredentials(std::vector<uint8_t> der_rsa_private_key,
+ std::vector<uint8_t> der_rsa_public_key,
+ std::vector<uint8_t> der_x509_cert);
+ ~TlsCredentials();
- // The DER-encoded raw bytes of the generated self signed certficate.
- const std::vector<uint8_t>& raw_der_certificate() const {
- return raw_der_certificate_;
- }
+ // DER-encoded RSA private key.
+ std::vector<uint8_t> der_rsa_private_key;
- private:
- TlsCredentials(bssl::UniquePtr<X509> certificate,
- RSAPrivateKey key_pair,
- std::vector<uint8_t> private_key_base64,
- std::vector<uint8_t> public_key_base64,
- std::vector<uint8_t> public_key_hash,
- std::vector<uint8_t> raw_der_certificate);
+ // DER-encoded RSA public key.
+ std::vector<uint8_t> der_rsa_public_key;
- bssl::UniquePtr<X509> certificate_;
- RSAPrivateKey key_pair_;
- std::vector<uint8_t> private_key_base64_;
- std::vector<uint8_t> public_key_base64_;
- std::vector<uint8_t> public_key_hash_;
- std::vector<uint8_t> raw_der_certificate_;
+ // DER-encoded X509 Certificate that is based on the above keys.
+ std::vector<uint8_t> der_x509_cert;
};
} // namespace platform
diff --git a/platform/base/tls_credentials_unittest.cc b/platform/base/tls_credentials_unittest.cc
deleted file mode 100644
index b5d40360..00000000
--- a/platform/base/tls_credentials_unittest.cc
+++ /dev/null
@@ -1,89 +0,0 @@
-// Copyright 2019 The Chromium Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#include "platform/base/tls_credentials.h"
-
-#include <openssl/bio.h>
-#include <openssl/bn.h>
-#include <openssl/rsa.h>
-#include <openssl/x509.h>
-
-#include <algorithm>
-#include <chrono>
-#include <utility>
-
-#include "gmock/gmock.h"
-#include "gtest/gtest.h"
-#include "platform/api/time.h"
-#include "platform/base/error.h"
-#include "platform/test/fake_clock.h"
-#include "util/std_util.h"
-
-namespace openscreen {
-namespace platform {
-
-using std::chrono::seconds;
-using testing::EndsWith;
-using testing::StartsWith;
-
-namespace {
-
-bssl::UniquePtr<EVP_PKEY> GenerateRsaKeypair() {
- bssl::UniquePtr<BIGNUM> prime(BN_new());
- EXPECT_NE(0, BN_set_word(prime.get(), RSA_F4));
-
- bssl::UniquePtr<RSA> rsa(RSA_new());
- EXPECT_NE(0, RSA_generate_key_ex(rsa.get(), 2048, prime.get(), nullptr));
-
- bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
- EXPECT_NE(0, EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
-
- return pkey;
-}
-
-} // namespace
-
-TEST(TlsCredentialsTest, CredentialsAreGeneratedAppropriately) {
- bssl::UniquePtr<EVP_PKEY> pkey = GenerateRsaKeypair();
-
- FakeClock clock(Clock::now());
- ErrorOr<TlsCredentials> creds_or_error = TlsCredentials::Create(
- "test.com", seconds(31556952), platform::FakeClock::now, pkey.get());
- EXPECT_TRUE(creds_or_error.is_value());
- TlsCredentials credentials = std::move(creds_or_error.value());
-
- // Validate the generated certificate. A const cast is necessary because
- // openssl is not const correct for this method.
- EXPECT_NE(0,
- X509_verify(const_cast<X509*>(&credentials.certificate()),
- const_cast<EVP_PKEY*>(credentials.key_pair().key())));
-
- const auto raw_cert = credentials.raw_der_certificate();
- EXPECT_GT(raw_cert.size(), 0u);
-
- // Calling d2i_X509 does validation of the certificate, beyond the checking
- // done in the i2d_X509 method that creates the raw_der_certificate.
- const unsigned char* raw_cert_begin = raw_cert.data();
- const bssl::UniquePtr<X509> x509_certificate(
- d2i_X509(nullptr, &raw_cert_begin, raw_cert.size()));
- EXPECT_NE(nullptr, x509_certificate);
-
- // Validate the private key
- // NOTE: both the private and public keys are base64 encoded, so we can't
- // actually validate their contents properly.
- const auto private_key = credentials.private_key_base64();
- EXPECT_GT(private_key.size(), 0u);
-
- // Validate the public key
- const auto public_key = credentials.public_key_base64();
- EXPECT_GT(public_key.size(), 0u);
-
- // Validate the hash
- // A SHA-256 hash should always be 256 bits, or 32 bytes.
- const unsigned int kSha256HashSizeInBytes = 32;
- EXPECT_EQ(credentials.public_key_hash().size(), kSha256HashSizeInBytes);
-}
-
-} // namespace platform
-} // namespace openscreen
diff --git a/platform/impl/tls_connection_factory_posix.cc b/platform/impl/tls_connection_factory_posix.cc
index eb99bf50..8b270dab 100644
--- a/platform/impl/tls_connection_factory_posix.cc
+++ b/platform/impl/tls_connection_factory_posix.cc
@@ -23,6 +23,7 @@
#include "platform/base/tls_listen_options.h"
#include "platform/impl/stream_socket.h"
#include "platform/impl/tls_connection_posix.h"
+#include "util/crypto/certificate_utils.h"
#include "util/crypto/openssl_util.h"
#include "util/logging.h"
#include "util/trace_logging.h"
@@ -30,6 +31,17 @@
namespace openscreen {
namespace platform {
+namespace {
+
+ErrorOr<std::vector<uint8_t>> GetDEREncodedPeerCertificate(const SSL& ssl) {
+ X509* const peer_cert = SSL_get_peer_certificate(&ssl);
+ ErrorOr<std::vector<uint8_t>> der_peer_cert = ExportCertificate(*peer_cert);
+ X509_free(peer_cert);
+ return der_peer_cert;
+}
+
+} // namespace
+
std::unique_ptr<TlsConnectionFactory> TlsConnectionFactory::CreateFactory(
Client* client,
TaskRunner* task_runner) {
@@ -84,11 +96,20 @@ void TlsConnectionFactoryPosix::Connect(const IPEndpoint& remote_address,
return;
}
+ ErrorOr<std::vector<uint8_t>> der_peer_cert =
+ GetDEREncodedPeerCertificate(*connection->ssl_);
+ if (!der_peer_cert) {
+ DispatchConnectionFailed(connection->GetRemoteEndpoint());
+ TRACE_SET_RESULT(der_peer_cert.error());
+ return;
+ }
+
task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr(),
+ der = std::move(der_peer_cert.value()),
moved_connection = std::move(connection)]() mutable {
if (auto* self = weak_this.get()) {
- X509* peer_cert = SSL_get_peer_certificate(moved_connection->ssl_.get());
- self->client_->OnConnected(self, peer_cert, std::move(moved_connection));
+ self->client_->OnConnected(self, std::move(der),
+ std::move(moved_connection));
}
});
}
@@ -97,10 +118,10 @@ void TlsConnectionFactoryPosix::SetListenCredentials(
const TlsCredentials& credentials) {
EnsureInitialized();
- // We don't really change the certificate, but we do apply ref counting to
- // it, so a const cast is unfortunately necessary.
- X509* non_const_cert = const_cast<X509*>(&credentials.certificate());
- if (SSL_CTX_use_certificate(ssl_context_.get(), non_const_cert) != 1) {
+ ErrorOr<bssl::UniquePtr<X509>> cert = ImportCertificate(
+ credentials.der_x509_cert.data(), credentials.der_x509_cert.size());
+ if (!cert ||
+ SSL_CTX_use_certificate(ssl_context_.get(), cert.value().get()) != 1) {
DispatchError(Error::Code::kSocketListenFailure);
TRACE_SET_RESULT(Error::Code::kSocketListenFailure);
return;
@@ -170,11 +191,20 @@ void TlsConnectionFactoryPosix::OnSocketAccepted(
return;
}
+ ErrorOr<std::vector<uint8_t>> der_peer_cert =
+ GetDEREncodedPeerCertificate(*connection->ssl_);
+ if (!der_peer_cert) {
+ DispatchConnectionFailed(connection->GetRemoteEndpoint());
+ TRACE_SET_RESULT(der_peer_cert.error());
+ return;
+ }
+
task_runner_->PostTask([weak_this = weak_factory_.GetWeakPtr(),
+ der = std::move(der_peer_cert.value()),
moved_connection = std::move(connection)]() mutable {
if (auto* self = weak_this.get()) {
- X509* peer_cert = SSL_get_peer_certificate(moved_connection->ssl_.get());
- self->client_->OnAccepted(self, peer_cert, std::move(moved_connection));
+ self->client_->OnAccepted(self, std::move(der),
+ std::move(moved_connection));
}
});
}
diff --git a/platform/impl/tls_connection_factory_posix.h b/platform/impl/tls_connection_factory_posix.h
index aa2aee1a..46ba6e25 100644
--- a/platform/impl/tls_connection_factory_posix.h
+++ b/platform/impl/tls_connection_factory_posix.h
@@ -5,6 +5,8 @@
#ifndef PLATFORM_IMPL_TLS_CONNECTION_FACTORY_POSIX_H_
#define PLATFORM_IMPL_TLS_CONNECTION_FACTORY_POSIX_H_
+#include <openssl/ssl.h>
+
#include <memory>
#include "platform/api/tls_connection.h"
diff --git a/util/BUILD.gn b/util/BUILD.gn
index 475b2dd6..6d8b80ef 100644
--- a/util/BUILD.gn
+++ b/util/BUILD.gn
@@ -10,6 +10,8 @@ source_set("util") {
"alarm.h",
"big_endian.cc",
"big_endian.h",
+ "crypto/certificate_utils.cc",
+ "crypto/certificate_utils.h",
"crypto/openssl_util.cc",
"crypto/openssl_util.h",
"crypto/rsa_private_key.cc",
@@ -52,6 +54,7 @@ source_set("unittests") {
sources = [
"alarm_unittest.cc",
"big_endian_unittest.cc",
+ "crypto/certificate_utils_unittest.cc",
"crypto/rsa_private_key_unittest.cc",
"crypto/secure_hash_unittest.cc",
"crypto/sha2_unittest.cc",
diff --git a/util/crypto/certificate_utils.cc b/util/crypto/certificate_utils.cc
new file mode 100644
index 00000000..1d6873f4
--- /dev/null
+++ b/util/crypto/certificate_utils.cc
@@ -0,0 +1,124 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "util/crypto/certificate_utils.h"
+
+#include <openssl/asn1.h>
+#include <openssl/bio.h>
+#include <openssl/crypto.h>
+#include <openssl/evp.h>
+#include <openssl/rsa.h>
+#include <openssl/ssl.h>
+#include <time.h>
+
+#include <atomic>
+#include <string>
+
+#include "util/crypto/openssl_util.h"
+#include "util/crypto/sha2.h"
+
+namespace openscreen {
+
+namespace {
+
+// Returns whether or not the certificate field successfully was added.
+bool AddCertificateField(X509_NAME* certificate_name,
+ absl::string_view field,
+ absl::string_view value) {
+ return X509_NAME_add_entry_by_txt(
+ certificate_name, std::string(field).c_str(), MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>(value.data()),
+ value.length(), -1, 0) == 1;
+}
+
+bssl::UniquePtr<ASN1_TIME> ToAsn1Time(std::chrono::seconds time_since_epoch) {
+ return bssl::UniquePtr<ASN1_TIME>(
+ ASN1_TIME_set(nullptr, time_since_epoch.count()));
+}
+
+bssl::UniquePtr<X509> CreateCertificateInternal(
+ absl::string_view name,
+ std::chrono::seconds certificate_duration,
+ EVP_PKEY key_pair,
+ std::chrono::seconds time_since_unix_epoch) {
+ bssl::UniquePtr<X509> certificate(X509_new());
+
+ // Serial numbers must be unique for this session. As a pretend CA, we should
+ // not issue certificates with the same serial number in the same session.
+ static std::atomic_int serial_number(1);
+ if (ASN1_INTEGER_set(X509_get_serialNumber(certificate.get()),
+ serial_number++) != 1) {
+ return nullptr;
+ }
+
+ const bssl::UniquePtr<ASN1_TIME> now(ToAsn1Time(time_since_unix_epoch));
+ const bssl::UniquePtr<ASN1_TIME> expiration_time(
+ ToAsn1Time(time_since_unix_epoch + certificate_duration));
+
+ if ((X509_set_notBefore(certificate.get(), now.get()) != 1) ||
+ (X509_set_notAfter(certificate.get(), expiration_time.get()) != 1)) {
+ return nullptr;
+ }
+
+ X509_NAME* certificate_name = X509_get_subject_name(certificate.get());
+ if (!AddCertificateField(certificate_name, "CN", name)) {
+ return nullptr;
+ }
+
+ if ((X509_set_issuer_name(certificate.get(), certificate_name) != 1) ||
+ (X509_set_pubkey(certificate.get(), &key_pair) != 1) ||
+ // Unlike all of the other BoringSSL methods here, X509_sign returns
+ // the size of the signature in bytes.
+ (X509_sign(certificate.get(), &key_pair, EVP_sha256()) <= 0) ||
+ (X509_verify(certificate.get(), &key_pair) != 1)) {
+ return nullptr;
+ }
+
+ return certificate;
+}
+
+} // namespace
+
+ErrorOr<bssl::UniquePtr<X509>> CreateCertificate(
+ absl::string_view name,
+ std::chrono::seconds duration,
+ const EVP_PKEY& key_pair,
+ std::chrono::seconds time_since_unix_epoch) {
+ bssl::UniquePtr<X509> certificate = CreateCertificateInternal(
+ name, duration, key_pair, time_since_unix_epoch);
+ if (!certificate) {
+ return Error::Code::kCertificateCreationError;
+ }
+ return certificate;
+}
+
+ErrorOr<std::vector<uint8_t>> ExportCertificate(const X509& certificate) {
+ unsigned char* buffer = nullptr;
+ // Casting-away the const because the legacy i2d_X509() function is not
+ // const-correct.
+ X509* const certificate_ptr = const_cast<X509*>(&certificate);
+ const int len = i2d_X509(certificate_ptr, &buffer);
+ if (len <= 0) {
+ return Error::Code::kCertificateValidationError;
+ }
+ std::vector<uint8_t> raw_der_certificate(buffer, buffer + len);
+ // BoringSSL doesn't free the temporary buffer.
+ OPENSSL_free(buffer);
+ return raw_der_certificate;
+}
+
+ErrorOr<bssl::UniquePtr<X509>> ImportCertificate(const uint8_t* der_x509_cert,
+ int der_x509_cert_length) {
+ if (!der_x509_cert) {
+ return Error::Code::kErrCertsMissing;
+ }
+ bssl::UniquePtr<X509> certificate(
+ d2i_X509(nullptr, &der_x509_cert, der_x509_cert_length));
+ if (!certificate) {
+ return Error::Code::kCertificateValidationError;
+ }
+ return certificate;
+}
+
+} // namespace openscreen
diff --git a/util/crypto/certificate_utils.h b/util/crypto/certificate_utils.h
new file mode 100644
index 00000000..4bfe6e37
--- /dev/null
+++ b/util/crypto/certificate_utils.h
@@ -0,0 +1,40 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef UTIL_CRYPTO_CERTIFICATE_UTILS_H_
+#define UTIL_CRYPTO_CERTIFICATE_UTILS_H_
+
+#include <openssl/x509.h>
+#include <stdint.h>
+
+#include <chrono>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "platform/api/time.h"
+#include "platform/base/error.h"
+#include "util/crypto/rsa_private_key.h"
+
+namespace openscreen {
+
+// Creates a new self-signed X509 certificate having the given |name| and
+// |duration| until expiration, and based on the given |key_pair|.
+// |time_since_unix_epoch| is the current time.
+ErrorOr<bssl::UniquePtr<X509>> CreateCertificate(
+ absl::string_view name,
+ std::chrono::seconds duration,
+ const EVP_PKEY& key_pair,
+ std::chrono::seconds time_since_unix_epoch =
+ platform::GetWallTimeSinceUnixEpoch());
+
+// Exports the given X509 certificate as its DER-encoded binary form.
+ErrorOr<std::vector<uint8_t>> ExportCertificate(const X509& certificate);
+
+// Parses a DER-encoded X509 certificate from its binary form.
+ErrorOr<bssl::UniquePtr<X509>> ImportCertificate(const uint8_t* der_x509_cert,
+ int der_x509_cert_length);
+
+} // namespace openscreen
+
+#endif // UTIL_CRYPTO_CERTIFICATE_UTILS_H_
diff --git a/util/crypto/certificate_utils_unittest.cc b/util/crypto/certificate_utils_unittest.cc
new file mode 100644
index 00000000..7475756b
--- /dev/null
+++ b/util/crypto/certificate_utils_unittest.cc
@@ -0,0 +1,70 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "util/crypto/certificate_utils.h"
+
+#include <openssl/bio.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+#include <openssl/x509.h>
+
+#include <chrono>
+
+#include "gtest/gtest.h"
+#include "platform/api/time.h"
+#include "platform/base/error.h"
+#include "util/std_util.h"
+
+namespace openscreen {
+namespace {
+
+constexpr char kName[] = "test.com";
+constexpr auto kDuration = std::chrono::seconds(31556952);
+
+bssl::UniquePtr<EVP_PKEY> GenerateRsaKeypair() {
+ bssl::UniquePtr<BIGNUM> prime(BN_new());
+ EXPECT_NE(0, BN_set_word(prime.get(), RSA_F4));
+
+ bssl::UniquePtr<RSA> rsa(RSA_new());
+ EXPECT_NE(0, RSA_generate_key_ex(rsa.get(), 2048, prime.get(), nullptr));
+
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ EXPECT_NE(0, EVP_PKEY_set1_RSA(pkey.get(), rsa.get()));
+
+ return pkey;
+}
+
+TEST(CertificateUtilTest, CreatesValidCertificate) {
+ bssl::UniquePtr<EVP_PKEY> pkey = GenerateRsaKeypair();
+
+ ErrorOr<bssl::UniquePtr<X509>> certificate =
+ CreateCertificate(kName, kDuration, *pkey);
+ ASSERT_TRUE(certificate.is_value());
+
+ // Validate the generated certificate.
+ EXPECT_NE(0, X509_verify(certificate.value().get(), pkey.get()));
+}
+
+TEST(CertificateUtilTest, ExportsAndImportsCertificate) {
+ bssl::UniquePtr<EVP_PKEY> pkey = GenerateRsaKeypair();
+ ErrorOr<bssl::UniquePtr<X509>> certificate =
+ CreateCertificate(kName, kDuration, *pkey);
+ ASSERT_TRUE(certificate.is_value());
+
+ ErrorOr<std::vector<uint8_t>> exported =
+ ExportCertificate(*certificate.value());
+ ASSERT_TRUE(exported.is_value()) << exported.error();
+ EXPECT_FALSE(exported.value().empty());
+
+ ErrorOr<bssl::UniquePtr<X509>> imported =
+ ImportCertificate(exported.value().data(), exported.value().size());
+ ASSERT_TRUE(imported.is_value()) << imported.error();
+ ASSERT_TRUE(imported.value().get());
+
+ // Validate the imported certificate.
+ EXPECT_NE(0, X509_verify(imported.value().get(), pkey.get()));
+}
+
+} // namespace
+} // namespace openscreen