/* Copyright (c) 2023, Google Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include "cert_errors.h" #include "cert_issuer_source_static.h" #include "certificate_policies.h" #include "common_cert_errors.h" #include "encode_values.h" #include "input.h" #include "parse_certificate.h" #include "parse_values.h" #include "parsed_certificate.h" #include "path_builder.h" #include "simple_path_builder_delegate.h" #include "trust_store.h" #include "trust_store_in_memory.h" #include "verify_certificate_chain.h" namespace bssl { namespace { std::optional> InternalParseCertificate(Span cert, std::string *out_diagnostic) { ParseCertificateOptions default_options{}; // We follow Chromium in setting |allow_invalid_serial_numbers| in order to // not choke on 21-byte serial numbers, which are common. // // The reason for the discrepancy is that unsigned numbers with the high bit // otherwise set get an extra 0 byte in front to keep them positive. So if you // do: // var num [20]byte // fillWithRandom(num[:]) // serialNumber := new(big.Int).SetBytes(num[:]) // encodeASN1Integer(serialNumber) // // Then half of your serial numbers will be encoded with 21 bytes. (And // 1/512th will have 19 bytes instead of 20.) default_options.allow_invalid_serial_numbers = true; UniquePtr buffer( CRYPTO_BUFFER_new(cert.data(), cert.size(), nullptr)); CertErrors errors; std::shared_ptr parsed_cert( ParsedCertificate::Create(std::move(buffer), default_options, &errors)); if (!parsed_cert) { *out_diagnostic = errors.ToDebugString(); return {}; } return parsed_cert; } } // namespace CertPool::CertPool() {} CertificateVerifyOptions::CertificateVerifyOptions() {} static std::unique_ptr WrapTrustStore( std::unique_ptr trust_store) { std::unique_ptr ret(new VerifyTrustStore); ret->trust_store = std::move(trust_store); return ret; } VerifyTrustStore::~VerifyTrustStore() {} std::unique_ptr VerifyTrustStore::FromDER( std::string_view der_certs, std::string *out_diagnostic) { auto store = std::make_unique(); CBS cbs = StringAsBytes(der_certs); for (size_t cert_num = 1; CBS_len(&cbs) != 0; cert_num++) { CBS cert; if (!CBS_get_asn1_element(&cbs, &cert, CBS_ASN1_SEQUENCE)) { *out_diagnostic = "failed to get ASN.1 SEQUENCE from input at cert " + std::to_string(cert_num); return {}; } auto parsed_cert = InternalParseCertificate(cert, out_diagnostic); if (!parsed_cert.has_value()) { return {}; } store->AddTrustAnchor(parsed_cert.value()); } return WrapTrustStore(std::move(store)); } std::unique_ptr VerifyTrustStore::FromDER( const std::vector &der_roots, std::string *out_diagnostic) { auto store = std::make_unique(); for (const std::string_view &cert : der_roots) { auto parsed_cert = InternalParseCertificate(StringAsBytes(cert), out_diagnostic); if (!parsed_cert.has_value()) { return {}; } store->AddTrustAnchor(parsed_cert.value()); } return WrapTrustStore(std::move(store)); } CertPool::~CertPool() {} std::unique_ptr CertPool::FromCerts( const std::vector &der_certs, std::string *out_diagnostic) { auto pool = std::make_unique(); pool->impl_ = std::make_unique(); for (const std::string_view &cert : der_certs) { auto parsed_cert = InternalParseCertificate(StringAsBytes(cert), out_diagnostic); if (!parsed_cert.has_value()) { return {}; } pool->impl_->AddCert(std::move(parsed_cert.value())); } return pool; } CertificateVerifyStatus::CertificateVerifyStatus() {} size_t CertificateVerifyStatus::IterationCount() const { return iteration_count_; } size_t CertificateVerifyStatus::MaxDepthSeen() const { return max_depth_seen_; } // PathBuilderDelegateImpl implements a deadline and allows for the // use of a SignatureVerifyCache if an implementation is provided. class PathBuilderDelegateImpl : public SimplePathBuilderDelegate { public: PathBuilderDelegateImpl( size_t min_rsa_modulus_length_bits, DigestPolicy digest_policy, std::chrono::time_point deadline, SignatureVerifyCache *cache) : SimplePathBuilderDelegate(min_rsa_modulus_length_bits, digest_policy), deadline_(deadline), cache_(cache) {} bool IsDeadlineExpired() override { return (std::chrono::steady_clock::now() > deadline_); } SignatureVerifyCache *GetVerifyCache() override { return cache_; } private: const std::chrono::time_point deadline_; SignatureVerifyCache *cache_; }; std::optional>> CertificateVerifyInternal( const CertificateVerifyOptions &opts, VerifyError *out_error, CertificateVerifyStatus *out_status, bool all_paths) { VerifyError dummy; if (!out_error) { out_error = &dummy; } if (out_status != nullptr) { out_status->iteration_count_ = 0; out_status->max_depth_seen_ = 0; } std::string diagnostic; std::optional> maybe_leaf = InternalParseCertificate(StringAsBytes(opts.leaf_cert), &diagnostic); if (!maybe_leaf.has_value()) { *out_error = {VerifyError::StatusCode::CERTIFICATE_INVALID, 0, diagnostic}; return {}; } std::shared_ptr leaf_cert = maybe_leaf.value(); int64_t now; if (opts.time.has_value()) { now = opts.time.value(); } else { now = time(NULL); } der::GeneralizedTime verification_time; if (!der::EncodePosixTimeAsGeneralizedTime(now, &verification_time)) { *out_error = {VerifyError::StatusCode::VERIFICATION_FAILURE, -1, "\nCould not encode verification time\n"}; return {}; } TrustStore *trust_store = nullptr; if (opts.trust_store) { trust_store = opts.trust_store->trust_store.get(); } auto digest_policy = SimplePathBuilderDelegate::DigestPolicy::kStrong; // TODO(b/111551631): remove this if (opts.insecurely_allow_sha1) { digest_policy = SimplePathBuilderDelegate::DigestPolicy::kWeakAllowSha1; } std::chrono::time_point deadline = std::chrono::time_point::max(); if (opts.deadline.has_value()) { deadline = opts.deadline.value(); } PathBuilderDelegateImpl path_builder_delegate( opts.min_rsa_modulus_length, digest_policy, deadline, opts.signature_verify_cache); KeyPurpose key_purpose = KeyPurpose::SERVER_AUTH; switch (opts.key_purpose) { case CertificateVerifyOptions::KeyPurpose::ANY_EKU: key_purpose = KeyPurpose::ANY_EKU; break; case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH: key_purpose = KeyPurpose::SERVER_AUTH; break; case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH: key_purpose = KeyPurpose::CLIENT_AUTH; break; case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH_STRICT: key_purpose = KeyPurpose::SERVER_AUTH_STRICT; break; case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH_STRICT: key_purpose = KeyPurpose::CLIENT_AUTH_STRICT; break; case CertificateVerifyOptions::KeyPurpose::SERVER_AUTH_STRICT_LEAF: key_purpose = KeyPurpose::SERVER_AUTH_STRICT_LEAF; break; case CertificateVerifyOptions::KeyPurpose::CLIENT_AUTH_STRICT_LEAF: key_purpose = KeyPurpose::CLIENT_AUTH_STRICT_LEAF; break; } CertPathBuilder path_builder(leaf_cert, trust_store, &path_builder_delegate, verification_time, key_purpose, InitialExplicitPolicy::kFalse, /* user_initial_policy_set= */ {der::Input(kAnyPolicyOid)}, InitialPolicyMappingInhibit::kFalse, InitialAnyPolicyInhibit::kFalse); CertIssuerSourceStatic intermediates; for (const std::string_view &cert : opts.intermediates) { std::string diag_string; std::optional> parsed = InternalParseCertificate(StringAsBytes(cert), &diag_string); if (!parsed.has_value()) { if (path_builder_delegate.IsDebugLogEnabled()) { path_builder_delegate.DebugLog("skipping bad intermediate: " + diag_string); } continue; } intermediates.AddCert(std::move(parsed.value())); } path_builder.AddCertIssuerSource(&intermediates); if (opts.extra_intermediates != nullptr) { path_builder.AddCertIssuerSource(opts.extra_intermediates->impl_.get()); } if (opts.max_iteration_count > 0) { path_builder.SetIterationLimit(opts.max_iteration_count); } if (opts.max_path_building_depth > 0) { path_builder.SetDepthLimit(opts.max_path_building_depth); } path_builder.SetExploreAllPaths(all_paths); CertPathBuilder::Result result = path_builder.Run(); if (out_status != nullptr) { out_status->iteration_count_ = result.iteration_count; out_status->max_depth_seen_ = result.max_depth_seen; } *out_error = result.GetBestPathVerifyError(); if (result.HasValidPath()) { std::vector> ret; if (!all_paths) { auto best_path = result.GetBestValidPath(); ret.push_back(std::vector()); for (size_t i = 0; i < best_path->certs.size(); i++) { ret[0].emplace_back(BytesAsStringView(best_path->certs[i]->der_cert())); } return ret; } for (const auto &path : result.paths) { if (!path->IsValid()) { continue; } std::vector ret_path; for (const auto &cert : path->certs) { ret_path.emplace_back(BytesAsStringView(cert->der_cert())); } ret.push_back(ret_path); } return ret; } return {}; } std::optional> CertificateVerify( const CertificateVerifyOptions &opts, VerifyError *out_error, CertificateVerifyStatus *out_status) { auto single_path = CertificateVerifyInternal(opts, out_error, out_status, /*all_paths=*/false); if (!single_path.has_value()) { return {}; } return single_path.value()[0]; } std::optional>> CertificateVerifyAllPaths( const CertificateVerifyOptions &opts) { return CertificateVerifyInternal(opts, nullptr, nullptr, /*all_paths=*/true); } } // namespace bssl