Commit 99e84c85 authored by Nina Satragno's avatar Nina Satragno Committed by Commit Bot

[fido] Large blob PinUvAuthToken support

Add support for PinUvAuthToken on large blobs.

https://w3c.github.io/webauthn/#sctn-large-blob-extension

Bug: 1114875
Change-Id: I57a094eed4cb0cd3d8b20722804826a46ccf0e27
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2422707
Commit-Queue: Nina Satragno <nsatragno@chromium.org>
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#809523}
parent 279b611b
......@@ -12,6 +12,7 @@
#include "device/fido/fido_constants.h"
#include "device/fido/fido_parsing_utils.h"
#include "device/fido/large_blob.h"
#include "device/fido/pin.h"
#include "device/fido/test_callback_receiver.h"
#include "device/fido/virtual_ctap2_device.h"
#include "device/fido/virtual_fido_device.h"
......@@ -27,6 +28,9 @@ using WriteCallback =
using ReadCallback = device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<std::vector<std::pair<LargeBlobKey, std::vector<uint8_t>>>>>;
using PinCallback = device::test::StatusAndValueCallbackReceiver<
CtapDeviceResponseCode,
base::Optional<pin::TokenResponse>>;
constexpr LargeBlobKey kDummyKey1 = {{0x01}};
constexpr LargeBlobKey kDummyKey2 = {{0x02}};
......@@ -34,6 +38,7 @@ constexpr std::array<uint8_t, 4> kSmallBlob1 = {'r', 'o', 's', 'a'};
constexpr std::array<uint8_t, 4> kSmallBlob2 = {'l', 'u', 'm', 'a'};
constexpr std::array<uint8_t, 4> kSmallBlob3 = {'s', 't', 'a', 'r'};
constexpr size_t kMaxStorageSize = 4096;
constexpr char kPin[] = "1234";
class FidoDeviceAuthenticatorTest : public testing::Test {
public:
......@@ -43,10 +48,13 @@ class FidoDeviceAuthenticatorTest : public testing::Test {
config.large_blob_support = true;
config.resident_key_support = true;
config.available_large_blob_storage = kMaxStorageSize;
config.pin_uv_auth_token_support = true;
config.ctap2_versions = {Ctap2Version::kCtap2_1};
authenticator_state_ = base::MakeRefCounted<VirtualFidoDevice::State>();
auto virtual_device =
std::make_unique<VirtualCtap2Device>(authenticator_state_, config);
virtual_device_ = virtual_device.get();
authenticator_ =
std::make_unique<FidoDeviceAuthenticator>(std::move(virtual_device));
......@@ -56,13 +64,9 @@ class FidoDeviceAuthenticatorTest : public testing::Test {
}
protected:
void SetPin() {
authenticator_state_->pin = "1234";
authenticator_state_->pin_retries = device::kMaxPinRetries;
}
scoped_refptr<VirtualFidoDevice::State> authenticator_state_;
std::unique_ptr<FidoDeviceAuthenticator> authenticator_;
VirtualCtap2Device* virtual_device_;
private:
base::test::SingleThreadTaskEnvironment task_environment_;
......@@ -140,6 +144,36 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) {
EXPECT_EQ(large_blob, large_blob_array->at(0).second);
}
// Test reading and writing a blob using a PinUvAuthToken.
TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
virtual_device_->SetPin(kPin);
PinCallback pin_callback;
authenticator_->GetPINToken(kPin, {pin::Permissions::kLargeBlobWrite},
/*rp_id=*/base::nullopt, pin_callback.callback());
pin_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, pin_callback.status());
pin::TokenResponse pin_token = *pin_callback.value();
std::vector<uint8_t> small_blob =
fido_parsing_utils::Materialize(kSmallBlob1);
WriteCallback write_callback;
authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, pin_token,
write_callback.callback());
write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, pin_token,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array);
ASSERT_EQ(1u, large_blob_array->size());
EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first);
EXPECT_EQ(small_blob, large_blob_array->at(0).second);
}
// Test updating a large blob in an array with multiple entries corresponding to
// other keys.
TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
......
......@@ -83,7 +83,18 @@ LargeBlobsRequest::~LargeBlobsRequest() = default;
void LargeBlobsRequest::SetPinParam(
const pin::TokenResponse& pin_uv_auth_token) {
// TODO(nsatragno): implement & test pin uv auth token.
pin_uv_auth_protocol_ = pin::kProtocolVersion;
std::vector<uint8_t> pin_auth(pin::kPinUvAuthTokenSafetyPadding.begin(),
pin::kPinUvAuthTokenSafetyPadding.end());
pin_auth.insert(pin_auth.end(), kLargeBlobPinPrefix.begin(),
kLargeBlobPinPrefix.end());
const std::array<uint8_t, 4> offset_array =
fido_parsing_utils::Uint32LittleEndian(offset_);
pin_auth.insert(pin_auth.end(), offset_array.begin(), offset_array.end());
if (set_) {
pin_auth.insert(pin_auth.end(), set_->begin(), set_->end());
}
pin_uv_auth_param_ = pin_uv_auth_token.PinAuth(pin_auth);
}
// static
......
......@@ -48,6 +48,7 @@ using LargeBlobKey = std::array<uint8_t, kLargeBlobKeyLength>;
constexpr size_t kLargeBlobDefaultMaxFragmentLength = 960;
constexpr size_t kLargeBlobReadEncodingOverhead = 64;
constexpr size_t kLargeBlobArrayNonceLength = 12;
constexpr std::array<uint8_t, 2> kLargeBlobPinPrefix = {0x0c, 0x00};
struct COMPONENT_EXPORT(DEVICE_FIDO) LargeBlobArrayFragment {
LargeBlobArrayFragment(std::vector<uint8_t> bytes, size_t offset);
......
......@@ -38,6 +38,13 @@ enum class Permissions : uint8_t {
// implements.
constexpr int kProtocolVersion = 1;
// Some commands that validate PinUvAuthTokens include this padding to ensure a
// PinUvAuthParam cannot be reused across different commands.
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};
// IsValid returns true if |pin|, which must be UTF-8, is a syntactically valid
// PIN.
COMPONENT_EXPORT(DEVICE_FIDO) bool IsValid(const std::string& pin);
......
......@@ -57,18 +57,12 @@ constexpr std::array<uint8_t, kAaguidLength> kDeviceAaguid = {
{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08}};
// Some commands that validate PinUvAuthTokens include this padding to ensure a
// PinUvAuthParam cannot be reused across different commands.
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};
constexpr uint8_t kSupportedPermissionsMask =
static_cast<uint8_t>(pin::Permissions::kMakeCredential) |
static_cast<uint8_t>(pin::Permissions::kGetAssertion) |
static_cast<uint8_t>(pin::Permissions::kCredentialManagement) |
static_cast<uint8_t>(pin::Permissions::kBioEnrollment);
static_cast<uint8_t>(pin::Permissions::kBioEnrollment) |
static_cast<uint8_t>(pin::Permissions::kLargeBlobWrite);
struct PinUvAuthTokenPermissions {
uint8_t permissions;
......@@ -601,6 +595,16 @@ VirtualCtap2Device::VirtualCtap2Device(scoped_refptr<State> state,
VirtualCtap2Device::~VirtualCtap2Device() = default;
void VirtualCtap2Device::SetPin(std::string pin) {
DCHECK_NE(
device_info_->options.client_pin_availability,
AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported);
mutable_state()->pin = std::move(pin);
mutable_state()->pin_retries = device::kMaxPinRetries;
device_info_->options.client_pin_availability =
AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet;
}
// As all operations for VirtualCtap2Device are synchronous and we do not wait
// for user touch, Cancel command is no-op.
void VirtualCtap2Device::Cancel(CancelToken) {}
......@@ -2294,9 +2298,10 @@ CtapDeviceResponseCode VirtualCtap2Device::OnLargeBlobs(
// pinUvAuthParam)
std::vector<uint8_t> pinauth_bytes;
pinauth_bytes.insert(pinauth_bytes.begin(),
kPinUvAuthTokenSafetyPadding.begin(),
kPinUvAuthTokenSafetyPadding.end());
pinauth_bytes.insert(pinauth_bytes.end(), {0x0c, 0x00});
pin::kPinUvAuthTokenSafetyPadding.begin(),
pin::kPinUvAuthTokenSafetyPadding.end());
pinauth_bytes.insert(pinauth_bytes.end(), kLargeBlobPinPrefix.begin(),
kLargeBlobPinPrefix.end());
auto offset_vec = fido_parsing_utils::Uint32LittleEndian(offset);
pinauth_bytes.insert(pinauth_bytes.end(), offset_vec.begin(),
offset_vec.end());
......
......@@ -185,6 +185,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) VirtualCtap2Device
VirtualCtap2Device(scoped_refptr<State> state, const Config& config);
~VirtualCtap2Device() override;
// Configures and sets a PIN on the authenticator.
void SetPin(std::string pin);
// FidoDevice:
void Cancel(CancelToken) override;
CancelToken DeviceTransact(std::vector<uint8_t> command,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment