get_assertion_task.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431
  1. // Copyright 2018 The Chromium 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. #include "device/fido/get_assertion_task.h"
  5. #include <algorithm>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/containers/contains.h"
  9. #include "device/base/features.h"
  10. #include "device/fido/authenticator_get_assertion_response.h"
  11. #include "device/fido/ctap2_device_operation.h"
  12. #include "device/fido/make_credential_task.h"
  13. #include "device/fido/pin.h"
  14. #include "device/fido/u2f_sign_operation.h"
  15. namespace device {
  16. namespace {
  17. bool MayFallbackToU2fWithAppIdExtension(
  18. const FidoDevice& device,
  19. const CtapGetAssertionRequest& request) {
  20. bool ctap2_device_supports_u2f =
  21. device.device_info() &&
  22. base::Contains(device.device_info()->versions, ProtocolVersion::kU2f);
  23. return request.alternative_application_parameter &&
  24. ctap2_device_supports_u2f && !request.allow_list.empty();
  25. }
  26. // SetResponseCredential sets the credential information in |response|. If the
  27. // allow list sent to the authenticator contained only a single entry then the
  28. // authenticator may omit the chosen credential in the response and this
  29. // function will fill it in. Otherwise, the credential chosen by the
  30. // authenticator must be one of the ones requested in the allow list, unless the
  31. // allow list was empty.
  32. bool SetResponseCredential(
  33. AuthenticatorGetAssertionResponse* response,
  34. const std::vector<PublicKeyCredentialDescriptor>& allow_list) {
  35. if (response->credential) {
  36. if (!allow_list.empty() &&
  37. std::none_of(allow_list.cbegin(), allow_list.cend(),
  38. [&response](const auto& credential) {
  39. return credential.id == response->credential->id;
  40. })) {
  41. return false;
  42. }
  43. return true;
  44. }
  45. if (allow_list.size() != 1) {
  46. return false;
  47. }
  48. response->credential = allow_list[0];
  49. return true;
  50. }
  51. // HasCredentialSpecificPRFInputs returns true if |options| specifies any PRF
  52. // inputs that are specific to a credential ID.
  53. bool HasCredentialSpecificPRFInputs(const CtapGetAssertionOptions& options) {
  54. const size_t num = options.prf_inputs.size();
  55. return num > 1 ||
  56. (num == 1 && options.prf_inputs[0].credential_id.has_value());
  57. }
  58. // GetDefaultPRFInput returns the default PRF input from |options|, if any.
  59. const CtapGetAssertionOptions::PRFInput* GetDefaultPRFInput(
  60. const CtapGetAssertionOptions& options) {
  61. if (options.prf_inputs.empty() ||
  62. options.prf_inputs[0].credential_id.has_value()) {
  63. return nullptr;
  64. }
  65. return &options.prf_inputs[0];
  66. }
  67. // GetPRFInputForCredential returns the PRF input specific to the given
  68. // credential ID from |options|, or the default PRF input if there's nothing
  69. // specific for |id|, or |nullptr| if there's not a default value.
  70. const CtapGetAssertionOptions::PRFInput* GetPRFInputForCredential(
  71. const CtapGetAssertionOptions& options,
  72. const std::vector<uint8_t>& id) {
  73. for (const auto& prf_input : options.prf_inputs) {
  74. if (prf_input.credential_id == id) {
  75. return &prf_input;
  76. }
  77. }
  78. return GetDefaultPRFInput(options);
  79. }
  80. } // namespace
  81. GetAssertionTask::GetAssertionTask(FidoDevice* device,
  82. CtapGetAssertionRequest request,
  83. CtapGetAssertionOptions options,
  84. GetAssertionTaskCallback callback)
  85. : FidoTask(device),
  86. request_(std::move(request)),
  87. options_(std::move(options)),
  88. callback_(std::move(callback)) {
  89. // This code assumes that user-presence is requested in order to implement
  90. // possible U2F-fallback.
  91. DCHECK(request_.user_presence_required);
  92. // The UV parameter should have been made binary by this point because CTAP2
  93. // only takes a binary value.
  94. DCHECK_NE(request_.user_verification,
  95. UserVerificationRequirement::kPreferred);
  96. }
  97. GetAssertionTask::~GetAssertionTask() = default;
  98. void GetAssertionTask::Cancel() {
  99. canceled_ = true;
  100. if (sign_operation_) {
  101. sign_operation_->Cancel();
  102. }
  103. if (dummy_register_operation_) {
  104. dummy_register_operation_->Cancel();
  105. }
  106. }
  107. // static
  108. bool GetAssertionTask::StringFixupPredicate(
  109. const std::vector<const cbor::Value*>& path) {
  110. // This filters out all elements that are not string-keyed, direct children
  111. // of key 0x04, which is the `user` element of a getAssertion response.
  112. if (path.size() != 2 || !path[0]->is_unsigned() ||
  113. path[0]->GetUnsigned() != 4 || !path[1]->is_string()) {
  114. return false;
  115. }
  116. // Of those string-keyed children, only `name` and `displayName` may have
  117. // truncated UTF-8 in their values.
  118. const std::string& user_key = path[1]->GetString();
  119. return user_key == "name" || user_key == "displayName";
  120. }
  121. void GetAssertionTask::StartTask() {
  122. if (device()->supported_protocol() == ProtocolVersion::kCtap2 &&
  123. !request_.is_u2f_only) {
  124. GetAssertion();
  125. } else {
  126. // |device_info| should be present iff the device is CTAP2.
  127. // |MaybeRevertU2fFallbackAndInvokeCallback| uses this to restore the
  128. // protocol of CTAP2 devices once this task is complete.
  129. DCHECK_EQ(device()->supported_protocol() == ProtocolVersion::kCtap2,
  130. device()->device_info().has_value());
  131. device()->set_supported_protocol(ProtocolVersion::kU2f);
  132. U2fSign();
  133. }
  134. }
  135. CtapGetAssertionRequest GetAssertionTask::NextSilentRequest() {
  136. DCHECK(current_allow_list_batch_ < allow_list_batches_.size());
  137. CtapGetAssertionRequest request = request_;
  138. request.allow_list = allow_list_batches_.at(current_allow_list_batch_++);
  139. request.user_presence_required = false;
  140. request.user_verification = UserVerificationRequirement::kDiscouraged;
  141. return request;
  142. }
  143. void GetAssertionTask::GetAssertion() {
  144. if (request_.allow_list.empty()) {
  145. MaybeSetPRFParameters(&request_, GetDefaultPRFInput(options_));
  146. sign_operation_ = std::make_unique<Ctap2DeviceOperation<
  147. CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
  148. device(), request_,
  149. base::BindOnce(&GetAssertionTask::HandleResponse,
  150. weak_factory_.GetWeakPtr(), request_.allow_list),
  151. base::BindOnce(&ReadCTAPGetAssertionResponse,
  152. device()->DeviceTransport()),
  153. StringFixupPredicate);
  154. sign_operation_->Start();
  155. return;
  156. }
  157. // Most authenticators can only process allowList parameters up to a certain
  158. // size. Batch the list into chunks according to what the device can handle
  159. // and filter out IDs that are too large to originate from this device.
  160. allow_list_batches_ =
  161. FilterAndBatchCredentialDescriptors(request_.allow_list, *device());
  162. DCHECK(!allow_list_batches_.empty());
  163. // If filtering eliminated all entries from the allowList, just collect a
  164. // dummy touch, then fail the request.
  165. if (allow_list_batches_.size() == 1 && allow_list_batches_[0].empty()) {
  166. dummy_register_operation_ = std::make_unique<Ctap2DeviceOperation<
  167. CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
  168. device(), MakeCredentialTask::GetTouchRequest(device()),
  169. base::BindOnce(&GetAssertionTask::HandleDummyMakeCredentialComplete,
  170. weak_factory_.GetWeakPtr()),
  171. base::BindOnce(&ReadCTAPMakeCredentialResponse,
  172. device()->DeviceTransport()),
  173. /*string_fixup_predicate=*/nullptr);
  174. dummy_register_operation_->Start();
  175. return;
  176. }
  177. // If the filtered allowList is small enough to be sent in a single request,
  178. // do so.
  179. if (allow_list_batches_.size() == 1 &&
  180. !MayFallbackToU2fWithAppIdExtension(*device(), request_) &&
  181. !HasCredentialSpecificPRFInputs(options_)) {
  182. CtapGetAssertionRequest request = request_;
  183. request.allow_list = allow_list_batches_.front();
  184. MaybeSetPRFParameters(&request, GetDefaultPRFInput(options_));
  185. sign_operation_ = std::make_unique<Ctap2DeviceOperation<
  186. CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
  187. device(), std::move(request),
  188. base::BindOnce(&GetAssertionTask::HandleResponse,
  189. weak_factory_.GetWeakPtr(), request.allow_list),
  190. base::BindOnce(&ReadCTAPGetAssertionResponse,
  191. device()->DeviceTransport()),
  192. StringFixupPredicate);
  193. sign_operation_->Start();
  194. return;
  195. }
  196. // If the filtered list is too large to be sent at once, or if an App ID might
  197. // need to be tested because the site used the appid extension, or if we might
  198. // need to send specific PRF inputs, probe the credential IDs silently.
  199. sign_operation_ =
  200. std::make_unique<Ctap2DeviceOperation<CtapGetAssertionRequest,
  201. AuthenticatorGetAssertionResponse>>(
  202. device(), NextSilentRequest(),
  203. base::BindOnce(&GetAssertionTask::HandleResponseToSilentRequest,
  204. weak_factory_.GetWeakPtr()),
  205. base::BindOnce(&ReadCTAPGetAssertionResponse,
  206. device()->DeviceTransport()),
  207. /*string_fixup_predicate=*/nullptr);
  208. sign_operation_->Start();
  209. }
  210. void GetAssertionTask::U2fSign() {
  211. DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
  212. sign_operation_ = std::make_unique<U2fSignOperation>(
  213. device(), request_,
  214. base::BindOnce(&GetAssertionTask::MaybeRevertU2fFallbackAndInvokeCallback,
  215. weak_factory_.GetWeakPtr()));
  216. sign_operation_->Start();
  217. }
  218. void GetAssertionTask::HandleResponse(
  219. std::vector<PublicKeyCredentialDescriptor> allow_list,
  220. CtapDeviceResponseCode response_code,
  221. absl::optional<AuthenticatorGetAssertionResponse> response_data) {
  222. if (canceled_) {
  223. return;
  224. }
  225. if (response_code == CtapDeviceResponseCode::kCtap2ErrInvalidCredential) {
  226. // Some authenticators will return this error before waiting for a touch if
  227. // they don't recognise a credential. In other cases the result can be
  228. // returned immediately.
  229. // The request failed in a way that didn't request a touch. Simulate it.
  230. dummy_register_operation_ = std::make_unique<Ctap2DeviceOperation<
  231. CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
  232. device(), MakeCredentialTask::GetTouchRequest(device()),
  233. base::BindOnce(&GetAssertionTask::HandleDummyMakeCredentialComplete,
  234. weak_factory_.GetWeakPtr()),
  235. base::BindOnce(&ReadCTAPMakeCredentialResponse,
  236. device()->DeviceTransport()),
  237. /*string_fixup_predicate=*/nullptr);
  238. dummy_register_operation_->Start();
  239. return;
  240. }
  241. if (response_code == CtapDeviceResponseCode::kSuccess) {
  242. if (response_data->user_selected && !allow_list.empty()) {
  243. // The userSelected signal is only valid if the request had an empty
  244. // allowList.
  245. FIDO_LOG(DEBUG)
  246. << "Assertion response has userSelected for non-empty allowList";
  247. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
  248. absl::nullopt);
  249. return;
  250. }
  251. if (!SetResponseCredential(&response_data.value(), allow_list)) {
  252. FIDO_LOG(DEBUG)
  253. << "Assertion response has invalid credential information";
  254. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
  255. absl::nullopt);
  256. return;
  257. }
  258. // Decrypt any hmac-secret response.
  259. const absl::optional<cbor::Value>& extensions_cbor =
  260. response_data->authenticator_data.extensions();
  261. if (extensions_cbor) {
  262. // Parsing has already checked that |extensions_cbor| is a map.
  263. const cbor::Value::MapValue& extensions = extensions_cbor->GetMap();
  264. auto it = extensions.find(cbor::Value(kExtensionHmacSecret));
  265. if (it != extensions.end()) {
  266. if (!hmac_secret_request_ || !it->second.is_bytestring()) {
  267. FIDO_LOG(DEBUG) << "Unexpected or invalid hmac_secret extension";
  268. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
  269. absl::nullopt);
  270. return;
  271. }
  272. absl::optional<std::vector<uint8_t>> plaintext =
  273. hmac_secret_request_->Decrypt(it->second.GetBytestring());
  274. if (!plaintext) {
  275. FIDO_LOG(DEBUG) << "Failed to decrypt hmac_secret extension";
  276. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrOther,
  277. absl::nullopt);
  278. return;
  279. }
  280. response_data->hmac_secret = std::move(plaintext.value());
  281. }
  282. }
  283. }
  284. std::move(callback_).Run(response_code, std::move(response_data));
  285. }
  286. void GetAssertionTask::HandleResponseToSilentRequest(
  287. CtapDeviceResponseCode response_code,
  288. absl::optional<AuthenticatorGetAssertionResponse> response_data) {
  289. DCHECK(request_.allow_list.size() > 0);
  290. DCHECK(allow_list_batches_.size() > 0);
  291. DCHECK(0 < current_allow_list_batch_ &&
  292. current_allow_list_batch_ <= allow_list_batches_.size());
  293. if (canceled_) {
  294. return;
  295. }
  296. // One credential from the previous batch was recognized by the device. As
  297. // this authentication was a silent authentication (i.e. user touch was not
  298. // provided), try again with only that credential, user presence enforced and
  299. // with the original user verification configuration.
  300. if (response_code == CtapDeviceResponseCode::kSuccess &&
  301. SetResponseCredential(
  302. &response_data.value(),
  303. allow_list_batches_.at(current_allow_list_batch_ - 1))) {
  304. CtapGetAssertionRequest request = request_;
  305. const PublicKeyCredentialDescriptor& matching_credential =
  306. *response_data->credential;
  307. request.allow_list = {matching_credential};
  308. MaybeSetPRFParameters(
  309. &request, GetPRFInputForCredential(options_, matching_credential.id));
  310. sign_operation_ = std::make_unique<Ctap2DeviceOperation<
  311. CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
  312. device(), std::move(request),
  313. base::BindOnce(&GetAssertionTask::HandleResponse,
  314. weak_factory_.GetWeakPtr(), request.allow_list),
  315. base::BindOnce(&ReadCTAPGetAssertionResponse,
  316. device()->DeviceTransport()),
  317. /*string_fixup_predicate=*/nullptr);
  318. sign_operation_->Start();
  319. return;
  320. }
  321. // Credential was not recognized or an error occurred. Probe the next
  322. // credential.
  323. if (current_allow_list_batch_ < allow_list_batches_.size()) {
  324. sign_operation_ = std::make_unique<Ctap2DeviceOperation<
  325. CtapGetAssertionRequest, AuthenticatorGetAssertionResponse>>(
  326. device(), NextSilentRequest(),
  327. base::BindOnce(&GetAssertionTask::HandleResponseToSilentRequest,
  328. weak_factory_.GetWeakPtr()),
  329. base::BindOnce(&ReadCTAPGetAssertionResponse,
  330. device()->DeviceTransport()),
  331. /*string_fixup_predicate=*/nullptr);
  332. sign_operation_->Start();
  333. return;
  334. }
  335. // None of the credentials were recognized. Fall back to U2F or collect a
  336. // dummy touch.
  337. if (MayFallbackToU2fWithAppIdExtension(*device(), request_)) {
  338. device()->set_supported_protocol(ProtocolVersion::kU2f);
  339. U2fSign();
  340. return;
  341. }
  342. dummy_register_operation_ = std::make_unique<Ctap2DeviceOperation<
  343. CtapMakeCredentialRequest, AuthenticatorMakeCredentialResponse>>(
  344. device(), MakeCredentialTask::GetTouchRequest(device()),
  345. base::BindOnce(&GetAssertionTask::HandleDummyMakeCredentialComplete,
  346. weak_factory_.GetWeakPtr()),
  347. base::BindOnce(&ReadCTAPMakeCredentialResponse,
  348. device()->DeviceTransport()),
  349. /*string_fixup_predicate=*/nullptr);
  350. dummy_register_operation_->Start();
  351. }
  352. void GetAssertionTask::HandleDummyMakeCredentialComplete(
  353. CtapDeviceResponseCode response_code,
  354. absl::optional<AuthenticatorMakeCredentialResponse> response_data) {
  355. std::move(callback_).Run(CtapDeviceResponseCode::kCtap2ErrNoCredentials,
  356. absl::nullopt);
  357. }
  358. void GetAssertionTask::MaybeSetPRFParameters(
  359. CtapGetAssertionRequest* request,
  360. const CtapGetAssertionOptions::PRFInput* maybe_inputs) {
  361. if (maybe_inputs == nullptr) {
  362. return;
  363. }
  364. hmac_secret_request_ = std::make_unique<pin::HMACSecretRequest>(
  365. *request->pin_protocol, *options_.pin_key_agreement, maybe_inputs->salt1,
  366. maybe_inputs->salt2);
  367. request->hmac_secret.emplace(hmac_secret_request_->public_key_x962,
  368. hmac_secret_request_->encrypted_salts,
  369. hmac_secret_request_->salts_auth);
  370. }
  371. void GetAssertionTask::MaybeRevertU2fFallbackAndInvokeCallback(
  372. CtapDeviceResponseCode status,
  373. absl::optional<AuthenticatorGetAssertionResponse> response) {
  374. DCHECK_EQ(ProtocolVersion::kU2f, device()->supported_protocol());
  375. if (device()->device_info()) {
  376. // This was actually a CTAP2 device, but the protocol version was set to U2F
  377. // in order to execute a sign command.
  378. device()->set_supported_protocol(ProtocolVersion::kCtap2);
  379. }
  380. std::move(callback_).Run(status, std::move(response));
  381. }
  382. } // namespace device