chromium/chrome/browser/ash/platform_keys/platform_keys_service.h

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_BROWSER_ASH_PLATFORM_KEYS_PLATFORM_KEYS_SERVICE_H_
#define CHROME_BROWSER_ASH_PLATFORM_KEYS_PLATFORM_KEYS_SERVICE_H_

#include <stddef.h>

#include <memory>
#include <optional>
#include <string>
#include <vector>

#include "base/functional/callback.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "chrome/browser/chromeos/platform_keys/platform_keys.h"
#include "components/keyed_service/core/keyed_service.h"
#include "net/cert/x509_certificate.h"

namespace net {
class NSSCertDatabase;
class ClientCertStore;
}  // namespace net

namespace ash::platform_keys {

// This callback is used for both encrypting and decrypting.
using EncryptDecryptCallback =
    base::OnceCallback<void(std::vector<uint8_t> output_data,
                            chromeos::platform_keys::Status status)>;

using GenerateKeyCallback =
    base::OnceCallback<void(std::vector<uint8_t> key_identifier,
                            chromeos::platform_keys::Status status)>;

using SignCallback =
    base::OnceCallback<void(std::vector<uint8_t> signature,
                            chromeos::platform_keys::Status status)>;

// If the certificate request could be processed successfully, |matches| will
// contain the list of matching certificates (which may be empty). If an error
// occurred, |matches| will be null.
using SelectCertificatesCallback =
    base::OnceCallback<void(std::unique_ptr<net::CertificateList> matches,
                            chromeos::platform_keys::Status status)>;

// If the list of certificates could be successfully retrieved, |certs| will
// contain the list of available certificates (maybe empty). If an error
// occurred, |certs| will be empty.
using GetCertificatesCallback =
    base::OnceCallback<void(std::unique_ptr<net::CertificateList> certs,
                            chromeos::platform_keys::Status status)>;

// If the list of key pairs could be successfully retrieved,
// |public_key_spki_der_list| will contain the list of available key pairs (may
// be empty if no key pairs exist). Each available key pair is represented as a
// DER-encoded SubjectPublicKeyInfo. If an error occurred,
// |public_key_spki_der_list| will be empty.
using GetAllKeysCallback = base::OnceCallback<void(
    std::vector<std::vector<uint8_t>> public_key_spki_der_list,
    chromeos::platform_keys::Status status)>;

using ImportCertificateCallback =
    base::OnceCallback<void(chromeos::platform_keys::Status status)>;

using RemoveCertificateCallback =
    base::OnceCallback<void(chromeos::platform_keys::Status status)>;

using RemoveKeyCallback =
    base::OnceCallback<void(chromeos::platform_keys::Status status)>;

// If the list of available tokens could be successfully retrieved, |token_ids|
// will contain the token ids. If an error occurs, |token_ids| will be nullptr.
using GetTokensCallback = base::OnceCallback<void(
    std::vector<chromeos::platform_keys::TokenId> token_ids,
    chromeos::platform_keys::Status status)>;

// If token ids have been successfully retrieved, two cases are possible then:
// If |token_ids| is not empty, |token_ids| has been filled with the identifiers
// of the tokens the private key was found on and the user has access to.
// If |token_ids| is empty, the private key has not been found on any token the
// user has access to. Note that this is also the case if the key exists on the
// system token, but the current user does not have access to the system token.
// If an error occurred during processing, |token_ids| will be empty.
using GetKeyLocationsCallback = base::OnceCallback<void(
    const std::vector<chromeos::platform_keys::TokenId>& token_ids,
    chromeos::platform_keys::Status status)>;

using SetAttributeForKeyCallback =
    base::OnceCallback<void(chromeos::platform_keys::Status status)>;

// If the attribute value has been successfully retrieved, |attribute_value|
// will contain the result. If an error occurs, |attribute_value| will be empty.
using GetAttributeForKeyCallback =
    base::OnceCallback<void(std::optional<std::vector<uint8_t>> attribute_value,
                            chromeos::platform_keys::Status status)>;

// If the availability of the key on the provided token has been successfully
// determined, |on_token| will contain the result. If an error occurs,
// |on_token| will be empty and an error |status| will be returned.
using IsKeyOnTokenCallback =
    base::OnceCallback<void(std::optional<bool> on_token,
                            chromeos::platform_keys::Status status)>;

// An observer that gets notified when the PlatformKeysService is being shut
// down.
class PlatformKeysServiceObserver : public base::CheckedObserver {
 public:
  // Called when the PlatformKeysService is being shut down.
  // It may not be used after this call - any usage except for removing the
  // observer will DCHECK.
  virtual void OnPlatformKeysServiceShutDown() = 0;
};

// Functions of this class shouldn't be called directly from the context of
// an extension. Instead use ExtensionPlatformKeysService which enforces
// restrictions upon extensions.
// All public methods of this class should be called on the UI thread and all of
// the callbacks will be called with the result on the UI thread as well.
// When the underlying key store is not available anymore, a PlatformKeysService
// is shut down. Any function called after that will fail with an error.
// For a Profile-specific PlatformKeysService, this will be when the Profile is
// being destroyed.
// For a device-wide PlatformKeysService, this will be at some point during
// chrome shut down.
// Use AddObserver to get a notification when the service shuts down.
class PlatformKeysService : public KeyedService {
 public:
  PlatformKeysService() = default;
  PlatformKeysService(const PlatformKeysService&) = delete;
  PlatformKeysService& operator=(const PlatformKeysService&) = delete;
  ~PlatformKeysService() override = default;

  // Adds |observer| which will be notified when this service is being shut
  // down.
  virtual void AddObserver(PlatformKeysServiceObserver* observer) = 0;
  // Removes a previously added |observer|.
  virtual void RemoveObserver(PlatformKeysServiceObserver* observer) = 0;

  // Generates a symmetric key with a given id.
  virtual void GenerateSymKey(chromeos::platform_keys::TokenId token_id,
                              std::vector<uint8_t> key_id,
                              int key_size,
                              chromeos::platform_keys::SymKeyType key_type,
                              GenerateKeyCallback callback) = 0;

  // Generates a RSA key pair with |modulus_length_bits|. |token_id| specifies
  // the token to store the key pair on. |callback| will be invoked with the
  // resulting public key or an error status.
  virtual void GenerateRSAKey(chromeos::platform_keys::TokenId token_id,
                              unsigned int modulus_length_bits,
                              bool sw_backed,
                              GenerateKeyCallback callback) = 0;

  // Generates a EC key pair with |named_curve|. |token_id| specifies the token
  // to store the key pair on. |callback| will be invoked with the resulting
  // public key or an error status.
  virtual void GenerateECKey(chromeos::platform_keys::TokenId token_id,
                             std::string named_curve,
                             GenerateKeyCallback callback) = 0;

  // Decrypts the |encrypted_data| buffer using a symmetric key. Currently only
  // AES-CBC |decrypt_algorithm| is supported and it requires a 16-byte
  // initialization vector |init_vector|.
  virtual void DecryptAES(chromeos::platform_keys::TokenId token_id,
                          std::vector<uint8_t> encrypted_data,
                          std::vector<uint8_t> key_id,
                          std::string decrypt_algorithm,
                          std::vector<uint8_t> init_vector,
                          EncryptDecryptCallback callback) = 0;

  // Encrypts the |data| buffer using a symmetric key. Currently only
  // AES-CBC |encrypt_algorithm| is supported and it requires a 16-byte
  // initialization vector |init_vector|.
  virtual void EncryptAES(chromeos::platform_keys::TokenId token_id,
                          std::vector<uint8_t> data,
                          std::vector<uint8_t> key_id,
                          std::string encrypt_algorithm,
                          std::vector<uint8_t> init_vector,
                          EncryptDecryptCallback callback) = 0;

  // Digests |data|, applies PKCS1 padding and afterwards signs the data with
  // the private key matching |public_key_spki_der|. If the key is not found in
  // that |token_id| (or in none of the available tokens if |token_id| is not
  // specified), the operation aborts. |callback| will be invoked with the
  // signature or an error status.
  virtual void SignRsaPkcs1(
      std::optional<chromeos::platform_keys::TokenId> token_id,
      std::vector<uint8_t> data,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::HashAlgorithm hash_algorithm,
      SignCallback callback) = 0;

  // Applies PKCS1 padding and afterwards signs the data with the private key
  // matching |public_key_spki_der|. |data| is not digested, PKCS1 DigestInfo is
  // not prepended. If the key is not found in that |token_id| (or in none of
  // the available tokens if |token_id| is not specified), the operation aborts.
  // The size of |data| (number of octets) must be smaller than k - 11, where k
  // is the key size in octets. |callback| will be invoked with the signature or
  // an error status.
  virtual void SignRSAPKCS1Raw(
      std::optional<chromeos::platform_keys::TokenId> token_id,
      std::vector<uint8_t> data,
      std::vector<uint8_t> public_key_spki_der,
      SignCallback callback) = 0;

  // Digests |data| and afterwards signs the data with the private key matching
  // |public_key_spki_der|. If the key is not found in that |token_id| (or in
  // none of the available tokens if |token_id| is not specified), the operation
  // aborts. |callback| will be invoked with the ECDSA signature or an error
  // status.
  virtual void SignEcdsa(
      std::optional<chromeos::platform_keys::TokenId> token_id,
      std::vector<uint8_t> data,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::HashAlgorithm hash_algorithm,
      SignCallback callback) = 0;

  // Signs the data with the symmetric key matching |key_id| using SHA-256 HMAC
  // algorithm. |callback| will be invoked with the signature or an error
  // status.
  virtual void SignWithSymKey(
      std::optional<chromeos::platform_keys::TokenId> token_id,
      std::vector<uint8_t> data,
      std::vector<uint8_t> key_id,
      SignCallback callback) = 0;

  // Returns the list of all certificates that were issued by one of the
  // |certificate_authorities|. If |certificate_authorities| is empty, all
  // certificates will be returned. |callback| will be invoked with the matches
  // or an error status.
  virtual void SelectClientCertificates(
      std::vector<std::string> certificate_authorities,
      const SelectCertificatesCallback callback) = 0;

  // Returns the list of all certificates with stored private key available from
  // the given token. Only certificates from the specified |token_id| are
  // listed. |callback| will be invoked with the list of available certificates
  // or an error status.
  virtual void GetCertificates(chromeos::platform_keys::TokenId token_id,
                               const GetCertificatesCallback callback) = 0;

  // Returns the list of all public keys available from the given |token_id|
  // that have corresponding private keys on the same token as a list of
  // DER-encoded SubjectPublicKeyInfo strings. |callback| will be invoked on the
  // UI thread with the list of available public keys, possibly with an error
  // status.
  virtual void GetAllKeys(chromeos::platform_keys::TokenId token_id,
                          GetAllKeysCallback callback) = 0;

  // Imports |certificate| to the given token if the certified key is already
  // stored in this token. Any intermediate of |certificate| will be ignored.
  // |token_id| specifies the token to store the certificate on. The private key
  // must be stored on the same token. |callback| will be invoked when the
  // import is finished, possibly with an error status.
  virtual void ImportCertificate(
      chromeos::platform_keys::TokenId token_id,
      const scoped_refptr<net::X509Certificate>& certificate,
      ImportCertificateCallback callback) = 0;

  // Removes |certificate| from the given token. Any intermediate of
  // |certificate| will be ignored. |token_id| specifies the token to remove the
  // certificate from. |callback| will be invoked when the removal is finished,
  // possibly with an error status.
  virtual void RemoveCertificate(
      chromeos::platform_keys::TokenId token_id,
      const scoped_refptr<net::X509Certificate>& certificate,
      RemoveCertificateCallback callback) = 0;

  // Removes the key pair if no matching certificates exist. Only keys in the
  // given |token_id| are considered. |callback| will be invoked on the UI
  // thread when the removal is finished, possibly with an error status.
  virtual void RemoveKey(chromeos::platform_keys::TokenId token_id,
                         std::vector<uint8_t> public_key_spki_der,
                         RemoveKeyCallback callback) = 0;

  // Removes the symmetric key with CKA_ID equal to |key_id|. Only keys in the
  // given |token_id| are considered. |callback| will be invoked on the UI
  // thread when the removal is finished, possibly with an error status.
  virtual void RemoveSymKey(chromeos::platform_keys::TokenId token_id,
                            std::vector<uint8_t> key_id,
                            RemoveKeyCallback callback) = 0;

  // Gets the list of available tokens. |callback| will be invoked when the list
  // of available tokens is determined, possibly with an error status.
  // Calls |callback| on the UI thread.
  virtual void GetTokens(GetTokensCallback callback) = 0;

  // Determines the token(s) on which the private key corresponding to
  // |public_key_spki_der| is stored. |callback| will be invoked when the token
  // ids are determined, possibly with an error status. Calls |callback| on the
  // UI thread.
  virtual void GetKeyLocations(std::vector<uint8_t> public_key_spki_der,
                               GetKeyLocationsCallback callback) = 0;

  // Sets |attribute_type| for the private key corresponding to
  // |public_key_spki_der| to |attribute_value| only if the key is in
  // |token_id|. |callback| will be invoked on the UI thread when setting the
  // attribute is done, possibly with an error status.
  virtual void SetAttributeForKey(
      chromeos::platform_keys::TokenId token_id,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::KeyAttributeType attribute_type,
      std::vector<uint8_t> attribute_value,
      SetAttributeForKeyCallback callback) = 0;

  // Gets |attribute_type| for the private key corresponding to
  // |public_key_spki_der| only if the key is in |token_id|. |callback| will be
  // invoked on the UI thread when getting the attribute is done, possibly with
  // an error message. In case no value was set for |attribute_type|, an error
  // |status| and std::nullopt |attribute_value| will be returned.
  virtual void GetAttributeForKey(
      chromeos::platform_keys::TokenId token_id,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::KeyAttributeType attribute_type,
      GetAttributeForKeyCallback callback) = 0;

  // Determines if |public_key_spki_der| resides on |token_id|. |callback| will
  // be invoked on the UI thread with the result. If an error occurred, an error
  // |status| will be returned and std::nullopt |on_token| will be returned.
  virtual void IsKeyOnToken(chromeos::platform_keys::TokenId token_id,
                            std::vector<uint8_t> public_key_spki_der,
                            IsKeyOnTokenCallback callback) = 0;

  // Softoken NSS PKCS11 module (used for testing) allows only predefined key
  // attributes to be set and retrieved. Chaps supports setting and retrieving
  // custom attributes.
  // If |map_to_softoken_attrs_for_testing| is true, the service will use
  // fake KeyAttribute mappings predefined in softoken module for testing.
  // Otherwise, the real mappings to constants in
  // third_party/cros_system_api/constants/pkcs11_custom_attributes.h will be
  // used.
  virtual void SetMapToSoftokenAttrsForTesting(
      const bool map_to_softoken_attrs_for_testing) = 0;
};

class PlatformKeysServiceImplDelegate {
 public:
  PlatformKeysServiceImplDelegate();
  virtual ~PlatformKeysServiceImplDelegate();
  PlatformKeysServiceImplDelegate(
      const PlatformKeysServiceImplDelegate& other) = delete;
  PlatformKeysServiceImplDelegate& operator=(
      const PlatformKeysServiceImplDelegate& other) = delete;

  // |on_shutdown_callback| will be called when the underlying key/certificate
  // store is shut down. It is an error to call this twice, or after the
  // delegate has been shut down.
  void SetOnShutdownCallback(base::OnceClosure on_shutdown_callback);

  // This callback is invoked by GetNSSCertDatabase.
  using OnGotNSSCertDatabase = base::OnceCallback<void(net::NSSCertDatabase*)>;

  // Retrieves the NSSCertDatabase that should be used for certificate
  // operations. |callback| will be called on the thread that GetNSSCertDatabase
  // has been called on.
  virtual void GetNSSCertDatabase(OnGotNSSCertDatabase callback) = 0;

  // Creates a ClientCertStore that should be used to list / operate on client
  // certificates.
  virtual std::unique_ptr<net::ClientCertStore> CreateClientCertStore() = 0;

  bool IsShutDown() const;

 protected:
  void ShutDown();

 private:
  // A callback that should be called when the underlying key/certificate store
  // is shut down.
  base::OnceClosure on_shutdown_callback_;

  // True if the underlying key/certificate store has already been shut down.
  bool shut_down_ = false;
};

class PlatformKeysServiceImpl final : public PlatformKeysService {
 public:
  explicit PlatformKeysServiceImpl(
      std::unique_ptr<PlatformKeysServiceImplDelegate> delegate);
  ~PlatformKeysServiceImpl() override;

  // PlatformKeysService
  void AddObserver(PlatformKeysServiceObserver* observer) override;
  void RemoveObserver(PlatformKeysServiceObserver* observer) override;
  void GenerateSymKey(chromeos::platform_keys::TokenId token_id,
                      std::vector<uint8_t> key_id,
                      int key_size,
                      chromeos::platform_keys::SymKeyType key_type,
                      GenerateKeyCallback callback) override;
  void GenerateRSAKey(chromeos::platform_keys::TokenId token_id,
                      unsigned int modulus_length_bits,
                      bool sw_backed,
                      GenerateKeyCallback callback) override;
  void GenerateECKey(chromeos::platform_keys::TokenId token_id,
                     std::string named_curve,
                     GenerateKeyCallback callback) override;
  void EncryptAES(chromeos::platform_keys::TokenId token_id,
                  std::vector<uint8_t> data,
                  std::vector<uint8_t> key_id,
                  std::string encrypt_algorithm,
                  std::vector<uint8_t> init_vector,
                  EncryptDecryptCallback callback) override;
  void DecryptAES(chromeos::platform_keys::TokenId token_id,
                  std::vector<uint8_t> encrypted_data,
                  std::vector<uint8_t> key_id,
                  std::string decrypt_algorithm,
                  std::vector<uint8_t> init_vector,
                  EncryptDecryptCallback callback) override;
  void SignRsaPkcs1(std::optional<chromeos::platform_keys::TokenId> token_id,
                    std::vector<uint8_t> data,
                    std::vector<uint8_t> public_key_spki_der,
                    chromeos::platform_keys::HashAlgorithm hash_algorithm,
                    SignCallback callback) override;
  void SignRSAPKCS1Raw(std::optional<chromeos::platform_keys::TokenId> token_id,
                       std::vector<uint8_t> data,
                       std::vector<uint8_t> public_key_spki_der,
                       SignCallback callback) override;
  void SignEcdsa(std::optional<chromeos::platform_keys::TokenId> token_id,
                 std::vector<uint8_t> data,
                 std::vector<uint8_t> public_key_spki_der,
                 chromeos::platform_keys::HashAlgorithm hash_algorithm,
                 SignCallback callback) override;
  void SignWithSymKey(std::optional<chromeos::platform_keys::TokenId> token_id,
                      std::vector<uint8_t> data,
                      std::vector<uint8_t> key_id,
                      SignCallback callback) override;
  void SelectClientCertificates(
      std::vector<std::string> certificate_authorities,
      SelectCertificatesCallback callback) override;
  void GetCertificates(chromeos::platform_keys::TokenId token_id,
                       GetCertificatesCallback callback) override;
  void GetAllKeys(chromeos::platform_keys::TokenId token_id,
                  GetAllKeysCallback callback) override;
  void ImportCertificate(chromeos::platform_keys::TokenId token_id,
                         const scoped_refptr<net::X509Certificate>& certificate,
                         ImportCertificateCallback callback) override;
  void RemoveCertificate(chromeos::platform_keys::TokenId token_id,
                         const scoped_refptr<net::X509Certificate>& certificate,
                         RemoveCertificateCallback callback) override;
  void RemoveKey(chromeos::platform_keys::TokenId token_id,
                 std::vector<uint8_t> public_key_spki_der,
                 RemoveKeyCallback callback) override;
  void RemoveSymKey(chromeos::platform_keys::TokenId token_id,
                    std::vector<uint8_t> key_id,
                    RemoveKeyCallback callback) override;
  void GetTokens(GetTokensCallback callback) override;
  void GetKeyLocations(std::vector<uint8_t> public_key_spki_der,
                       const GetKeyLocationsCallback callback) override;
  void SetAttributeForKey(
      chromeos::platform_keys::TokenId token_id,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::KeyAttributeType attribute_type,
      std::vector<uint8_t> attribute_value,
      SetAttributeForKeyCallback callback) override;
  void GetAttributeForKey(
      chromeos::platform_keys::TokenId token_id,
      std::vector<uint8_t> public_key_spki_der,
      chromeos::platform_keys::KeyAttributeType attribute_type,
      GetAttributeForKeyCallback callback) override;
  void IsKeyOnToken(chromeos::platform_keys::TokenId token_id,
                    std::vector<uint8_t> public_key_spki_der,
                    IsKeyOnTokenCallback callback) override;
  void SetMapToSoftokenAttrsForTesting(
      bool map_to_softoken_attrs_for_testing) override;
  bool IsSetMapToSoftokenAttrsForTesting();

 private:
  void EncryptDecryptAES(chromeos::platform_keys::TokenId token_id,
                         std::vector<uint8_t>& key_id,
                         std::vector<uint8_t>& input_data,
                         std::string& encrypt_algorithm,
                         std::vector<uint8_t>& init_vector,
                         EncryptDecryptCallback callback,
                         chromeos::platform_keys::OperationType operation_type);
  void OnDelegateShutDown();

  std::unique_ptr<PlatformKeysServiceImplDelegate> delegate_;

  // List of observers that will be notified when the service is shut down.
  base::ObserverList<PlatformKeysServiceObserver> observers_;
  bool map_to_softoken_attrs_for_testing_ = false;

  base::WeakPtrFactory<PlatformKeysServiceImpl> weak_factory_{this};
};

void RunCallBackIfCallableElseRunCleanUp(base::OnceCallback<void()> callback,
                                         base::OnceCallback<void()> cleanup);
}  // namespace ash::platform_keys

#endif  // CHROME_BROWSER_ASH_PLATFORM_KEYS_PLATFORM_KEYS_SERVICE_H_