// 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 "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 unexportable_keys {

// Service providing access to `UnexportableSigningKey`s.
//
// The service doesn't give clients direct access to the keys. Instead,
// `UnexportableKeyService` returns a key handle, `UnexportableKeyId`, 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:
//
// 1. Generate a new `UnexportableSigningKey` and obtain its key ID:
//
//  UnexportableKeyService& service = GetUnexportableKeyService();
//  ServiceErrorOr<UnexportableKeyId> 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<UnexportableKeyId> 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 `UnexportableKeyId` 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<UnexportableKeyId>)> callback) = 0;

  // Creates a new signing key from a `wrapped_key` asynchronously and returns
  // an ID of this key.
  // Returned `UnexportableKeyId` 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 `UnexportableKeyId`.
  // Invokes `callback` with a `ServiceError` if `wrapped_key` cannot be
  // imported.
  virtual void FromWrappedSigningKeySlowlyAsync(
      base::span<const uint8_t> wrapped_key,
      BackgroundTaskPriority priority,
      base::OnceCallback<void(ServiceErrorOr<UnexportableKeyId>)> 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. In case of a failure,
  // the task might be retried up to `max_retries` times.
  // 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()`
  // TODO(crbug.com/405408610): remove `max_retries` parameter once all callers
  // adopt the retry logic.
  virtual void SignSlowlyAsync(
      const UnexportableKeyId& key_id,
      base::span<const uint8_t> data,
      BackgroundTaskPriority priority,
      size_t max_retries,
      base::OnceCallback<void(ServiceErrorOr<std::vector<uint8_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(
      UnexportableKeyId 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(
      UnexportableKeyId 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(UnexportableKeyId key_id) const = 0;
};

}  // namespace unexportable_keys

#endif  // COMPONENTS_UNEXPORTABLE_KEYS_UNEXPORTABLE_KEY_SERVICE_H_
