| // 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 <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/abseil-cpp/absl/types/optional.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>>; |
| 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, |
| }; |
| |
| // 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, |
| }; |
| |
| 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; |
| absl::optional<std::string> nickname; |
| // Custom ChromeOS attributes. |
| absl::optional<chaps::KeyPermissions> key_permissions; |
| absl::optional<std::string> cert_provisioning_profile_id; |
| }; |
| |
| 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 absl::optional<Token>& GetTokenInternal() const { return token_; } |
| const Pkcs11Id& GetPkcs11IdInternal() const { return pkcs11_id_; } |
| const PublicKeySpki& GetSpkiInternal() const { return pub_key_spki_; } |
| |
| 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. |
| absl::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 |
| // absl::optional<Error>, but then result.has_value() would mean opposite |
| // things for methods with base::expected vs absl::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 GetTokenInfoCallback = |
| base::OnceCallback<void(base::expected<TokenInfo, Error>)>; |
| using GetKeyInfoCallback = |
| base::OnceCallback<void(base::expected<KeyInfo, 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, |
| uint32_t 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`. 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. |
| virtual void ImportPkcs12Cert(Token token, |
| Pkcs12Blob pkcs12_blob, |
| std::string password, |
| bool hardware_backed, |
| 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 base::flat_set<Token> GetAvailableTokens() = 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; |
| |
| // 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 internal { |
| class KcerToken; |
| // Creates an instance of Kcer interface, should only be used by a dedicated |
| // factory. Tokens are expected to be owned by the factory and live on a non-UI |
| // thread. All the requests for the tokens should be posted on the |
| // `token_task_runner`. Kcer doesn't take ownership of the tokens and accesses |
| // them via weak pointers. |
| COMPONENT_EXPORT(KCER) |
| std::unique_ptr<Kcer> CreateKcer( |
| scoped_refptr<base::TaskRunner> token_task_runner, |
| base::WeakPtr<internal::KcerToken> user_token, |
| base::WeakPtr<internal::KcerToken> device_token); |
| } // namespace internal |
| |
| } // namespace kcer |
| |
| #endif // CHROMEOS_COMPONENTS_KCER_KCER_H_ |