chromium/chromeos/components/kcer/kcer.h

// 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 CHROMEOS_COMPONENTS_KCER_KCER_H_
#define CHROMEOS_COMPONENTS_KCER_KCER_H_

#include <stdint.h>

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

#include "base/callback_list.h"
#include "base/component_export.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/task_runner.h"
#include "base/types/expected.h"
#include "base/types/strong_alias.h"
#include "chromeos/components/kcer/key_permissions.pb.h"
#include "net/cert/x509_certificate.h"
#include "third_party/boringssl/src/include/openssl/ssl.h"

namespace kcer {

// Strong alias to Kcer related types to prevent incorrect cross-assignment.
using Pkcs11Id = base::StrongAlias<class TypeTagPkcs11Id, std::vector<uint8_t>>;
using Signature =
    base::StrongAlias<class TypeTagSignature, std::vector<uint8_t>>;
using PublicKeySpki =
    base::StrongAlias<class TypeTagPublicKeySpki, std::vector<uint8_t>>;
using CertDer = base::StrongAlias<class TypeTagCertDer, std::vector<uint8_t>>;
using Pkcs8PrivateKeyInfoDer =
    base::StrongAlias<class TypeTagPkcs8PrivateKeyInfoDer,
                      std::vector<uint8_t>>;
using Pkcs12Blob =
    base::StrongAlias<class TypeTagPkcs12Blob, std::vector<uint8_t>>;
using DataToSign =
    base::StrongAlias<class TypeTagDataToSign, std::vector<uint8_t>>;
// Digest of the DataToSign. If the signing algorithm expects a prefix (such as
// DigestInfo for RSA), it is already prepended for this type.
using DigestWithPrefix =
    base::StrongAlias<class TypeTagDigestWithPrefix, std::vector<uint8_t>>;

// Values should not be reused or renumbered.
enum class COMPONENT_EXPORT(KCER) Error {
  kUnknownError = 0,
  kNotImplemented = 1,
  kNotSupported = 2,
  kTokenIsNotAvailable = 3,
  kTokenInitializationFailed = 4,
  kFailedToGenerateKey = 5,
  kFailedToExportPublicKey = 6,
  kFailedToEncodePublicKey = 7,
  kFailedToImportKey = 8,
  kInvalidCertificate = 9,
  kFailedToImportCertificate = 10,
  kFailedToRemoveCertificate = 11,
  kKeyNotFound = 12,
  kUnknownKeyType = 13,
  kFailedToGetKeyId = 14,
  kFailedToReadAttribute = 15,
  kFailedToWriteAttribute = 16,
  kFailedToParseKeyPermissions = 17,
  kUnexpectedSigningScheme = 18,
  kKeyDoesNotSupportSigningScheme = 19,
  kFailedToSignFailedToDigest = 20,
  kFailedToSignFailedToAddPrefix = 21,
  kFailedToSignFailedToGetSignatureLength = 22,
  kFailedToSign = 23,
  kFailedToSignBadSignatureLength = 24,
  kFailedToDerEncode = 25,
  kInputTooLong = 26,
  kFailedToListKeys = 27,
  kFailedToRemovePrivateKey = 28,
  kFailedToRemovePublicKey = 29,
  kFailedToRemoveObjects = 30,
  kFailedToCreateSpki = 31,
  kFailedToGetPkcs11Id = 32,
  kFailedToSearchForObjects = 33,
  kPkcs11SessionFailure = 34,
  kBadKeyParams = 35,
  kUnexpectedFindResult = 36,
  kFailedToDecodeKeyAttributes = 37,
  kFailedToRetrieveMechanismList = 38,
  kFailedToParseKey = 39,
  kFailedToGetIssuerName = 40,
  kFailedToGetSubjectName = 41,
  kFailedToGetSerialNumber = 42,
  kFailedToParsePkcs12 = 43,
  kInvalidPkcs12 = 44,
  kPkcs12WrongPassword = 45,
  kPkcs12InvalidMac = 46,
  kFailedToMakeCertNickname = 47,
  kAlreadyExists = 48,
  kMaxValue = kAlreadyExists,
};

// Handles for tokens on ChromeOS.
enum class COMPONENT_EXPORT(KCER) Token {
  // Keys and certificates storage that belongs to a specific user.
  kUser,
  // Keys and certificates storage that belongs to the entire
  // device (some users might still not have access to it).
  kDevice,
  kMaxValue = kDevice,
};

// Additional info related to a token.
struct COMPONENT_EXPORT(KCER) TokenInfo {
  // PKCS#11 id assigned to the token by Chaps.
  // Unstable across restarts.
  uint32_t pkcs11_id = 0;
  // Human readable name of the token.
  std::string token_name;
  // Human readable name of the module (i.e. Chaps).
  std::string module_name;
};

enum class COMPONENT_EXPORT(KCER) KeyType {
  kRsa,
  kEcc,
};

// Supported sizes for RSA keys. It's allowed to static_cast the values to
// uint32_t.
enum class COMPONENT_EXPORT(KCER) RsaModulusLength {
  k1024 = 1024,
  k2048 = 2048,
  k4096 = 4096
};

enum class COMPONENT_EXPORT(KCER) EllipticCurve {
  kP256,
};

// Possible sign schemes (aka algorithms) for Kcer::Sign() method. Maps 1-to-1
// to OpenSSL SSL_* constants. It is allowed to cast SigningScheme to SSL_*.
enum class COMPONENT_EXPORT(KCER) SigningScheme {
  kRsaPkcs1Sha1 = SSL_SIGN_RSA_PKCS1_SHA1,
  kRsaPkcs1Sha256 = SSL_SIGN_RSA_PKCS1_SHA256,
  kRsaPkcs1Sha384 = SSL_SIGN_RSA_PKCS1_SHA384,
  kRsaPkcs1Sha512 = SSL_SIGN_RSA_PKCS1_SHA512,
  kEcdsaSecp256r1Sha256 = SSL_SIGN_ECDSA_SECP256R1_SHA256,
  kEcdsaSecp384r1Sha384 = SSL_SIGN_ECDSA_SECP384R1_SHA384,
  kEcdsaSecp521r1Sha512 = SSL_SIGN_ECDSA_SECP521R1_SHA512,
  kRsaPssRsaeSha256 = SSL_SIGN_RSA_PSS_RSAE_SHA256,
  kRsaPssRsaeSha384 = SSL_SIGN_RSA_PSS_RSAE_SHA384,
  kRsaPssRsaeSha512 = SSL_SIGN_RSA_PSS_RSAE_SHA512,
};

class COMPONENT_EXPORT(KCER) PublicKey {
 public:
  // Public for implementations of Kcer interface, should not be used by end
  // users.
  PublicKey(Token token, Pkcs11Id pkcs11_id, PublicKeySpki pub_key_spki);
  PublicKey(const PublicKey&);
  PublicKey& operator=(const PublicKey&);
  PublicKey(PublicKey&&);
  PublicKey& operator=(PublicKey&&);
  ~PublicKey();

  bool operator==(const PublicKey& other) const;
  bool operator!=(const PublicKey& other) const;

  Token GetToken() const { return token_; }
  const Pkcs11Id& GetPkcs11Id() const { return pkcs11_id_; }
  const PublicKeySpki& GetSpki() const { return pub_key_spki_; }

 private:
  Token token_;
  Pkcs11Id pkcs11_id_;
  PublicKeySpki pub_key_spki_;
};

// Additional info related to a key pair.
struct COMPONENT_EXPORT(KCER) KeyInfo {
  KeyInfo();
  ~KeyInfo();
  KeyInfo(const KeyInfo&);
  KeyInfo& operator=(const KeyInfo&);
  KeyInfo(KeyInfo&&);
  KeyInfo& operator=(KeyInfo&&);

  bool is_hardware_backed;
  KeyType key_type;
  std::vector<SigningScheme> supported_signing_schemes;
  std::optional<std::string> nickname;
};

class COMPONENT_EXPORT(KCER) Cert : public base::RefCountedThreadSafe<Cert> {
 public:
  // Public for implementations of Kcer interface, should not be used by end
  // users.
  Cert(Token token,
       Pkcs11Id pkcs11_id,
       std::string nickname,
       scoped_refptr<net::X509Certificate> x509_cert);

  Token GetToken() const { return token_; }
  const Pkcs11Id& GetPkcs11Id() const { return pkcs11_id_; }
  // Gets nickname of the certificate (not to be confused with
  // the nickname of the key).
  const std::string& GetNickname() const { return nickname_; }
  scoped_refptr<net::X509Certificate> GetX509Cert() const { return x509_cert_; }

 private:
  friend class base::RefCountedThreadSafe<Cert>;
  ~Cert();

  const Token token_;
  const Pkcs11Id pkcs11_id_;
  const std::string nickname_;
  const scoped_refptr<net::X509Certificate> x509_cert_;
};

// Handle for a private key. Can be trivially constructed from other related
// objects. It's primarily just a convenience class to call methods that usually
// would require a private key (e.g. Sign). If the corresponding private key
// does not actually exist, the methods will return an error.
class COMPONENT_EXPORT(KCER) PrivateKeyHandle {
 public:
  explicit PrivateKeyHandle(const PublicKey& public_key);
  explicit PrivateKeyHandle(const Cert&);
  PrivateKeyHandle(Token token, PublicKeySpki pub_key_spki);
  // If possible, prefer specifying the token (use another constructor) for
  // better performance. Calling a Kcer method using this overload will make it
  // search on all tokens for the key before proceeding with the request.
  explicit PrivateKeyHandle(PublicKeySpki pub_key_spki);
  // Used internally to convert from `PrivateKeyHandle(PublicKeySpki)`. `other`
  // must have no token set.
  PrivateKeyHandle(Token token, PrivateKeyHandle&& other);
  ~PrivateKeyHandle();

  PrivateKeyHandle(const PrivateKeyHandle&);
  PrivateKeyHandle& operator=(const PrivateKeyHandle&);
  PrivateKeyHandle(PrivateKeyHandle&&);
  PrivateKeyHandle& operator=(PrivateKeyHandle&&);

  // Public for implementations of Kcer only.
  const std::optional<Token>& GetTokenInternal() const { return token_; }
  const Pkcs11Id& GetPkcs11IdInternal() const { return pkcs11_id_; }
  const PublicKeySpki& GetSpkiInternal() const { return pub_key_spki_; }
  void SetPkcs11IdInternal(Pkcs11Id pkcs11_id) {
    pkcs11_id_ = std::move(pkcs11_id);
  }

 private:
  // Depending on how PrivateKeyHandle is constructed, some member variables
  // might not contain valid values, possible combinations:
  // * Only `token_` and `pkcs11_id_` are populated.
  // * Only `token_` and `pub_key_spki_` are populated.
  // * Only `pub_key_spki_` is populated.
  // * All member variables are populated.
  std::optional<Token> token_;
  Pkcs11Id pkcs11_id_;
  PublicKeySpki pub_key_spki_;
};

// All the methods provided by Kcer. If the underlying storage is not ready when
// a method is called, it will be queued and executed later.
// Implementation note: most methods take arguments by value so they can be
// moved into base::BindOnce (without extra copy) and posted on a different
// sequence (where tokens live). The callbacks might be executed synchronously
// (without re-posting them).
class COMPONENT_EXPORT(KCER) Kcer {
 public:
  // base::expected<void, Error> could also be expressed as
  // std::optional<Error>, but then result.has_value() would mean opposite
  // things for methods with base::expected vs std::optional.
  using StatusCallback = base::OnceCallback<void(base::expected<void, Error>)>;
  using GenerateKeyCallback =
      base::OnceCallback<void(base::expected<PublicKey, Error>)>;
  using ImportKeyCallback =
      base::OnceCallback<void(base::expected<PublicKey, Error>)>;
  using ExportPkcs12Callback =
      base::OnceCallback<void(base::expected<CertDer, Error>)>;
  using ListKeysCallback =
      base::OnceCallback<void(std::vector<PublicKey>,
                              base::flat_map<Token, Error>)>;
  using ListCertsCallback =
      base::OnceCallback<void(std::vector<scoped_refptr<const Cert>>,
                              base::flat_map<Token, Error>)>;
  using DoesKeyExistCallback =
      base::OnceCallback<void(base::expected<bool, Error>)>;
  using SignCallback =
      base::OnceCallback<void(base::expected<Signature, Error>)>;
  using GetAvailableTokensCallback =
      base::OnceCallback<void(base::flat_set<Token>)>;
  using GetTokenInfoCallback =
      base::OnceCallback<void(base::expected<TokenInfo, Error>)>;
  using GetKeyInfoCallback =
      base::OnceCallback<void(base::expected<KeyInfo, Error>)>;
  using GetKeyPermissionsCallback = base::OnceCallback<void(
      base::expected<std::optional<chaps::KeyPermissions>, Error>)>;
  using GetCertProvisioningProfileIdCallback = base::OnceCallback<void(
      base::expected<std::optional<std::string>, Error>)>;

  Kcer() = default;
  virtual ~Kcer() = default;

  // Saves the `callback` that will be called when client certificates are
  // imported / removed.
  virtual base::CallbackListSubscription AddObserver(
      base::RepeatingClosure callback) = 0;

  // Generates a new RSA key pair in the `token`. If `hardware_backed` is false,
  // the key pair will not be hardware protected (by the TPM). Software keys are
  // usually faster, but less secure. Returns a public key on success, an error
  // otherwise.
  // TODO(miersh): Software keys are currently only implemented in Ash because
  // they are only used there. When Kcer-without-NSS is implemented, they should
  // work everywhere.
  virtual void GenerateRsaKey(Token token,
                              RsaModulusLength modulus_length_bits,
                              bool hardware_backed,
                              GenerateKeyCallback callback) = 0;
  // Generates a new EC key pair in the `token`. If `hardware_backed` is false,
  // the key pair will not be hardware protected (by the TPM). Software keys are
  // usually faster, but less secure. Returns a public key on success, an error
  // otherwise.
  virtual void GenerateEcKey(Token token,
                             EllipticCurve curve,
                             bool hardware_backed,
                             GenerateKeyCallback callback) = 0;

  // Imports a key pair from bytes `key_pair` in the PKCS#8 format (DER encoded)
  // into the `token` (as software-backed). It is caller's responsibility to
  // make sure that the same key doesn't end up on several different tokens at
  // the same time (otherwise Kcer is allowed to perform any future operations,
  // such as RemoveKey, with only one of the keys). Returns a public key on
  // success, an error otherwise. WARNING: With the current implementation the
  // key can be used with most other methods, but it won't appear in the
  // ListKeys() results.
  // TODO(miersh): Make ListKeys() return imported keys.
  virtual void ImportKey(Token token,
                         Pkcs8PrivateKeyInfoDer pkcs8_private_key_info_der,
                         ImportKeyCallback callback) = 0;
  // Imports a client certificate from bytes `cert` (DER-encoded X.509
  // certificate) into the `token`. A key pair for it should already exist on
  // the token (will fail otherwise). Returns an error on failure.
  virtual void ImportCertFromBytes(Token token,
                                   CertDer cert_der,
                                   StatusCallback callback) = 0;
  // Imports an X.509 certificate `cert` into the `token`. A key pair for it
  // should already exist on the token (will fail otherwise). Returns
  // base::nullopt on success, an error otherwise.
  virtual void ImportX509Cert(Token token,
                              scoped_refptr<net::X509Certificate> cert,
                              StatusCallback callback) = 0;
  // Imports a client certificate and its private key from `pkcs12_blob` encoded
  // in the PKCS#12 format into the `token`. If `hardware_backed` is false, the
  // key will not be hardware protected (by the TPM). Returns an error on
  // failure. If `mark_as_migrated` is true, all created objects will be marked
  // with a special attribute to allow a rollback for b/264387231.
  virtual void ImportPkcs12Cert(Token token,
                                Pkcs12Blob pkcs12_blob,
                                std::string password,
                                bool hardware_backed,
                                bool mark_as_migrated,
                                StatusCallback callback) = 0;

  // Exports an existing certificate in the PKCS#12 format. Returns a non-empty
  // certificate (as bytes) on success, an error otherwise. Only certificates
  // that were imported using `ImportPkcs12Cert` and are not hardware protected
  // are guaranteed to be exportable. Certificates with hardware protected keys
  // can never be exported in PKCS#12 format.
  virtual void ExportPkcs12Cert(scoped_refptr<const Cert> cert,
                                ExportPkcs12Callback callback) = 0;

  // Removes the key pair and associated client certificates (if any exist).
  // Returns an error on failure.
  virtual void RemoveKeyAndCerts(PrivateKeyHandle key,
                                 StatusCallback callback) = 0;
  // Removes the client certificate. The key for the certificate will remain in
  // the storage. Returns success if the cert was removed or already not
  // present. Returns an error on failure.
  virtual void RemoveCert(scoped_refptr<const Cert> cert,
                          StatusCallback callback) = 0;

  // Lists available key pairs from `tokens`. Each key pair is represented by
  // its public key. Returns a vector of public keys that were successfully
  // retrieved and a map with errors from each token (can be empty).
  virtual void ListKeys(base::flat_set<Token> tokens,
                        ListKeysCallback callback) = 0;
  // Lists available client certificates from `tokens`. Returns a vector of
  // scoped_refptr<const Cert>'s that were successfully retrieved and a map with
  // errors from each token (can be empty).
  virtual void ListCerts(base::flat_set<Token> tokens,
                         ListCertsCallback callback) = 0;

  // Checks whether the private key for the handle `key` exists in the
  // certificate storage. Returns true if the key exists, false if it doesn't
  // exist, an error if failed to check.
  virtual void DoesPrivateKeyExist(PrivateKeyHandle key,
                                   DoesKeyExistCallback callback) = 0;

  // Signs `data` using the private `key`. The data will be hashed and signed
  // according to the `signing_scheme`. Returns a non-empty signature on
  // success, an error otherwise.
  virtual void Sign(PrivateKeyHandle key,
                    SigningScheme signing_scheme,
                    DataToSign data,
                    SignCallback callback) = 0;

  // Applies RSASSA-PKCS1-v1_5 padding and afterwards signs the `digest` (a
  // pre-hashed value) with the private `key` (must be RSA). PKCS1 DigestInfo is
  // expected to already be prepended to the `digest`. The size of `digest`
  // (number of octets) must be smaller than k-11, where k is the key size in
  // octets. Returns a non-empty signature on success, an error otherwise.
  //
  // WARNING: digest_with_prefix must be the result of hashing the data to be
  // signed with a secure hash function, then wrapping in the corresponding
  // DigestInfo structure, as described in RSASSA-PKCS1-v1_5. Passing in any
  // other input, notably skipping the hash operation, will not result in a
  // secure signature scheme. This function does not check either of these and,
  // instead, callers are assumed to be trusted to do this.
  //
  // This is currently only used for the CertProvisioning feature and may not be
  // used in other contexts. Please consult Kcer owners if the Sign API does not
  // meet your needs.
  virtual void SignRsaPkcs1Raw(PrivateKeyHandle key,
                               DigestWithPrefix digest_with_prefix,
                               SignCallback callback) = 0;

  // Returns tokens that are available to the current instance of Kcer.
  virtual void GetAvailableTokens(GetAvailableTokensCallback callback) = 0;

  // Retrieves additional info for the loaded `token`. Returns a `TokenInfo`
  // struct on success, kTokenNotAvailable if the `token` will never be loaded,
  // kTokenLoading if the `token` is still loading (can eventually transition
  // into kTokenNotAvailable), some other error otherwise.
  virtual void GetTokenInfo(Token token, GetTokenInfoCallback callback) = 0;
  // Retrieves additional info for the `key`. Returns a `KeyInfo` struct on
  // success, an error otherwise.
  virtual void GetKeyInfo(PrivateKeyHandle key,
                          GetKeyInfoCallback callback) = 0;
  // Retrieves key permissions for the `key` (see key_permissions.proto).
  virtual void GetKeyPermissions(PrivateKeyHandle key,
                                 GetKeyPermissionsCallback callback) = 0;
  // Retrieves "certificate provisioning profile id" for the `key` (i.e.
  // "cert_profile_id" from RequiredClientCertificateForUser.yaml and
  // RequiredClientCertificateForDevice.yaml).
  virtual void GetCertProvisioningProfileId(
      PrivateKeyHandle key,
      GetCertProvisioningProfileIdCallback callback) = 0;

  // Sets the `nickname` on the `key`. (Not to be confused with the nickname of
  // the certificate.) Returns an error on failure.
  // The nickname on the key is partially independent from the certificates'
  // nicknames and is stored as CKA_LABEL in PKCS#11 attributes of the key
  // object. When a new certificate is imported, its nickname might be copied
  // into the key's nickname (TODO(miersh): this part should be changed in the
  // future), but generally speaking they are not kept in sync.
  virtual void SetKeyNickname(PrivateKeyHandle key,
                              std::string nickname,
                              StatusCallback callback) = 0;
  // Sets the `key_permissions` on the `key`. Returns an error on failure.
  virtual void SetKeyPermissions(PrivateKeyHandle key,
                                 chaps::KeyPermissions key_permissions,
                                 StatusCallback callback) = 0;
  // Sets the Built-In Certificate Provisioning `profile_id` on the `key`.
  // Returns an error on failure.
  virtual void SetCertProvisioningProfileId(PrivateKeyHandle key,
                                            std::string profile_id,
                                            StatusCallback callback) = 0;
};

}  // namespace kcer

#endif  // CHROMEOS_COMPONENTS_KCER_KCER_H_