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(
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) {
device_info_map.emplace(0x0c, cbor::Value(*response.force_pin_change));
}
......
......@@ -51,6 +51,7 @@ struct COMPONENT_EXPORT(DEVICE_FIDO) AuthenticatorGetInfoResponse {
std::vector<int32_t> algorithms = {
static_cast<int32_t>(CoseAlgorithmIdentifier::kEs256),
};
base::Optional<uint32_t> max_serialized_large_blob_array;
base::Optional<uint32_t> remaining_discoverable_credentials;
base::Optional<bool> force_pin_change;
base::Optional<uint32_t> min_pin_length;
......
......@@ -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));
if (it != response_map.end()) {
if (!it->second.is_bool()) {
......
......@@ -829,8 +829,14 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
large_blob_array->emplace_back(std::move(new_large_blob_data));
}
WriteLargeBlobArray(std::move(pin_uv_auth_token),
LargeBlobArrayWriter(*large_blob_array),
LargeBlobArrayWriter writer(*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));
}
......
......@@ -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> kSmallBlob2 = {'l', 'u', 'm', 'a'};
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";
class FidoDeviceAuthenticatorTest : public testing::Test {
......@@ -50,7 +50,7 @@ class FidoDeviceAuthenticatorTest : public testing::Test {
config.pin_support = true;
config.large_blob_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.ctap2_versions = {Ctap2Version::kCtap2_1};
SetUpAuthenticator(std::move(config));
......@@ -84,8 +84,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadEmptyLargeBlob) {
callback.callback());
callback.WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kSuccess, callback.status());
EXPECT_EQ(0u, callback.value()->size());
EXPECT_EQ(callback.status(), CtapDeviceResponseCode::kSuccess);
EXPECT_EQ(callback.value()->size(), 0u);
}
TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
......@@ -95,8 +95,8 @@ TEST_F(FidoDeviceAuthenticatorTest, TestReadInvalidLargeBlob) {
callback.callback());
callback.WaitForCallback();
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrIntegrityFailure,
callback.status());
EXPECT_EQ(callback.status(),
CtapDeviceResponseCode::kCtap2ErrIntegrityFailure);
EXPECT_FALSE(callback.value());
}
......@@ -109,18 +109,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlob) {
write_callback.callback());
write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
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);
ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(large_blob_array->at(0).second, small_blob);
}
// Test reading and writing a blob that must fit in multiple fragments.
......@@ -136,18 +136,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteLargeBlob) {
write_callback.callback());
write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, base::nullopt,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
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(large_blob, large_blob_array->at(0).second);
ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(large_blob_array->at(0).second, large_blob);
}
// Test reading and writing a blob using a PinUvAuthToken.
......@@ -157,7 +157,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
authenticator_->GetPINToken(kPin, {pin::Permissions::kLargeBlobWrite},
/*rp_id=*/base::nullopt, pin_callback.callback());
pin_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, pin_callback.status());
ASSERT_EQ(pin_callback.status(), CtapDeviceResponseCode::kSuccess);
pin::TokenResponse pin_token = *pin_callback.value();
std::vector<uint8_t> small_blob =
......@@ -166,18 +166,18 @@ TEST_F(FidoDeviceAuthenticatorTest, TestWriteSmallBlobWithToken) {
authenticator_->WriteLargeBlob(small_blob, {kDummyKey1}, pin_token,
write_callback.callback());
write_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback.value());
ASSERT_EQ(write_callback.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1}, pin_token,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
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);
ASSERT_EQ(large_blob_array->size(), 1u);
EXPECT_EQ(large_blob_array->at(0).first, kDummyKey1);
EXPECT_EQ(large_blob_array->at(0).second, small_blob);
}
// Test updating a large blob in an array with multiple entries corresponding to
......@@ -188,7 +188,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
{kDummyKey1}, base::nullopt,
write_callback1.callback());
write_callback1.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback1.value());
ASSERT_EQ(write_callback1.value(), CtapDeviceResponseCode::kSuccess);
WriteCallback write_callback2;
std::vector<uint8_t> small_blob2 =
......@@ -196,7 +196,7 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
authenticator_->WriteLargeBlob(small_blob2, {kDummyKey2}, base::nullopt,
write_callback2.callback());
write_callback2.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback2.value());
ASSERT_EQ(write_callback2.value(), CtapDeviceResponseCode::kSuccess);
// Update the first entry.
WriteCallback write_callback3;
......@@ -205,13 +205,13 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
authenticator_->WriteLargeBlob(small_blob3, {kDummyKey1}, base::nullopt,
write_callback3.callback());
write_callback3.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, write_callback3.value());
ASSERT_EQ(write_callback3.value(), CtapDeviceResponseCode::kSuccess);
ReadCallback read_callback;
authenticator_->ReadLargeBlob({kDummyKey1, kDummyKey2}, base::nullopt,
read_callback.callback());
read_callback.WaitForCallback();
ASSERT_EQ(CtapDeviceResponseCode::kSuccess, read_callback.status());
ASSERT_EQ(read_callback.status(), CtapDeviceResponseCode::kSuccess);
auto large_blob_array = read_callback.value();
ASSERT_TRUE(large_blob_array);
EXPECT_THAT(*large_blob_array, testing::UnorderedElementsAre(
......@@ -219,6 +219,44 @@ TEST_F(FidoDeviceAuthenticatorTest, TestUpdateLargeBlob) {
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.
TEST_F(FidoDeviceAuthenticatorTest, TestGetTouch) {
for (Ctap2Version version :
......
......@@ -582,6 +582,8 @@ VirtualCtap2Device::VirtualCtap2Device(scoped_refptr<State> state,
"uv-enabled authenticators";
options_updated = true;
options.supports_large_blobs = true;
device_info_->max_serialized_large_blob_array =
config.available_large_blob_storage;
}
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