Commit bad8e6a2 authored by Adam Langley's avatar Adam Langley

Replicate |VirtualCtap2Device|'s UV behaviour to getAssertion.

The initial steps of both makeCredential and getAssertion are the same.
This change pulls that behaviour out of makeCredential and call it for
both operations.

Change-Id: If10dac3dce6c5dd4d1080c80730066afd5365b0c
Reviewed-on: https://chromium-review.googlesource.com/c/1461216
Commit-Queue: Adam Langley <agl@chromium.org>
Auto-Submit: Adam Langley <agl@chromium.org>
Reviewed-by: default avatarMartin Kreichgauer <martinkr@google.com>
Cr-Commit-Position: refs/heads/master@{#630535}
parent dfa3bea5
...@@ -50,16 +50,87 @@ void ReturnCtap2Response( ...@@ -50,16 +50,87 @@ void ReturnCtap2Response(
data.value_or(std::vector<uint8_t>{})))); data.value_or(std::vector<uint8_t>{}))));
} }
bool AreGetAssertionOptionsValid(const AuthenticatorSupportedOptions& options, // CheckUserVerification implements the first, common steps of
const CtapGetAssertionRequest& request) { // makeCredential and getAssertion from the CTAP2 spec.
if (request.user_presence_required() && !options.user_presence_required) CtapDeviceResponseCode CheckUserVerification(
return false; bool is_make_credential,
const AuthenticatorSupportedOptions& options,
return request.user_verification() != const base::Optional<std::vector<uint8_t>>& pin_auth,
UserVerificationRequirement::kRequired || const base::Optional<uint8_t>& pin_protocol,
options.user_verification_availability == UserVerificationRequirement user_verification,
AuthenticatorSupportedOptions::UserVerificationAvailability:: base::RepeatingCallback<void(void)> simulate_press_callback,
kSupportedAndConfigured; bool* out_user_verified) {
// The following quotes are from the CTAP2 spec:
// 1. "If authenticator supports clientPin and platform sends a zero length
// pinAuth, wait for user touch and then return either CTAP2_ERR_PIN_NOT_SET
// if pin is not set or CTAP2_ERR_PIN_INVALID if pin has been set."
const bool supports_pin =
options.client_pin_availability !=
AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
if (supports_pin && pin_auth && pin_auth->empty()) {
if (simulate_press_callback) {
simulate_press_callback.Run();
}
switch (options.client_pin_availability) {
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedAndPinSet:
return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedButPinNotSet:
return CtapDeviceResponseCode::kCtap2ErrPinNotSet;
case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
NOTREACHED();
}
}
// 2. "If authenticator supports clientPin and pinAuth parameter is present
// and the pinProtocol is not supported, return CTAP2_ERR_PIN_AUTH_INVALID
// error."
if (supports_pin && pin_auth && (!pin_protocol || *pin_protocol != 1)) {
return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
}
// 3. "If authenticator is not protected by some form of user verification and
// platform has set "uv" or pinAuth to get the user verification, return
// CTAP2_ERR_INVALID_OPTION."
const bool can_do_uv =
options.user_verification_availability ==
AuthenticatorSupportedOptions::UserVerificationAvailability::
kSupportedAndConfigured ||
options.client_pin_availability ==
AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedAndPinSet;
if (!can_do_uv &&
(user_verification == UserVerificationRequirement::kRequired ||
pin_auth)) {
return CtapDeviceResponseCode::kCtap2ErrInvalidOption;
}
// Step 4.
bool uv = false;
if (can_do_uv) {
if (user_verification == UserVerificationRequirement::kRequired) {
// Internal UV is assumed to always succeed.
if (simulate_press_callback) {
simulate_press_callback.Run();
}
uv = true;
}
if (pin_auth) {
DCHECK(pin_protocol && *pin_protocol == 1);
// The pin_auth argument is assumed to be correct.
uv = true;
}
if (is_make_credential && !uv) {
return CtapDeviceResponseCode::kCtap2ErrPinRequired;
}
}
*out_user_verified = uv;
return CtapDeviceResponseCode::kSuccess;
} }
// Checks that whether the received MakeCredential request includes EA256 // Checks that whether the received MakeCredential request includes EA256
...@@ -262,77 +333,15 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential( ...@@ -262,77 +333,15 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential(
CtapMakeCredentialRequest request = std::get<0>(*request_and_hash); CtapMakeCredentialRequest request = std::get<0>(*request_and_hash);
CtapMakeCredentialRequest::ClientDataHash client_data_hash = CtapMakeCredentialRequest::ClientDataHash client_data_hash =
std::get<1>(*request_and_hash); std::get<1>(*request_and_hash);
const AuthenticatorSupportedOptions& options = device_info_.options(); const AuthenticatorSupportedOptions& options = device_info_.options();
// The following quotes are from the CTAP2 spec: bool user_verified;
const CtapDeviceResponseCode uv_error = CheckUserVerification(
// 1. "If authenticator supports clientPin and platform sends a zero length true /* is makeCredential */, options, request.pin_auth(),
// pinAuth, wait for user touch and then return either CTAP2_ERR_PIN_NOT_SET request.pin_protocol(), request.user_verification(),
// if pin is not set or CTAP2_ERR_PIN_INVALID if pin has been set." mutable_state()->simulate_press_callback, &user_verified);
const bool supports_pin = if (uv_error != CtapDeviceResponseCode::kSuccess) {
options.client_pin_availability != return uv_error;
AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported;
if (supports_pin && request.pin_auth() && request.pin_auth()->empty()) {
if (mutable_state()->simulate_press_callback) {
mutable_state()->simulate_press_callback.Run();
}
switch (device_info_.options().client_pin_availability) {
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedAndPinSet:
return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
case AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedButPinNotSet:
return CtapDeviceResponseCode::kCtap2ErrPinNotSet;
case AuthenticatorSupportedOptions::ClientPinAvailability::kNotSupported:
NOTREACHED();
}
}
// 2. "If authenticator supports clientPin and pinAuth parameter is present
// and the pinProtocol is not supported, return CTAP2_ERR_PIN_AUTH_INVALID
// error."
if (supports_pin && request.pin_auth() &&
(!request.pin_protocol() || *request.pin_protocol() != 1)) {
return CtapDeviceResponseCode::kCtap2ErrPinInvalid;
}
// 3. "If authenticator is not protected by some form of user verification and
// platform has set "uv" or pinAuth to get the user verification, return
// CTAP2_ERR_INVALID_OPTION."
const bool can_do_uv =
options.user_verification_availability ==
AuthenticatorSupportedOptions::UserVerificationAvailability::
kSupportedAndConfigured ||
options.client_pin_availability ==
AuthenticatorSupportedOptions::ClientPinAvailability::
kSupportedAndPinSet;
if (!can_do_uv &&
(request.user_verification() == UserVerificationRequirement::kRequired ||
request.pin_auth())) {
return CtapDeviceResponseCode::kCtap2ErrInvalidOption;
}
// Step 4.
bool uv = false;
if (can_do_uv) {
if (request.user_verification() == UserVerificationRequirement::kRequired) {
// Internal UV is assumed to always succeed.
if (mutable_state()->simulate_press_callback) {
mutable_state()->simulate_press_callback.Run();
}
uv = true;
}
if (request.pin_auth()) {
DCHECK(request.pin_protocol() && *request.pin_protocol() == 1);
// The pin_auth argument is assumed to be correct.
uv = true;
}
if (!uv) {
return CtapDeviceResponseCode::kCtap2ErrPinAuthInvalid;
}
} }
// 6. Check for already registered credentials. // 6. Check for already registered credentials.
...@@ -394,7 +403,7 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential( ...@@ -394,7 +403,7 @@ CtapDeviceResponseCode VirtualCtap2Device::OnMakeCredential(
} }
auto authenticator_data = ConstructAuthenticatorData( auto authenticator_data = ConstructAuthenticatorData(
rp_id_hash, uv, 01ul, std::move(attested_credential_data), rp_id_hash, user_verified, 01ul, std::move(attested_credential_data),
std::move(extensions)); std::move(extensions));
auto sign_buffer = auto sign_buffer =
ConstructSignatureBuffer(authenticator_data, client_data_hash); ConstructSignatureBuffer(authenticator_data, client_data_hash);
...@@ -436,6 +445,16 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion( ...@@ -436,6 +445,16 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion(
CtapGetAssertionRequest request = std::get<0>(*request_and_hash); CtapGetAssertionRequest request = std::get<0>(*request_and_hash);
CtapGetAssertionRequest::ClientDataHash client_data_hash = CtapGetAssertionRequest::ClientDataHash client_data_hash =
std::get<1>(*request_and_hash); std::get<1>(*request_and_hash);
const AuthenticatorSupportedOptions& options = device_info_.options();
bool user_verified;
const CtapDeviceResponseCode uv_error = CheckUserVerification(
false /* not makeCredential */, options, request.pin_auth(),
request.pin_protocol(), request.user_verification(),
mutable_state()->simulate_press_callback, &user_verified);
if (uv_error != CtapDeviceResponseCode::kSuccess) {
return uv_error;
}
// Resident keys are not supported. // Resident keys are not supported.
if (!request.allow_list() || request.allow_list()->empty()) { if (!request.allow_list() || request.allow_list()->empty()) {
...@@ -444,17 +463,6 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion( ...@@ -444,17 +463,6 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion(
return CtapDeviceResponseCode::kCtap2ErrNoCredentials; return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
} }
// Client pin option is not supported.
if (request.pin_auth()) {
DLOG(ERROR) << "Virtual CTAP2 device does not support client pin.";
return CtapDeviceResponseCode::kCtap2ErrOther;
}
if (!AreGetAssertionOptionsValid(device_info_.options(), request)) {
DLOG(ERROR) << "Unsupported options required from the request.";
return CtapDeviceResponseCode::kCtap2ErrOther;
}
const auto rp_id_hash = fido_parsing_utils::CreateSHA256Hash(request.rp_id()); const auto rp_id_hash = fido_parsing_utils::CreateSHA256Hash(request.rp_id());
RegistrationData* found_data = nullptr; RegistrationData* found_data = nullptr;
...@@ -471,9 +479,9 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion( ...@@ -471,9 +479,9 @@ CtapDeviceResponseCode VirtualCtap2Device::OnGetAssertion(
return CtapDeviceResponseCode::kCtap2ErrNoCredentials; return CtapDeviceResponseCode::kCtap2ErrNoCredentials;
found_data->counter++; found_data->counter++;
auto authenticator_data = ConstructAuthenticatorData( auto authenticator_data =
rp_id_hash, false /* user verified */, found_data->counter, base::nullopt, ConstructAuthenticatorData(rp_id_hash, user_verified, found_data->counter,
base::nullopt); base::nullopt, base::nullopt);
auto signature_buffer = auto signature_buffer =
ConstructSignatureBuffer(authenticator_data, client_data_hash); ConstructSignatureBuffer(authenticator_data, client_data_hash);
......
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