chromium/chrome/browser/certificate_provider/thread_safe_certificate_map.cc

// 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 <string_view>
#include <vector>

#include "base/containers/flat_map.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) {
  std::string_view 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