| // Copyright 2015 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/certificate_provider/thread_safe_certificate_map.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/flat_map.h" |
| #include "base/strings/string_piece.h" |
| #include "base/synchronization/lock.h" |
| #include "chromeos/components/certificate_provider/certificate_info.h" |
| #include "net/base/hash_value.h" |
| #include "net/cert/asn1_util.h" |
| #include "net/cert/x509_certificate.h" |
| #include "net/cert/x509_util.h" |
| |
| namespace chromeos { |
| namespace certificate_provider { |
| namespace { |
| |
| std::string GetSubjectPublicKeyInfo(const net::X509Certificate& certificate) { |
| base::StringPiece spki_bytes; |
| if (!net::asn1::ExtractSPKIFromDERCert( |
| net::x509_util::CryptoBufferAsStringPiece(certificate.cert_buffer()), |
| &spki_bytes)) { |
| return {}; |
| } |
| return std::string(spki_bytes); |
| } |
| |
| } // namespace |
| |
| ThreadSafeCertificateMap::ThreadSafeCertificateMap() {} |
| |
| ThreadSafeCertificateMap::~ThreadSafeCertificateMap() {} |
| |
| void ThreadSafeCertificateMap::UpdateCertificatesForExtension( |
| const std::string& extension_id, |
| const CertificateInfoList& certificates) { |
| base::AutoLock auto_lock(lock_); |
| RemoveCertificatesProvidedByExtension(extension_id); |
| |
| for (const CertificateInfo& cert_info : certificates) { |
| const net::SHA256HashValue fingerprint = |
| net::X509Certificate::CalculateFingerprint256( |
| cert_info.certificate->cert_buffer()); |
| fingerprint_to_extension_and_cert_[fingerprint][extension_id] = cert_info; |
| |
| const std::string spki = GetSubjectPublicKeyInfo(*cert_info.certificate); |
| spki_to_extension_and_cert_[spki][extension_id] = cert_info; |
| } |
| } |
| |
| std::vector<scoped_refptr<net::X509Certificate>> |
| ThreadSafeCertificateMap::GetCertificates() { |
| base::AutoLock auto_lock(lock_); |
| std::vector<scoped_refptr<net::X509Certificate>> certificates; |
| for (const auto& fingerprint_entry : fingerprint_to_extension_and_cert_) { |
| const ExtensionToCertificateMap* extension_to_certificate_map = |
| &fingerprint_entry.second; |
| if (!extension_to_certificate_map->empty()) { |
| // If there are multiple entries with the same fingerprint, they are the |
| // same certificate as SHA256 should not have collisions. |
| // Since we need each certificate only once, we can return any entry. |
| certificates.push_back( |
| extension_to_certificate_map->begin()->second.certificate); |
| } |
| } |
| return certificates; |
| } |
| |
| bool ThreadSafeCertificateMap::LookUpCertificate( |
| const net::X509Certificate& cert, |
| bool* is_currently_provided, |
| CertificateInfo* info, |
| std::string* extension_id) { |
| *is_currently_provided = false; |
| const net::SHA256HashValue fingerprint = |
| net::X509Certificate::CalculateFingerprint256(cert.cert_buffer()); |
| |
| base::AutoLock auto_lock(lock_); |
| const auto it = fingerprint_to_extension_and_cert_.find(fingerprint); |
| if (it == fingerprint_to_extension_and_cert_.end()) |
| return false; |
| |
| ExtensionToCertificateMap* const map = &it->second; |
| if (!map->empty()) { |
| // If multiple entries are found, it is unspecified which is returned. |
| const auto map_entry = map->begin(); |
| *is_currently_provided = true; |
| *info = map_entry->second; |
| *extension_id = map_entry->first; |
| } |
| return true; |
| } |
| |
| bool ThreadSafeCertificateMap::LookUpCertificateBySpki( |
| const std::string& subject_public_key_info, |
| bool* is_currently_provided, |
| CertificateInfo* info, |
| std::string* extension_id) { |
| *is_currently_provided = false; |
| base::AutoLock auto_lock(lock_); |
| const auto it = spki_to_extension_and_cert_.find(subject_public_key_info); |
| if (it == spki_to_extension_and_cert_.end()) |
| return false; |
| |
| ExtensionToCertificateMap* const map = &it->second; |
| if (!map->empty()) { |
| // If multiple entries are found, it is unspecified which is returned. |
| const auto map_entry = map->begin(); |
| *is_currently_provided = true; |
| *info = map_entry->second; |
| *extension_id = map_entry->first; |
| } |
| return true; |
| } |
| |
| void ThreadSafeCertificateMap::RemoveExtension( |
| const std::string& extension_id) { |
| base::AutoLock auto_lock(lock_); |
| RemoveCertificatesProvidedByExtension(extension_id); |
| } |
| |
| void ThreadSafeCertificateMap::RemoveCertificatesProvidedByExtension( |
| const std::string& extension_id) { |
| for (auto& entry : fingerprint_to_extension_and_cert_) { |
| ExtensionToCertificateMap* map = &entry.second; |
| map->erase(extension_id); |
| } |
| |
| for (auto& entry : spki_to_extension_and_cert_) { |
| ExtensionToCertificateMap* map = &entry.second; |
| map->erase(extension_id); |
| } |
| } |
| |
| } // namespace certificate_provider |
| } // namespace chromeos |