blob: e4c5f59b3b823330a17277faee5826f45cf781ed [file] [log] [blame] [edit]
// 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_