// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/cert/x509_util.h" #include #include #include #include #include "base/lazy_instance.h" #include "base/logging.h" #include "base/memory/raw_ptr.h" #include "base/notreached.h" #include "base/strings/string_split.h" #include "base/strings/string_util.h" #include "base/time/time.h" #include "build/build_config.h" #include "crypto/openssl_util.h" #include "crypto/rsa_private_key.h" #include "crypto/sha2.h" #include "net/base/hash_value.h" #include "net/cert/asn1_util.h" #include "net/cert/time_conversions.h" #include "net/cert/x509_certificate.h" #include "third_party/boringssl/src/include/openssl/bytestring.h" #include "third_party/boringssl/src/include/openssl/digest.h" #include "third_party/boringssl/src/include/openssl/ec.h" #include "third_party/boringssl/src/include/openssl/ec_key.h" #include "third_party/boringssl/src/include/openssl/evp.h" #include "third_party/boringssl/src/include/openssl/mem.h" #include "third_party/boringssl/src/include/openssl/pkcs7.h" #include "third_party/boringssl/src/include/openssl/pool.h" #include "third_party/boringssl/src/include/openssl/stack.h" #include "third_party/boringssl/src/pki/cert_errors.h" #include "third_party/boringssl/src/pki/input.h" #include "third_party/boringssl/src/pki/name_constraints.h" #include "third_party/boringssl/src/pki/parse_certificate.h" #include "third_party/boringssl/src/pki/parse_name.h" #include "third_party/boringssl/src/pki/parse_values.h" #include "third_party/boringssl/src/pki/signature_algorithm.h" namespace net::x509_util { namespace { bool AddSignatureAlgorithm(CBB* cbb, base::span oid_bytes, bool null_param) { // 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) || !CBB_add_bytes(&oid, oid_bytes.data(), oid_bytes.size()) || (null_param && !CBB_add_asn1(&sequence, ¶ms, CBS_ASN1_NULL)) || !CBB_flush(cbb)) { return false; } return true; } bool AddSignatureAlgorithm(CBB* cbb, const EVP_PKEY* pkey, DigestAlgorithm digest_alg) { if (digest_alg != DIGEST_SHA256) { return false; } if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) { // See RFC 4055. static const uint8_t kSHA256WithRSAEncryption[] = { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b}; // RSA always has null parameters. return AddSignatureAlgorithm(cbb, kSHA256WithRSAEncryption, /*null_param=*/true); } else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) { // 1.2.840.10045.4.3.2 static const uint8_t kECDSAWithSHA256[] = {0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02}; return AddSignatureAlgorithm(cbb, kECDSAWithSHA256, /*null_param=*/false); } return false; } const EVP_MD* ToEVP(DigestAlgorithm alg) { switch (alg) { case DIGEST_SHA256: return EVP_sha256(); } return nullptr; } class BufferPoolSingleton { public: BufferPoolSingleton() { crypto::EnsureOpenSSLInit(); pool_ = CRYPTO_BUFFER_POOL_new(); } CRYPTO_BUFFER_POOL* pool() { return pool_; } private: // The singleton is leaky, so there is no need to use a smart pointer. raw_ptr pool_; }; base::LazyInstance::Leaky g_buffer_pool_singleton = LAZY_INSTANCE_INITIALIZER; } // namespace // Adds an X.509 Name with the specified distinguished name to |cbb|. bool AddName(CBB* cbb, std::string_view name) { // See RFC 4519. static const uint8_t kCommonName[] = {0x55, 0x04, 0x03}; static const uint8_t kCountryName[] = {0x55, 0x04, 0x06}; static const uint8_t kOrganizationName[] = {0x55, 0x04, 0x0a}; static const uint8_t kOrganizationalUnitName[] = {0x55, 0x04, 0x0b}; std::vector attributes = SplitString( name, /*separators=*/",", base::WhitespaceHandling::TRIM_WHITESPACE, base::SplitResult::SPLIT_WANT_NONEMPTY); if (attributes.size() == 0) { LOG(ERROR) << "Missing DN or wrong format"; return false; } // See RFC 5280, section 4.1.2.4. CBB rdns; if (!CBB_add_asn1(cbb, &rdns, CBS_ASN1_SEQUENCE)) { return false; } for (const std::string& attribute : attributes) { std::vector parts = SplitString(attribute, /*separators=*/"=", base::WhitespaceHandling::KEEP_WHITESPACE, base::SplitResult::SPLIT_WANT_ALL); if (parts.size() != 2) { LOG(ERROR) << "Wrong DN format at " + attribute; return false; } const std::string& type_string = parts[0]; const std::string& value_string = parts[1]; base::span type_bytes; if (type_string == "CN") { type_bytes = kCommonName; } else if (type_string == "C") { type_bytes = kCountryName; } else if (type_string == "O") { type_bytes = kOrganizationName; } else if (type_string == "OU") { type_bytes = kOrganizationalUnitName; } else { LOG(ERROR) << "Unrecognized type " + type_string; 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, type_bytes.data(), type_bytes.size()) || !CBB_add_asn1(&attr, &value, type_string == "C" ? CBS_ASN1_PRINTABLESTRING : CBS_ASN1_UTF8STRING) || !CBB_add_bytes(&value, reinterpret_cast(value_string.data()), value_string.size()) || !CBB_flush(&rdns)) { return false; } } if (!CBB_flush(cbb)) { return false; } return true; } NET_EXPORT net::CertificateList ConvertToX509CertificatesIgnoreErrors( const std::vector>& certs_bytes) { net::CertificateList x509_certs; for (const auto& cert_uint8 : certs_bytes) { scoped_refptr x509_cert = net::X509Certificate::CreateFromBytes(base::as_byte_span(cert_uint8)); if (x509_cert) { x509_certs.push_back(std::move(x509_cert)); } } return x509_certs; } bssl::ParsedCertificateList ParseAllValidCerts( const CertificateList& x509_certs) { bssl::ParsedCertificateList parsed_certs; for (const auto& x509_cert : x509_certs) { std::shared_ptr cert = bssl::ParsedCertificate::Create( bssl::UpRef(x509_cert->cert_buffer()), net::x509_util::DefaultParseCertificateOptions(), NULL); if (cert) { parsed_certs.push_back(std::move(cert)); } } return parsed_certs; } bool CBBAddTime(CBB* cbb, base::Time time) { bssl::der::GeneralizedTime generalized_time; if (!EncodeTimeAsGeneralizedTime(time, &generalized_time)) { return false; } // Per RFC 5280, 4.1.2.5, times which fit in UTCTime must be encoded as // UTCTime rather than GeneralizedTime. CBB child; uint8_t* out; if (generalized_time.InUTCTimeRange()) { return CBB_add_asn1(cbb, &child, CBS_ASN1_UTCTIME) && CBB_add_space(&child, &out, bssl::der::kUTCTimeLength) && bssl::der::EncodeUTCTime(generalized_time, out) && CBB_flush(cbb); } return CBB_add_asn1(cbb, &child, CBS_ASN1_GENERALIZEDTIME) && CBB_add_space(&child, &out, bssl::der::kGeneralizedTimeLength) && bssl::der::EncodeGeneralizedTime(generalized_time, out) && CBB_flush(cbb); } bool GetTLSServerEndPointChannelBinding(const X509Certificate& certificate, std::string* token) { static const char kChannelBindingPrefix[] = "tls-server-end-point:"; std::string_view der_encoded_certificate = x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer()); bssl::der::Input tbs_certificate_tlv; bssl::der::Input signature_algorithm_tlv; bssl::der::BitString signature_value; if (!bssl::ParseCertificate(bssl::der::Input(der_encoded_certificate), &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, nullptr)) { return false; } std::optional signature_algorithm = bssl::ParseSignatureAlgorithm(signature_algorithm_tlv); if (!signature_algorithm) { return false; } std::optional binding_digest = bssl::GetTlsServerEndpointDigestAlgorithm(*signature_algorithm); if (!binding_digest) { return false; } const EVP_MD* digest_evp_md = nullptr; switch (binding_digest.value()) { case bssl::DigestAlgorithm::Md2: case bssl::DigestAlgorithm::Md4: case bssl::DigestAlgorithm::Md5: case bssl::DigestAlgorithm::Sha1: // Legacy digests are not supported, and // `GetTlsServerEndpointDigestAlgorithm` internally maps MD5 and SHA-1 to // SHA-256. NOTREACHED(); break; case bssl::DigestAlgorithm::Sha256: digest_evp_md = EVP_sha256(); break; case bssl::DigestAlgorithm::Sha384: digest_evp_md = EVP_sha384(); break; case bssl::DigestAlgorithm::Sha512: digest_evp_md = EVP_sha512(); break; } if (!digest_evp_md) return false; uint8_t digest[EVP_MAX_MD_SIZE]; unsigned int out_size; if (!EVP_Digest(der_encoded_certificate.data(), der_encoded_certificate.size(), digest, &out_size, digest_evp_md, nullptr)) return false; token->assign(kChannelBindingPrefix); token->append(digest, digest + out_size); return true; } // RSA keys created by CreateKeyAndSelfSignedCert will be of this length. static const uint16_t kRSAKeyLength = 1024; // Certificates made by CreateKeyAndSelfSignedCert will be signed using this // digest algorithm. static const DigestAlgorithm kSignatureDigestAlgorithm = DIGEST_SHA256; bool CreateKeyAndSelfSignedCert(std::string_view subject, uint32_t serial_number, base::Time not_valid_before, base::Time not_valid_after, std::unique_ptr* key, std::string* der_cert) { std::unique_ptr new_key( crypto::RSAPrivateKey::Create(kRSAKeyLength)); if (!new_key) return false; bool success = CreateSelfSignedCert(new_key->key(), kSignatureDigestAlgorithm, subject, serial_number, not_valid_before, not_valid_after, {}, der_cert); if (success) *key = std::move(new_key); return success; } Extension::Extension(base::span in_oid, bool in_critical, base::span in_contents) : oid(in_oid), critical(in_critical), contents(in_contents) {} Extension::~Extension() = default; Extension::Extension(const Extension&) = default; bool CreateCert(EVP_PKEY* subject_key, DigestAlgorithm digest_alg, std::string_view subject, uint32_t serial_number, base::Time not_valid_before, base::Time not_valid_after, const std::vector& extension_specs, std::string_view issuer, EVP_PKEY* issuer_key, std::string* der_encoded) { crypto::EnsureOpenSSLInit(); crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); // 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; 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) || !CBB_add_asn1_uint64(&tbs_cert, serial_number) || !AddSignatureAlgorithm(&tbs_cert, issuer_key, digest_alg) || // signature !AddName(&tbs_cert, issuer) || !CBB_add_asn1(&tbs_cert, &validity, CBS_ASN1_SEQUENCE) || !CBBAddTime(&validity, not_valid_before) || !CBBAddTime(&validity, not_valid_after) || !AddName(&tbs_cert, subject) || // subject !EVP_marshal_public_key(&tbs_cert, subject_key)) { // subjectPublicKeyInfo return false; } if (!extension_specs.empty()) { CBB outer_extensions, extensions; if (!CBB_add_asn1(&tbs_cert, &outer_extensions, 3 | CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED) || !CBB_add_asn1(&outer_extensions, &extensions, CBS_ASN1_SEQUENCE)) { return false; } for (const auto& extension_spec : extension_specs) { CBB extension, oid, value; if (!CBB_add_asn1(&extensions, &extension, CBS_ASN1_SEQUENCE) || !CBB_add_asn1(&extension, &oid, CBS_ASN1_OBJECT) || !CBB_add_bytes(&oid, extension_spec.oid.data(), extension_spec.oid.size()) || (extension_spec.critical && !CBB_add_asn1_bool(&extension, 1)) || !CBB_add_asn1(&extension, &value, CBS_ASN1_OCTETSTRING) || !CBB_add_bytes(&value, extension_spec.contents.data(), extension_spec.contents.size()) || !CBB_flush(&extensions)) { return false; } } if (!CBB_flush(&tbs_cert)) { return false; } } if (!CBB_finish(cbb.get(), &tbs_cert_bytes, &tbs_cert_len)) return false; bssl::UniquePtr 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) || !AddSignatureAlgorithm(&cert, issuer_key, digest_alg) || !CBB_add_asn1(&cert, &signature, CBS_ASN1_BITSTRING) || !CBB_add_u8(&signature, 0 /* no unused bits */) || !EVP_DigestSignInit(ctx.get(), nullptr, ToEVP(digest_alg), nullptr, issuer_key) || // 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 false; } bssl::UniquePtr delete_cert_bytes(cert_bytes); der_encoded->assign(reinterpret_cast(cert_bytes), cert_len); return true; } bool CreateSelfSignedCert(EVP_PKEY* key, DigestAlgorithm digest_alg, std::string_view subject, uint32_t serial_number, base::Time not_valid_before, base::Time not_valid_after, const std::vector& extension_specs, std::string* der_encoded) { return CreateCert(/*subject_key=*/key, digest_alg, subject, serial_number, not_valid_before, not_valid_after, extension_specs, /*issuer=*/subject, /*issuer_key=*/key, der_encoded); } CRYPTO_BUFFER_POOL* GetBufferPool() { return g_buffer_pool_singleton.Get().pool(); } bssl::UniquePtr CreateCryptoBuffer( base::span data) { return bssl::UniquePtr( CRYPTO_BUFFER_new(data.data(), data.size(), GetBufferPool())); } bssl::UniquePtr CreateCryptoBuffer(std::string_view data) { return bssl::UniquePtr( CRYPTO_BUFFER_new(reinterpret_cast(data.data()), data.size(), GetBufferPool())); } bssl::UniquePtr CreateCryptoBufferFromStaticDataUnsafe( base::span data) { return bssl::UniquePtr( CRYPTO_BUFFER_new_from_static_data_unsafe(data.data(), data.size(), GetBufferPool())); } bool CryptoBufferEqual(const CRYPTO_BUFFER* a, const CRYPTO_BUFFER* b) { DCHECK(a && b); if (a == b) return true; return CRYPTO_BUFFER_len(a) == CRYPTO_BUFFER_len(b) && memcmp(CRYPTO_BUFFER_data(a), CRYPTO_BUFFER_data(b), CRYPTO_BUFFER_len(a)) == 0; } std::string_view CryptoBufferAsStringPiece(const CRYPTO_BUFFER* buffer) { return std::string_view( reinterpret_cast(CRYPTO_BUFFER_data(buffer)), CRYPTO_BUFFER_len(buffer)); } base::span CryptoBufferAsSpan(const CRYPTO_BUFFER* buffer) { return base::make_span(CRYPTO_BUFFER_data(buffer), CRYPTO_BUFFER_len(buffer)); } scoped_refptr CreateX509CertificateFromBuffers( const STACK_OF(CRYPTO_BUFFER) * buffers) { if (sk_CRYPTO_BUFFER_num(buffers) == 0) { NOTREACHED(); return nullptr; } std::vector> intermediate_chain; for (size_t i = 1; i < sk_CRYPTO_BUFFER_num(buffers); ++i) { intermediate_chain.push_back( bssl::UpRef(sk_CRYPTO_BUFFER_value(buffers, i))); } return X509Certificate::CreateFromBuffer( bssl::UpRef(sk_CRYPTO_BUFFER_value(buffers, 0)), std::move(intermediate_chain)); } bool CreateCertBuffersFromPKCS7Bytes( base::span data, std::vector>* handles) { crypto::EnsureOpenSSLInit(); crypto::OpenSSLErrStackTracer err_cleaner(FROM_HERE); CBS der_data; CBS_init(&der_data, data.data(), data.size()); STACK_OF(CRYPTO_BUFFER)* certs = sk_CRYPTO_BUFFER_new_null(); bool success = PKCS7_get_raw_certificates(certs, &der_data, x509_util::GetBufferPool()); if (success) { for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(certs); ++i) { handles->push_back( bssl::UniquePtr(sk_CRYPTO_BUFFER_value(certs, i))); } } // |handles| took ownership of the individual buffers, so only free the list // itself. sk_CRYPTO_BUFFER_free(certs); return success; } bssl::ParseCertificateOptions DefaultParseCertificateOptions() { bssl::ParseCertificateOptions options; options.allow_invalid_serial_numbers = true; return options; } bool CalculateSha256SpkiHash(const CRYPTO_BUFFER* buffer, HashValue* hash) { std::string_view spki; if (!asn1::ExtractSPKIFromDERCert(CryptoBufferAsStringPiece(buffer), &spki)) { return false; } *hash = HashValue(HASH_VALUE_SHA256); crypto::SHA256HashString(spki, hash->data(), hash->size()); return true; } bool SignatureVerifierInitWithCertificate( crypto::SignatureVerifier* verifier, crypto::SignatureVerifier::SignatureAlgorithm signature_algorithm, base::span signature, const CRYPTO_BUFFER* certificate) { std::string_view cert_der = x509_util::CryptoBufferAsStringPiece(certificate); bssl::der::Input tbs_certificate_tlv; bssl::der::Input signature_algorithm_tlv; bssl::der::BitString signature_value; bssl::ParsedTbsCertificate tbs; if (!bssl::ParseCertificate(bssl::der::Input(cert_der), &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, nullptr) || !ParseTbsCertificate(tbs_certificate_tlv, DefaultParseCertificateOptions(), &tbs, nullptr)) { return false; } // The key usage extension, if present, must assert the digitalSignature bit. if (tbs.extensions_tlv) { std::map extensions; if (!ParseExtensions(tbs.extensions_tlv.value(), &extensions)) { return false; } bssl::ParsedExtension key_usage_ext; if (ConsumeExtension(bssl::der::Input(bssl::kKeyUsageOid), &extensions, &key_usage_ext)) { bssl::der::BitString key_usage; if (!bssl::ParseKeyUsage(key_usage_ext.value, &key_usage) || !key_usage.AssertsBit(bssl::KEY_USAGE_BIT_DIGITAL_SIGNATURE)) { return false; } } } return verifier->VerifyInit(signature_algorithm, signature, tbs.spki_tlv); } bool HasRsaPkcs1Sha1Signature(const CRYPTO_BUFFER* cert_buffer) { bssl::der::Input tbs_certificate_tlv; bssl::der::Input signature_algorithm_tlv; bssl::der::BitString signature_value; if (!bssl::ParseCertificate(bssl::der::Input(CRYPTO_BUFFER_data(cert_buffer), CRYPTO_BUFFER_len(cert_buffer)), &tbs_certificate_tlv, &signature_algorithm_tlv, &signature_value, /*out_errors=*/nullptr)) { return false; } std::optional signature_algorithm = bssl::ParseSignatureAlgorithm(signature_algorithm_tlv); return signature_algorithm && *signature_algorithm == bssl::SignatureAlgorithm::kRsaPkcs1Sha1; } } // namespace net::x509_util