// 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 #include #include "cast/common/certificate/cast_crl.h" #include "cast/common/certificate/testing/test_helpers.h" #include "cast/common/certificate/types.h" #include "platform/test/paths.h" #include "util/crypto/certificate_utils.h" #include "util/crypto/digest_sign.h" #include "util/crypto/pem_helpers.h" #include "util/crypto/sha2.h" #include "util/osp_logging.h" namespace openscreen { namespace cast { namespace { std::string* AddRevokedPublicKeyHash(TbsCrl* tbs_crl, X509* cert) { std::string* pubkey_hash = tbs_crl->add_revoked_public_key_hashes(); std::string pubkey_spki = GetSpkiTlv(cert); *pubkey_hash = SHA256HashString(pubkey_spki).value(); return pubkey_hash; } void AddSerialNumberRange(TbsCrl* tbs_crl, X509* issuer, uint64_t first, uint64_t last) { SerialNumberRange* serial_range = tbs_crl->add_revoked_serial_number_ranges(); std::string issuer_spki = GetSpkiTlv(issuer); serial_range->set_issuer_public_key_hash( SHA256HashString(issuer_spki).value()); serial_range->set_first_serial_number(first); serial_range->set_last_serial_number(last); } TbsCrl MakeTbsCrl(uint64_t not_before, uint64_t not_after, X509* device_cert, X509* inter_cert) { TbsCrl tbs_crl; tbs_crl.set_version(0); tbs_crl.set_not_before_seconds(not_before); tbs_crl.set_not_after_seconds(not_after); // NOTE: By default, include a hash which should not match any included certs. std::string* pubkey_hash = AddRevokedPublicKeyHash(&tbs_crl, device_cert); (*pubkey_hash)[0] ^= 0xff; // NOTE: Include default serial number range at device-level, which should not // include any of our certs. ErrorOr maybe_serial = ParseDerUint64(X509_get0_serialNumber(device_cert)); OSP_DCHECK(maybe_serial); uint64_t serial = maybe_serial.value(); OSP_DCHECK_LE(serial, UINT64_MAX - 200); AddSerialNumberRange(&tbs_crl, inter_cert, serial + 100, serial + 200); return tbs_crl; } // Pack into a CrlBundle and sign with |crl_inter_key|. |crl_inter_der| must be // directly signed by a Cast CRL root CA (possibly distinct from Cast root CA). void PackCrlIntoFile(const std::string& filename, const TbsCrl& tbs_crl, const std::string& crl_inter_der, EVP_PKEY* crl_inter_key) { CrlBundle crl_bundle; Crl* crl = crl_bundle.add_crls(); std::string* tbs_crl_serial = crl->mutable_tbs_crl(); tbs_crl.SerializeToString(tbs_crl_serial); crl->set_signer_cert(crl_inter_der); ErrorOr signature = SignData(EVP_sha256(), crl_inter_key, absl::Span{ reinterpret_cast(tbs_crl_serial->data()), tbs_crl_serial->size()}); OSP_DCHECK(signature); crl->set_signature(std::move(signature.value())); std::string output; crl_bundle.SerializeToString(&output); int fd = open(filename.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644); OSP_DCHECK_GE(fd, 0); OSP_DCHECK_EQ(write(fd, output.data(), output.size()), static_cast(output.size())); close(fd); } int CastMain() { const std::string data_path = GetTestDataPath() + "cast/receiver/channel/"; bssl::UniquePtr inter_key = ReadKeyFromPemFile(data_path + "inter_key.pem"); bssl::UniquePtr crl_inter_key = ReadKeyFromPemFile(data_path + "crl_inter_key.pem"); OSP_DCHECK(inter_key); OSP_DCHECK(crl_inter_key); std::vector chain_der = ReadCertificatesFromPemFile(data_path + "device_chain.pem"); std::vector crl_inter_der = ReadCertificatesFromPemFile(data_path + "crl_inter.pem"); OSP_DCHECK_EQ(chain_der.size(), 3u); OSP_DCHECK_EQ(crl_inter_der.size(), 1u); std::string& device_der = chain_der[0]; std::string& inter_der = chain_der[1]; std::string& root_der = chain_der[2]; auto* data = reinterpret_cast(device_der.data()); bssl::UniquePtr device_cert{ d2i_X509(nullptr, &data, device_der.size())}; data = reinterpret_cast(inter_der.data()); bssl::UniquePtr inter_cert{d2i_X509(nullptr, &data, inter_der.size())}; data = reinterpret_cast(root_der.data()); bssl::UniquePtr root_cert{d2i_X509(nullptr, &data, root_der.size())}; data = reinterpret_cast(crl_inter_der[0].data()); bssl::UniquePtr crl_inter_cert{ d2i_X509(nullptr, &data, crl_inter_der[0].size())}; OSP_DCHECK(device_cert); OSP_DCHECK(inter_cert); OSP_DCHECK(root_cert); OSP_DCHECK(crl_inter_cert); // NOTE: CRL where everything should pass. DateTime july2019 = {}; july2019.month = 7; july2019.year = 2019; july2019.day = 16; DateTime july2020 = {}; july2020.month = 7; july2020.year = 2020; july2020.day = 23; std::chrono::seconds not_before = DateTimeToSeconds(july2019); std::chrono::seconds not_after = DateTimeToSeconds(july2020); TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); PackCrlIntoFile(data_path + "good_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); // NOTE: CRL used outside its valid time range. { DateTime august2019 = {}; august2019.month = 8; august2019.year = 2019; august2019.day = 16; std::chrono::seconds not_after = DateTimeToSeconds(august2019); TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); PackCrlIntoFile(data_path + "invalid_time_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); } // NOTE: Device's issuer revoked. { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); AddRevokedPublicKeyHash(&tbs_crl, inter_cert.get()); PackCrlIntoFile(data_path + "issuer_revoked_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); } // NOTE: Device revoked. { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); AddRevokedPublicKeyHash(&tbs_crl, device_cert.get()); PackCrlIntoFile(data_path + "device_revoked_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); } // NOTE: Issuer serial revoked. { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); ErrorOr maybe_serial = ParseDerUint64(X509_get0_serialNumber(inter_cert.get())); OSP_DCHECK(maybe_serial); uint64_t serial = maybe_serial.value(); OSP_DCHECK_GE(serial, 10); OSP_DCHECK_LE(serial, UINT64_MAX - 20); AddSerialNumberRange(&tbs_crl, root_cert.get(), serial - 10, serial + 20); PackCrlIntoFile(data_path + "issuer_serial_revoked_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); } // NOTE: Device serial revoked. { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); ErrorOr maybe_serial = ParseDerUint64(X509_get0_serialNumber(device_cert.get())); OSP_DCHECK(maybe_serial); uint64_t serial = maybe_serial.value(); OSP_DCHECK_GE(serial, 10); OSP_DCHECK_LE(serial, UINT64_MAX - 20); AddSerialNumberRange(&tbs_crl, inter_cert.get(), serial - 10, serial + 20); PackCrlIntoFile(data_path + "device_serial_revoked_crl.pb", tbs_crl, crl_inter_der[0], crl_inter_key.get()); } // NOTE: Bad |signer_cert| used for Crl (not issued by Cast CRL root). { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); PackCrlIntoFile(data_path + "bad_signer_cert_crl.pb", tbs_crl, inter_der, inter_key.get()); } // NOTE: Mismatched key for signature in Crl (just looks like bad signature). { TbsCrl tbs_crl = MakeTbsCrl(not_before.count(), not_after.count(), device_cert.get(), inter_cert.get()); PackCrlIntoFile(data_path + "bad_signature_crl.pb", tbs_crl, crl_inter_der[0], inter_key.get()); } return 0; } } // namespace } // namespace cast } // namespace openscreen int main() { return openscreen::cast::CastMain(); }