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

#ifndef COMPONENTS_UNEXPORTABLE_KEYS_UNEXPORTABLE_KEY_SERVICE_H_
#define COMPONENTS_UNEXPORTABLE_KEYS_UNEXPORTABLE_KEY_SERVICE_H_

#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/functional/callback_forward.h"
#include "base/time/time.h"
#include "components/unexportable_keys/background_task_priority.h"
#include "components/unexportable_keys/service_error.h"
#include "components/unexportable_keys/unexportable_key_id.h"
#include "crypto/signature_verifier.h"

namespace crypto {
struct AttestationStatement;
}  // namespace crypto

namespace unexportable_keys {

// Service providing access to `UnexportableSigningKey`s and
// `UnexportableAttestationKey`s.
//
// The service doesn't give clients direct access to the keys. Instead,
// `UnexportableKeyService` returns a key handle, `UnexportableSigningKeyId` (or
// its subclass `UnexportableAttestationKeyId`), that can be passed back to the
// service to perform operations with the key.
//
// To use the same key across several sessions, a client should perform the
// following steps (shown for signing keys, but similar for attestation keys):
//
// 1. Generate a new `UnexportableSigningKey` and obtain its key ID:
//
//  UnexportableKeyService& service = GetUnexportableKeyService();
//  ServiceErrorOr<UnexportableSigningKeyId> key_id;
//  service.GenerateSigningKeySlowlyAsync(
//      kAlgorithm, kPriority, [&key_id](auto result) { key_id = result; });
//
// 2. Get a wrapped key for this key and save it to disk:
//
//  std::vector<uint8_t> wrapped_key = service.GetWrappedKey(*key_id);
//  SaveToDisk(wrapped_key);
//
// 3. After the process restart, restore the same `UnexportableSigningKey` from
//    the wrapped key:
//
//  UnexportableKeyService& service = GetUnexportableKeyService();
//  ServiceErrorOr<UnexportableSigningKeyId> key_id;
//  std::vector<uint8_t> wrapped_key = ReadFromDisk();
//  service.FromWrappedSigningKeySlowlyAsync(
//    wrapped_key, kPriority, [&key_id](auto result) { key_id = result; });
//
// 4. Use obtained key ID to sign data:
//
//  service.SignSlowlyAsync(*key_id, kData, kPriority, std::move(callback));
class COMPONENT_EXPORT(UNEXPORTABLE_KEYS) UnexportableKeyService {
 public:
  UnexportableKeyService() = default;

  UnexportableKeyService(const UnexportableKeyService&) = delete;
  UnexportableKeyService& operator=(const UnexportableKeyService&) = delete;

  virtual ~UnexportableKeyService() = default;

  // Generates a new signing key asynchronously and returns an ID of this key.
  // Returned `UnexportableSigningKeyId` can be used later to perform key
  // operations on this `UnexportableKeyService`.
  // The first supported value of `acceptable_algorithms` determines the type of
  // the key.
  // Invokes `callback` with a `ServiceError` if no supported hardware exists,
  // if no value in `acceptable_algorithms` is supported, or if there was an
  // error creating the key.
  virtual void GenerateSigningKeySlowlyAsync(
      base::span<const crypto::SignatureVerifier::SignatureAlgorithm>
          acceptable_algorithms,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<UnexportableSigningKeyId>)>
          callback) = 0;

  // Creates a new signing key from a `wrapped_key` asynchronously and returns
  // an ID of this key.
  // Returned `UnexportableSigningKeyId` can be used later to perform key
  // operations on this `UnexportableKeyService`.
  // `wrapped_key` can be read from disk but must have initially resulted from
  // calling `GetWrappedKey()` on a previous instance of
  // `UnexportableSigningKeyId`. Invokes `callback` with a `ServiceError` if
  // `wrapped_key` cannot be imported.
  //
  // This method is also a supported way of transferring a key between
  // `UnexportableKeyService` instances. A key's lifetime is controlled by the
  // source `UnexportableKeyService` instance until this method completes in the
  // destination service.
  virtual void FromWrappedSigningKeySlowlyAsync(
      base::span<const uint8_t> wrapped_key,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<UnexportableSigningKeyId>)>
          callback) = 0;

  // Generates a new attestation key asynchronously and returns an ID of this
  // key. Returned `UnexportableAttestationKeyId` can be used later to perform
  // key operations on this `UnexportableKeyService`. The first supported value
  // of `acceptable_algorithms` determines the type of the key. Invokes
  // `callback` with a `ServiceError` if no supported hardware exists, if no
  // value in `acceptable_algorithms` is supported, or if there was an error
  // creating the key.
  //
  // TODO(crbug.com/501306852): Attestation keys are not really useful yet, but
  // a CertifySlowly() API involving them will be added shortly.
  virtual void GenerateAttestationKeySlowlyAsync(
      base::span<const crypto::SignatureVerifier::SignatureAlgorithm>
          acceptable_algorithms,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<UnexportableAttestationKeyId>)>
          callback) = 0;

  // Creates a new attestation key from a `wrapped_key` asynchronously and
  // returns an ID of this key. Returned `UnexportableAttestationKeyId` can be
  // used later to perform key operations on this `UnexportableKeyService`.
  // `wrapped_key` can be read from disk but must have initially resulted from
  // calling `GetWrappedKey()` on a previous instance of
  // `UnexportableAttestationKeyId`. Invokes `callback` with a `ServiceError` if
  // `wrapped_key` cannot be imported.
  //
  // TODO(crbug.com/501306852): Attestation keys are not really useful yet, but
  // a CertifySlowly() API involving them will be added shortly.
  virtual void FromWrappedAttestationKeySlowlyAsync(
      base::span<const uint8_t> wrapped_key,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<UnexportableAttestationKeyId>)>
          callback) = 0;

  // Returns all signing keys currently stored by the OS that are have been
  // managed by this service.
  //
  // Invokes `callback` with a `ServiceError` if an error occurs during
  // retrieval, or the list of stored keys otherwise.
  //
  // Note: The intended use case for this method is garbage collecting obsolete
  // keys. That is, clients are expected to call this method, and then compare
  // the returned keys to the ones present in the client's storage. Keys that
  // are returned but no longer recognized by the client should be passed to
  // `DeleteKeySlowlyAsync()` soon after.
  //
  // Note: This method is meaningless on platforms that do not support storing
  // keys in the OS, and will return an empty list on those platforms. Here
  // clients are not expected to perform garbage collection.
  //
  // Example usage:
  //
  // void OnKeys(
  //     ServiceErrorOr<std::vector<UnexportableSigningKeyId>> maybe_keys) {
  //   if (!maybe_keys.has_value()) {
  //     // Handle error.
  //     return;
  //   }
  //
  //   UnexportableKeyService& service = GetUnexportableKeyService();
  //   for (const auto& key : *maybe_keys) {
  //     if (!MyCodeRecognizesThisKey(key)) {
  //       // Perform garbage collection.
  //       service.DeleteKeySlowlyAsync(key, ...);
  //     }
  //   }
  // }
  //
  //
  // UnexportableKeyService& service = GetUnexportableKeyService();
  // service.GetAllKeysForGarbageCollectionSlowlyAsync(
  //     kPriority,
  //     base::BindOnce(OnKeys));
  virtual void GetAllKeysForGarbageCollectionSlowlyAsync(
      BackgroundTaskPriority priority,
      base::OnceCallback<void(
          ServiceErrorOr<std::vector<UnexportableSigningKeyId>>)> callback) = 0;

  // Schedules a new asynchronous signing task.
  // Might return a cached result if a task with the same combination of
  // `signing_key` and `data` has been completed recently.
  // Invokes `callback` with a signature of `data`, or a `ServiceError` if
  // `key_id` is not found or an error occurs during signing.
  // `key_id` must have resulted from calling `GenerateSigningKeySlowlyAsync()`
  // or `FromWrappedSigningKeySlowlyAsync()`.
  virtual void SignSlowlyAsync(
      UnexportableSigningKeyId key_id,
      base::span<const uint8_t> data,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<std::vector<uint8_t>>)>
          callback) = 0;

  // Asynchronously certifies `signing_key_id` with `attestation_key_id` and
  // `challenge`.
  // Invokes `callback` with either:
  // - attestation statement if the certification was successful, or
  // - `ServiceError` if the keys are not found or if there was a certification
  //   error.
  virtual void CertifySlowlyAsync(
      UnexportableAttestationKeyId attestation_key_id,
      UnexportableSigningKeyId signing_key_id,
      base::span<const uint8_t> challenge,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<crypto::AttestationStatement>)>
          callback) = 0;

  // Deletes a collection of keys.
  //
  // Invokes `callback` with the number of deleted keys or a `ServiceError` if
  // an error occurs during deletion.
  //
  // The entries in `key_ids` must have resulted from calling
  // `GenerateSigningKeySlowlyAsync()` or `FromWrappedSigningKeySlowlyAsync()`.
  //
  // Assuming the entries in `key_ids` were found, they are invalidated
  // immediately and should not be used again.
  //
  // Note: On platforms like macOS this will delete the keys from the OS, and
  // thus future calls to `FromWrappedSigningKeySlowlyAsync()` with the same
  // wrapped keys will fail.
  virtual void DeleteKeysSlowlyAsync(
      base::span<const UnexportableSigningKeyId> key_ids,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<size_t>)> callback) = 0;

  // Deletes all keys.
  //
  // This will remove all keys from the in-memory cache synchronously, cancel
  // **all** outstanding key operation requests, and schedule an asynchronous
  // deletion task. This will invoke `callback` with a `ServiceError` if an
  // error occurs during deletion and the number of deleted keys otherwise.
  // Furthermore, the background task is scheduled with the highest priority,
  // ensuring that it is completed as soon as possible.
  //
  // Note: On platforms like macOS this will delete all keys from the OS, and
  // thus future calls to `FromWrappedSigningKeySlowlyAsync()` will fail.
  virtual void DeleteAllKeysSlowlyAsync(
      base::OnceCallback<void(ServiceErrorOr<size_t>)> callback) = 0;

  // Returns an SPKI that contains the public key of a key that `key_id` refers
  // to.
  // Returns a `ServiceError` if `key_id` is not found.
  // `key_id` must have resulted from calling `GenerateSigningKeySlowlyAsync()`
  // or `FromWrappedSigningKeySlowlyAsync()`
  virtual ServiceErrorOr<std::vector<uint8_t>> GetSubjectPublicKeyInfo(
      UnexportableSigningKeyId key_id) const = 0;

  // Returns the encrypted private key of a key that `key_id` refers to. It is
  // encrypted to a key that is kept in hardware and the unencrypted private key
  // never exists in the CPU's memory.
  // Returns a `ServiceError` if `key_id` is not found.
  // `key_id` must have resulted from calling `GenerateSigningKeySlowlyAsync()`
  // or `FromWrappedSigningKeySlowlyAsync()`
  virtual ServiceErrorOr<std::vector<uint8_t>> GetWrappedKey(
      UnexportableSigningKeyId key_id) const = 0;

  // Returns the algorithm of a key that `key_id` refers to.
  // Returns a `ServiceError` if `key_id` is not found.
  // `key_id` must have resulted from calling `GenerateSigningKeySlowlyAsync()`
  // or `FromWrappedSigningKeySlowlyAsync()`
  virtual ServiceErrorOr<crypto::SignatureVerifier::SignatureAlgorithm>
  GetAlgorithm(UnexportableSigningKeyId key_id) const = 0;

  // Returns the tag of a key that `key_id` refers to.
  // Returns a `ServiceError` if `key_id` is not found, or if the key does not
  // support stateful operations.
  virtual ServiceErrorOr<std::string> GetKeyTag(
      UnexportableSigningKeyId key_id) const = 0;

  // Returns the time a key that `key_id` refers to was created.
  // Returns a `ServiceError` if `key_id` is not found, or if the key does not
  // support stateful operations.
  virtual ServiceErrorOr<base::Time> GetCreationTime(
      UnexportableSigningKeyId key_id) const = 0;
};

}  // namespace unexportable_keys

#endif  // COMPONENTS_UNEXPORTABLE_KEYS_UNEXPORTABLE_KEY_SERVICE_H_
