Commit b9499af0 authored by Jun Choi's avatar Jun Choi Committed by Commit Bot

Manually set credential for GetAssertion response with empty credentials

According to the CTAP spec, it is valid for external authenticators to
return response to GetAssertion request with an empty credential when
only one credential is listed in the allowed list section of the
corresponding GetAssertion request. In this case, manually set
credential id before returning the response to the relying party.

Bug: 900577
Change-Id: I9ea65eea1c1dc83d6f5312839c0d5db81b0febed
Reviewed-on: https://chromium-review.googlesource.com/c/1318973Reviewed-by: default avatarKim Paulhamus <kpaulhamus@chromium.org>
Commit-Queue: Jun Choi <hongjunchoi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#605986}
parent 52491875
......@@ -1352,6 +1352,19 @@ constexpr uint8_t kTestGetAssertionResponse[] = {
0xB2, 0x17, 0x6B, 0xBD, 0x30, 0x36, 0x59, 0xC9, 0xCD, 0x92,
};
constexpr uint8_t kTestGetAssertionResponseWithEmptyCredential[] = {
0x00, 0xA2, 0x02, 0x58, 0x25, 0x11, 0x94, 0x22, 0x8d, 0xa8, 0xfd, 0xbd,
0xee, 0xfd, 0x26, 0x1b, 0xd7, 0xb6, 0x59, 0x5c, 0xfd, 0x70, 0xa5, 0x0d,
0x70, 0xc6, 0x40, 0x7b, 0xcf, 0x01, 0x3d, 0xe9, 0x6d, 0x4e, 0xfb, 0x17,
0xde, 0x01, 0x00, 0x00, 0x00, 0x5F, 0x03, 0x58, 0x46, 0x30, 0x44, 0x02,
0x20, 0x62, 0xB8, 0xC4, 0x37, 0xB0, 0xB6, 0xFC, 0x89, 0x37, 0xF6, 0x45,
0xC0, 0x1E, 0x26, 0xCE, 0x0E, 0x26, 0x58, 0x38, 0xFE, 0xC4, 0xA8, 0x74,
0xC5, 0x5D, 0xDD, 0x6D, 0xEC, 0xF0, 0xA0, 0x83, 0xD3, 0x02, 0x20, 0x7C,
0xD4, 0x1C, 0xAF, 0x4F, 0xD8, 0x7F, 0x73, 0xBF, 0x01, 0x25, 0x06, 0x78,
0x11, 0x45, 0x2B, 0x5F, 0xB8, 0x17, 0xA3, 0xFA, 0x73, 0xB2, 0x17, 0x6B,
0xBD, 0x30, 0x36, 0x59, 0xC9, 0xCD, 0x92,
};
constexpr uint8_t kTestGetAssertionResponseWithIncorrectRpIdHash[] = {
0x00, 0xA3, 0x01, 0xA2, 0x62, 0x69, 0x64, 0x58, 0x40, 0x9C, 0x06, 0x98,
0x05, 0xA7, 0xE9, 0x0C, 0xED, 0xF9, 0x24, 0xAC, 0x5A, 0x29, 0x36, 0x95,
......
......@@ -326,6 +326,32 @@ TEST_F(FidoGetAssertionHandlerTest, InvalidCredential) {
EXPECT_FALSE(get_assertion_callback().was_called());
}
// Tests a scenario where the authenticator responds with an empty credential.
// When GetAssertion request only has a single credential in the allow list,
// this is a valid response. Check that credential is set by the client before
// the response is returned to the relying party.
TEST_F(FidoGetAssertionHandlerTest, ValidEmptyCredential) {
auto request_handler = CreateGetAssertionHandlerCtap();
discovery()->WaitForCallToStartAndSimulateSuccess();
// Resident Keys must be disabled, otherwise allow list check is skipped.
auto device = MockFidoDevice::MakeCtapWithGetInfoExpectation(
test_data::kTestGetInfoResponseWithoutResidentKeySupport);
device->ExpectCtap2CommandAndRespondWith(
CtapRequestCommand::kAuthenticatorGetAssertion,
test_data::kTestGetAssertionResponseWithEmptyCredential);
discovery()->AddDevice(std::move(device));
get_assertion_callback().WaitForCallback();
const auto& response = get_assertion_callback().value<0>();
EXPECT_TRUE(request_handler->is_complete());
EXPECT_EQ(FidoReturnCode::kSuccess, get_assertion_callback().status());
ASSERT_TRUE(response);
EXPECT_TRUE(response->credential());
EXPECT_THAT(
response->raw_credential_id(),
::testing::ElementsAreArray(test_data::kTestGetAssertionCredentialId));
}
// Tests a scenario where authenticator responds without user entity in its
// response but client is expecting a resident key credential.
TEST_F(FidoGetAssertionHandlerTest, IncorrectUserEntity) {
......
......@@ -85,6 +85,18 @@ bool CheckResponseCredentialIdMatchesRequestAllowList(
});
}
// When the response from the authenticator does not contain a credential and
// the allow list from the GetAssertion request only contains a single
// credential id, manually set credential id in the returned response.
void SetCredentialIdForResponseWithEmptyCredential(
const CtapGetAssertionRequest& request,
AuthenticatorGetAssertionResponse& response) {
if (request.allow_list() && request.allow_list()->size() == 1 &&
!response.credential()) {
response.SetCredential(request.allow_list()->at(0));
}
}
// Checks UserVerificationRequirement enum passed from the relying party is
// compatible with the authenticator, and updates the request to the
// "effective" user verification requirement.
......@@ -220,6 +232,7 @@ void GetAssertionRequestHandler::HandleResponse(
return;
}
SetCredentialIdForResponseWithEmptyCredential(request_, *response);
OnAuthenticatorResponse(authenticator, response_code, std::move(response));
}
......
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