forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
make_credential_task.cc
153 lines (132 loc) · 6.13 KB
/
make_credential_task.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "device/fido/make_credential_task.h"
#include <utility>
#include "base/bind.h"
#include "device/base/features.h"
#include "device/fido/ctap2_device_operation.h"
#include "device/fido/u2f_command_constructor.h"
#include "device/fido/u2f_register_operation.h"
namespace device {
namespace {
// Checks whether the incoming MakeCredential request has ClientPin option that
// is compatible with the Chrome's CTAP2 implementation. According to the CTAP
// spec, CTAP2 authenticators that have client pin set will always error out on
// MakeCredential request when "pinAuth" parameter in the request is not set. As
// ClientPin is not supported on Chrome yet, this check allows Chrome to avoid
// such failures if possible by defaulting to U2F request when user verification
// is not required and the device supports U2F protocol.
// TODO(hongjunchoi): Remove this once ClientPin command is implemented.
// See: https://crbug.com/870892
bool ShouldUseU2fBecauseCtapRequiresClientPin(
const FidoDevice* device,
const CtapMakeCredentialRequest& request) {
if (request.user_verification() == UserVerificationRequirement::kRequired)
return false;
DCHECK(device && device->device_info());
bool client_pin_set =
device->device_info()->options().client_pin_availability ==
AuthenticatorSupportedOptions::ClientPinAvailability::kSupportedAndPinSet;
bool supports_u2f = base::ContainsKey(device->device_info()->versions(),
ProtocolVersion::kU2f);
return client_pin_set && supports_u2f;
}
} // namespace
MakeCredentialTask::MakeCredentialTask(
FidoDevice* device,
CtapMakeCredentialRequest request_parameter,
MakeCredentialTaskCallback callback)
: FidoTask(device),
request_parameter_(std::move(request_parameter)),
callback_(std::move(callback)),
weak_factory_(this) {
// The UV parameter should have been made binary by this point because CTAP2
// only takes a binary value.
DCHECK_NE(request_parameter_.user_verification(),
UserVerificationRequirement::kPreferred);
}
MakeCredentialTask::~MakeCredentialTask() = default;
// static
CtapMakeCredentialRequest MakeCredentialTask::GetTouchRequest(
const FidoDevice* device) {
// We want to flash and wait for a touch. Newer versions of the CTAP2 spec
// include a provision for blocking for a touch when an empty pinAuth is
// specified, but devices exist that predate this part of the spec and also
// the spec says that devices need only do that if they implement PIN support.
// Therefore, in order to portably wait for a touch, a dummy credential is
// created. This does assume that the device supports ECDSA P-256, however.
PublicKeyCredentialUserEntity user({1} /* user ID */);
// The user name is incorrectly marked as optional in the CTAP2 spec.
user.SetUserName("dummy");
CtapMakeCredentialRequest req(
"" /* client_data_json */, PublicKeyCredentialRpEntity(".dummy"),
std::move(user),
PublicKeyCredentialParams(
{{CredentialType::kPublicKey,
base::strict_cast<int>(CoseAlgorithmIdentifier::kCoseEs256)}}));
req.SetExcludeList({});
// If a device supports CTAP2 and has PIN support then setting an empty
// pinAuth should trigger just a touch[1]. Our U2F code also understands
// this convention.
// [1]
// https://fidoalliance.org/specs/fido-v2.0-ps-20190130/fido-client-to-authenticator-protocol-v2.0-ps-20190130.html#using-pinToken-in-authenticatorGetAssertion
if (device->supported_protocol() == ProtocolVersion::kU2f ||
(device->device_info() &&
device->device_info()->options().client_pin_availability !=
AuthenticatorSupportedOptions::ClientPinAvailability::
kNotSupported)) {
req.SetPinAuth({});
}
DCHECK(IsConvertibleToU2fRegisterCommand(req));
return req;
}
void MakeCredentialTask::StartTask() {
if (device()->supported_protocol() == ProtocolVersion::kCtap &&
!request_parameter_.is_u2f_only() &&
!ShouldUseU2fBecauseCtapRequiresClientPin(device(), request_parameter_)) {
MakeCredential();
} else {
// |device_info| should be present iff the device is CTAP2. This will be
// used in |MaybeRevertU2fFallback| to restore the protocol of CTAP2 devices
// once this task is complete.
DCHECK((device()->supported_protocol() == ProtocolVersion::kCtap) ==
static_cast<bool>(device()->device_info()));
device()->set_supported_protocol(ProtocolVersion::kU2f);
U2fRegister();
}
}
void MakeCredentialTask::MakeCredential() {
register_operation_ = std::make_unique<Ctap2DeviceOperation<
CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
device(), std::move(request_parameter_), std::move(callback_),
base::BindOnce(&ReadCTAPMakeCredentialResponse,
device()->DeviceTransport()));
register_operation_->Start();
}
void MakeCredentialTask::U2fRegister() {
if (!IsConvertibleToU2fRegisterCommand(request_parameter_)) {
std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
base::nullopt);
return;
}
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
register_operation_ = std::make_unique<U2fRegisterOperation>(
device(), std::move(request_parameter_),
base::BindOnce(&MakeCredentialTask::MaybeRevertU2fFallback,
weak_factory_.GetWeakPtr()));
register_operation_->Start();
}
void MakeCredentialTask::MaybeRevertU2fFallback(
CtapDeviceResponseCode status,
base::Optional<AuthenticatorMakeCredentialResponse> response) {
DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
if (device()->device_info()) {
// This was actually a CTAP2 device, but the protocol version was set to U2F
// because it had a PIN set and so, in order to make a credential, the U2F
// interface was used.
device()->set_supported_protocol(ProtocolVersion::kCtap);
}
std::move(callback_).Run(status, std::move(response));
}
} // namespace device