| // Copyright 2022 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIBHWSEC_BACKEND_PINWEAVER_H_ |
| #define LIBHWSEC_BACKEND_PINWEAVER_H_ |
| |
| #include <cstdint> |
| #include <map> |
| #include <optional> |
| #include <vector> |
| |
| #include <brillo/secure_blob.h> |
| |
| #include "libhwsec/status.h" |
| #include "libhwsec/structures/no_default_init.h" |
| #include "libhwsec/structures/operation_policy.h" |
| |
| namespace hwsec { |
| |
| constexpr int PinWeaverEccPointSize = 32; |
| |
| // Random provide the functions related to pinweaver. |
| class PinWeaver { |
| public: |
| // The result object for those operation that will modify the hash tree. |
| // If an operation return this struct, the tree should be updated to |
| // |new_root|, otherwise the tree would out of sync. |
| struct CredentialTreeResult { |
| enum class ErrorCode { |
| // The operation was successful. |
| kSuccess = 0, |
| // Failed due to incorrect Low Entropy credential provided. |
| kInvalidLeSecret, |
| // Failed due to incorrect Reset credential provided. |
| kInvalidResetSecret, |
| // Failed since the credential has been locked out due to too many |
| // attempts per the delay schedule. |
| kTooManyAttempts, |
| // Failed due to the hash tree being out of sync. This should |
| // prompt a hash tree resynchronization and retry. |
| kHashTreeOutOfSync, |
| // The device policy (e.g. PCR) is in unexpected state. The basic way to |
| // proceed from here is to |
| // reboot the device. |
| kPolicyNotMatch, |
| // Failed since the credential has been locked out due to expiration time |
| // reached. |
| kExpired, |
| // Other error. |
| kOther, |
| }; |
| |
| // Detail error code for more information. |
| // Whatever the error code we return at here, we should always update the |
| // tree with |new_root|. |
| NoDefault<ErrorCode> error; |
| // The |new_root| hash of the hash tree. |
| NoDefault<brillo::Blob> new_root; |
| // The credential metadata that should be updated. |
| std::optional<brillo::Blob> new_cred_metadata; |
| // The MAC of the credential that should be updated. |
| std::optional<brillo::Blob> new_mac; |
| // The high entropy secret that would be returned in a successful check |
| // operation. |
| std::optional<brillo::SecureBlob> he_secret; |
| // The reset secret that would be returned in a successful check |
| // operation. |
| std::optional<brillo::SecureBlob> reset_secret; |
| |
| // The below 3 fields are returned in a successful start_bio_auth operation. |
| // |server_nonce| is the nonce used for session key exchange with the |
| // client. |
| std::optional<brillo::Blob> server_nonce; |
| // |iv| is used for the AES-CTR encryption of the below |
| // |encrypted_he_secret| field. |
| std::optional<brillo::Blob> iv; |
| std::optional<brillo::Blob> encrypted_he_secret; |
| }; |
| |
| struct GetLogResult { |
| // Enum used to denote the log entry type. |
| enum class LogEntryType { |
| kInsert, |
| kCheck, |
| kRemove, |
| kReset, |
| kInvalid, |
| }; |
| |
| // Container for the log replay data obtained from the backend. |
| // This struct is used during synchronization operations which occur when |
| // the on-disk hash tree state and backend hash tree state are |
| // out-of-sync. |
| struct LogEntry { |
| LogEntryType type; |
| // Label on which the log operation is performed. |
| uint64_t label; |
| // Value of root hash after the log operation is performed. |
| brillo::Blob root; |
| // For insert operations, this signifies the MAC of the inserted leaf. |
| brillo::Blob mac; |
| }; |
| |
| brillo::Blob root_hash; |
| std::vector<LogEntry> log_entries; |
| }; |
| |
| struct ReplayLogOperationResult { |
| brillo::Blob new_cred_metadata; |
| brillo::Blob new_mac; |
| }; |
| |
| // The timestamp object used in PinWeaver servers. In addition to the |
| // timer_value, a boot_count is stored because PinWeaver servers typically |
| // reset the timer_value when they reboot. |
| struct PinWeaverTimestamp { |
| uint32_t boot_count; |
| uint64_t timer_value; |
| }; |
| |
| struct PinWeaverEccPoint { |
| uint8_t x[PinWeaverEccPointSize]; |
| uint8_t y[PinWeaverEccPointSize]; |
| }; |
| |
| enum class AuthChannel : uint8_t { |
| kFingerprintAuthChannel = 0, |
| // This channel is only for testing purpose, so we aren't setting a |
| // dedicated value for it. |
| kTestAuthChannel, |
| kMaxValue = kTestAuthChannel, |
| }; |
| |
| // The delay schedule which determines the delay enforced between |
| // authentication attempts. |
| using DelaySchedule = std::map<uint32_t, uint32_t>; |
| |
| // Is the pinweaver enabled or not. |
| virtual StatusOr<bool> IsEnabled() = 0; |
| |
| // Gets the version of pinweaver. |
| virtual StatusOr<uint8_t> GetVersion() = 0; |
| |
| // Resets the PinWeaver Hash Tree root hash with its initial known value, |
| // which assumes all MACs are all-zero. |
| // |
| // This function should be executed only when we are setting up a hash tree |
| // on a new / wiped device, or resetting the hash tree due to an |
| // unrecoverable error. |
| // |
| // |bits_per_level| is the number of bits per level of the hash tree. |
| // |length_labels| is the length of the leaf bit string. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> Reset(uint32_t bits_per_level, |
| uint32_t length_labels) = 0; |
| |
| // Tries to insert a credential into the TPM. |
| // |
| // The label of the leaf node is in |label|, the list of auxiliary hashes is |
| // in |h_aux|, the LE credential to be added is in |le_secret|. Along with |
| // it, its associated reset_secret |reset_secret| and the high entropy |
| // credential it protects |he_secret| are also provided. The delay schedule |
| // which determines the delay enforced between authentication attempts is |
| // provided by |delay_schedule|. The credential is bound to the |policies|, |
| // the check credential operation would only success when one of policy match. |
| // And the credential has an expiration window of |expiration_delay|, it |
| // expires after that many seconds after creation and each strong reset. |
| // |
| // |h_aux| requires a particular order: starting from left child to right |
| // child, from leaf upwards till the children of the root label. |
| // |
| // If successful, the new credential metadata will be placed in the blob |
| // pointed by |new_cred_metadata|. The MAC of the credential will be |
| // returned in |new_mac|. The resulting root hash is returned in |new_root|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> InsertCredential( |
| const std::vector<OperationPolicySetting>& policies, |
| const uint64_t label, |
| const std::vector<brillo::Blob>& h_aux, |
| const brillo::SecureBlob& le_secret, |
| const brillo::SecureBlob& he_secret, |
| const brillo::SecureBlob& reset_secret, |
| const DelaySchedule& delay_schedule, |
| std::optional<uint32_t> expiration_delay) = 0; |
| |
| // Tries to verify/authenticate a credential. |
| // |
| // The obfuscated LE Credential is |le_secret| and the credential metadata |
| // is in |orig_cred_metadata|. |
| // |
| // On success, or failure due to an invalid |le_secret|, the updated |
| // credential metadata and corresponding new MAC will be returned in |
| // |new_cred_metadata| and |new_mac|. |
| // |
| // On success, the released high entropy credential will be returned in |
| // |he_secret| and the reset secret in |reset_secret|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> CheckCredential( |
| const uint64_t label, |
| const std::vector<brillo::Blob>& h_aux, |
| const brillo::Blob& orig_cred_metadata, |
| const brillo::SecureBlob& le_secret) = 0; |
| |
| // Removes the credential which has label |label|. |
| // |
| // The corresponding list of auxiliary hashes is in |h_aux|, and the MAC of |
| // the label that needs to be removed is |mac|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> RemoveCredential( |
| const uint64_t label, |
| const std::vector<std::vector<uint8_t>>& h_aux, |
| const std::vector<uint8_t>& mac) = 0; |
| |
| // Tries to reset a (potentially locked out) credential. |
| // |
| // The reset credential is |reset_secret| and the credential metadata is |
| // in |orig_cred_metadata|. |strong_reset| indicates whether the expiration |
| // should be reset too. |
| // |
| // On success, the updated credential metadata and corresponding new MAC |
| // will be returned in |new_cred_metadata| and |new_mac|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> ResetCredential( |
| const uint64_t label, |
| const std::vector<std::vector<uint8_t>>& h_aux, |
| const std::vector<uint8_t>& orig_cred_metadata, |
| const brillo::SecureBlob& reset_secret, |
| bool strong_reset) = 0; |
| |
| // Retrieves the replay log. |
| // |
| // The current on-disk root hash is supplied via |cur_disk_root_hash|. |
| virtual StatusOr<GetLogResult> GetLog( |
| const std::vector<uint8_t>& cur_disk_root_hash) = 0; |
| |
| // Replays the log operation referenced by |log_entry_root|, where |
| // |log_entry_root| is the resulting root hash after the operation, and is |
| // retrieved from the log entry. |
| // |h_aux| and |orig_cred_metadata| refer to, respectively, the list of |
| // auxiliary hashes and the original credential metadata associated with the |
| // label concerned (available in the log entry). The resulting metadata and |
| // MAC are stored in |new_cred_metadata| and |new_mac|. |
| virtual StatusOr<ReplayLogOperationResult> ReplayLogOperation( |
| const brillo::Blob& log_entry_root, |
| const std::vector<brillo::Blob>& h_aux, |
| const brillo::Blob& orig_cred_metadata) = 0; |
| |
| // Looks into the metadata and retrieves the number of wrong authentication |
| // attempts. |
| virtual StatusOr<uint32_t> GetWrongAuthAttempts( |
| const brillo::Blob& cred_metadata) = 0; |
| |
| // Looks into the metadata and retrieves the delay schedule. |
| virtual StatusOr<DelaySchedule> GetDelaySchedule( |
| const brillo::Blob& cred_metadata) = 0; |
| |
| // Get the remaining delay in seconds. |
| virtual StatusOr<uint32_t> GetDelayInSeconds( |
| const brillo::Blob& cred_metadata) = 0; |
| |
| // Get the remaining time until the credential expires, in seconds. Nullopt |
| // means the credential won't expire. 0 means the credential already expired. |
| virtual StatusOr<std::optional<uint32_t>> GetExpirationInSeconds( |
| const brillo::Blob& cred_metadata) = 0; |
| |
| // Tries to establish the pairing secret of the |auth_channel| auth channel. |
| // |
| // The secret is established using ECDH key exchange, and |client_public_key| |
| // is the public key that needs to be provided by the caller. |
| // |
| // If successful, the secret is established and the server's public key is |
| // returned. |
| virtual StatusOr<PinWeaverEccPoint> GeneratePk( |
| AuthChannel auth_channel, const PinWeaverEccPoint& client_public_key) = 0; |
| |
| // Tries to insert a rate-limiter credential into the TPM, bound to the |
| // |auth_channel| auth channel. |
| // |
| // The label of the leaf node is in |label|, the list of auxiliary hashes is |
| // in |h_aux|. Its associated reset_secret |reset_secret| is provided. The |
| // delay schedule which determines the delay enforced between authentication |
| // attempts is provided by |delay_schedule|. The credential is bound to the |
| // |policies|, the check credential operation would only success when one of |
| // policy match. And the credential has an expiration window of |
| // |expiration_delay|, it expires after that many seconds after creation and |
| // each strong reset. |
| // |
| // |h_aux| requires a particular order: starting from left child to right |
| // child, from leaf upwards till the children of the root label. |
| // |
| // If successful, the new credential metadata will be placed in the blob |
| // pointed by |new_cred_metadata|. The MAC of the credential will be |
| // returned in |new_mac|. The resulting root hash is returned in |new_root|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> InsertRateLimiter( |
| AuthChannel auth_channel, |
| const std::vector<OperationPolicySetting>& policies, |
| const uint64_t label, |
| const std::vector<brillo::Blob>& h_aux, |
| const brillo::SecureBlob& reset_secret, |
| const DelaySchedule& delay_schedule, |
| std::optional<uint32_t> expiration_delay) = 0; |
| |
| // Tries to start an authentication attempt with a rate-limiter bound to the |
| // |auth_channel| auth channel. |
| // |
| // The label of the leaf node is in |label|, the list of auxiliary hashes is |
| // in |h_aux|. The credential metadata is in |orig_cred_metadata|. |
| // The |client_nonce| is a nonce to perform session key exchange, used for |
| // encrypting the |encrypted_he_secret| in the response. |
| // |
| // On success, or failure due to an incorrect |auth_channel|, the updated |
| // credential metadata and corresponding new MAC will be returned in |
| // |new_cred_metadata| and |new_mac|. |
| // |
| // On success, the released high entropy credential will be returned encrypted |
| // in |encrypted_he_secret|, and the IV used for encryption is in |iv|. The |
| // nonce generated to perform the session key exchange is in |server_nonce|. |
| // |
| // In all cases, the resulting root hash is returned in |new_root|. |
| virtual StatusOr<CredentialTreeResult> StartBiometricsAuth( |
| AuthChannel auth_channel, |
| const uint64_t label, |
| const std::vector<brillo::Blob>& h_aux, |
| const brillo::Blob& orig_cred_metadata, |
| const brillo::Blob& client_nonce) = 0; |
| |
| // Blocks future establishments of the pairing secrets until the server |
| // restarts. |
| // |
| // If successful, future secret establishments are blocked. |
| virtual Status BlockGeneratePk() = 0; |
| |
| protected: |
| PinWeaver() = default; |
| ~PinWeaver() = default; |
| }; |
| |
| } // namespace hwsec |
| |
| #endif // LIBHWSEC_BACKEND_PINWEAVER_H_ |