Commit 9c611482 authored by Nina Satragno's avatar Nina Satragno Committed by Chromium LUCI CQ

[fido] Don't exceed maxSerializedLargeBlobArray

Chrome should not attempt to write a large blob that exceeds the
maxSerializedLargeBlobArray reported by the authenticator.

Fixed: 1143767
Change-Id: I2cc4f553c131655e75d7cd5380092d1747e09708
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2626412
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@{#843691}
parent ed48a495
...@@ -122,6 +122,12 @@ std::vector<uint8_t> AuthenticatorGetInfoResponse::EncodeToCBOR( ...@@ -122,6 +122,12 @@ std::vector<uint8_t> AuthenticatorGetInfoResponse::EncodeToCBOR(
device_info_map.emplace(0x0a, std::move(algorithms_cbor)); device_info_map.emplace(0x0a, std::move(algorithms_cbor));
} }
if (response.max_serialized_large_blob_array) {
device_info_map.emplace(
0x0b,
base::strict_cast<int64_t>(*response.max_serialized_large_blob_array));
}
if (response.force_pin_change) { if (response.force_pin_change) {
device_info_map.emplace(0x0c, cbor::Value(*response.force_pin_change)); device_info_map.emplace(0x0c, cbor::Value(*response.force_pin_change));
} }
......
...@@ -51,6 +51,7 @@ struct COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse { ...@@ -51,6 +51,7 @@ struct COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
std::vector<int32_t> algorithms = { std::vector<int32_t> algorithms = {
static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256), static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256),
}; };
base::Optional<uint32_t> max_serialized_large_blob_array;
base::Optional<uint32_t> remaining_discoverable_credentials; base::Optional<uint32_t> remaining_discoverable_credentials;
base::Optional<bool> force_pin_change; base::Optional<bool> force_pin_change;
base::Optional<uint32_t> min_pin_length; base::Optional<uint32_t> min_pin_length;
......
...@@ -550,6 +550,16 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse( ...@@ -550,6 +550,16 @@ base::Optional<AuthenticatorGetInfoResponse> ReadCTAPGetInfoResponse(
} }
} }
it = response_map.find(CBOR(0x0b));
if (it != response_map.end()) {
if (!it->second.is_unsigned()) {
return base::nullopt;
}
response.max_serialized_large_blob_array =
base::saturated_cast<uint32_t>(it->second.GetUnsigned());
}
it = response_map.find(CBOR(0x0c)); it = response_map.find(CBOR(0x0c));
if (it != response_map.end()) { if (it != response_map.end()) {
if (!it->second.is_bool()) { if (!it->second.is_bool()) {
......
...@@ -829,8 +829,14 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite( ...@@ -829,8 +829,14 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
large_blob_array->emplace_back(std::move(new_large_blob_data)); large_blob_array->emplace_back(std::move(new_large_blob_data));
} }
WriteLargeBlobArray(std::move(pin_uv_auth_token), LargeBlobArrayWriter writer(*large_blob_array);
LargeBlobArrayWriter(*large_blob_array), if (writer.size() >
*device_->device_info()->max_serialized_large_blob_array) {
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrRequestTooLarge);
return;
}
WriteLargeBlobArray(std::move(pin_uv_auth_token), std::move(writer),
std::move(callback)); std::move(callback));
} }
......
...@@ -40,7 +40,7 @@ constexpr LargeBlobKey kDummyKey2 = {{0x02}}; ...@@ -40,7 +40,7 @@ constexpr LargeBlobKey kDummyKey2 = {{0x02}};
constexpr std::array<uint8_t, 4> kSmallBlob1 = {'r', 'o', 's', 'a'}; 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> kSmallBlob2 = {'l', 'u', 'm', 'a'};
constexpr std::array<uint8_t, 4> kSmallBlob3 = {'s', 't', 'a', 'r'}; constexpr std::array<uint8_t, 4> kSmallBlob3 = {'s', 't', 'a', 'r'};
constexpr size_t kMaxStorageSize = 4096; constexpr size_t kLargeBlobStorageSize = 4096;
constexpr char kPin[] = "1234"; constexpr char kPin[] = "1234";
class FidoDeviceAuthenticatorTest : public testing::Test { class FidoDeviceAuthenticatorTest : public testing::Test {
...@@ -50,7 +50,7 @@ class FidoDeviceAuthenticatorTest : public testing::Test { ...@@ -50,7 +50,7 @@ class FidoDeviceAuthenticatorTest : public testing::Test {
config.pin_support = true; config.pin_support = true;
config.large_blob_support = true; config.large_blob_support = true;
config.resident_key_support = true; config.resident_key_support = true;
config.available_large_blob_storage = kMaxStorageSize; config.available_large_blob_storage = kLargeBlobStorageSize;
config.pin_uv_auth_token_support = true; config.pin_uv_auth_token_support = true;
config.ctap2_versions = {Ctap2Version::kCtap2_1}; config.ctap2_versions = {Ctap2Version::kCtap2_1};
SetUpAuthenticator(std::move(config)); SetUpAuthenticator(std::move(config));
...@@ -84,8 +84,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) { ...@@ -84,8 +84,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) {
callback.callback()); callback.callback());
callback.WaitForCallback(); callback.WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess, callback.status()); EXPECT_EQ(callback.status(), CtapDeviceResponseCode::kSuccess);
EXPECT_EQ(0u, callback.value()->size()); EXPECT_EQ(callback.value()->size(), 0u);
} }
TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) { TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
...@@ -95,8 +95,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) { ...@@ -95,8 +95,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
callback.callback()); callback.callback());
callback.WaitForCallback(); callback.WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrIntegrityFailure, EXPECT_EQ(callback.status(),
callback.status()); CtapDeviceResponseCode::kCtap2ErrIntegrityFailure);
EXPECT_FALSE(callback.value()); EXPECT_FALSE(callback.value());
} }
...@@ -109,18 +109,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) { ...@@ -109,18 +109,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) {
write_callback.callback()); write_callback.callback());
write_callback.WaitForCallback(); write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value()); ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback; ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt,
read_callback.callback()); read_callback.callback());
read_callback.WaitForCallback(); read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value(); auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array); ASSERT_TRUE(large_blob_array);
ASSERT_EQ(1u, large_blob_array->size()); ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first); EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(small_blob, large_blob_array->at(0).second); EXPECT_EQ(large_blob_array->at(0).second, small_blob);
} }
// Test reading and writing a blob that must fit in multiple fragments. // Test reading and writing a blob that must fit in multiple fragments.
...@@ -136,18 +136,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) { ...@@ -136,18 +136,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) {
write_callback.callback()); write_callback.callback());
write_callback.WaitForCallback(); write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value()); ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback; ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt, authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt,
read_callback.callback()); read_callback.callback());
read_callback.WaitForCallback(); read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value(); auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array); ASSERT_TRUE(large_blob_array);
ASSERT_EQ(1u, large_blob_array->size()); ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first); EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(large_blob, large_blob_array->at(0).second); EXPECT_EQ(large_blob_array->at(0).second, large_blob);
} }
// Test reading and writing a blob using a PinUvAuthToken. // Test reading and writing a blob using a PinUvAuthToken.
...@@ -157,7 +157,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) { ...@@ -157,7 +157,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
authenticator_->GetPINToken(kPin, {pin::Permissions::kLargeBlobWrite}, authenticator_->GetPINToken(kPin, {pin::Permissions::kLargeBlobWrite},
/*rp_id=*/base::nullopt, pin_callback.callback()); /*rp_id=*/base::nullopt, pin_callback.callback());
pin_callback.WaitForCallback(); pin_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, pin_callback.status()); ASSERT_EQ(pin_callback.status(), CtapDeviceResponseCode::kSuccess);
pin::TokenResponse pin_token = *pin_callback.value(); pin::TokenResponse pin_token = *pin_callback.value();
std::vector<uint8_t> small_blob = std::vector<uint8_t> small_blob =
...@@ -166,18 +166,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) { ...@@ -166,18 +166,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, pin_token, authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, pin_token,
write_callback.callback()); write_callback.callback());
write_callback.WaitForCallback(); write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value()); ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback; ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, pin_token, authenticator_->ReadLargeBlob({kDummyKey1}, pin_token,
read_callback.callback()); read_callback.callback());
read_callback.WaitForCallback(); read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value(); auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array); ASSERT_TRUE(large_blob_array);
ASSERT_EQ(1u, large_blob_array->size()); ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first); EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(small_blob, large_blob_array->at(0).second); EXPECT_EQ(large_blob_array->at(0).second, small_blob);
} }
// Test updating a large blob in an array with multiple entries corresponding to // Test updating a large blob in an array with multiple entries corresponding to
...@@ -188,7 +188,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) { ...@@ -188,7 +188,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
{kDummyKey1}, base::nullopt, {kDummyKey1}, base::nullopt,
write_callback1.callback()); write_callback1.callback());
write_callback1.WaitForCallback(); write_callback1.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback1.value()); ASSERT_EQ(write_callback1.value(), CtapDeviceResponseCode::kSuccess);
WriteCallback write_callback2; WriteCallback write_callback2;
std::vector<uint8_t> small_blob2 = std::vector<uint8_t> small_blob2 =
...@@ -196,7 +196,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) { ...@@ -196,7 +196,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
authenticator_->WriteLargeBlob(small_blob2, {kDummyKey2}, base::nullopt, authenticator_->WriteLargeBlob(small_blob2, {kDummyKey2}, base::nullopt,
write_callback2.callback()); write_callback2.callback());
write_callback2.WaitForCallback(); write_callback2.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback2.value()); ASSERT_EQ(write_callback2.value(), CtapDeviceResponseCode::kSuccess);
// Update the first entry. // Update the first entry.
WriteCallback write_callback3; WriteCallback write_callback3;
...@@ -205,13 +205,13 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) { ...@@ -205,13 +205,13 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
authenticator_->WriteLargeBlob(small_blob3, {kDummyKey1}, base::nullopt, authenticator_->WriteLargeBlob(small_blob3, {kDummyKey1}, base::nullopt,
write_callback3.callback()); write_callback3.callback());
write_callback3.WaitForCallback(); write_callback3.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback3.value()); ASSERT_EQ(write_callback3.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback; ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1, kDummyKey2}, base::nullopt, authenticator_->ReadLargeBlob({kDummyKey1, kDummyKey2}, base::nullopt,
read_callback.callback()); read_callback.callback());
read_callback.WaitForCallback(); read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status()); ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value(); auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array); ASSERT_TRUE(large_blob_array);
EXPECT_THAT(*large_blob_array, testing::UnorderedElementsAre( EXPECT_THAT(*large_blob_array, testing::UnorderedElementsAre(
...@@ -219,6 +219,44 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) { ...@@ -219,6 +219,44 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
std::make_pair(kDummyKey2, small_blob2))); std::make_pair(kDummyKey2, small_blob2)));
} }
// Test attempting to write a large blob with a serialized size larger than the
// maximum. Chrome should not attempt writing the blob in this case.
TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlobTooLarge) {
// First write a valid blob to make sure it isn't overwritten.
WriteCallback write_callback1;
std::vector<uint8_t> small_blob =
fido_parsing_utils::Materialize(kSmallBlob1);
authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, base::nullopt,
write_callback1.callback());
write_callback1.WaitForCallback();
ASSERT_EQ(write_callback1.value(), CtapDeviceResponseCode::kSuccess);
// Then, attempt writing a blob that is too large.
std::vector<uint8_t> large_blob;
large_blob.reserve(kLargeBlobStorageSize + 1);
for (size_t i = 0; i < large_blob.capacity(); ++i) {
large_blob.emplace_back(i % 0xFF);
}
WriteCallback write_callback2;
authenticator_->WriteLargeBlob(large_blob, {kDummyKey1}, base::nullopt,
write_callback2.callback());
write_callback2.WaitForCallback();
ASSERT_EQ(write_callback2.value(),
CtapDeviceResponseCode::kCtap2ErrRequestTooLarge);
// Make sure the first blob was not overwritten.
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array);
ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(kDummyKey1, large_blob_array->at(0).first);
EXPECT_EQ(small_blob, large_blob_array->at(0).second);
}
// Tests getting a touch. // Tests getting a touch.
TEST_F(FidoDeviceAuthenticatorTest, TestGetTouch) { TEST_F(FidoDeviceAuthenticatorTest, TestGetTouch) {
for (Ctap2Version version : for (Ctap2Version version :
......
...@@ -582,6 +582,8 @@ VirtualCtap2Device::VirtualCtap2Device(scoped_refptr<State> state, ...@@ -582,6 +582,8 @@ VirtualCtap2Device::VirtualCtap2Device(scoped_refptr<State> state,
"uv-enabled authenticators"; "uv-enabled authenticators";
options_updated = true; options_updated = true;
options.supports_large_blobs = true; options.supports_large_blobs = true;
device_info_->max_serialized_large_blob_array =
config.available_large_blob_storage;
} }
if (config.always_uv) { if (config.always_uv) {
......
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