[webauthn] Fix PRF extension for multiple creds
Fix evaluating the WebAuthn PRF extension for credentials returned by
GetNextAssertion.
Fixed: 1520646
Change-Id: Iad653f0b84d47149b598efcbe78177eef9acceea
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5239134
Auto-Submit: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: Adam Langley <agl@chromium.org>
Commit-Queue: Adam Langley <agl@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1255690}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
da56b501bf
commit
8d73f1974b
@ -8182,6 +8182,99 @@ TEST_F(ResidentKeyAuthenticatorImplTest, PRFExtension) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the PRF function is evaluated for all credentials in an empty
|
||||
// allow-list request. Regression test for crbug.com/1520646.
|
||||
TEST_F(ResidentKeyAuthenticatorImplTest, PRFEvaluationForMultipleCreds) {
|
||||
NavigateAndCommit(GURL(kTestOrigin1));
|
||||
device::PublicKeyCredentialDescriptor cred1;
|
||||
device::PublicKeyCredentialDescriptor cred2;
|
||||
device::VirtualCtap2Device::Config config;
|
||||
config.prf_support = false;
|
||||
config.hmac_secret_support = true;
|
||||
config.pin_support = true;
|
||||
config.resident_key_support = true;
|
||||
virtual_device_factory_->SetCtap2Config(config);
|
||||
{
|
||||
PublicKeyCredentialCreationOptionsPtr options =
|
||||
GetTestPublicKeyCredentialCreationOptions();
|
||||
options->prf_enable = true;
|
||||
options->authenticator_selection->resident_key =
|
||||
device::ResidentKeyRequirement::kRequired;
|
||||
options->user.id = {1};
|
||||
options->user.name = "noah";
|
||||
options->user.display_name = "Noah";
|
||||
MakeCredentialResult result =
|
||||
AuthenticatorMakeCredential(std::move(options));
|
||||
EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
|
||||
ASSERT_TRUE(result.response->echo_prf);
|
||||
ASSERT_EQ(result.response->prf, true);
|
||||
device::AuthenticatorData auth_data =
|
||||
AuthDataFromMakeCredentialResponse(result.response);
|
||||
cred1 = device::PublicKeyCredentialDescriptor(
|
||||
device::CredentialType::kPublicKey, auth_data.GetCredentialId());
|
||||
}
|
||||
{
|
||||
PublicKeyCredentialCreationOptionsPtr options =
|
||||
GetTestPublicKeyCredentialCreationOptions();
|
||||
options->prf_enable = true;
|
||||
options->authenticator_selection->resident_key =
|
||||
device::ResidentKeyRequirement::kRequired;
|
||||
options->user.id = {2};
|
||||
options->user.name = "mio";
|
||||
options->user.display_name = "Mio";
|
||||
MakeCredentialResult result =
|
||||
AuthenticatorMakeCredential(std::move(options));
|
||||
EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
|
||||
ASSERT_TRUE(result.response->echo_prf);
|
||||
ASSERT_EQ(result.response->prf, true);
|
||||
device::AuthenticatorData auth_data =
|
||||
AuthDataFromMakeCredentialResponse(result.response);
|
||||
cred2 = device::PublicKeyCredentialDescriptor(
|
||||
device::CredentialType::kPublicKey, auth_data.GetCredentialId());
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> salt(32, 1);
|
||||
std::vector<uint8_t> salt1_eval;
|
||||
std::vector<uint8_t> salt2_eval;
|
||||
{
|
||||
PublicKeyCredentialRequestOptionsPtr options =
|
||||
GetTestPublicKeyCredentialRequestOptions();
|
||||
options->extensions->prf = true;
|
||||
auto prf_value = blink::mojom::PRFValues::New();
|
||||
prf_value->first = salt;
|
||||
std::vector<blink::mojom::PRFValuesPtr> inputs;
|
||||
inputs.emplace_back(std::move(prf_value));
|
||||
options->extensions->prf_inputs = std::move(inputs);
|
||||
options->allow_credentials.clear();
|
||||
test_client_.delegate_config.expected_accounts = "01:noah:Noah/02:mio:Mio";
|
||||
test_client_.delegate_config.selected_user_id = {1};
|
||||
GetAssertionResult result = AuthenticatorGetAssertion(std::move(options));
|
||||
EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
|
||||
ASSERT_TRUE(result.response->extensions->prf_results);
|
||||
ASSERT_FALSE(result.response->extensions->prf_results->id);
|
||||
salt1_eval = result.response->extensions->prf_results->first;
|
||||
}
|
||||
{
|
||||
PublicKeyCredentialRequestOptionsPtr options =
|
||||
GetTestPublicKeyCredentialRequestOptions();
|
||||
options->extensions->prf = true;
|
||||
auto prf_value = blink::mojom::PRFValues::New();
|
||||
prf_value->first = salt;
|
||||
std::vector<blink::mojom::PRFValuesPtr> inputs;
|
||||
inputs.emplace_back(std::move(prf_value));
|
||||
options->extensions->prf_inputs = std::move(inputs);
|
||||
options->allow_credentials.clear();
|
||||
test_client_.delegate_config.expected_accounts = "01:noah:Noah/02:mio:Mio";
|
||||
test_client_.delegate_config.selected_user_id = {2};
|
||||
GetAssertionResult result = AuthenticatorGetAssertion(std::move(options));
|
||||
EXPECT_EQ(result.status, AuthenticatorStatus::SUCCESS);
|
||||
ASSERT_TRUE(result.response->extensions->prf_results);
|
||||
ASSERT_FALSE(result.response->extensions->prf_results->id);
|
||||
salt2_eval = result.response->extensions->prf_results->first;
|
||||
}
|
||||
EXPECT_NE(salt1_eval, salt2_eval);
|
||||
}
|
||||
|
||||
TEST_F(ResidentKeyAuthenticatorImplTest, PRFEvaluationDuringMakeCredential) {
|
||||
// The WebAuthn "prf" extension supports evaluating the PRF when making a
|
||||
// credential. The hmac-secret extension does not support this, but hybrid
|
||||
|
@ -121,7 +121,7 @@ void FidoDeviceAuthenticator::InitializeAuthenticatorDone(
|
||||
void FidoDeviceAuthenticator::ExcludeAppIdCredentialsBeforeMakeCredential(
|
||||
CtapMakeCredentialRequest request,
|
||||
MakeCredentialOptions options,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<bool>)>
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, std::optional<bool>)>
|
||||
callback) {
|
||||
// If the device (or request) is U2F-only then |MakeCredential| will handle
|
||||
// the AppID-excluded credentials, if any. There's no interaction with PUATs
|
||||
@ -131,7 +131,7 @@ void FidoDeviceAuthenticator::ExcludeAppIdCredentialsBeforeMakeCredential(
|
||||
// without a PUAT, so they aren't excluded here.)
|
||||
if (!MakeCredentialTask::WillUseCTAP2(device_.get(), request, options) ||
|
||||
device_->NoSilentRequests()) {
|
||||
std::move(callback).Run(CtapDeviceResponseCode::kSuccess, absl::nullopt);
|
||||
std::move(callback).Run(CtapDeviceResponseCode::kSuccess, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -140,7 +140,7 @@ void FidoDeviceAuthenticator::ExcludeAppIdCredentialsBeforeMakeCredential(
|
||||
// Therefore appidExclude probing has to happen before the PUAT is obtained.
|
||||
// For CTAP 2.0 devices we follow the same pattern, even though a PIN token
|
||||
// doesn't have that issue.
|
||||
RunTask<AppIdExcludeProbeTask, bool, CtapMakeCredentialRequest,
|
||||
RunTask<AppIdExcludeProbeTask, std::optional<bool>, CtapMakeCredentialRequest,
|
||||
MakeCredentialOptions>(std::move(request), std::move(options),
|
||||
std::move(callback));
|
||||
}
|
||||
@ -161,7 +161,8 @@ void FidoDeviceAuthenticator::MakeCredential(
|
||||
request.user_verification = UserVerificationRequirement::kDiscouraged;
|
||||
}
|
||||
|
||||
RunTask<MakeCredentialTask, AuthenticatorMakeCredentialResponse,
|
||||
RunTask<MakeCredentialTask,
|
||||
std::optional<AuthenticatorMakeCredentialResponse>,
|
||||
CtapMakeCredentialRequest, MakeCredentialOptions>(
|
||||
std::move(request), std::move(request_options), std::move(callback));
|
||||
}
|
||||
@ -216,7 +217,7 @@ void FidoDeviceAuthenticator::OnHaveCompressedLargeBlobForGetAssertion(
|
||||
// sent directly in the request. Otherwise it's saved in `large_blob_` to
|
||||
// be written after the request, using the result of the `largeBlobKey`
|
||||
// extension.
|
||||
absl::optional<LargeBlob>* destination;
|
||||
std::optional<LargeBlob>* destination;
|
||||
if (options_.large_blob_type == LargeBlobSupportType::kExtension) {
|
||||
destination = &request.large_blob_extension_write;
|
||||
} else {
|
||||
@ -251,7 +252,7 @@ void FidoDeviceAuthenticator::OnHaveEphemeralKeyForGetAssertion(
|
||||
CtapGetAssertionOptions options,
|
||||
GetAssertionCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::KeyAgreementResponse> key) {
|
||||
std::optional<pin::KeyAgreementResponse> key) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, {});
|
||||
return;
|
||||
@ -280,49 +281,28 @@ void FidoDeviceAuthenticator::DoGetAssertion(CtapGetAssertionRequest request,
|
||||
|
||||
CtapGetAssertionRequest request_copy(request);
|
||||
CtapGetAssertionOptions options_copy(options);
|
||||
RunTask<GetAssertionTask, AuthenticatorGetAssertionResponse,
|
||||
RunTask<GetAssertionTask, std::vector<AuthenticatorGetAssertionResponse>,
|
||||
CtapGetAssertionRequest, CtapGetAssertionOptions>(
|
||||
std::move(request), std::move(options),
|
||||
base::BindOnce(&FidoDeviceAuthenticator::OnHaveNextAssertion,
|
||||
base::BindOnce(&FidoDeviceAuthenticator::OnHaveAssertion,
|
||||
weak_factory_.GetWeakPtr(), std::move(request_copy),
|
||||
std::move(options_copy),
|
||||
std::vector<AuthenticatorGetAssertionResponse>{},
|
||||
std::move(callback)));
|
||||
std::move(options_copy), std::move(callback)));
|
||||
}
|
||||
|
||||
void FidoDeviceAuthenticator::OnHaveNextAssertion(
|
||||
void FidoDeviceAuthenticator::OnHaveAssertion(
|
||||
CtapGetAssertionRequest request,
|
||||
CtapGetAssertionOptions options,
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses,
|
||||
GetAssertionCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<AuthenticatorGetAssertionResponse> response) {
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, {});
|
||||
return;
|
||||
}
|
||||
responses.emplace_back(std::move(*response));
|
||||
uint8_t num_responses = responses.at(0).num_credentials.value_or(1u);
|
||||
if (num_responses == 0 ||
|
||||
(num_responses > 1 && !request.allow_list.empty())) {
|
||||
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, {});
|
||||
return;
|
||||
}
|
||||
if (responses.size() >= num_responses) {
|
||||
PerformGetAssertionLargeBlobOperation(
|
||||
std::move(request), std::move(options), std::move(responses),
|
||||
std::move(callback));
|
||||
return;
|
||||
}
|
||||
// Read the next response.
|
||||
RunOperation<CtapGetNextAssertionRequest, AuthenticatorGetAssertionResponse>(
|
||||
CtapGetNextAssertionRequest(),
|
||||
base::BindOnce(&FidoDeviceAuthenticator::OnHaveNextAssertion,
|
||||
weak_factory_.GetWeakPtr(), std::move(request),
|
||||
std::move(options), std::move(responses),
|
||||
std::move(callback)),
|
||||
base::BindOnce(&ReadCTAPGetAssertionResponse, device_->DeviceTransport()),
|
||||
GetAssertionTask::StringFixupPredicate);
|
||||
PerformGetAssertionLargeBlobOperation(std::move(request), std::move(options),
|
||||
std::move(responses),
|
||||
std::move(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
void FidoDeviceAuthenticator::PerformGetAssertionLargeBlobOperation(
|
||||
@ -406,7 +386,7 @@ void FidoDeviceAuthenticator::GetTouch(base::OnceClosure callback) {
|
||||
base::BindOnce(
|
||||
[](std::string authenticator_id, base::OnceClosure callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::EmptyResponse> _) {
|
||||
std::optional<pin::EmptyResponse> _) {
|
||||
if (status == CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run();
|
||||
return;
|
||||
@ -423,7 +403,7 @@ void FidoDeviceAuthenticator::GetTouch(base::OnceClosure callback) {
|
||||
base::BindOnce(
|
||||
[](std::string authenticator_id, base::OnceCallback<void()> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<AuthenticatorMakeCredentialResponse>) {
|
||||
std::optional<AuthenticatorMakeCredentialResponse>) {
|
||||
// If the device didn't understand/process the request it may
|
||||
// fail immediately. Rather than count that as a touch, ignore
|
||||
// those cases completely.
|
||||
@ -466,7 +446,7 @@ void FidoDeviceAuthenticator::GetEphemeralKey(
|
||||
void FidoDeviceAuthenticator::GetPINToken(
|
||||
std::string pin,
|
||||
std::vector<pin::Permissions> permissions,
|
||||
absl::optional<std::string> rp_id,
|
||||
std::optional<std::string> rp_id,
|
||||
GetTokenCallback callback) {
|
||||
DCHECK(options_.client_pin_availability !=
|
||||
ClientPinAvailability::kNotSupported);
|
||||
@ -484,12 +464,12 @@ void FidoDeviceAuthenticator::GetPINToken(
|
||||
void FidoDeviceAuthenticator::OnHaveEphemeralKeyForGetPINToken(
|
||||
std::string pin,
|
||||
std::vector<pin::Permissions> permissions,
|
||||
absl::optional<std::string> rp_id,
|
||||
std::optional<std::string> rp_id,
|
||||
GetTokenCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::KeyAgreementResponse> key) {
|
||||
std::optional<pin::KeyAgreementResponse> key) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -526,9 +506,9 @@ void FidoDeviceAuthenticator::OnHaveEphemeralKeyForSetPIN(
|
||||
std::string pin,
|
||||
SetPINCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::KeyAgreementResponse> key) {
|
||||
std::optional<pin::KeyAgreementResponse> key) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -554,9 +534,9 @@ void FidoDeviceAuthenticator::OnHaveEphemeralKeyForChangePIN(
|
||||
std::string new_pin,
|
||||
SetPINCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::KeyAgreementResponse> key) {
|
||||
std::optional<pin::KeyAgreementResponse> key) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -761,8 +741,7 @@ void FidoDeviceAuthenticator::OperationClearProxy(
|
||||
template <typename Task, typename Response, typename... RequestArgs>
|
||||
void FidoDeviceAuthenticator::RunTask(
|
||||
RequestArgs&&... request_args,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<Response>)>
|
||||
callback) {
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, Response)> callback) {
|
||||
DCHECK(!task_);
|
||||
DCHECK(!operation_);
|
||||
DCHECK(initialized_);
|
||||
@ -771,7 +750,7 @@ void FidoDeviceAuthenticator::RunTask(
|
||||
device_.get(), std::forward<RequestArgs>(request_args)...,
|
||||
base::BindOnce(
|
||||
&FidoDeviceAuthenticator::TaskClearProxy<CtapDeviceResponseCode,
|
||||
absl::optional<Response>>,
|
||||
Response>,
|
||||
weak_factory_.GetWeakPtr(), std::move(callback)));
|
||||
}
|
||||
|
||||
@ -780,10 +759,10 @@ void FidoDeviceAuthenticator::RunTask(
|
||||
template <typename Request, typename Response>
|
||||
void FidoDeviceAuthenticator::RunOperation(
|
||||
Request request,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, absl::optional<Response>)>
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, std::optional<Response>)>
|
||||
callback,
|
||||
base::OnceCallback<
|
||||
absl::optional<Response>(const absl::optional<cbor::Value>&)> parser,
|
||||
std::optional<Response>(const absl::optional<cbor::Value>&)> parser,
|
||||
bool (*string_fixup_predicate)(const std::vector<const cbor::Value*>&)) {
|
||||
DCHECK(!task_);
|
||||
DCHECK(!operation_);
|
||||
@ -792,7 +771,7 @@ void FidoDeviceAuthenticator::RunOperation(
|
||||
operation_ = std::make_unique<Ctap2DeviceOperation<Request, Response>>(
|
||||
device_.get(), std::move(request),
|
||||
base::BindOnce(&FidoDeviceAuthenticator::OperationClearProxy<
|
||||
CtapDeviceResponseCode, absl::optional<Response>>,
|
||||
CtapDeviceResponseCode, std::optional<Response>>,
|
||||
weak_factory_.GetWeakPtr(), std::move(callback)),
|
||||
std::move(parser), string_fixup_predicate);
|
||||
operation_->Start();
|
||||
@ -801,12 +780,12 @@ void FidoDeviceAuthenticator::RunOperation(
|
||||
void FidoDeviceAuthenticator::OnEnumerateRPsDone(
|
||||
EnumerateCredentialsState state,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<EnumerateRPsResponse> response) {
|
||||
std::optional<EnumerateRPsResponse> response) {
|
||||
DCHECK_EQ(state.rp_id_hashes.size(), state.responses.size());
|
||||
DCHECK_LE(state.rp_id_hashes.size(), state.rp_count);
|
||||
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(state.callback).Run(status, absl::nullopt);
|
||||
std::move(state.callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
if (state.rp_count == 0) {
|
||||
@ -849,13 +828,13 @@ void FidoDeviceAuthenticator::OnEnumerateRPsDone(
|
||||
void FidoDeviceAuthenticator::OnEnumerateCredentialsDone(
|
||||
EnumerateCredentialsState state,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<EnumerateCredentialsResponse> response) {
|
||||
std::optional<EnumerateCredentialsResponse> response) {
|
||||
DCHECK_EQ(state.rp_id_hashes.size(), state.responses.size());
|
||||
DCHECK_EQ(state.rp_id_hashes.size(), state.rp_count);
|
||||
DCHECK_LT(state.current_rp, state.rp_count);
|
||||
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(state.callback).Run(status, absl::nullopt);
|
||||
std::move(state.callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -958,7 +937,7 @@ void FidoDeviceAuthenticator::GetSensorInfo(BioEnrollmentCallback callback) {
|
||||
|
||||
void FidoDeviceAuthenticator::BioEnrollFingerprint(
|
||||
const pin::TokenResponse& pin_token,
|
||||
absl::optional<std::vector<uint8_t>> template_id,
|
||||
std::optional<std::vector<uint8_t>> template_id,
|
||||
BioEnrollmentCallback callback) {
|
||||
DCHECK(chosen_pin_uv_auth_protocol_ == pin_token.protocol());
|
||||
|
||||
@ -1029,7 +1008,7 @@ void FidoDeviceAuthenticator::OnReadLargeBlobForGetAssertion(
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses,
|
||||
GetAssertionCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> blobs) {
|
||||
std::optional<std::vector<std::pair<LargeBlobKey, LargeBlob>>> blobs) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
FIDO_LOG(ERROR) << "Reading large blob failed with code "
|
||||
<< static_cast<int>(status);
|
||||
@ -1127,7 +1106,7 @@ void FidoDeviceAuthenticator::GarbageCollectLargeBlob(
|
||||
void FidoDeviceAuthenticator::FetchLargeBlobArray(
|
||||
LargeBlobArrayReader large_blob_array_reader,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode,
|
||||
absl::optional<LargeBlobArrayReader>)> callback) {
|
||||
std::optional<LargeBlobArrayReader>)> callback) {
|
||||
size_t bytes_to_read = max_large_blob_fragment_length();
|
||||
LargeBlobsRequest request =
|
||||
LargeBlobsRequest::ForRead(bytes_to_read, large_blob_array_reader.size());
|
||||
@ -1143,11 +1122,11 @@ void FidoDeviceAuthenticator::OnReadLargeBlobFragment(
|
||||
const size_t bytes_requested,
|
||||
LargeBlobArrayReader large_blob_array_reader,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode,
|
||||
absl::optional<LargeBlobArrayReader>)> callback,
|
||||
std::optional<LargeBlobArrayReader>)> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<LargeBlobsResponse> response) {
|
||||
std::optional<LargeBlobsResponse> response) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1167,16 +1146,16 @@ void FidoDeviceAuthenticator::OnReadLargeBlobFragment(
|
||||
|
||||
void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
|
||||
const LargeBlobKey& large_blob_key,
|
||||
const absl::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
const std::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode)> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
std::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status);
|
||||
return;
|
||||
}
|
||||
|
||||
absl::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
std::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
large_blob_array_reader->Materialize();
|
||||
if (!large_blob_array) {
|
||||
FIDO_LOG(ERROR) << "Large blob array corrupted. Replacing with a new one";
|
||||
@ -1185,7 +1164,7 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
|
||||
|
||||
auto existing_large_blob = base::ranges::find_if(
|
||||
*large_blob_array, [&large_blob_key](const cbor::Value& blob_cbor) {
|
||||
absl::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
return blob && blob->Decrypt(large_blob_key).has_value();
|
||||
});
|
||||
|
||||
@ -1212,7 +1191,7 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForWrite(
|
||||
}
|
||||
|
||||
void FidoDeviceAuthenticator::WriteLargeBlobArray(
|
||||
const absl::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
const std::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
LargeBlobArrayWriter large_blob_array_writer,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode)> callback) {
|
||||
LargeBlobArrayFragment fragment =
|
||||
@ -1235,10 +1214,10 @@ void FidoDeviceAuthenticator::WriteLargeBlobArray(
|
||||
|
||||
void FidoDeviceAuthenticator::OnWriteLargeBlobFragment(
|
||||
LargeBlobArrayWriter large_blob_array_writer,
|
||||
const absl::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
const std::optional<pin::TokenResponse> pin_uv_auth_token,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode)> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<LargeBlobsResponse> response) {
|
||||
std::optional<LargeBlobsResponse> response) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status);
|
||||
return;
|
||||
@ -1258,28 +1237,28 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForRead(
|
||||
const std::vector<LargeBlobKey>& large_blob_keys,
|
||||
LargeBlobReadCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
std::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
absl::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
std::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
large_blob_array_reader->Materialize();
|
||||
if (!large_blob_array) {
|
||||
std::move(callback).Run(CtapDeviceResponseCode::kCtap2ErrIntegrityFailure,
|
||||
absl::nullopt);
|
||||
std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<LargeBlobKey, LargeBlob>> result;
|
||||
for (const cbor::Value& blob_cbor : *large_blob_array) {
|
||||
absl::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
if (!blob.has_value()) {
|
||||
continue;
|
||||
}
|
||||
for (const LargeBlobKey& key : large_blob_keys) {
|
||||
absl::optional<LargeBlob> plaintext = blob->Decrypt(key);
|
||||
std::optional<LargeBlob> plaintext = blob->Decrypt(key);
|
||||
if (plaintext) {
|
||||
result.emplace_back(key, std::move(*plaintext));
|
||||
break;
|
||||
@ -1294,7 +1273,7 @@ void FidoDeviceAuthenticator::OnCredentialsEnumeratedForGarbageCollect(
|
||||
const pin::TokenResponse& pin_uv_auth_token,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode)> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
|
||||
std::optional<std::vector<AggregatedEnumerateCredentialsResponse>>
|
||||
credentials) {
|
||||
if (status == CtapDeviceResponseCode::kCtap2ErrNoCredentials) {
|
||||
credentials.emplace();
|
||||
@ -1316,13 +1295,13 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForGarbageCollect(
|
||||
const pin::TokenResponse& pin_uv_auth_token,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode)> callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
std::optional<LargeBlobArrayReader> large_blob_array_reader) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status);
|
||||
return;
|
||||
}
|
||||
|
||||
absl::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
std::optional<cbor::Value::ArrayValue> large_blob_array =
|
||||
large_blob_array_reader->Materialize();
|
||||
if (!large_blob_array) {
|
||||
FIDO_LOG(ERROR) << "Large blob array corrupted. Replacing with a new one";
|
||||
@ -1341,7 +1320,7 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForGarbageCollect(
|
||||
}
|
||||
bool did_erase = base::EraseIf(
|
||||
*large_blob_array, [&large_blob_keys](const cbor::Value& blob_cbor) {
|
||||
absl::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
std::optional<LargeBlobData> blob = LargeBlobData::Parse(blob_cbor);
|
||||
return blob &&
|
||||
base::ranges::none_of(
|
||||
large_blob_keys,
|
||||
@ -1365,7 +1344,7 @@ void FidoDeviceAuthenticator::OnHaveLargeBlobArrayForGarbageCollect(
|
||||
std::move(callback));
|
||||
}
|
||||
|
||||
absl::optional<base::span<const int32_t>>
|
||||
std::optional<base::span<const int32_t>>
|
||||
FidoDeviceAuthenticator::GetAlgorithms() {
|
||||
if (device_->supported_protocol() == ProtocolVersion::kU2f) {
|
||||
static constexpr int32_t kU2fAlgorithms[1] = {
|
||||
@ -1373,12 +1352,12 @@ FidoDeviceAuthenticator::GetAlgorithms() {
|
||||
return kU2fAlgorithms;
|
||||
}
|
||||
|
||||
const absl::optional<AuthenticatorGetInfoResponse>& get_info_response =
|
||||
const std::optional<AuthenticatorGetInfoResponse>& get_info_response =
|
||||
device_->device_info();
|
||||
if (get_info_response) {
|
||||
return get_info_response->algorithms;
|
||||
}
|
||||
return absl::nullopt;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
bool FidoDeviceAuthenticator::DiscoverableCredentialStorageFull() const {
|
||||
@ -1425,7 +1404,7 @@ const AuthenticatorSupportedOptions& FidoDeviceAuthenticator::Options() const {
|
||||
return options_;
|
||||
}
|
||||
|
||||
absl::optional<FidoTransportProtocol>
|
||||
std::optional<FidoTransportProtocol>
|
||||
FidoDeviceAuthenticator::AuthenticatorTransport() const {
|
||||
return device_->DeviceTransport();
|
||||
}
|
||||
@ -1454,7 +1433,7 @@ bool FidoDeviceAuthenticator::CanGetUvToken() {
|
||||
|
||||
void FidoDeviceAuthenticator::GetUvToken(
|
||||
std::vector<pin::Permissions> permissions,
|
||||
absl::optional<std::string> rp_id,
|
||||
std::optional<std::string> rp_id,
|
||||
GetTokenCallback callback) {
|
||||
GetEphemeralKey(
|
||||
base::BindOnce(&FidoDeviceAuthenticator::OnHaveEphemeralKeyForUvToken,
|
||||
@ -1475,13 +1454,13 @@ bool FidoDeviceAuthenticator::ForcePINChange() {
|
||||
}
|
||||
|
||||
void FidoDeviceAuthenticator::OnHaveEphemeralKeyForUvToken(
|
||||
absl::optional<std::string> rp_id,
|
||||
std::optional<std::string> rp_id,
|
||||
std::vector<pin::Permissions> permissions,
|
||||
GetTokenCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<pin::KeyAgreementResponse> key) {
|
||||
std::optional<pin::KeyAgreementResponse> key) {
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback).Run(status, absl::nullopt);
|
||||
std::move(callback).Run(status, std::nullopt);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -158,13 +158,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
|
||||
void MaybeGetEphemeralKeyForGetAssertion(CtapGetAssertionRequest request,
|
||||
CtapGetAssertionOptions options,
|
||||
GetAssertionCallback callback);
|
||||
void OnHaveNextAssertion(
|
||||
void OnHaveAssertion(
|
||||
CtapGetAssertionRequest request,
|
||||
CtapGetAssertionOptions options,
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses,
|
||||
GetAssertionCallback callback,
|
||||
CtapDeviceResponseCode status,
|
||||
absl::optional<AuthenticatorGetAssertionResponse> response);
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses);
|
||||
void PerformGetAssertionLargeBlobOperation(
|
||||
CtapGetAssertionRequest request,
|
||||
CtapGetAssertionOptions options,
|
||||
@ -276,9 +275,9 @@ class COMPONENT_EXPORT(DEVICE_FIDO) FidoDeviceAuthenticator
|
||||
void OperationClearProxy(base::OnceCallback<void(Args...)> callback,
|
||||
Args... args);
|
||||
template <typename Task, typename Response, typename... RequestArgs>
|
||||
void RunTask(RequestArgs&&... request_args,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode,
|
||||
absl::optional<Response>)> callback);
|
||||
void RunTask(
|
||||
RequestArgs&&... request_args,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode, Response)> callback);
|
||||
template <typename Request, typename Response>
|
||||
void RunOperation(Request request,
|
||||
base::OnceCallback<void(CtapDeviceResponseCode,
|
||||
|
@ -268,45 +268,79 @@ void GetAssertionTask::HandleResponse(
|
||||
return;
|
||||
}
|
||||
|
||||
if (response_code == CtapDeviceResponseCode::kSuccess) {
|
||||
if (response_data->user_selected && !allow_list.empty()) {
|
||||
// The userSelected signal is only valid if the request had an empty
|
||||
// allowList.
|
||||
return LogAndFail(
|
||||
"Assertion response has userSelected for non-empty allowList");
|
||||
}
|
||||
if (response_code != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback_).Run(response_code, {});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SetResponseCredential(&response_data.value(), allow_list)) {
|
||||
return LogAndFail(
|
||||
"Assertion response has invalid credential information");
|
||||
}
|
||||
if (response_data->user_selected && !allow_list.empty()) {
|
||||
// The userSelected signal is only valid if the request had an empty
|
||||
// allowList.
|
||||
return LogAndFail(
|
||||
"Assertion response has userSelected for non-empty allowList");
|
||||
}
|
||||
|
||||
// Extract any hmac-secret or prf response.
|
||||
const absl::optional<cbor::Value>& extensions_cbor =
|
||||
response_data->authenticator_data.extensions();
|
||||
if (extensions_cbor) {
|
||||
// Parsing has already checked that |extensions_cbor| is a map.
|
||||
const cbor::Value::MapValue& extensions = extensions_cbor->GetMap();
|
||||
auto it = extensions.find(cbor::Value(kExtensionHmacSecret));
|
||||
if (it != extensions.end()) {
|
||||
if (!hmac_secret_request_ || !it->second.is_bytestring()) {
|
||||
return LogAndFail("Unexpected or invalid hmac-secret extension");
|
||||
}
|
||||
if (response_data->hmac_secret.has_value()) {
|
||||
return LogAndFail(
|
||||
"Assertion response has both hmac-secret and prf extensions");
|
||||
}
|
||||
absl::optional<std::vector<uint8_t>> plaintext =
|
||||
hmac_secret_request_->Decrypt(it->second.GetBytestring());
|
||||
if (!plaintext) {
|
||||
return LogAndFail("Failed to decrypt hmac-secret extension");
|
||||
}
|
||||
response_data->hmac_secret = std::move(plaintext.value());
|
||||
if (!SetResponseCredential(&response_data.value(), allow_list)) {
|
||||
return LogAndFail("Assertion response has invalid credential information");
|
||||
}
|
||||
|
||||
uint8_t num_responses = response_data->num_credentials.value_or(1u);
|
||||
if (num_responses == 0 || (num_responses > 1 && !allow_list.empty())) {
|
||||
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrInvalidCBOR, {});
|
||||
return;
|
||||
}
|
||||
|
||||
HandleNextResponse(num_responses, response_code, std::move(response_data));
|
||||
}
|
||||
|
||||
void GetAssertionTask::HandleNextResponse(
|
||||
uint8_t num_responses,
|
||||
CtapDeviceResponseCode response_code,
|
||||
absl::optional<AuthenticatorGetAssertionResponse> response_data) {
|
||||
if (response_code != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback_).Run(response_code, {});
|
||||
}
|
||||
|
||||
// Extract any hmac-secret or prf response.
|
||||
const absl::optional<cbor::Value>& extensions_cbor =
|
||||
response_data->authenticator_data.extensions();
|
||||
if (extensions_cbor) {
|
||||
// Parsing has already checked that |extensions_cbor| is a map.
|
||||
const cbor::Value::MapValue& extensions = extensions_cbor->GetMap();
|
||||
auto it = extensions.find(cbor::Value(kExtensionHmacSecret));
|
||||
if (it != extensions.end()) {
|
||||
if (!hmac_secret_request_ || !it->second.is_bytestring()) {
|
||||
return LogAndFail("Unexpected or invalid hmac-secret extension");
|
||||
}
|
||||
if (response_data->hmac_secret.has_value()) {
|
||||
return LogAndFail(
|
||||
"Assertion response has both hmac-secret and prf extensions");
|
||||
}
|
||||
absl::optional<std::vector<uint8_t>> plaintext =
|
||||
hmac_secret_request_->Decrypt(it->second.GetBytestring());
|
||||
if (!plaintext) {
|
||||
return LogAndFail("Failed to decrypt hmac-secret extension");
|
||||
}
|
||||
response_data->hmac_secret = std::move(plaintext.value());
|
||||
}
|
||||
}
|
||||
|
||||
std::move(callback_).Run(response_code, std::move(response_data));
|
||||
responses_.emplace_back(std::move(*response_data));
|
||||
|
||||
if (responses_.size() < num_responses) {
|
||||
// Read the next response.
|
||||
next_assertion_operation_ = std::make_unique<Ctap2DeviceOperation<
|
||||
CtapGetNextAssertionRequest, AuthenticatorGetAssertionResponse>>(
|
||||
device(), CtapGetNextAssertionRequest(),
|
||||
base::BindOnce(&GetAssertionTask::HandleNextResponse,
|
||||
weak_factory_.GetWeakPtr(), num_responses),
|
||||
base::BindOnce(&ReadCTAPGetAssertionResponse,
|
||||
device()->DeviceTransport()),
|
||||
GetAssertionTask::StringFixupPredicate);
|
||||
next_assertion_operation_->Start();
|
||||
return;
|
||||
}
|
||||
std::move(callback_).Run(response_code, std::move(responses_));
|
||||
}
|
||||
|
||||
void GetAssertionTask::HandleResponseToSilentRequest(
|
||||
@ -384,8 +418,7 @@ void GetAssertionTask::HandleResponseToSilentRequest(
|
||||
void GetAssertionTask::HandleDummyMakeCredentialComplete(
|
||||
CtapDeviceResponseCode response_code,
|
||||
absl::optional<AuthenticatorMakeCredentialResponse> response_data) {
|
||||
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
|
||||
absl::nullopt);
|
||||
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials, {});
|
||||
}
|
||||
|
||||
void GetAssertionTask::MaybeSetPRFParameters(CtapGetAssertionRequest* request,
|
||||
@ -414,13 +447,17 @@ void GetAssertionTask::MaybeRevertU2fFallbackAndInvokeCallback(
|
||||
// in order to execute a sign command.
|
||||
device()->set_supported_protocol(ProtocolVersion::kCtap2);
|
||||
}
|
||||
std::move(callback_).Run(status, std::move(response));
|
||||
if (status != CtapDeviceResponseCode::kSuccess) {
|
||||
std::move(callback_).Run(status, {});
|
||||
return;
|
||||
}
|
||||
responses_.emplace_back(std::move(*response));
|
||||
std::move(callback_).Run(status, std::move(responses_));
|
||||
}
|
||||
|
||||
void GetAssertionTask::LogAndFail(const char* error) {
|
||||
FIDO_LOG(DEBUG) << error;
|
||||
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
|
||||
absl::nullopt);
|
||||
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther, {});
|
||||
}
|
||||
|
||||
} // namespace device
|
||||
|
@ -34,9 +34,12 @@ class AuthenticatorMakeCredentialResponse;
|
||||
// https://fidoalliance.org/specs/fido-v2.0-rd-20161004/fido-client-to-authenticator-protocol-v2.0-rd-20161004.html#authenticatorgetassertion
|
||||
class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
|
||||
public:
|
||||
using GetAssertionTaskCallback = base::OnceCallback<void(
|
||||
CtapDeviceResponseCode,
|
||||
absl::optional<AuthenticatorGetAssertionResponse>)>;
|
||||
using GetAssertionTaskCallback =
|
||||
base::OnceCallback<void(CtapDeviceResponseCode,
|
||||
std::vector<AuthenticatorGetAssertionResponse>)>;
|
||||
using GetNextAssertionOperation =
|
||||
DeviceOperation<CtapGetNextAssertionRequest,
|
||||
AuthenticatorGetAssertionResponse>;
|
||||
using SignOperation = DeviceOperation<CtapGetAssertionRequest,
|
||||
AuthenticatorGetAssertionResponse>;
|
||||
using RegisterOperation =
|
||||
@ -77,6 +80,13 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
|
||||
CtapDeviceResponseCode response_code,
|
||||
absl::optional<AuthenticatorGetAssertionResponse> response_data);
|
||||
|
||||
// HandleNextResponse processes an assertion and requests the next one if
|
||||
// necessary.
|
||||
void HandleNextResponse(
|
||||
uint8_t num_responses,
|
||||
CtapDeviceResponseCode response_code,
|
||||
absl::optional<AuthenticatorGetAssertionResponse> response_data);
|
||||
|
||||
// HandleResponseToSilentRequest is a callback to a request without user-
|
||||
// presence requested used to silently probe credentials from the allow list.
|
||||
void HandleResponseToSilentRequest(
|
||||
@ -103,10 +113,12 @@ class COMPONENT_EXPORT(DEVICE_FIDO) GetAssertionTask : public FidoTask {
|
||||
std::vector<std::vector<PublicKeyCredentialDescriptor>> allow_list_batches_;
|
||||
size_t current_allow_list_batch_ = 0;
|
||||
|
||||
std::unique_ptr<GetNextAssertionOperation> next_assertion_operation_;
|
||||
std::unique_ptr<SignOperation> sign_operation_;
|
||||
std::unique_ptr<RegisterOperation> dummy_register_operation_;
|
||||
GetAssertionTaskCallback callback_;
|
||||
std::unique_ptr<pin::HMACSecretRequest> hmac_secret_request_;
|
||||
std::vector<AuthenticatorGetAssertionResponse> responses_;
|
||||
|
||||
bool canceled_ = false;
|
||||
|
||||
|
@ -34,11 +34,11 @@ namespace {
|
||||
using TestGetAssertionTaskCallbackReceiver =
|
||||
::device::test::StatusAndValueCallbackReceiver<
|
||||
CtapDeviceResponseCode,
|
||||
absl::optional<AuthenticatorGetAssertionResponse>>;
|
||||
std::vector<AuthenticatorGetAssertionResponse>>;
|
||||
|
||||
class FidoGetAssertionTaskTest : public testing::Test {
|
||||
public:
|
||||
FidoGetAssertionTaskTest() {}
|
||||
FidoGetAssertionTaskTest() = default;
|
||||
|
||||
TestGetAssertionTaskCallbackReceiver& get_assertion_callback_receiver() {
|
||||
return cb_;
|
||||
@ -69,7 +69,7 @@ TEST_F(FidoGetAssertionTaskTest, TestGetAssertionSuccess) {
|
||||
get_assertion_callback_receiver().WaitForCallback();
|
||||
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
|
||||
get_assertion_callback_receiver().status());
|
||||
EXPECT_TRUE(get_assertion_callback_receiver().value());
|
||||
EXPECT_EQ(get_assertion_callback_receiver().value().size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) {
|
||||
@ -92,7 +92,7 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignSuccess) {
|
||||
get_assertion_callback_receiver().WaitForCallback();
|
||||
EXPECT_EQ(CtapDeviceResponseCode::kSuccess,
|
||||
get_assertion_callback_receiver().status());
|
||||
EXPECT_TRUE(get_assertion_callback_receiver().value());
|
||||
EXPECT_EQ(get_assertion_callback_receiver().value().size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) {
|
||||
@ -122,16 +122,19 @@ TEST_F(FidoGetAssertionTaskTest, TestSignSuccessWithFake) {
|
||||
ASSERT_GE(32u + 1u + 4u + 8u, // Minimal ECDSA signature is 8 bytes
|
||||
get_assertion_callback_receiver()
|
||||
.value()
|
||||
->authenticator_data.SerializeToByteArray()
|
||||
.at(0)
|
||||
.authenticator_data.SerializeToByteArray()
|
||||
.size());
|
||||
EXPECT_EQ(0x01,
|
||||
get_assertion_callback_receiver()
|
||||
.value()
|
||||
->authenticator_data.SerializeToByteArray()[32]); // UP flag
|
||||
.at(0)
|
||||
.authenticator_data.SerializeToByteArray()[32]); // UP flag
|
||||
// Counter starts at zero and is incremented for every sign request.
|
||||
EXPECT_EQ(1, get_assertion_callback_receiver()
|
||||
.value()
|
||||
->authenticator_data.SerializeToByteArray()[36]); // counter
|
||||
.at(0)
|
||||
.authenticator_data.SerializeToByteArray()[36]); // counter
|
||||
}
|
||||
|
||||
TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
|
||||
@ -148,7 +151,7 @@ TEST_F(FidoGetAssertionTaskTest, TestIncorrectGetAssertionResponse) {
|
||||
get_assertion_callback_receiver().WaitForCallback();
|
||||
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrOther,
|
||||
get_assertion_callback_receiver().status());
|
||||
EXPECT_FALSE(get_assertion_callback_receiver().value());
|
||||
EXPECT_TRUE(get_assertion_callback_receiver().value().empty());
|
||||
}
|
||||
|
||||
TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
|
||||
@ -168,7 +171,7 @@ TEST_F(FidoGetAssertionTaskTest, TestU2fSignRequestWithEmptyAllowedList) {
|
||||
get_assertion_callback_receiver().WaitForCallback();
|
||||
EXPECT_EQ(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
|
||||
get_assertion_callback_receiver().status());
|
||||
EXPECT_FALSE(get_assertion_callback_receiver().value());
|
||||
EXPECT_TRUE(get_assertion_callback_receiver().value().empty());
|
||||
}
|
||||
|
||||
// Checks that when device supports both CTAP2 and U2F protocol and when
|
||||
|
Reference in New Issue
Block a user