#ifndef DEVICE_FIDO_PIN_H_
#define DEVICE_FIDO_PIN_H_
#include <stdint.h>
#include <array>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "components/cbor/values.h"
#include "device/fido/fido_constants.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace device {
namespace pin {
enum class PINEntryReason {
kSet,
kChange,
kChallenge
};
enum class PINEntryError {
kNoError,
kInternalUvLocked,
kWrongPIN,
kTooShort,
kInvalidCharacters,
kSameAsCurrentPIN,
};
enum class Permissions : uint8_t {
kMakeCredential = 0x01,
kGetAssertion = 0x02,
kCredentialManagement = 0x04,
kBioEnrollment = 0x08,
kLargeBlobWrite = 0x10,
};
constexpr std::array<uint8_t, 32> kPinUvAuthTokenSafetyPadding = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
COMPONENT_EXPORT(DEVICE_FIDO)
PINEntryError ValidatePIN(
const std::string& pin,
uint32_t min_pin_length = kMinPinLength,
absl::optional<std::string> current_pin = absl::nullopt);
COMPONENT_EXPORT(DEVICE_FIDO)
PINEntryError ValidatePIN(
const std::u16string& pin16,
uint32_t min_pin_length = kMinPinLength,
absl::optional<std::string> current_pin = absl::nullopt);
constexpr size_t kMinBytes = 4;
constexpr size_t kMaxBytes = 63;
COMPONENT_EXPORT(DEVICE_FIDO)
cbor::Value::MapValue EncodeCOSEPublicKey(
base::span<const uint8_t, kP256X962Length> x962);
struct PinRetriesRequest {
PINUVAuthProtocol protocol;
};
struct UvRetriesRequest {
PINUVAuthProtocol protocol;
};
struct RetriesResponse {
static absl::optional<RetriesResponse> ParsePinRetries(
const absl::optional<cbor::Value>& cbor);
static absl::optional<RetriesResponse> ParseUvRetries(
const absl::optional<cbor::Value>& cbor);
int retries;
private:
static absl::optional<RetriesResponse> Parse(
const absl::optional<cbor::Value>& cbor,
const int retries_key);
RetriesResponse();
};
struct KeyAgreementRequest {
PINUVAuthProtocol protocol;
};
struct COMPONENT_EXPORT(DEVICE_FIDO) KeyAgreementResponse {
static absl::optional<KeyAgreementResponse> Parse(
const absl::optional<cbor::Value>& cbor);
static absl::optional<KeyAgreementResponse> ParseFromCOSE(
const cbor::Value::MapValue& cose_key);
std::array<uint8_t, kP256X962Length> X962() const;
uint8_t x[32], y[32];
private:
KeyAgreementResponse();
};
class SetRequest {
public:
SetRequest(PINUVAuthProtocol protocol,
const std::string& pin,
const KeyAgreementResponse& peer_key);
friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const SetRequest&);
private:
const PINUVAuthProtocol protocol_;
const KeyAgreementResponse peer_key_;
uint8_t pin_[kMaxBytes + 1];
};
struct EmptyResponse {
static absl::optional<EmptyResponse> Parse(
const absl::optional<cbor::Value>& cbor);
};
class ChangeRequest {
public:
ChangeRequest(PINUVAuthProtocol protocol,
const std::string& old_pin,
const std::string& new_pin,
const KeyAgreementResponse& peer_key);
friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const ChangeRequest&);
private:
const PINUVAuthProtocol protocol_;
const KeyAgreementResponse peer_key_;
uint8_t old_pin_hash_[16];
uint8_t new_pin_[kMaxBytes + 1];
};
struct ResetRequest {};
using ResetResponse = EmptyResponse;
class TokenRequest {
public:
TokenRequest(const TokenRequest&) = delete;
const std::vector<uint8_t>& shared_key() const;
protected:
TokenRequest(TokenRequest&&);
TokenRequest(PINUVAuthProtocol protocol,
const KeyAgreementResponse& peer_key);
~TokenRequest();
const PINUVAuthProtocol protocol_;
std::vector<uint8_t> shared_key_;
std::array<uint8_t, kP256X962Length> public_key_;
};
class PinTokenRequest : public TokenRequest {
public:
PinTokenRequest(PINUVAuthProtocol protocol,
const std::string& pin,
const KeyAgreementResponse& peer_key);
PinTokenRequest(PinTokenRequest&&);
PinTokenRequest(const PinTokenRequest&) = delete;
virtual ~PinTokenRequest();
friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const PinTokenRequest&);
protected:
uint8_t pin_hash_[16];
};
class PinTokenWithPermissionsRequest : public PinTokenRequest {
public:
PinTokenWithPermissionsRequest(PINUVAuthProtocol protocol,
const std::string& pin,
const KeyAgreementResponse& peer_key,
base::span<const pin::Permissions> permissions,
const absl::optional<std::string> rp_id);
PinTokenWithPermissionsRequest(PinTokenWithPermissionsRequest&&);
PinTokenWithPermissionsRequest(const PinTokenWithPermissionsRequest&) =
delete;
~PinTokenWithPermissionsRequest() override;
friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const PinTokenWithPermissionsRequest&);
private:
uint8_t permissions_;
absl::optional<std::string> rp_id_;
};
class UvTokenRequest : public TokenRequest {
public:
UvTokenRequest(PINUVAuthProtocol protocol,
const KeyAgreementResponse& peer_key,
absl::optional<std::string> rp_id,
base::span<const pin::Permissions> permissions);
UvTokenRequest(UvTokenRequest&&);
UvTokenRequest(const UvTokenRequest&) = delete;
virtual ~UvTokenRequest();
friend std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const UvTokenRequest&);
private:
absl::optional<std::string> rp_id_;
uint8_t permissions_;
};
class HMACSecretRequest {
public:
HMACSecretRequest(PINUVAuthProtocol protocol,
const KeyAgreementResponse& peer_key,
base::span<const uint8_t, 32> salt1,
const absl::optional<std::array<uint8_t, 32>>& salt2);
HMACSecretRequest(const HMACSecretRequest&);
~HMACSecretRequest();
HMACSecretRequest& operator=(const HMACSecretRequest&);
absl::optional<std::vector<uint8_t>> Decrypt(
base::span<const uint8_t> ciphertext);
private:
const PINUVAuthProtocol protocol_;
std::vector<uint8_t> shared_key_;
public:
const std::array<uint8_t, kP256X962Length> public_key_x962;
const std::vector<uint8_t> encrypted_salts;
const std::vector<uint8_t> salts_auth;
};
class COMPONENT_EXPORT(DEVICE_FIDO) TokenResponse {
public:
~TokenResponse();
TokenResponse(const TokenResponse&);
TokenResponse& operator=(const TokenResponse&);
static absl::optional<TokenResponse> Parse(
PINUVAuthProtocol protocol,
base::span<const uint8_t> shared_key,
const absl::optional<cbor::Value>& cbor);
std::pair<PINUVAuthProtocol, std::vector<uint8_t>> PinAuth(
base::span<const uint8_t> client_data_hash) const;
PINUVAuthProtocol protocol() const { return protocol_; }
const std::vector<uint8_t>& token_for_testing() const { return token_; }
private:
explicit TokenResponse(PINUVAuthProtocol protocol);
PINUVAuthProtocol protocol_;
std::vector<uint8_t> token_;
};
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const PinRetriesRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const UvRetriesRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const KeyAgreementRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const SetRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const ChangeRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const ResetRequest&);
std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
AsCTAPRequestValuePair(const TokenRequest&);
}
}
#endif