// Copyright 2020 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 "cast/receiver/channel/static_credentials.h" #include #include #include #include #include #include #include #include "platform/base/tls_credentials.h" #include "util/crypto/certificate_utils.h" #include "util/osp_logging.h" namespace openscreen { namespace cast { namespace { using FileUniquePtr = std::unique_ptr; constexpr char kGeneratedRootCertificateName[] = "generated_root_cast_receiver.crt"; constexpr char kGeneratedPrivateKey[] = "generated_root_cast_receiver.key"; constexpr int kThreeDaysInSeconds = 3 * 24 * 60 * 60; constexpr auto kCertificateDuration = std::chrono::seconds(kThreeDaysInSeconds); ErrorOr GenerateCredentials( std::string device_certificate_id, EVP_PKEY* root_key, X509* root_cert) { OSP_CHECK(root_key); OSP_CHECK(root_cert); bssl::UniquePtr intermediate_key = GenerateRsaKeyPair(); bssl::UniquePtr device_key = GenerateRsaKeyPair(); OSP_CHECK(intermediate_key); OSP_CHECK(device_key); ErrorOr> intermediate_cert_or_error = CreateSelfSignedX509Certificate( "Cast Intermediate", kCertificateDuration, *intermediate_key, GetWallTimeSinceUnixEpoch(), true, root_cert, root_key); OSP_CHECK(intermediate_cert_or_error); bssl::UniquePtr intermediate_cert = std::move(intermediate_cert_or_error.value()); ErrorOr> device_cert_or_error = CreateSelfSignedX509Certificate( device_certificate_id, kCertificateDuration, *device_key, GetWallTimeSinceUnixEpoch(), false, intermediate_cert.get(), intermediate_key.get()); OSP_CHECK(device_cert_or_error); bssl::UniquePtr device_cert = std::move(device_cert_or_error.value()); // Device cert chain plumbing + serialization. DeviceCredentials device_creds; device_creds.private_key = std::move(device_key); int cert_length = i2d_X509(device_cert.get(), nullptr); std::string cert_serial(cert_length, 0); uint8_t* out = reinterpret_cast(&cert_serial[0]); i2d_X509(device_cert.get(), &out); device_creds.certs.emplace_back(std::move(cert_serial)); cert_length = i2d_X509(intermediate_cert.get(), nullptr); cert_serial.resize(cert_length); out = reinterpret_cast(&cert_serial[0]); i2d_X509(intermediate_cert.get(), &out); device_creds.certs.emplace_back(std::move(cert_serial)); cert_length = i2d_X509(root_cert, nullptr); std::vector trust_anchor_der(cert_length); out = &trust_anchor_der[0]; i2d_X509(root_cert, &out); // TLS key pair + certificate generation. bssl::UniquePtr tls_key = GenerateRsaKeyPair(); OSP_CHECK_EQ(EVP_PKEY_id(tls_key.get()), EVP_PKEY_RSA); ErrorOr> tls_cert_or_error = CreateSelfSignedX509Certificate("Test Device TLS", kCertificateDuration, *tls_key, GetWallTimeSinceUnixEpoch()); OSP_CHECK(tls_cert_or_error); bssl::UniquePtr tls_cert = std::move(tls_cert_or_error.value()); // TLS private key serialization. RSA* rsa_key = EVP_PKEY_get0_RSA(tls_key.get()); size_t pkey_len = 0; uint8_t* pkey_bytes = nullptr; OSP_CHECK(RSA_private_key_to_bytes(&pkey_bytes, &pkey_len, rsa_key)); OSP_CHECK_GT(pkey_len, 0u); std::vector tls_key_serial(pkey_bytes, pkey_bytes + pkey_len); OPENSSL_free(pkey_bytes); // TLS public key serialization. pkey_len = 0; pkey_bytes = nullptr; OSP_CHECK(RSA_public_key_to_bytes(&pkey_bytes, &pkey_len, rsa_key)); OSP_CHECK_GT(pkey_len, 0u); std::vector tls_pub_serial(pkey_bytes, pkey_bytes + pkey_len); OPENSSL_free(pkey_bytes); // TLS cert serialization. cert_length = 0; cert_length = i2d_X509(tls_cert.get(), nullptr); OSP_CHECK_GT(cert_length, 0); std::vector tls_cert_serial(cert_length); out = &tls_cert_serial[0]; i2d_X509(tls_cert.get(), &out); auto provider = std::make_unique( std::move(device_creds), tls_cert_serial); return GeneratedCredentials{ std::move(provider), TlsCredentials{std::move(tls_key_serial), std::move(tls_pub_serial), std::move(tls_cert_serial)}, std::move(trust_anchor_der)}; } bssl::UniquePtr GeneratePrivateKey() { bssl::UniquePtr root_key = GenerateRsaKeyPair(); OSP_CHECK(root_key); return root_key; } bssl::UniquePtr GenerateRootCert(const EVP_PKEY& root_key) { ErrorOr> root_cert_or_error = CreateSelfSignedX509Certificate("Cast Root CA", kCertificateDuration, root_key, GetWallTimeSinceUnixEpoch(), true); OSP_CHECK(root_cert_or_error); return std::move(root_cert_or_error.value()); } } // namespace StaticCredentialsProvider::StaticCredentialsProvider() = default; StaticCredentialsProvider::StaticCredentialsProvider( DeviceCredentials device_creds, std::vector tls_cert_der) : device_creds(std::move(device_creds)), tls_cert_der(std::move(tls_cert_der)) {} StaticCredentialsProvider::StaticCredentialsProvider( StaticCredentialsProvider&&) noexcept = default; StaticCredentialsProvider& StaticCredentialsProvider::operator=( StaticCredentialsProvider&&) = default; StaticCredentialsProvider::~StaticCredentialsProvider() = default; void GenerateDeveloperCredentialsToFile() { bssl::UniquePtr root_key = GeneratePrivateKey(); bssl::UniquePtr root_cert = GenerateRootCert(*root_key); FileUniquePtr f(fopen(kGeneratedPrivateKey, "w"), &fclose); if (PEM_write_PrivateKey(f.get(), root_key.get(), nullptr, nullptr, 0, 0, nullptr) != 1) { OSP_LOG_ERROR << "Failed to write private key, check permissions?"; return; } OSP_LOG_INFO << "Generated new private key for session: ./" << kGeneratedPrivateKey; FileUniquePtr cert_file(fopen(kGeneratedRootCertificateName, "w"), &fclose); if (PEM_write_X509(cert_file.get(), root_cert.get()) != 1) { OSP_LOG_ERROR << "Failed to write root certificate, check permissions?"; return; } OSP_LOG_INFO << "Generated new root certificate for session: ./" << kGeneratedRootCertificateName; } ErrorOr GenerateCredentialsForTesting( const std::string& device_certificate_id) { bssl::UniquePtr root_key = GeneratePrivateKey(); bssl::UniquePtr root_cert = GenerateRootCert(*root_key); return GenerateCredentials(device_certificate_id, root_key.get(), root_cert.get()); } ErrorOr GenerateCredentials( const std::string& device_certificate_id, const std::string& private_key_path, const std::string& server_certificate_path) { if (private_key_path.empty() || server_certificate_path.empty()) { return Error(Error::Code::kParameterInvalid, "Missing either private key or server certificate"); } FileUniquePtr key_file(fopen(private_key_path.c_str(), "r"), &fclose); if (!key_file) { return Error(Error::Code::kParameterInvalid, "Missing private key file path"); } bssl::UniquePtr root_key = bssl::UniquePtr(PEM_read_PrivateKey( key_file.get(), nullptr /* x */, nullptr /* cb */, nullptr /* u */)); FileUniquePtr cert_file(fopen(server_certificate_path.c_str(), "r"), &fclose); if (!cert_file) { return Error(Error::Code::kParameterInvalid, "Missing server certificate file path"); } bssl::UniquePtr root_cert = bssl::UniquePtr(PEM_read_X509( cert_file.get(), nullptr /* x */, nullptr /* cb */, nullptr /* u */)); return GenerateCredentials(device_certificate_id, root_key.get(), root_cert.get()); } } // namespace cast } // namespace openscreen