blob: e7155a6f2363f58627562c03c4faadd94013d5b3 [file] [log] [blame]
Louis Collardb7664ae2019-10-05 13:57:01 +08001// Copyright 2019 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "u2fd/webauthn_handler.h"
6
Yicheng Li546ea542019-11-15 14:28:06 -08007#include <memory>
Yicheng Lic4bc7412020-03-04 12:02:16 -08008#include <string>
Yicheng Li546ea542019-11-15 14:28:06 -08009#include <utility>
Yicheng Lic4bc7412020-03-04 12:02:16 -080010#include <vector>
Yicheng Li546ea542019-11-15 14:28:06 -080011
hscham4ce3c992021-02-19 16:37:23 +090012#include <base/callback_helpers.h>
Qijiang Fan713061e2021-03-08 15:45:12 +090013#include <base/check.h>
14#include <base/check_op.h>
Qijiang Fan886c4692021-02-19 11:54:10 +090015#include <base/notreached.h>
Yicheng Lic03ac7e2019-12-09 16:13:26 -080016#include <base/time/time.h>
Yicheng Li421da3b2020-01-06 16:25:16 -080017#include <chromeos/cbor/values.h>
18#include <chromeos/cbor/writer.h>
Yicheng Lib46d4362020-06-05 15:15:45 -070019#include <chromeos/dbus/service_constants.h>
Yicheng Li1090c902020-11-10 11:31:43 -080020#include <openssl/rand.h>
Louis Collardb7664ae2019-10-05 13:57:01 +080021#include <u2f/proto_bindings/u2f_interface.pb.h>
22
Yicheng Lic4bc7412020-03-04 12:02:16 -080023#include "u2fd/util.h"
24
Louis Collardb7664ae2019-10-05 13:57:01 +080025namespace u2f {
26
Yicheng Lic4bc7412020-03-04 12:02:16 -080027namespace {
28
Yicheng Li9e902962020-11-01 11:07:11 -080029// User a big timeout for cryptohome. See b/172945202.
30constexpr base::TimeDelta kCryptohomeTimeout = base::TimeDelta::FromMinutes(2);
Yicheng Lic4bc7412020-03-04 12:02:16 -080031constexpr int kVerificationTimeoutMs = 10000;
32constexpr int kVerificationRetryDelayUs = 500 * 1000;
Yicheng Li98006782020-09-09 18:19:54 -070033constexpr int kCancelUVFlowTimeoutMs = 5000;
Yicheng Lic4bc7412020-03-04 12:02:16 -080034
35// Cr50 Response codes.
36// TODO(louiscollard): Don't duplicate these.
37constexpr uint32_t kCr50StatusNotAllowed = 0x507;
38
39constexpr char kAttestationFormatNone[] = "none";
Yicheng Lia40e2b52020-01-03 17:05:32 -080040// \xa0 is empty map in CBOR
Yicheng Lic4bc7412020-03-04 12:02:16 -080041constexpr char kAttestationStatementNone = '\xa0';
Yicheng Li1bbf5292021-01-25 17:19:56 -080042constexpr char kAttestationFormatU2f[] = "fido-u2f";
43// Keys for attestation statement CBOR map.
44constexpr char kSignatureKey[] = "sig";
45constexpr char kX509CertKey[] = "x5c";
Yicheng Lic4bc7412020-03-04 12:02:16 -080046
Yicheng Li9e0b3562021-02-10 10:33:43 -080047// The AAGUID for none-attestation (for platform-authenticator). For u2f/g2f
48// attestation, empty AAGUID should be used.
49const std::vector<uint8_t> kAaguid = {0x84, 0x03, 0x98, 0x77, 0xa5, 0x4b,
50 0xdf, 0xbb, 0x04, 0xa8, 0x2d, 0xf2,
51 0xfa, 0x2a, 0x11, 0x6e};
Yicheng Lia40e2b52020-01-03 17:05:32 -080052
53// AuthenticatorData flags are defined in
54// https://www.w3.org/TR/webauthn-2/#sctn-authenticator-data
55enum class AuthenticatorDataFlag : uint8_t {
56 kTestOfUserPresence = 1u << 0,
57 kTestOfUserVerification = 1u << 2,
58 kAttestedCredentialData = 1u << 6,
59 kExtensionDataIncluded = 1u << 7,
60};
61
Yicheng Li421da3b2020-01-06 16:25:16 -080062// COSE key parameters.
63// https://tools.ietf.org/html/rfc8152#section-7.1
64const int kCoseKeyKtyLabel = 1;
65const int kCoseKeyKtyEC2 = 2;
66const int kCoseKeyAlgLabel = 3;
67const int kCoseKeyAlgES256 = -7;
68
69// Double coordinate curve parameters.
70// https://tools.ietf.org/html/rfc8152#section-13.1.1
71const int kCoseECKeyCrvLabel = -1;
72const int kCoseECKeyXLabel = -2;
73const int kCoseECKeyYLabel = -3;
74
Yicheng Li67abd182020-11-18 15:31:41 -080075// Key label in cryptohome.
76constexpr char kCryptohomePinLabel[] = "pin";
77
78// Relative DBus object path for fingerprint manager in biod.
79const char kCrosFpBiometricsManagerRelativePath[] = "/CrosFpBiometricsManager";
80
Yicheng Lie7a26252021-02-03 12:18:37 -080081constexpr char kPerformingUserVerificationMetric[] =
82 "WebAuthentication.ChromeOS.UserVerificationRequired";
83
Yicheng Lia40e2b52020-01-03 17:05:32 -080084std::vector<uint8_t> Uint16ToByteVector(uint16_t value) {
85 return std::vector<uint8_t>({static_cast<uint8_t>((value >> 8) & 0xff),
86 static_cast<uint8_t>(value & 0xff)});
87}
88
Yicheng Lic4bc7412020-03-04 12:02:16 -080089void AppendToString(const std::vector<uint8_t>& vect, std::string* str) {
90 str->append(reinterpret_cast<const char*>(vect.data()), vect.size());
91}
92
Yicheng Lic4bc7412020-03-04 12:02:16 -080093void AppendAttestedCredential(const std::vector<uint8_t>& credential_id,
94 const std::vector<uint8_t>& credential_public_key,
95 std::vector<uint8_t>* authenticator_data) {
Yicheng Lic4bc7412020-03-04 12:02:16 -080096 util::AppendToVector(credential_id, authenticator_data);
97 util::AppendToVector(credential_public_key, authenticator_data);
98}
99
Yicheng Lia40e2b52020-01-03 17:05:32 -0800100// Returns the current time in seconds since epoch as a privacy-preserving
101// signature counter. Because of the conversion to a 32-bit unsigned integer,
102// the counter will overflow in the year 2108.
103std::vector<uint8_t> GetTimestampSignatureCounter() {
104 uint32_t sign_counter = static_cast<uint32_t>(base::Time::Now().ToDoubleT());
105 return std::vector<uint8_t>{
106 static_cast<uint8_t>((sign_counter >> 24) & 0xff),
107 static_cast<uint8_t>((sign_counter >> 16) & 0xff),
108 static_cast<uint8_t>((sign_counter >> 8) & 0xff),
109 static_cast<uint8_t>(sign_counter & 0xff),
110 };
111}
112
Yicheng Li421da3b2020-01-06 16:25:16 -0800113std::vector<uint8_t> EncodeCredentialPublicKeyInCBOR(
114 const std::vector<uint8_t>& credential_public_key) {
Louis Collardbe159282020-02-12 15:29:56 +0800115 DCHECK_EQ(credential_public_key.size(), sizeof(struct u2f_ec_point));
Yicheng Li421da3b2020-01-06 16:25:16 -0800116 cbor::Value::MapValue cbor_map;
117 cbor_map[cbor::Value(kCoseKeyKtyLabel)] = cbor::Value(kCoseKeyKtyEC2);
118 cbor_map[cbor::Value(kCoseKeyAlgLabel)] = cbor::Value(kCoseKeyAlgES256);
119 cbor_map[cbor::Value(kCoseECKeyCrvLabel)] = cbor::Value(1);
Tom Hughes0f7203b2020-08-24 18:29:15 -0700120 cbor_map[cbor::Value(kCoseECKeyXLabel)] = cbor::Value(base::make_span(
121 credential_public_key.data() + offsetof(struct u2f_ec_point, x),
122 U2F_EC_KEY_SIZE));
123 cbor_map[cbor::Value(kCoseECKeyYLabel)] = cbor::Value(base::make_span(
124 credential_public_key.data() + offsetof(struct u2f_ec_point, y),
125 U2F_EC_KEY_SIZE));
Yicheng Li421da3b2020-01-06 16:25:16 -0800126 return *cbor::Writer::Write(cbor::Value(std::move(cbor_map)));
127}
128
Yicheng Li1bbf5292021-01-25 17:19:56 -0800129std::vector<uint8_t> EncodeU2fAttestationStatementInCBOR(
130 const std::vector<uint8_t>& signature, const std::vector<uint8_t>& cert) {
131 cbor::Value::MapValue attestation_statement_map;
132 attestation_statement_map[cbor::Value(kSignatureKey)] =
133 cbor::Value(signature);
134 // The "x5c" field is an array of just one cert.
135 std::vector<cbor::Value> certificate_array;
136 certificate_array.push_back(cbor::Value(cert));
137 attestation_statement_map[cbor::Value(kX509CertKey)] =
138 cbor::Value(std::move(certificate_array));
139 return *cbor::Writer::Write(
140 cbor::Value(std::move(attestation_statement_map)));
141}
142
Yicheng Lic4bc7412020-03-04 12:02:16 -0800143} // namespace
144
Yicheng Li546ea542019-11-15 14:28:06 -0800145WebAuthnHandler::WebAuthnHandler()
Yicheng Li1090c902020-11-10 11:31:43 -0800146 : tpm_proxy_(nullptr),
147 user_state_(nullptr),
148 webauthn_storage_(std::make_unique<WebAuthnStorage>()) {}
Yicheng Li546ea542019-11-15 14:28:06 -0800149
Yicheng Li1bbf5292021-01-25 17:19:56 -0800150void WebAuthnHandler::Initialize(
151 dbus::Bus* bus,
152 TpmVendorCommandProxy* tpm_proxy,
153 UserState* user_state,
154 U2fMode u2f_mode,
155 std::function<void()> request_presence,
Yicheng Lie7a26252021-02-03 12:18:37 -0800156 std::unique_ptr<AllowlistingUtil> allowlisting_util,
157 MetricsLibraryInterface* metrics) {
Yicheng Li4d27fa72020-12-10 11:09:21 -0800158 if (Initialized()) {
159 LOG(INFO) << "WebAuthn handler already initialized, doing nothing.";
160 return;
161 }
162
Yicheng Lie7a26252021-02-03 12:18:37 -0800163 metrics_ = metrics;
Yicheng Li546ea542019-11-15 14:28:06 -0800164 tpm_proxy_ = tpm_proxy;
165 user_state_ = user_state;
Yicheng Li9e902962020-11-01 11:07:11 -0800166 user_state_->SetSessionStartedCallback(
167 base::Bind(&WebAuthnHandler::OnSessionStarted, base::Unretained(this)));
168 user_state_->SetSessionStoppedCallback(
169 base::Bind(&WebAuthnHandler::OnSessionStopped, base::Unretained(this)));
Yicheng Lia42e6d42021-01-22 14:58:46 -0800170 u2f_mode_ = u2f_mode;
Yicheng Li546ea542019-11-15 14:28:06 -0800171 request_presence_ = request_presence;
Yicheng Li1bbf5292021-01-25 17:19:56 -0800172 allowlisting_util_ = std::move(allowlisting_util);
Yicheng Lib46d4362020-06-05 15:15:45 -0700173 bus_ = bus;
174 auth_dialog_dbus_proxy_ = bus_->GetObjectProxy(
175 chromeos::kUserAuthenticationServiceName,
176 dbus::ObjectPath(chromeos::kUserAuthenticationServicePath));
Yicheng Li67abd182020-11-18 15:31:41 -0800177 // Testing can inject a mock.
178 if (!cryptohome_proxy_)
179 cryptohome_proxy_ =
180 std::make_unique<org::chromium::CryptohomeInterfaceProxy>(bus_);
Yicheng Lib46d4362020-06-05 15:15:45 -0700181 DCHECK(auth_dialog_dbus_proxy_);
Yicheng Li30b6abc2020-11-13 14:51:15 -0800182
183 if (user_state_->HasUser()) {
184 // WebAuthnHandler should normally initialize on boot, before any user has
185 // logged in. If there's already a user, then we have crashed during a user
186 // session, so catch up on the state.
187 base::Optional<std::string> user = user_state_->GetUser();
188 DCHECK(user);
189 OnSessionStarted(*user);
190 }
Yicheng Li546ea542019-11-15 14:28:06 -0800191}
192
193bool WebAuthnHandler::Initialized() {
194 return tpm_proxy_ != nullptr && user_state_ != nullptr;
195}
196
Yicheng Lia42e6d42021-01-22 14:58:46 -0800197bool WebAuthnHandler::AllowPresenceMode() {
198 return u2f_mode_ == U2fMode::kU2f || u2f_mode_ == U2fMode::kU2fExtended;
199}
200
Yicheng Li9e902962020-11-01 11:07:11 -0800201void WebAuthnHandler::OnSessionStarted(const std::string& account_id) {
Yicheng Li1090c902020-11-10 11:31:43 -0800202 // Do this first because there's a timeout for reading the secret.
Yicheng Li9381f972021-01-13 16:23:07 -0800203 GetWebAuthnSecretAsync(account_id);
Yicheng Li1090c902020-11-10 11:31:43 -0800204
205 webauthn_storage_->set_allow_access(true);
206 base::Optional<std::string> sanitized_user = user_state_->GetSanitizedUser();
207 DCHECK(sanitized_user);
208 webauthn_storage_->set_sanitized_user(*sanitized_user);
209
210 if (!webauthn_storage_->LoadRecords()) {
211 LOG(ERROR) << "Did not load all records for user " << *sanitized_user;
212 return;
213 }
Yicheng Lie7a26252021-02-03 12:18:37 -0800214 webauthn_storage_->SendRecordCountToUMA(metrics_);
Yicheng Li9e902962020-11-01 11:07:11 -0800215}
216
217void WebAuthnHandler::OnSessionStopped() {
218 auth_time_secret_hash_.reset();
Yicheng Li1090c902020-11-10 11:31:43 -0800219 webauthn_storage_->Reset();
Yicheng Li9e902962020-11-01 11:07:11 -0800220}
221
Yicheng Li9381f972021-01-13 16:23:07 -0800222void WebAuthnHandler::GetWebAuthnSecretAsync(const std::string& account_id) {
Yicheng Li9e902962020-11-01 11:07:11 -0800223 cryptohome::AccountIdentifier id;
224 id.set_account_id(account_id);
225 cryptohome::GetWebAuthnSecretRequest req;
226
Yicheng Li9381f972021-01-13 16:23:07 -0800227 cryptohome_proxy_->GetWebAuthnSecretAsync(
228 id, req,
229 base::Bind(&WebAuthnHandler::OnGetWebAuthnSecretResp,
230 base::Unretained(this)),
231 base::Bind(&WebAuthnHandler::OnGetWebAuthnSecretCallFailed,
232 base::Unretained(this)),
233 kCryptohomeTimeout.InMilliseconds());
234}
235
236void WebAuthnHandler::OnGetWebAuthnSecretCallFailed(brillo::Error* error) {
237 LOG(ERROR) << "Failed to call GetWebAuthnSecret on cryptohome, error: "
238 << error->GetMessage();
239}
240
241void WebAuthnHandler::OnGetWebAuthnSecretResp(
242 const cryptohome::BaseReply& reply) {
243 // In case there's any error, read the backup hash first.
244 auth_time_secret_hash_ = webauthn_storage_->LoadAuthTimeSecretHash();
Yicheng Li9e902962020-11-01 11:07:11 -0800245
Yicheng Li07786dd2020-12-01 15:01:18 -0800246 if (reply.has_error()) {
247 LOG(ERROR) << "GetWebAuthnSecret reply has error " << reply.error();
Yicheng Li9381f972021-01-13 16:23:07 -0800248 return;
Yicheng Li07786dd2020-12-01 15:01:18 -0800249 }
250
Yicheng Li67abd182020-11-18 15:31:41 -0800251 if (!reply.HasExtension(cryptohome::GetWebAuthnSecretReply::reply)) {
Yicheng Li07786dd2020-12-01 15:01:18 -0800252 LOG(ERROR) << "GetWebAuthnSecret reply doesn't have the correct extension.";
Yicheng Li9381f972021-01-13 16:23:07 -0800253 return;
Yicheng Li9e902962020-11-01 11:07:11 -0800254 }
255
256 brillo::SecureBlob secret(
Yicheng Li67abd182020-11-18 15:31:41 -0800257 reply.GetExtension(cryptohome::GetWebAuthnSecretReply::reply)
Yicheng Li9e902962020-11-01 11:07:11 -0800258 .webauthn_secret());
Yicheng Li07786dd2020-12-01 15:01:18 -0800259 if (secret.size() != SHA256_DIGEST_LENGTH) {
260 LOG(ERROR) << "WebAuthn auth time secret size is wrong.";
Yicheng Li9381f972021-01-13 16:23:07 -0800261 return;
Yicheng Li07786dd2020-12-01 15:01:18 -0800262 }
263
Yicheng Li9381f972021-01-13 16:23:07 -0800264 std::unique_ptr<brillo::Blob> fresh_secret_hash =
265 std::make_unique<brillo::Blob>(util::Sha256(secret));
266
267 if (fresh_secret_hash) {
268 // Persist to daemon-store in case we crash during a user session.
269 webauthn_storage_->PersistAuthTimeSecretHash(*fresh_secret_hash);
270 auth_time_secret_hash_ = std::move(fresh_secret_hash);
271 }
Yicheng Li9e902962020-11-01 11:07:11 -0800272}
273
Yicheng Lic03ac7e2019-12-09 16:13:26 -0800274void WebAuthnHandler::MakeCredential(
275 std::unique_ptr<MakeCredentialMethodResponse> method_response,
Louis Collardb7664ae2019-10-05 13:57:01 +0800276 const MakeCredentialRequest& request) {
Yicheng Lic4bc7412020-03-04 12:02:16 -0800277 MakeCredentialResponse response;
278
279 if (!Initialized()) {
280 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
281 method_response->Return(response);
282 return;
283 }
284
Yicheng Li98006782020-09-09 18:19:54 -0700285 if (pending_uv_make_credential_session_ ||
286 pending_uv_get_assertion_session_) {
287 response.set_status(MakeCredentialResponse::REQUEST_PENDING);
288 method_response->Return(response);
289 return;
290 }
291
Yicheng Lic4bc7412020-03-04 12:02:16 -0800292 if (request.rp_id().empty()) {
293 response.set_status(MakeCredentialResponse::INVALID_REQUEST);
294 method_response->Return(response);
295 return;
296 }
297
Yicheng Lib46d4362020-06-05 15:15:45 -0700298 if (request.verification_type() == VerificationType::VERIFICATION_UNKNOWN) {
Yicheng Lic4bc7412020-03-04 12:02:16 -0800299 response.set_status(MakeCredentialResponse::VERIFICATION_FAILED);
300 method_response->Return(response);
301 return;
302 }
303
304 struct MakeCredentialSession session = {
305 static_cast<uint64_t>(base::Time::Now().ToTimeT()), request,
306 std::move(method_response)};
Yicheng Lib46d4362020-06-05 15:15:45 -0700307
Yicheng Lia42e6d42021-01-22 14:58:46 -0800308 if (!AllowPresenceMode()) {
Yicheng Lid0d21e22020-12-17 18:06:23 -0800309 // Upgrade UP requests to UV.
310 session.request.set_verification_type(
311 VerificationType::VERIFICATION_USER_VERIFICATION);
312 }
Yicheng Lieadff032020-08-27 11:21:30 -0700313
Yicheng Lid0d21e22020-12-17 18:06:23 -0800314 if (session.request.verification_type() ==
315 VerificationType::VERIFICATION_USER_VERIFICATION) {
Yicheng Lie7a26252021-02-03 12:18:37 -0800316 metrics_->SendBoolToUMA(kPerformingUserVerificationMetric, true);
Yicheng Lid0d21e22020-12-17 18:06:23 -0800317 dbus::MethodCall call(
318 chromeos::kUserAuthenticationServiceInterface,
319 chromeos::kUserAuthenticationServiceShowAuthDialogMethod);
320 dbus::MessageWriter writer(&call);
321 writer.AppendString(session.request.rp_id());
322 writer.AppendInt32(session.request.verification_type());
323 writer.AppendUint64(session.request.request_id());
Yicheng Lib46d4362020-06-05 15:15:45 -0700324
Yicheng Lid0d21e22020-12-17 18:06:23 -0800325 pending_uv_make_credential_session_ = std::move(session);
326 auth_dialog_dbus_proxy_->CallMethod(
327 &call, dbus::ObjectProxy::TIMEOUT_INFINITE,
328 base::Bind(&WebAuthnHandler::HandleUVFlowResultMakeCredential,
329 base::Unretained(this)));
330 return;
331 }
332
Yicheng Lie7a26252021-02-03 12:18:37 -0800333 metrics_->SendBoolToUMA(kPerformingUserVerificationMetric, false);
Yicheng Lid0d21e22020-12-17 18:06:23 -0800334 DoMakeCredential(std::move(session), PresenceRequirement::kPowerButton);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800335}
336
Yicheng Li98006782020-09-09 18:19:54 -0700337CancelWebAuthnFlowResponse WebAuthnHandler::Cancel(
338 const CancelWebAuthnFlowRequest& request) {
339 CancelWebAuthnFlowResponse response;
340 if (!pending_uv_make_credential_session_ &&
341 !pending_uv_get_assertion_session_) {
342 LOG(ERROR) << "No pending session to cancel.";
343 response.set_canceled(false);
344 return response;
345 }
346
347 if (pending_uv_make_credential_session_ &&
348 pending_uv_make_credential_session_->request.request_id() !=
349 request.request_id()) {
350 LOG(ERROR)
351 << "MakeCredential session has a different request_id, not cancelling.";
352 response.set_canceled(false);
353 return response;
354 }
355
356 if (pending_uv_get_assertion_session_ &&
357 pending_uv_get_assertion_session_->request.request_id() !=
358 request.request_id()) {
359 LOG(ERROR)
360 << "GetAssertion session has a different request_id, not cancelling.";
361 response.set_canceled(false);
362 return response;
363 }
364
365 dbus::MethodCall call(chromeos::kUserAuthenticationServiceInterface,
366 chromeos::kUserAuthenticationServiceCancelMethod);
367 std::unique_ptr<dbus::Response> cancel_ui_resp =
368 auth_dialog_dbus_proxy_->CallMethodAndBlock(&call,
369 kCancelUVFlowTimeoutMs);
370
371 if (!cancel_ui_resp) {
372 LOG(ERROR) << "Failed to dismiss WebAuthn user verification UI.";
373 response.set_canceled(false);
374 return response;
375 }
376
Yicheng Lidb8f2852020-11-18 17:21:14 -0800377 // We do not reset |pending_uv_make_credential_session_| or
378 // |pending_uv_get_assertion_session_| here because UI will still respond
379 // to the cancelled request through these, though the response will be
380 // ignored by Chrome.
381 if (pending_uv_make_credential_session_) {
382 pending_uv_make_credential_session_->canceled = true;
383 } else {
384 pending_uv_get_assertion_session_->canceled = true;
385 }
Yicheng Li98006782020-09-09 18:19:54 -0700386 response.set_canceled(true);
387 return response;
388}
389
Yicheng Lib46d4362020-06-05 15:15:45 -0700390void WebAuthnHandler::HandleUVFlowResultMakeCredential(
Yicheng Li98006782020-09-09 18:19:54 -0700391 dbus::Response* flow_response) {
Yicheng Lib46d4362020-06-05 15:15:45 -0700392 MakeCredentialResponse response;
393
Yicheng Li98006782020-09-09 18:19:54 -0700394 DCHECK(pending_uv_make_credential_session_);
395
Yicheng Lib46d4362020-06-05 15:15:45 -0700396 if (!flow_response) {
397 LOG(ERROR) << "User auth flow had no response.";
398 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700399 pending_uv_make_credential_session_->response->Return(response);
400 pending_uv_make_credential_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700401 return;
402 }
403
404 dbus::MessageReader response_reader(flow_response);
405 bool success;
406 if (!response_reader.PopBool(&success)) {
407 LOG(ERROR) << "Failed to parse user auth flow result.";
408 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700409 pending_uv_make_credential_session_->response->Return(response);
410 pending_uv_make_credential_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700411 return;
412 }
413
414 if (!success) {
Yicheng Lidb8f2852020-11-18 17:21:14 -0800415 if (pending_uv_make_credential_session_->canceled) {
416 LOG(INFO) << "WebAuthn MakeCredential operation canceled.";
417 response.set_status(MakeCredentialResponse::CANCELED);
418 } else {
419 LOG(ERROR) << "User auth flow failed. Aborting MakeCredential.";
420 response.set_status(MakeCredentialResponse::VERIFICATION_FAILED);
421 }
Yicheng Li98006782020-09-09 18:19:54 -0700422 pending_uv_make_credential_session_->response->Return(response);
423 pending_uv_make_credential_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700424 return;
425 }
426
Yicheng Li98006782020-09-09 18:19:54 -0700427 DoMakeCredential(std::move(*pending_uv_make_credential_session_),
428 PresenceRequirement::kNone);
429 pending_uv_make_credential_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700430}
431
432void WebAuthnHandler::HandleUVFlowResultGetAssertion(
Yicheng Li98006782020-09-09 18:19:54 -0700433 dbus::Response* flow_response) {
Yicheng Lib46d4362020-06-05 15:15:45 -0700434 GetAssertionResponse response;
435
Yicheng Li98006782020-09-09 18:19:54 -0700436 DCHECK(pending_uv_get_assertion_session_);
437
Yicheng Lib46d4362020-06-05 15:15:45 -0700438 if (!flow_response) {
439 LOG(ERROR) << "User auth flow had no response.";
440 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700441 pending_uv_get_assertion_session_->response->Return(response);
442 pending_uv_get_assertion_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700443 return;
444 }
445
446 dbus::MessageReader response_reader(flow_response);
447 bool success;
448 if (!response_reader.PopBool(&success)) {
449 LOG(ERROR) << "Failed to parse user auth flow result.";
450 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700451 pending_uv_get_assertion_session_->response->Return(response);
452 pending_uv_get_assertion_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700453 return;
454 }
455
456 if (!success) {
Yicheng Lidb8f2852020-11-18 17:21:14 -0800457 if (pending_uv_get_assertion_session_->canceled) {
458 LOG(INFO) << "WebAuthn GetAssertion operation canceled.";
459 response.set_status(GetAssertionResponse::CANCELED);
460 } else {
461 LOG(ERROR) << "User auth flow failed. Aborting GetAssertion.";
462 response.set_status(GetAssertionResponse::VERIFICATION_FAILED);
463 }
Yicheng Li98006782020-09-09 18:19:54 -0700464 pending_uv_get_assertion_session_->response->Return(response);
465 pending_uv_get_assertion_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700466 return;
467 }
468
Yicheng Li98006782020-09-09 18:19:54 -0700469 DoGetAssertion(std::move(*pending_uv_get_assertion_session_),
470 PresenceRequirement::kAuthorizationSecret);
471 pending_uv_get_assertion_session_.reset();
Yicheng Lib46d4362020-06-05 15:15:45 -0700472}
473
Yicheng Lic4bc7412020-03-04 12:02:16 -0800474void WebAuthnHandler::DoMakeCredential(
475 struct MakeCredentialSession session,
476 PresenceRequirement presence_requirement) {
477 MakeCredentialResponse response;
Yicheng Li98006782020-09-09 18:19:54 -0700478 const std::vector<uint8_t> rp_id_hash = util::Sha256(session.request.rp_id());
Yicheng Lic4bc7412020-03-04 12:02:16 -0800479 std::vector<uint8_t> credential_id;
480 std::vector<uint8_t> credential_public_key;
481
Yicheng Li1bbf5292021-01-25 17:19:56 -0800482 // If we are in u2f or g2f mode, and the request says it wants presence only,
483 // make a non-versioned (i.e. non-uv-compatible) credential.
484 bool uv_compatible = !(AllowPresenceMode() &&
485 session.request.verification_type() ==
486 VerificationType::VERIFICATION_USER_PRESENCE);
Yicheng Li5ca11f52020-05-14 16:49:48 -0700487
Yicheng Li4d27fa72020-12-10 11:09:21 -0800488 brillo::Blob credential_secret(kCredentialSecretSize);
Yicheng Li1bbf5292021-01-25 17:19:56 -0800489 if (uv_compatible) {
490 if (RAND_bytes(credential_secret.data(), credential_secret.size()) != 1) {
491 LOG(ERROR) << "Failed to generate secret for new credential.";
492 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
493 session.response->Return(response);
494 return;
495 }
496 } else {
497 // We are creating a credential that can only be signed with power button
498 // press, and can be signed by u2f/g2f, so we must use the legacy secret.
499 base::Optional<brillo::SecureBlob> legacy_secret =
500 user_state_->GetUserSecret();
501 if (!legacy_secret) {
502 LOG(ERROR) << "Cannot find user secret when trying to create u2f/g2f "
503 "credential.";
504 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
505 session.response->Return(response);
506 return;
507 }
508 credential_secret =
509 std::vector<uint8_t>(legacy_secret->begin(), legacy_secret->end());
Yicheng Li1090c902020-11-10 11:31:43 -0800510 }
511
Yicheng Li5ca11f52020-05-14 16:49:48 -0700512 MakeCredentialResponse::MakeCredentialStatus generate_status =
Yicheng Li1090c902020-11-10 11:31:43 -0800513 DoU2fGenerate(rp_id_hash, credential_secret, presence_requirement,
514 uv_compatible, &credential_id, &credential_public_key);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800515
516 if (generate_status != MakeCredentialResponse::SUCCESS) {
517 response.set_status(generate_status);
Yicheng Li98006782020-09-09 18:19:54 -0700518 session.response->Return(response);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800519 return;
520 }
521
Yicheng Lia40e2b52020-01-03 17:05:32 -0800522 if (credential_id.empty() || credential_public_key.empty()) {
523 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700524 session.response->Return(response);
Yicheng Lia40e2b52020-01-03 17:05:32 -0800525 return;
526 }
527
Yicheng Lif3da4022020-11-10 18:30:15 -0800528 if (uv_compatible)
529 InsertAuthTimeSecretHashToCredentialId(&credential_id);
530
Yicheng Li98006782020-09-09 18:19:54 -0700531 auto ret = HasExcludedCredentials(session.request);
Yicheng Li4652feb2020-04-27 15:18:59 -0700532 if (ret == HasCredentialsResponse::INTERNAL_ERROR) {
533 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
Yicheng Li98006782020-09-09 18:19:54 -0700534 session.response->Return(response);
Yicheng Liaeb3d682020-11-19 11:07:57 -0800535 return;
Yicheng Li4652feb2020-04-27 15:18:59 -0700536 } else if (ret == HasCredentialsResponse::SUCCESS) {
537 response.set_status(MakeCredentialResponse::EXCLUDED_CREDENTIAL_ID);
Yicheng Li98006782020-09-09 18:19:54 -0700538 session.response->Return(response);
Yicheng Liaeb3d682020-11-19 11:07:57 -0800539 return;
Yicheng Li4652feb2020-04-27 15:18:59 -0700540 }
541
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700542 const base::Optional<std::vector<uint8_t>> authenticator_data =
543 MakeAuthenticatorData(
544 rp_id_hash, credential_id,
545 EncodeCredentialPublicKeyInCBOR(credential_public_key),
546 /* user_verified = */ session.request.verification_type() ==
547 VerificationType::VERIFICATION_USER_VERIFICATION,
548 /* include_attested_credential_data = */ true,
549 /* is_u2f_authenticator_credential = */ !uv_compatible);
550 if (!authenticator_data) {
551 LOG(ERROR) << "MakeAuthenticatorData failed";
552 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
553 session.response->Return(response);
554 return;
555 }
556 AppendToString(*authenticator_data, response.mutable_authenticator_data());
Yicheng Li1bbf5292021-01-25 17:19:56 -0800557
558 // If a credential is not UV-compatible, it is a legacy U2F/G2F credential
559 // and should come with U2F/G2F attestation for backward compatibility.
560 if (uv_compatible) {
561 AppendNoneAttestation(&response);
562 } else {
563 const std::vector<uint8_t> data_to_sign =
564 util::BuildU2fRegisterResponseSignedData(
565 rp_id_hash, util::ToVector(session.request.client_data_hash()),
566 credential_public_key, credential_id);
567 base::Optional<std::vector<uint8_t>> attestation_statement =
568 MakeFidoU2fAttestationStatement(
569 data_to_sign, session.request.attestation_conveyance_preference());
570 if (!attestation_statement) {
571 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
572 session.response->Return(response);
573 return;
574 }
575 response.set_attestation_format(kAttestationFormatU2f);
576 AppendToString(*attestation_statement,
577 response.mutable_attestation_statement());
Yicheng Li1090c902020-11-10 11:31:43 -0800578 }
579
Yicheng Li1bbf5292021-01-25 17:19:56 -0800580 // u2f/g2f credentials should not be written to record.
581 if (uv_compatible) {
582 // All steps succeeded, so write to record.
583 WebAuthnRecord record;
584 AppendToString(credential_id, &record.credential_id);
585 record.secret = std::move(credential_secret);
586 record.rp_id = session.request.rp_id();
Yicheng Lid73908d2021-02-17 09:58:11 -0800587 record.rp_display_name = session.request.rp_display_name();
Yicheng Li1bbf5292021-01-25 17:19:56 -0800588 record.user_id = session.request.user_id();
589 record.user_display_name = session.request.user_display_name();
590 record.timestamp = base::Time::Now().ToDoubleT();
Yicheng Lid73908d2021-02-17 09:58:11 -0800591 record.is_resident_key = session.request.resident_key_required();
Yicheng Li1bbf5292021-01-25 17:19:56 -0800592 if (!webauthn_storage_->WriteRecord(std::move(record))) {
593 response.set_status(MakeCredentialResponse::INTERNAL_ERROR);
594 session.response->Return(response);
595 return;
596 }
597 }
Yicheng Lia40e2b52020-01-03 17:05:32 -0800598
Yicheng Lic4bc7412020-03-04 12:02:16 -0800599 response.set_status(MakeCredentialResponse::SUCCESS);
Yicheng Li98006782020-09-09 18:19:54 -0700600 session.response->Return(response);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800601}
602
Yicheng Lia40e2b52020-01-03 17:05:32 -0800603// AuthenticatorData layout:
604// (See https://www.w3.org/TR/webauthn-2/#table-authData)
605// -----------------------------------------------------------------------
606// | RP ID hash: 32 bytes
607// | Flags: 1 byte
608// | Signature counter: 4 bytes
609// | -------------------------------------------
610// | | AAGUID: 16 bytes
611// | Attested Credential Data: | Credential ID length (L): 2 bytes
612// | (if present) | Credential ID: L bytes
613// | | Credential public key: variable length
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700614base::Optional<std::vector<uint8_t>> WebAuthnHandler::MakeAuthenticatorData(
Yicheng Lia40e2b52020-01-03 17:05:32 -0800615 const std::vector<uint8_t>& rp_id_hash,
616 const std::vector<uint8_t>& credential_id,
617 const std::vector<uint8_t>& credential_public_key,
618 bool user_verified,
Yicheng Li9e0b3562021-02-10 10:33:43 -0800619 bool include_attested_credential_data,
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700620 bool is_u2f_authenticator_credential) {
Yicheng Lia40e2b52020-01-03 17:05:32 -0800621 std::vector<uint8_t> authenticator_data(rp_id_hash);
622 uint8_t flags =
623 static_cast<uint8_t>(AuthenticatorDataFlag::kTestOfUserPresence);
624 if (user_verified)
625 flags |=
626 static_cast<uint8_t>(AuthenticatorDataFlag::kTestOfUserVerification);
627 if (include_attested_credential_data)
628 flags |=
629 static_cast<uint8_t>(AuthenticatorDataFlag::kAttestedCredentialData);
630 authenticator_data.emplace_back(flags);
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700631
632 // The U2F authenticator keeps a user-global signature counter in UserState.
633 // For platform authenticator credentials, we derive a counter from a
634 // timestamp instead.
635 if (is_u2f_authenticator_credential) {
636 base::Optional<std::vector<uint8_t>> counter = user_state_->GetCounter();
637 if (!counter || !user_state_->IncrementCounter()) {
638 // UserState logs an error in this case.
639 return base::nullopt;
640 }
641 util::AppendToVector(*counter, &authenticator_data);
642 } else {
643 util::AppendToVector(GetTimestampSignatureCounter(), &authenticator_data);
644 }
Yicheng Lia40e2b52020-01-03 17:05:32 -0800645
646 if (include_attested_credential_data) {
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700647 util::AppendToVector(is_u2f_authenticator_credential
Yicheng Li9e0b3562021-02-10 10:33:43 -0800648 ? std::vector<uint8_t>(kAaguid.size(), 0)
649 : kAaguid,
650 &authenticator_data);
Yicheng Lia40e2b52020-01-03 17:05:32 -0800651 uint16_t length = credential_id.size();
652 util::AppendToVector(Uint16ToByteVector(length), &authenticator_data);
653
654 AppendAttestedCredential(credential_id, credential_public_key,
655 &authenticator_data);
656 }
657
658 return authenticator_data;
659}
660
661void WebAuthnHandler::AppendNoneAttestation(MakeCredentialResponse* response) {
662 response->set_attestation_format(kAttestationFormatNone);
663 response->mutable_attestation_statement()->push_back(
664 kAttestationStatementNone);
665}
666
Yicheng Li1bbf5292021-01-25 17:19:56 -0800667base::Optional<std::vector<uint8_t>>
668WebAuthnHandler::MakeFidoU2fAttestationStatement(
669 const std::vector<uint8_t>& data_to_sign,
670 const MakeCredentialRequest::AttestationConveyancePreference
671 attestation_conveyance_preference) {
672 std::vector<uint8_t> attestation_cert;
673 std::vector<uint8_t> signature;
674 if (attestation_conveyance_preference == MakeCredentialRequest::G2F &&
675 u2f_mode_ == U2fMode::kU2fExtended) {
676 base::Optional<std::vector<uint8_t>> g2f_cert =
677 util::GetG2fCert(tpm_proxy_);
678 if (g2f_cert.has_value()) {
679 attestation_cert = *g2f_cert;
680 } else {
681 LOG(ERROR) << "Failed to get G2f cert for MakeCredential";
682 return base::nullopt;
683 }
684
685 MakeCredentialResponse::MakeCredentialStatus attest_status =
686 DoG2fAttest(data_to_sign, U2F_ATTEST_FORMAT_REG_RESP, &signature);
687
688 if (attest_status != MakeCredentialResponse::SUCCESS) {
689 LOG(ERROR) << "Failed to do G2f attestation for MakeCredential";
690 return base::nullopt;
691 }
692
693 if (allowlisting_util_ != nullptr &&
694 !allowlisting_util_->AppendDataToCert(&attestation_cert)) {
695 LOG(ERROR) << "Failed to get allowlisting data for G2F Enroll Request";
696 return base::nullopt;
697 }
698 } else {
699 if (!util::DoSoftwareAttest(data_to_sign, &attestation_cert, &signature)) {
700 LOG(ERROR) << "Failed to do software attestation for MakeCredential";
701 return base::nullopt;
702 }
703 }
704
705 return EncodeU2fAttestationStatementInCBOR(signature, attestation_cert);
706}
707
Yicheng Lic4bc7412020-03-04 12:02:16 -0800708void WebAuthnHandler::CallAndWaitForPresence(std::function<uint32_t()> fn,
709 uint32_t* status) {
710 *status = fn();
711 base::TimeTicks verification_start = base::TimeTicks::Now();
712 while (*status == kCr50StatusNotAllowed &&
713 base::TimeTicks::Now() - verification_start <
714 base::TimeDelta::FromMilliseconds(kVerificationTimeoutMs)) {
715 // We need user presence. Show a notification requesting it, and try again.
716 request_presence_();
717 usleep(kVerificationRetryDelayUs);
718 *status = fn();
719 }
720}
721
722MakeCredentialResponse::MakeCredentialStatus WebAuthnHandler::DoU2fGenerate(
723 const std::vector<uint8_t>& rp_id_hash,
Yicheng Li4d27fa72020-12-10 11:09:21 -0800724 const std::vector<uint8_t>& credential_secret,
Yicheng Lic4bc7412020-03-04 12:02:16 -0800725 PresenceRequirement presence_requirement,
Yicheng Li5ca11f52020-05-14 16:49:48 -0700726 bool uv_compatible,
Yicheng Lic4bc7412020-03-04 12:02:16 -0800727 std::vector<uint8_t>* credential_id,
728 std::vector<uint8_t>* credential_public_key) {
729 DCHECK(rp_id_hash.size() == SHA256_DIGEST_LENGTH);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800730
Yicheng Li5ca11f52020-05-14 16:49:48 -0700731 struct u2f_generate_req generate_req = {};
Yi Chouad18f172020-11-18 14:54:18 +0800732 if (!util::VectorToObject(rp_id_hash, generate_req.appId,
733 sizeof(generate_req.appId))) {
734 return MakeCredentialResponse::INVALID_REQUEST;
735 }
736 if (!util::VectorToObject(credential_secret, generate_req.userSecret,
737 sizeof(generate_req.userSecret))) {
738 return MakeCredentialResponse::INVALID_REQUEST;
739 }
Yicheng Lic4bc7412020-03-04 12:02:16 -0800740
Yicheng Li5ca11f52020-05-14 16:49:48 -0700741 if (uv_compatible) {
Yicheng Li9e902962020-11-01 11:07:11 -0800742 if (!auth_time_secret_hash_) {
743 LOG(ERROR) << "No auth-time secret hash to use for u2f_generate.";
744 return MakeCredentialResponse::INTERNAL_ERROR;
745 }
Yicheng Li5ca11f52020-05-14 16:49:48 -0700746 generate_req.flags |= U2F_UV_ENABLED_KH;
Yicheng Li9e902962020-11-01 11:07:11 -0800747 memcpy(generate_req.authTimeSecretHash, auth_time_secret_hash_->data(),
748 auth_time_secret_hash_->size());
Yicheng Li5ca11f52020-05-14 16:49:48 -0700749 struct u2f_generate_versioned_resp generate_resp = {};
Yicheng Li0eaf36c2020-11-12 12:29:25 -0800750
Yicheng Li5ca11f52020-05-14 16:49:48 -0700751 if (presence_requirement != PresenceRequirement::kPowerButton) {
Yicheng Li0eaf36c2020-11-12 12:29:25 -0800752 uint32_t generate_status =
753 tpm_proxy_->SendU2fGenerate(generate_req, &generate_resp);
754 if (generate_status != 0)
755 return MakeCredentialResponse::INTERNAL_ERROR;
756
757 util::AppendToVector(generate_resp.pubKey, credential_public_key);
758 util::AppendToVector(generate_resp.keyHandle, credential_id);
759 return MakeCredentialResponse::SUCCESS;
Yicheng Li5ca11f52020-05-14 16:49:48 -0700760 }
Yicheng Li0eaf36c2020-11-12 12:29:25 -0800761
762 // Require user presence, consume.
763 generate_req.flags |= U2F_AUTH_ENFORCE;
Yicheng Li5ca11f52020-05-14 16:49:48 -0700764 return SendU2fGenerateWaitForPresence(&generate_req, &generate_resp,
765 credential_id, credential_public_key);
766 } else {
767 // Non-versioned KH must be signed with power button press.
768 if (presence_requirement != PresenceRequirement::kPowerButton)
769 return MakeCredentialResponse::INTERNAL_ERROR;
770 // Require user presence, consume.
771 generate_req.flags |= U2F_AUTH_ENFORCE;
772 struct u2f_generate_resp generate_resp = {};
773 return SendU2fGenerateWaitForPresence(&generate_req, &generate_resp,
774 credential_id, credential_public_key);
775 }
776}
Yicheng Lic4bc7412020-03-04 12:02:16 -0800777
Yicheng Li5ca11f52020-05-14 16:49:48 -0700778template <typename Response>
779MakeCredentialResponse::MakeCredentialStatus
780WebAuthnHandler::SendU2fGenerateWaitForPresence(
781 struct u2f_generate_req* generate_req,
782 Response* generate_resp,
783 std::vector<uint8_t>* credential_id,
784 std::vector<uint8_t>* credential_public_key) {
Yicheng Lic4bc7412020-03-04 12:02:16 -0800785 uint32_t generate_status = -1;
786 base::AutoLock(tpm_proxy_->GetLock());
787 CallAndWaitForPresence(
Yicheng Li5ca11f52020-05-14 16:49:48 -0700788 [this, generate_req, generate_resp]() {
789 return tpm_proxy_->SendU2fGenerate(*generate_req, generate_resp);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800790 },
791 &generate_status);
Tom Hughesd2b87542021-01-06 17:05:51 -0800792 brillo::SecureClearContainer(generate_req->userSecret);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800793
794 if (generate_status == 0) {
Yicheng Li5ca11f52020-05-14 16:49:48 -0700795 util::AppendToVector(generate_resp->pubKey, credential_public_key);
796 util::AppendToVector(generate_resp->keyHandle, credential_id);
Yicheng Lic4bc7412020-03-04 12:02:16 -0800797 return MakeCredentialResponse::SUCCESS;
798 }
799
800 return MakeCredentialResponse::VERIFICATION_FAILED;
Louis Collardb7664ae2019-10-05 13:57:01 +0800801}
802
Yicheng Lif3da4022020-11-10 18:30:15 -0800803// TODO(b/172971998): Remove this workaround once cr50 handles this.
804void WebAuthnHandler::InsertAuthTimeSecretHashToCredentialId(
805 std::vector<uint8_t>* input) {
806 CHECK(input->size() == sizeof(u2f_versioned_key_handle));
807 // The auth time secret hash should be inserted right after the header and
808 // the authorization salt, before the authorization hmac.
809 input->insert(
810 input->cbegin() + offsetof(u2f_versioned_key_handle, authorization_hmac),
811 auth_time_secret_hash_->cbegin(), auth_time_secret_hash_->cend());
812}
813
Yicheng Lic3ae78d2020-11-25 15:32:20 -0800814// TODO(b/172971998): Remove this workaround once cr50 handles this.
815void WebAuthnHandler::RemoveAuthTimeSecretHashFromCredentialId(
816 std::vector<uint8_t>* input) {
817 CHECK_EQ(input->size(),
818 sizeof(u2f_versioned_key_handle) + SHA256_DIGEST_LENGTH);
819 // The auth time secret hash is after the header and the authorization salt,
820 // before the authorization hmac. Remove it so that cr50 recognizes the KH.
821 const std::vector<uint8_t>::const_iterator remove_begin =
822 input->cbegin() + offsetof(u2f_versioned_key_handle, authorization_hmac);
823 input->erase(remove_begin, remove_begin + SHA256_DIGEST_LENGTH);
824}
825
Yicheng Li4652feb2020-04-27 15:18:59 -0700826HasCredentialsResponse::HasCredentialsStatus
827WebAuthnHandler::HasExcludedCredentials(const MakeCredentialRequest& request) {
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800828 MatchedCredentials matched =
829 FindMatchedCredentials(request.excluded_credential_id(), request.rp_id(),
830 request.app_id_exclude());
831 if (matched.has_internal_error) {
832 return HasCredentialsResponse::INTERNAL_ERROR;
Yicheng Li4652feb2020-04-27 15:18:59 -0700833 }
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800834
835 if (matched.platform_credentials.empty() &&
836 matched.legacy_credentials_for_rp_id.empty() &&
837 matched.legacy_credentials_for_app_id.empty()) {
838 return HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID;
839 }
840 return HasCredentialsResponse::SUCCESS;
Yicheng Li4652feb2020-04-27 15:18:59 -0700841}
842
Yicheng Lic03ac7e2019-12-09 16:13:26 -0800843void WebAuthnHandler::GetAssertion(
844 std::unique_ptr<GetAssertionMethodResponse> method_response,
Louis Collardb7664ae2019-10-05 13:57:01 +0800845 const GetAssertionRequest& request) {
Yicheng Lif5949a02020-03-27 11:34:41 -0700846 GetAssertionResponse response;
847
848 if (!Initialized()) {
849 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
850 method_response->Return(response);
851 return;
852 }
853
Yicheng Li98006782020-09-09 18:19:54 -0700854 if (pending_uv_make_credential_session_ ||
855 pending_uv_get_assertion_session_) {
856 response.set_status(GetAssertionResponse::REQUEST_PENDING);
857 method_response->Return(response);
858 return;
859 }
860
Yicheng Lif5949a02020-03-27 11:34:41 -0700861 if (request.rp_id().empty() ||
862 request.client_data_hash().size() != SHA256_DIGEST_LENGTH) {
863 response.set_status(GetAssertionResponse::INVALID_REQUEST);
864 method_response->Return(response);
865 return;
866 }
867
Yicheng Lib46d4362020-06-05 15:15:45 -0700868 if (request.verification_type() == VerificationType::VERIFICATION_UNKNOWN) {
Yicheng Lif5949a02020-03-27 11:34:41 -0700869 response.set_status(GetAssertionResponse::VERIFICATION_FAILED);
870 method_response->Return(response);
871 return;
872 }
873
Yicheng Lif5949a02020-03-27 11:34:41 -0700874 // TODO(louiscollard): Support resident credentials.
Yicheng Li6e673a62020-08-26 13:55:43 -0700875
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800876 std::string* credential_to_use;
877 bool is_legacy_credential = false;
878 bool use_app_id = false;
Yicheng Li6e673a62020-08-26 13:55:43 -0700879
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800880 MatchedCredentials matched = FindMatchedCredentials(
881 request.allowed_credential_id(), request.rp_id(), request.app_id());
882 if (matched.has_internal_error) {
883 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
884 method_response->Return(response);
885 return;
Yicheng Lif5949a02020-03-27 11:34:41 -0700886 }
887
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800888 if (!matched.platform_credentials.empty()) {
889 credential_to_use = &matched.platform_credentials[0];
890 } else if (!matched.legacy_credentials_for_rp_id.empty()) {
891 credential_to_use = &matched.legacy_credentials_for_rp_id[0];
892 is_legacy_credential = true;
893 } else if (!matched.legacy_credentials_for_app_id.empty()) {
894 credential_to_use = &matched.legacy_credentials_for_app_id[0];
895 is_legacy_credential = true;
896 use_app_id = true;
897 } else {
Yicheng Li4652feb2020-04-27 15:18:59 -0700898 response.set_status(GetAssertionResponse::UNKNOWN_CREDENTIAL_ID);
Yicheng Lif5949a02020-03-27 11:34:41 -0700899 method_response->Return(response);
900 return;
901 }
902
903 struct GetAssertionSession session = {
904 static_cast<uint64_t>(base::Time::Now().ToTimeT()), request,
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800905 *credential_to_use, std::move(method_response)};
906 if (use_app_id) {
907 // App id was matched instead of rp id, so discard rp id.
908 session.request.set_rp_id(request.app_id());
909 }
Yicheng Lib46d4362020-06-05 15:15:45 -0700910
Yicheng Lia42e6d42021-01-22 14:58:46 -0800911 if (!AllowPresenceMode()) {
Yicheng Lid0d21e22020-12-17 18:06:23 -0800912 // Upgrade UP requests to UV.
913 session.request.set_verification_type(
914 VerificationType::VERIFICATION_USER_VERIFICATION);
915 }
Yicheng Lieadff032020-08-27 11:21:30 -0700916
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800917 // Legacy credentials should go through power button, not UV.
Yicheng Lid0d21e22020-12-17 18:06:23 -0800918 if (session.request.verification_type() ==
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800919 VerificationType::VERIFICATION_USER_VERIFICATION &&
920 !is_legacy_credential) {
Yicheng Lie7a26252021-02-03 12:18:37 -0800921 metrics_->SendBoolToUMA(kPerformingUserVerificationMetric, true);
Yicheng Lid0d21e22020-12-17 18:06:23 -0800922 dbus::MethodCall call(
923 chromeos::kUserAuthenticationServiceInterface,
924 chromeos::kUserAuthenticationServiceShowAuthDialogMethod);
925 dbus::MessageWriter writer(&call);
926 writer.AppendString(session.request.rp_id());
927 writer.AppendInt32(session.request.verification_type());
928 writer.AppendUint64(session.request.request_id());
Yicheng Lib46d4362020-06-05 15:15:45 -0700929
Yicheng Lid0d21e22020-12-17 18:06:23 -0800930 pending_uv_get_assertion_session_ = std::move(session);
931 auth_dialog_dbus_proxy_->CallMethod(
932 &call, dbus::ObjectProxy::TIMEOUT_INFINITE,
933 base::Bind(&WebAuthnHandler::HandleUVFlowResultGetAssertion,
934 base::Unretained(this)));
935 return;
936 }
937
Yicheng Lie7a26252021-02-03 12:18:37 -0800938 metrics_->SendBoolToUMA(kPerformingUserVerificationMetric, false);
Yicheng Lid0d21e22020-12-17 18:06:23 -0800939 DoGetAssertion(std::move(session), PresenceRequirement::kPowerButton);
Yicheng Lif5949a02020-03-27 11:34:41 -0700940}
941
942// If already seeing failure, then no need to get user secret. This means
943// in the fingerprint case, this signal should ideally come from UI instead of
944// biod because only UI knows about retry.
945void WebAuthnHandler::DoGetAssertion(struct GetAssertionSession session,
946 PresenceRequirement presence_requirement) {
947 GetAssertionResponse response;
Yicheng Lif5949a02020-03-27 11:34:41 -0700948
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700949 bool is_u2f_authenticator_credential = false;
Yicheng Li4d27fa72020-12-10 11:09:21 -0800950 base::Optional<std::vector<uint8_t>> credential_secret =
Yicheng Li1090c902020-11-10 11:31:43 -0800951 webauthn_storage_->GetSecretByCredentialId(session.credential_id);
952 if (!credential_secret) {
Yicheng Lia42e6d42021-01-22 14:58:46 -0800953 if (!AllowPresenceMode()) {
Yicheng Lifa8c4e32020-12-02 14:21:05 -0800954 LOG(ERROR) << "No credential secret for credential id "
955 << session.credential_id << ", aborting GetAssertion.";
956 response.set_status(GetAssertionResponse::UNKNOWN_CREDENTIAL_ID);
957 session.response->Return(response);
958 return;
959 }
960
961 // Maybe signing u2fhid credentials. Use legacy secret instead.
962 base::Optional<brillo::SecureBlob> legacy_secret =
963 user_state_->GetUserSecret();
964 if (!legacy_secret) {
965 LOG(ERROR)
966 << "Cannot find user secret when trying to sign u2fhid credentials";
967 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
968 session.response->Return(response);
969 return;
970 }
971 credential_secret =
972 std::vector<uint8_t>(legacy_secret->begin(), legacy_secret->end());
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700973 is_u2f_authenticator_credential = true;
Yicheng Li1090c902020-11-10 11:31:43 -0800974 }
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -0700975
976 const std::vector<uint8_t> rp_id_hash = util::Sha256(session.request.rp_id());
977 const base::Optional<std::vector<uint8_t>> authenticator_data =
978 MakeAuthenticatorData(
979 rp_id_hash, std::vector<uint8_t>(), std::vector<uint8_t>(),
980 // If presence requirement is "power button" then the user was not
981 // verified. Otherwise the user was verified through UI.
982 /* user_verified = */ presence_requirement !=
983 PresenceRequirement::kPowerButton,
984 /* include_attested_credential_data = */ false,
985 is_u2f_authenticator_credential);
986 if (!authenticator_data) {
987 LOG(ERROR) << "MakeAuthenticatorData failed";
988 response.set_status(GetAssertionResponse::INTERNAL_ERROR);
989 session.response->Return(response);
990 return;
991 }
992
993 std::vector<uint8_t> data_to_sign(*authenticator_data);
994 util::AppendToVector(session.request.client_data_hash(), &data_to_sign);
995 std::vector<uint8_t> hash_to_sign = util::Sha256(data_to_sign);
996
Yicheng Lif5949a02020-03-27 11:34:41 -0700997 std::vector<uint8_t> signature;
998 GetAssertionResponse::GetAssertionStatus sign_status =
Yicheng Li6e673a62020-08-26 13:55:43 -0700999 DoU2fSign(rp_id_hash, hash_to_sign, util::ToVector(session.credential_id),
Yicheng Li1090c902020-11-10 11:31:43 -08001000 *credential_secret, presence_requirement, &signature);
Yicheng Lif5949a02020-03-27 11:34:41 -07001001 response.set_status(sign_status);
1002 if (sign_status == GetAssertionResponse::SUCCESS) {
1003 auto* assertion = response.add_assertion();
Yicheng Lica115752021-03-08 17:20:36 -08001004 assertion->set_credential_id(session.credential_id);
Martin Kreichgauer5eae00c2021-05-05 15:07:40 -07001005 AppendToString(*authenticator_data,
1006 assertion->mutable_authenticator_data());
Yicheng Lif5949a02020-03-27 11:34:41 -07001007 AppendToString(signature, assertion->mutable_signature());
1008 }
1009
Yicheng Li98006782020-09-09 18:19:54 -07001010 session.response->Return(response);
Yicheng Lif5949a02020-03-27 11:34:41 -07001011}
1012
1013GetAssertionResponse::GetAssertionStatus WebAuthnHandler::DoU2fSign(
1014 const std::vector<uint8_t>& rp_id_hash,
1015 const std::vector<uint8_t>& hash_to_sign,
1016 const std::vector<uint8_t>& credential_id,
Yicheng Li4d27fa72020-12-10 11:09:21 -08001017 const std::vector<uint8_t>& credential_secret,
Yicheng Lif5949a02020-03-27 11:34:41 -07001018 PresenceRequirement presence_requirement,
1019 std::vector<uint8_t>* signature) {
1020 DCHECK(rp_id_hash.size() == SHA256_DIGEST_LENGTH);
Yicheng Lif5949a02020-03-27 11:34:41 -07001021
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001022 if (credential_id.size() ==
1023 sizeof(u2f_versioned_key_handle) + SHA256_DIGEST_SIZE) {
Yicheng Li5ca11f52020-05-14 16:49:48 -07001024 // Allow waiving presence if sign_req.authTimeSecret is correct.
1025 struct u2f_sign_versioned_req sign_req = {};
Yi Chouad18f172020-11-18 14:54:18 +08001026 if (!util::VectorToObject(rp_id_hash, sign_req.appId,
1027 sizeof(sign_req.appId))) {
1028 return GetAssertionResponse::INVALID_REQUEST;
1029 }
1030 if (!util::VectorToObject(credential_secret, sign_req.userSecret,
1031 sizeof(sign_req.userSecret))) {
1032 return GetAssertionResponse::INVALID_REQUEST;
1033 }
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001034 std::vector<uint8_t> key_handle(credential_id);
1035 RemoveAuthTimeSecretHashFromCredentialId(&key_handle);
1036 if (!util::VectorToObject(key_handle, &sign_req.keyHandle,
Yi Chouad18f172020-11-18 14:54:18 +08001037 sizeof(sign_req.keyHandle))) {
1038 return GetAssertionResponse::INVALID_REQUEST;
1039 }
1040 if (!util::VectorToObject(hash_to_sign, sign_req.hash,
1041 sizeof(sign_req.hash))) {
1042 return GetAssertionResponse::INVALID_REQUEST;
1043 }
Yicheng Li5ca11f52020-05-14 16:49:48 -07001044 struct u2f_sign_resp sign_resp = {};
1045
1046 if (presence_requirement != PresenceRequirement::kPowerButton) {
Yicheng Li0eaf36c2020-11-12 12:29:25 -08001047 uint32_t sign_status = tpm_proxy_->SendU2fSign(sign_req, &sign_resp);
1048 if (sign_status != 0)
1049 return GetAssertionResponse::INTERNAL_ERROR;
1050
1051 base::Optional<std::vector<uint8_t>> opt_signature =
1052 util::SignatureToDerBytes(sign_resp.sig_r, sign_resp.sig_s);
1053 if (!opt_signature.has_value()) {
1054 return GetAssertionResponse::INTERNAL_ERROR;
1055 }
1056 *signature = *opt_signature;
1057 return GetAssertionResponse::SUCCESS;
Yicheng Li5ca11f52020-05-14 16:49:48 -07001058 }
1059
Yicheng Li0eaf36c2020-11-12 12:29:25 -08001060 // Require user presence, consume.
1061 sign_req.flags |= U2F_AUTH_ENFORCE;
Yicheng Li5ca11f52020-05-14 16:49:48 -07001062 return SendU2fSignWaitForPresence(&sign_req, &sign_resp, signature);
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001063 } else if (credential_id.size() == sizeof(u2f_key_handle)) {
Yicheng Li5ca11f52020-05-14 16:49:48 -07001064 // Non-versioned KH must be signed with power button press.
1065 if (presence_requirement != PresenceRequirement::kPowerButton)
1066 return GetAssertionResponse::INTERNAL_ERROR;
1067
1068 struct u2f_sign_req sign_req = {
1069 .flags = U2F_AUTH_ENFORCE // Require user presence, consume.
1070 };
Yi Chouad18f172020-11-18 14:54:18 +08001071 if (!util::VectorToObject(rp_id_hash, sign_req.appId,
1072 sizeof(sign_req.appId))) {
1073 return GetAssertionResponse::INVALID_REQUEST;
1074 }
1075 if (!util::VectorToObject(credential_secret, sign_req.userSecret,
1076 sizeof(sign_req.userSecret))) {
1077 return GetAssertionResponse::INVALID_REQUEST;
1078 }
1079 if (!util::VectorToObject(credential_id, &sign_req.keyHandle,
1080 sizeof(sign_req.keyHandle))) {
1081 return GetAssertionResponse::INVALID_REQUEST;
1082 }
1083 if (!util::VectorToObject(hash_to_sign, sign_req.hash,
1084 sizeof(sign_req.hash))) {
1085 return GetAssertionResponse::INVALID_REQUEST;
1086 }
Yicheng Li5ca11f52020-05-14 16:49:48 -07001087
1088 struct u2f_sign_resp sign_resp = {};
1089 return SendU2fSignWaitForPresence(&sign_req, &sign_resp, signature);
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001090 } else {
Martin Kreichgauerd73eec12021-05-21 15:26:14 -07001091 return GetAssertionResponse::UNKNOWN_CREDENTIAL_ID;
Yicheng Lif5949a02020-03-27 11:34:41 -07001092 }
Yicheng Li5ca11f52020-05-14 16:49:48 -07001093}
Yicheng Lif5949a02020-03-27 11:34:41 -07001094
Yicheng Li5ca11f52020-05-14 16:49:48 -07001095template <typename Request>
1096GetAssertionResponse::GetAssertionStatus
1097WebAuthnHandler::SendU2fSignWaitForPresence(Request* sign_req,
1098 struct u2f_sign_resp* sign_resp,
1099 std::vector<uint8_t>* signature) {
Yicheng Lif5949a02020-03-27 11:34:41 -07001100 uint32_t sign_status = -1;
1101 base::AutoLock(tpm_proxy_->GetLock());
1102 CallAndWaitForPresence(
Yicheng Li5ca11f52020-05-14 16:49:48 -07001103 [this, sign_req, sign_resp]() {
1104 return tpm_proxy_->SendU2fSign(*sign_req, sign_resp);
Yicheng Lif5949a02020-03-27 11:34:41 -07001105 },
1106 &sign_status);
Tom Hughesd2b87542021-01-06 17:05:51 -08001107 brillo::SecureClearContainer(sign_req->userSecret);
Yicheng Lif5949a02020-03-27 11:34:41 -07001108
1109 if (sign_status == 0) {
1110 base::Optional<std::vector<uint8_t>> opt_signature =
Yicheng Li5ca11f52020-05-14 16:49:48 -07001111 util::SignatureToDerBytes(sign_resp->sig_r, sign_resp->sig_s);
Yicheng Lif5949a02020-03-27 11:34:41 -07001112 if (!opt_signature.has_value()) {
1113 return GetAssertionResponse::INTERNAL_ERROR;
1114 }
1115 *signature = *opt_signature;
1116 return GetAssertionResponse::SUCCESS;
1117 }
1118
1119 return GetAssertionResponse::VERIFICATION_FAILED;
Louis Collardb7664ae2019-10-05 13:57:01 +08001120}
1121
Yicheng Li1bbf5292021-01-25 17:19:56 -08001122MakeCredentialResponse::MakeCredentialStatus WebAuthnHandler::DoG2fAttest(
1123 const std::vector<uint8_t>& data,
1124 uint8_t format,
1125 std::vector<uint8_t>* signature_out) {
1126 base::AutoLock(tpm_proxy_->GetLock());
1127 base::Optional<brillo::SecureBlob> user_secret = user_state_->GetUserSecret();
1128 if (!user_secret.has_value()) {
1129 return MakeCredentialResponse::INTERNAL_ERROR;
1130 }
1131
1132 struct u2f_attest_req attest_req = {
1133 .format = format, .dataLen = static_cast<uint8_t>(data.size())};
1134 if (!util::VectorToObject(*user_secret, attest_req.userSecret,
1135 sizeof(attest_req.userSecret))) {
1136 return MakeCredentialResponse::INTERNAL_ERROR;
1137 }
1138 if (!util::VectorToObject(data, attest_req.data, sizeof(attest_req.data))) {
1139 return MakeCredentialResponse::INTERNAL_ERROR;
1140 }
1141
1142 struct u2f_attest_resp attest_resp = {};
1143 uint32_t attest_status = tpm_proxy_->SendU2fAttest(attest_req, &attest_resp);
1144
Tom Hughesbd48f332021-01-07 10:18:05 -08001145 brillo::SecureClearBytes(&attest_req.userSecret,
1146 sizeof(attest_req.userSecret));
Yicheng Li1bbf5292021-01-25 17:19:56 -08001147
1148 if (attest_status != 0) {
1149 // We are attesting to a key handle that we just created, so if
1150 // attestation fails we have hit some internal error.
1151 LOG(ERROR) << "U2F_ATTEST failed, status: " << std::hex
1152 << static_cast<uint32_t>(attest_status);
1153 return MakeCredentialResponse::INTERNAL_ERROR;
1154 }
1155
1156 base::Optional<std::vector<uint8_t>> signature =
1157 util::SignatureToDerBytes(attest_resp.sig_r, attest_resp.sig_s);
1158
1159 if (!signature.has_value()) {
1160 LOG(ERROR) << "DER encoding of U2F_ATTEST signature failed.";
1161 return MakeCredentialResponse::INTERNAL_ERROR;
1162 }
1163
1164 *signature_out = *signature;
1165
1166 return MakeCredentialResponse::SUCCESS;
1167}
1168
Yicheng Lifa8c4e32020-12-02 14:21:05 -08001169MatchedCredentials WebAuthnHandler::FindMatchedCredentials(
1170 const RepeatedPtrField<std::string>& all_credentials,
1171 const std::string& rp_id,
1172 const std::string& app_id) {
1173 std::vector<uint8_t> rp_id_hash = util::Sha256(rp_id);
1174 std::vector<uint8_t> app_id_hash = util::Sha256(app_id);
1175 MatchedCredentials result;
1176
1177 // Platform authenticator credentials.
1178 for (const auto& credential_id : all_credentials) {
1179 base::Optional<std::vector<uint8_t>> credential_secret =
1180 webauthn_storage_->GetSecretByCredentialId(credential_id);
1181 if (!credential_secret)
1182 continue;
1183
1184 auto ret = DoU2fSignCheckOnly(rp_id_hash, util::ToVector(credential_id),
1185 *credential_secret);
1186 if (ret == HasCredentialsResponse::INTERNAL_ERROR) {
1187 result.has_internal_error = true;
1188 return result;
1189 } else if (ret == HasCredentialsResponse::SUCCESS) {
1190 result.platform_credentials.emplace_back(credential_id);
1191 }
1192 }
1193
1194 const base::Optional<brillo::SecureBlob> user_secret =
1195 user_state_->GetUserSecret();
1196 if (!user_secret) {
1197 result.has_internal_error = true;
1198 return result;
1199 }
1200
1201 // Legacy credentials. If a legacy credential matches both rp_id and app_id,
1202 // it will only appear in result.legacy_credentials_for_rp_id.
1203 for (const auto& credential_id : all_credentials) {
1204 // First try matching rp_id.
1205 HasCredentialsResponse::HasCredentialsStatus ret = DoU2fSignCheckOnly(
1206 rp_id_hash, util::ToVector(credential_id),
1207 std::vector<uint8_t>(user_secret->begin(), user_secret->end()));
1208 DCHECK(HasCredentialsResponse::HasCredentialsStatus_IsValid(ret));
1209 switch (ret) {
1210 case HasCredentialsResponse::SUCCESS:
1211 // rp_id matched, it's a credential registered with u2fhid on WebAuthn
1212 // API.
1213 result.legacy_credentials_for_rp_id.emplace_back(credential_id);
1214 continue;
1215 case HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID:
1216 break;
1217 case HasCredentialsResponse::UNKNOWN:
1218 case HasCredentialsResponse::INVALID_REQUEST:
1219 case HasCredentialsResponse::INTERNAL_ERROR:
1220 result.has_internal_error = true;
1221 return result;
1222 case google::protobuf::kint32min:
1223 case google::protobuf::kint32max:
1224 NOTREACHED();
1225 }
1226
1227 // Try matching app_id.
1228 ret = DoU2fSignCheckOnly(
1229 app_id_hash, util::ToVector(credential_id),
1230 std::vector<uint8_t>(user_secret->begin(), user_secret->end()));
1231 DCHECK(HasCredentialsResponse::HasCredentialsStatus_IsValid(ret));
1232 switch (ret) {
1233 case HasCredentialsResponse::SUCCESS:
1234 // App id extension matched. It's a legacy credential registered with
1235 // the U2F interface.
1236 result.legacy_credentials_for_app_id.emplace_back(credential_id);
1237 continue;
1238 case HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID:
1239 break;
1240 case HasCredentialsResponse::UNKNOWN:
1241 case HasCredentialsResponse::INVALID_REQUEST:
1242 case HasCredentialsResponse::INTERNAL_ERROR:
1243 result.has_internal_error = true;
1244 return result;
1245 case google::protobuf::kint32min:
1246 case google::protobuf::kint32max:
1247 NOTREACHED();
1248 }
1249 }
1250
1251 return result;
1252}
1253
Louis Collardb7664ae2019-10-05 13:57:01 +08001254HasCredentialsResponse WebAuthnHandler::HasCredentials(
1255 const HasCredentialsRequest& request) {
Yicheng Lif5949a02020-03-27 11:34:41 -07001256 HasCredentialsResponse response;
1257
Yicheng Li4652feb2020-04-27 15:18:59 -07001258 if (!Initialized()) {
1259 response.set_status(HasCredentialsResponse::INTERNAL_ERROR);
1260 return response;
1261 }
1262
1263 if (request.rp_id().empty() || request.credential_id().empty()) {
1264 response.set_status(HasCredentialsResponse::INVALID_REQUEST);
Yicheng Lif5949a02020-03-27 11:34:41 -07001265 return response;
1266 }
1267
Yicheng Lifa8c4e32020-12-02 14:21:05 -08001268 MatchedCredentials matched = FindMatchedCredentials(
1269 request.credential_id(), request.rp_id(), request.app_id());
1270 if (matched.has_internal_error) {
1271 response.set_status(HasCredentialsResponse::INTERNAL_ERROR);
1272 return response;
Yicheng Lif5949a02020-03-27 11:34:41 -07001273 }
1274
Yicheng Lifa8c4e32020-12-02 14:21:05 -08001275 for (const auto& credential_id : matched.platform_credentials) {
1276 *response.add_credential_id() = credential_id;
1277 }
1278 for (const auto& credential_id : matched.legacy_credentials_for_rp_id) {
1279 *response.add_credential_id() = credential_id;
1280 }
1281 for (const auto& credential_id : matched.legacy_credentials_for_app_id) {
1282 *response.add_credential_id() = credential_id;
Yicheng Lif2a083a2020-12-21 17:29:47 -08001283 }
1284
1285 response.set_status((response.credential_id_size() > 0)
1286 ? HasCredentialsResponse::SUCCESS
1287 : HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID);
1288 return response;
1289}
1290
1291HasCredentialsResponse WebAuthnHandler::HasLegacyCredentials(
1292 const HasCredentialsRequest& request) {
1293 HasCredentialsResponse response;
1294
1295 if (!Initialized()) {
1296 response.set_status(HasCredentialsResponse::INTERNAL_ERROR);
1297 return response;
1298 }
1299
1300 if (request.credential_id().empty()) {
1301 response.set_status(HasCredentialsResponse::INVALID_REQUEST);
1302 return response;
1303 }
1304
Yicheng Lifa8c4e32020-12-02 14:21:05 -08001305 MatchedCredentials matched = FindMatchedCredentials(
1306 request.credential_id(), request.rp_id(), request.app_id());
1307 if (matched.has_internal_error) {
Yicheng Lif2a083a2020-12-21 17:29:47 -08001308 response.set_status(HasCredentialsResponse::INTERNAL_ERROR);
1309 return response;
1310 }
Yicheng Lif2a083a2020-12-21 17:29:47 -08001311
Yicheng Lifa8c4e32020-12-02 14:21:05 -08001312 // Do not include platform credentials.
1313 for (const auto& credential_id : matched.legacy_credentials_for_rp_id) {
1314 *response.add_credential_id() = credential_id;
1315 }
1316 for (const auto& credential_id : matched.legacy_credentials_for_app_id) {
1317 *response.add_credential_id() = credential_id;
Yicheng Lif2a083a2020-12-21 17:29:47 -08001318 }
1319
Yicheng Li4652feb2020-04-27 15:18:59 -07001320 response.set_status((response.credential_id_size() > 0)
1321 ? HasCredentialsResponse::SUCCESS
1322 : HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID);
Yicheng Lif5949a02020-03-27 11:34:41 -07001323 return response;
1324}
1325
Yicheng Li4652feb2020-04-27 15:18:59 -07001326HasCredentialsResponse::HasCredentialsStatus
Yicheng Li1090c902020-11-10 11:31:43 -08001327WebAuthnHandler::DoU2fSignCheckOnly(
1328 const std::vector<uint8_t>& rp_id_hash,
1329 const std::vector<uint8_t>& credential_id,
Yicheng Li4d27fa72020-12-10 11:09:21 -08001330 const std::vector<uint8_t>& credential_secret) {
Yicheng Li5ca11f52020-05-14 16:49:48 -07001331 uint32_t sign_status;
Yicheng Lif5949a02020-03-27 11:34:41 -07001332
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001333 if (credential_id.size() ==
1334 sizeof(u2f_versioned_key_handle) + SHA256_DIGEST_SIZE) {
Yicheng Li5ca11f52020-05-14 16:49:48 -07001335 struct u2f_sign_versioned_req sign_req = {.flags = U2F_AUTH_CHECK_ONLY};
Yi Chouad18f172020-11-18 14:54:18 +08001336 if (!util::VectorToObject(rp_id_hash, sign_req.appId,
1337 sizeof(sign_req.appId))) {
1338 return HasCredentialsResponse::INVALID_REQUEST;
1339 }
1340 if (!util::VectorToObject(credential_secret, sign_req.userSecret,
1341 sizeof(sign_req.userSecret))) {
1342 return HasCredentialsResponse::INVALID_REQUEST;
1343 }
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001344 std::vector<uint8_t> key_handle(credential_id);
1345 RemoveAuthTimeSecretHashFromCredentialId(&key_handle);
1346 if (!util::VectorToObject(key_handle, &sign_req.keyHandle,
Yi Chouad18f172020-11-18 14:54:18 +08001347 sizeof(sign_req.keyHandle))) {
1348 return HasCredentialsResponse::INVALID_REQUEST;
1349 }
Yicheng Li5ca11f52020-05-14 16:49:48 -07001350
1351 struct u2f_sign_resp sign_resp;
1352 base::AutoLock(tpm_proxy_->GetLock());
1353 sign_status = tpm_proxy_->SendU2fSign(sign_req, &sign_resp);
Tom Hughesd2b87542021-01-06 17:05:51 -08001354 brillo::SecureClearContainer(sign_req.userSecret);
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001355 } else if (credential_id.size() == sizeof(u2f_key_handle)) {
Yicheng Li5ca11f52020-05-14 16:49:48 -07001356 struct u2f_sign_req sign_req = {.flags = U2F_AUTH_CHECK_ONLY};
Yi Chouad18f172020-11-18 14:54:18 +08001357 if (!util::VectorToObject(rp_id_hash, sign_req.appId,
1358 sizeof(sign_req.appId))) {
1359 return HasCredentialsResponse::INVALID_REQUEST;
1360 }
1361 if (!util::VectorToObject(credential_secret, sign_req.userSecret,
1362 sizeof(sign_req.userSecret))) {
1363 return HasCredentialsResponse::INVALID_REQUEST;
1364 }
1365 if (!util::VectorToObject(credential_id, &sign_req.keyHandle,
1366 sizeof(sign_req.keyHandle))) {
1367 return HasCredentialsResponse::INVALID_REQUEST;
1368 }
Yicheng Li5ca11f52020-05-14 16:49:48 -07001369
1370 struct u2f_sign_resp sign_resp;
1371 base::AutoLock(tpm_proxy_->GetLock());
1372 sign_status = tpm_proxy_->SendU2fSign(sign_req, &sign_resp);
Tom Hughesd2b87542021-01-06 17:05:51 -08001373 brillo::SecureClearContainer(sign_req.userSecret);
Yicheng Lic3ae78d2020-11-25 15:32:20 -08001374 } else {
Martin Kreichgauerd73eec12021-05-21 15:26:14 -07001375 return HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID;
Yicheng Li5ca11f52020-05-14 16:49:48 -07001376 }
Yicheng Lif5949a02020-03-27 11:34:41 -07001377
1378 // Return status of 0 indicates the credential is valid.
Yicheng Li4652feb2020-04-27 15:18:59 -07001379 return (sign_status == 0) ? HasCredentialsResponse::SUCCESS
1380 : HasCredentialsResponse::UNKNOWN_CREDENTIAL_ID;
Louis Collardb7664ae2019-10-05 13:57:01 +08001381}
1382
Yicheng Libc150862021-01-13 10:59:53 -08001383IsU2fEnabledResponse WebAuthnHandler::IsU2fEnabled(
1384 const IsU2fEnabledRequest& request) {
1385 IsU2fEnabledResponse response;
Yicheng Lia42e6d42021-01-22 14:58:46 -08001386 response.set_enabled(AllowPresenceMode());
Yicheng Libc150862021-01-13 10:59:53 -08001387 return response;
1388}
1389
Yicheng Lif17da5c2020-11-06 20:31:26 -08001390void WebAuthnHandler::IsUvpaa(
1391 std::unique_ptr<IsUvpaaMethodResponse> method_response,
1392 const IsUvpaaRequest& request) {
Yicheng Li67abd182020-11-18 15:31:41 -08001393 // Checking with the authentication dialog (in Ash) will not work, because
1394 // currently in Chrome the IsUvpaa is a blocking call, and Ash can't respond
1395 // to us since it runs in the same process as Chrome. After the Chrome side
1396 // is refactored to take a callback or Ash is split into a separate binary,
1397 // we can change the implementation here to query with Ash.
Yicheng Lif17da5c2020-11-06 20:31:26 -08001398
1399 IsUvpaaResponse response;
Yicheng Li67abd182020-11-18 15:31:41 -08001400
Yicheng Lia1705672020-12-02 17:18:50 -08001401 if (!Initialized()) {
1402 LOG(INFO) << "IsUvpaa called but WebAuthnHandler not initialized. Maybe "
1403 "U2F is on.";
1404 response.set_available(false);
1405 method_response->Return(response);
1406 return;
1407 }
1408
Yicheng Li7d537582020-12-17 12:35:57 -08001409 if (!auth_time_secret_hash_) {
1410 LOG(ERROR) << "No auth-time secret hash. MakeCredential will fail, so "
1411 "reporting IsUVPAA=false.";
1412 response.set_available(false);
1413 method_response->Return(response);
1414 return;
1415 }
1416
Yicheng Li67abd182020-11-18 15:31:41 -08001417 base::Optional<std::string> account_id = user_state_->GetUser();
1418 if (!account_id) {
1419 LOG(ERROR) << "IsUvpaa called but no user.";
Yicheng Lif17da5c2020-11-06 20:31:26 -08001420 response.set_available(false);
1421 method_response->Return(response);
1422 return;
1423 }
1424
Yicheng Li67abd182020-11-18 15:31:41 -08001425 if (HasPin(*account_id)) {
1426 response.set_available(true);
Yicheng Lif17da5c2020-11-06 20:31:26 -08001427 method_response->Return(response);
1428 return;
1429 }
1430
Yicheng Li67abd182020-11-18 15:31:41 -08001431 base::Optional<std::string> sanitized_user = user_state_->GetSanitizedUser();
1432 DCHECK(sanitized_user);
1433 if (HasFingerprint(*sanitized_user)) {
1434 response.set_available(true);
1435 method_response->Return(response);
1436 return;
1437 }
1438
1439 response.set_available(false);
Yicheng Lif17da5c2020-11-06 20:31:26 -08001440 method_response->Return(response);
1441}
1442
Yicheng Li67abd182020-11-18 15:31:41 -08001443bool WebAuthnHandler::HasPin(const std::string& account_id) {
1444 cryptohome::AccountIdentifier id;
1445 id.set_account_id(account_id);
1446 cryptohome::AuthorizationRequest auth;
1447 cryptohome::GetKeyDataRequest req;
1448 req.mutable_key()->mutable_data()->set_label(kCryptohomePinLabel);
1449 cryptohome::BaseReply reply;
1450 brillo::ErrorPtr error;
1451
1452 if (!cryptohome_proxy_->GetKeyDataEx(id, auth, req, &reply, &error,
1453 kCryptohomeTimeout.InMilliseconds())) {
1454 LOG(ERROR) << "Cannot query PIN availability from cryptohome, error: "
1455 << error->GetMessage();
1456 return false;
1457 }
1458
Yicheng Li07786dd2020-12-01 15:01:18 -08001459 if (reply.has_error()) {
1460 LOG(ERROR) << "GetKeyData response has error " << reply.error();
1461 return false;
1462 }
1463
Yicheng Li67abd182020-11-18 15:31:41 -08001464 if (!reply.HasExtension(cryptohome::GetKeyDataReply::reply)) {
1465 LOG(ERROR) << "GetKeyData response doesn't have the correct extension.";
1466 return false;
1467 }
1468
1469 return reply.GetExtension(cryptohome::GetKeyDataReply::reply)
1470 .key_data_size() > 0;
1471}
1472
1473bool WebAuthnHandler::HasFingerprint(const std::string& sanitized_user) {
1474 dbus::ObjectProxy* biod_proxy = bus_->GetObjectProxy(
1475 biod::kBiodServiceName,
1476 dbus::ObjectPath(std::string(biod::kBiodServicePath)
1477 .append(kCrosFpBiometricsManagerRelativePath)));
1478
1479 dbus::MethodCall method_call(biod::kBiometricsManagerInterface,
1480 biod::kBiometricsManagerGetRecordsForUserMethod);
1481 dbus::MessageWriter method_writer(&method_call);
1482 method_writer.AppendString(sanitized_user);
1483
1484 std::unique_ptr<dbus::Response> response = biod_proxy->CallMethodAndBlock(
1485 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT);
1486 if (!response) {
1487 LOG(ERROR)
1488 << "Cannot check fingerprint availability: no response from biod.";
1489 return false;
1490 }
1491
1492 dbus::MessageReader response_reader(response.get());
1493 dbus::MessageReader records_reader(nullptr);
1494 if (!response_reader.PopArray(&records_reader)) {
1495 LOG(ERROR) << "Cannot parse GetRecordsForUser response from biod.";
1496 return false;
1497 }
1498
1499 int records_count = 0;
1500 while (records_reader.HasMoreData()) {
1501 dbus::ObjectPath record_path;
1502 if (!records_reader.PopObjectPath(&record_path)) {
1503 LOG(WARNING) << "Cannot parse fingerprint record path";
1504 continue;
1505 }
1506 records_count++;
1507 }
1508 return records_count > 0;
1509}
1510
Yicheng Li1090c902020-11-10 11:31:43 -08001511void WebAuthnHandler::SetWebAuthnStorageForTesting(
1512 std::unique_ptr<WebAuthnStorage> storage) {
1513 webauthn_storage_ = std::move(storage);
1514}
1515
Yicheng Li67abd182020-11-18 15:31:41 -08001516void WebAuthnHandler::SetCryptohomeInterfaceProxyForTesting(
1517 std::unique_ptr<org::chromium::CryptohomeInterfaceProxyInterface>
1518 cryptohome_proxy) {
1519 cryptohome_proxy_ = std::move(cryptohome_proxy);
1520}
1521
Louis Collardb7664ae2019-10-05 13:57:01 +08001522} // namespace u2f