chromium/chrome/browser/chromeos/platform_keys/extension_platform_keys_service.h

// Copyright 2014 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_CHROMEOS_PLATFORM_KEYS_EXTENSION_PLATFORM_KEYS_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_EXTENSION_PLATFORM_KEYS_SERVICE_H_

#include <stdint.h>

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

#include "base/containers/queue.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/ash/platform_keys/key_permissions/key_permissions_service.h"
#include "chromeos/crosapi/mojom/keystore_error.mojom.h"
#include "chromeos/crosapi/mojom/keystore_service.mojom.h"
#include "components/keyed_service/core/keyed_service.h"

namespace content {
class BrowserContext;
class WebContents;
}  // namespace content

namespace net {
class X509Certificate;
typedef std::vector<scoped_refptr<X509Certificate>> CertificateList;
}  // namespace net

namespace chromeos {

class ExtensionPlatformKeysService : public KeyedService {
 public:
  // The SelectDelegate is used to select a single certificate from all
  // certificates matching a request (see SelectClientCertificates). E.g. this
  // can happen by exposing UI to let the user select.
  class SelectDelegate {
   public:
    using CertificateSelectedCallback =
        base::OnceCallback<void(scoped_refptr<net::X509Certificate> selection)>;

    SelectDelegate();
    SelectDelegate(const SelectDelegate&) = delete;
    auto operator=(const SelectDelegate&) = delete;
    virtual ~SelectDelegate();

    // Called on an interactive SelectClientCertificates call with the list of
    // matching certificates, |certs|.
    // The certificate passed to |callback| will be forwarded to the
    // calling extension and the extension will get unlimited sign permission
    // for this cert. By passing null to |callback|, no cert will be selected.
    // Must eventually call |callback| or be destructed. |callback| must not be
    // called after this delegate is destructed.
    // |web_contents| and |context| provide the context in which the
    // certificates were requested and are not null.
    virtual void Select(const std::string& extension_id,
                        const net::CertificateList& certs,
                        CertificateSelectedCallback callback,
                        content::WebContents* web_contents,
                        content::BrowserContext* context) = 0;
  };

  // |browser_context| must not be null and must outlive this object.
  explicit ExtensionPlatformKeysService(
      content::BrowserContext* browser_context);

  ExtensionPlatformKeysService(const ExtensionPlatformKeysService&) = delete;
  auto operator=(const ExtensionPlatformKeysService&) = delete;

  ~ExtensionPlatformKeysService() override;

  // Sets the delegate which will be used for interactive
  // SelectClientCertificates calls.
  void SetSelectDelegate(std::unique_ptr<SelectDelegate> delegate);

  // If the generation was successful, |public_key_spki_der| will contain the
  // DER encoding of the SubjectPublicKeyInfo of the generated key. If it
  // failed, |public_key_spki_der| will be empty.
  using GenerateKeyCallback = base::OnceCallback<void(
      std::vector<uint8_t> public_key_spki_der,
      std::optional<crosapi::mojom::KeystoreError> error)>;

  // Generates an RSA key pair with |modulus_length_bits| and registers the key
  // to allow a single sign operation by the given extension. |token_id|
  // specifies the token to store the key pair on. If |sw_backed| is true, the
  // generated RSA key pair will be software-backed. If the generation was
  // successful, |callback| will be invoked with the resulting public key. If it
  // failed, the resulting public key will be empty. Will only call back during
  // the lifetime of this object.
  void GenerateRSAKey(platform_keys::TokenId token_id,
                      unsigned int modulus_length_bits,
                      bool sw_backed,
                      std::string extension_id,
                      GenerateKeyCallback callback);

  // Generates an EC key pair with |named_curve| and registers the key to allow
  // a single sign operation by the given extension. |token_id| specifies the
  // token to store the key pair on. If the generation was successful,
  // |callback| will be invoked with the resulting public key. If it failed, the
  // resulting public key will be empty. Will only call back during the lifetime
  // of this object.
  void GenerateECKey(platform_keys::TokenId token_id,
                     std::string named_curve,
                     std::string extension_id,
                     GenerateKeyCallback callback);

  // Gets the current profile using the BrowserContext object and returns
  // whether the current profile is a sign in profile with
  // ProfileHelper::IsSigninProfile.
  bool IsUsingSigninProfile();

  // If signing was successful, |signature| will contain the signature. If it
  // failed, |signature| will be empty.
  using SignCallback = base::OnceCallback<void(
      std::vector<uint8_t> signature,
      std::optional<crosapi::mojom::KeystoreError> error)>;

  // Digests |data|, applies PKCS1 padding if specified by |hash_algorithm| and
  // chooses the signature algorithm according to |key_type| and signs the data
  // with the private key matching |public_key_spki_der|. If a |token_id|
  // is provided and the key is not found in that token, the operation aborts.
  // If |token_id| is not provided (nullopt), all tokens available to the caller
  // will be considered while searching for the key.
  // If the extension does not have permissions for signing with this key, the
  // operation aborts. In case of a one time permission (granted after
  // generating the key), this function also removes the permission to prevent
  // future signing attempts. If signing was successful, |callback| will be
  // invoked with the signature. If it failed, the resulting signature will be
  // empty. Will only call back during the lifetime of this object.
  void SignDigest(std::optional<platform_keys::TokenId> token_id,
                  std::vector<uint8_t> data,
                  std::vector<uint8_t> public_key_spki_der,
                  platform_keys::KeyType key_type,
                  platform_keys::HashAlgorithm hash_algorithm,
                  std::string extension_id,
                  SignCallback callback);

  // Applies PKCS1 padding and afterwards signs the data with the private key
  // matching |public_key_spki_der|. |data| is not digested. If a |token_id|
  // is provided and the key is not found in that token, the operation aborts.
  // If |token_id| is not provided (nullopt), all available tokens to the caller
  // will be considered while searching for the key. The size of |data| (number
  // of octets) must be smaller than k - 11, where k is the key size in octets.
  // If the extension does not have permissions for signing with this key, the
  // operation aborts. In case of a one time permission (granted after
  // generating the key), this function also removes the permission to prevent
  // future signing attempts. If signing was successful, |callback| will be
  // invoked with the signature. If it failed, the resulting signature will be
  // empty. Will only call back during the lifetime of this object.
  void SignRSAPKCS1Raw(std::optional<platform_keys::TokenId> token_id,
                       std::vector<uint8_t> data,
                       std::vector<uint8_t> public_key_spki_der,
                       std::string extension_id,
                       SignCallback callback);

  // If the certificate request could be processed successfully, |matches| will
  // contain the list of matching certificates (maybe empty). If an error
  // occurred, |matches| will be null.
  using SelectCertificatesCallback = base::OnceCallback<void(
      std::unique_ptr<net::CertificateList> matches,
      std::optional<crosapi::mojom::KeystoreError> error)>;

  // Returns a list of certificates matching |request|.
  // 1) all certificates that match the request (like being rooted in one of the
  // give CAs) are determined.
  // 2) if |client_certificates| is not null, drops all certificates that are
  // not elements of |client_certificates|,
  // 3) if |interactive| is true, the currently set SelectDelegate is used to
  // select a single certificate from these matches
  // which will the extension will also be granted access to.
  // 4) only certificates, that the extension has unlimited sign permission for,
  // will be returned.
  // If selection was successful, |callback| will be invoked with these
  // certificates. If it failed, the resulting certificate list will be empty
  // and an error status will be returned. Will only call back during the
  // lifetime of this object. |web_contents| must not be null.
  void SelectClientCertificates(
      const platform_keys::ClientCertificateRequest& request,
      std::unique_ptr<net::CertificateList> client_certificates,
      bool interactive,
      std::string extension_id,
      SelectCertificatesCallback callback,
      content::WebContents* web_contents);

 private:
  class GenerateRSAKeyTask;
  class GenerateECKeyTask;
  class GenerateKeyTask;
  class SelectTask;
  class SignTask;
  class Task;

  // Starts |task| eventually. To ensure that at most one |Task| is running at a
  // time, it queues |task| for later execution if necessary.
  void StartOrQueueTask(std::unique_ptr<Task> task);

  // Must be called after |task| is done. |task| will be invalid after this
  // call. This must not be called for any but the task that ran last. If any
  // other tasks are queued (see StartOrQueueTask()), it will start the next
  // one.
  void TaskFinished(Task* task);

  const raw_ptr<content::BrowserContext> browser_context_ = nullptr;
  const raw_ptr<crosapi::mojom::KeystoreService> keystore_service_ = nullptr;
  std::unique_ptr<SelectDelegate> select_delegate_;
  base::queue<std::unique_ptr<Task>> tasks_;
  base::WeakPtrFactory<ExtensionPlatformKeysService> weak_factory_{this};
};

}  // namespace chromeos

#endif  // CHROME_BROWSER_CHROMEOS_PLATFORM_KEYS_EXTENSION_PLATFORM_KEYS_SERVICE_H_