auth_token_requester.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. // Copyright 2020 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/auth_token_requester.h"
  5. #include <set>
  6. #include <utility>
  7. #include "base/bind.h"
  8. #include "base/containers/contains.h"
  9. #include "base/logging.h"
  10. #include "base/stl_util.h"
  11. #include "base/strings/utf_string_conversions.h"
  12. #include "components/device_event_log/device_event_log.h"
  13. #include "device/fido/authenticator_supported_options.h"
  14. #include "device/fido/fido_authenticator.h"
  15. #include "device/fido/fido_constants.h"
  16. namespace device {
  17. using ClientPinAvailability =
  18. AuthenticatorSupportedOptions::ClientPinAvailability;
  19. using UserVerificationAvailability =
  20. AuthenticatorSupportedOptions::UserVerificationAvailability;
  21. using BioEnrollmentAvailability =
  22. AuthenticatorSupportedOptions::BioEnrollmentAvailability;
  23. AuthTokenRequester::Delegate::~Delegate() = default;
  24. AuthTokenRequester::Options::Options() = default;
  25. AuthTokenRequester::Options::Options(Options&&) = default;
  26. AuthTokenRequester::Options& AuthTokenRequester::Options::operator=(Options&&) =
  27. default;
  28. AuthTokenRequester::Options::~Options() = default;
  29. AuthTokenRequester::AuthTokenRequester(Delegate* delegate,
  30. FidoAuthenticator* authenticator,
  31. Options options)
  32. : delegate_(delegate),
  33. authenticator_(authenticator),
  34. options_(std::move(options)),
  35. internal_uv_locked_(options_.internal_uv_locked) {
  36. DCHECK(delegate_);
  37. DCHECK(authenticator_);
  38. DCHECK(authenticator_->Options());
  39. DCHECK(!options_.token_permissions.empty());
  40. DCHECK(!options_.rp_id || !options_.rp_id->empty());
  41. // Authenticators with CTAP2.0-style pinToken support only support certain
  42. // default permissions.
  43. DCHECK(
  44. authenticator_->Options()->supports_pin_uv_auth_token ||
  45. base::STLSetDifference<std::set<pin::Permissions>>(
  46. options_.token_permissions,
  47. std::set<pin::Permissions>{pin::Permissions::kMakeCredential,
  48. pin::Permissions::kGetAssertion,
  49. pin::Permissions::kBioEnrollment,
  50. pin::Permissions::kCredentialManagement})
  51. .empty());
  52. }
  53. AuthTokenRequester::~AuthTokenRequester() = default;
  54. void AuthTokenRequester::ObtainPINUVAuthToken() {
  55. if (authenticator_->Options()->supports_pin_uv_auth_token) {
  56. // Only attempt to obtain a token through internal UV if the authenticator
  57. // supports CTAP 2.1 pinUvAuthTokens. If it does not, it could be a 2.0
  58. // authenticator that supports UV without any sort of token.
  59. const UserVerificationAvailability user_verification_availability =
  60. authenticator_->Options()->user_verification_availability;
  61. switch (user_verification_availability) {
  62. case UserVerificationAvailability::kNotSupported:
  63. case UserVerificationAvailability::kSupportedButNotConfigured:
  64. // Try PIN first.
  65. break;
  66. case UserVerificationAvailability::kSupportedAndConfigured:
  67. ObtainTokenFromInternalUV();
  68. return;
  69. }
  70. }
  71. const ClientPinAvailability client_pin_availability =
  72. authenticator_->Options()->client_pin_availability;
  73. switch (client_pin_availability) {
  74. case ClientPinAvailability::kNotSupported:
  75. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  76. authenticator_, Result::kPreTouchUnsatisfiableRequest, absl::nullopt);
  77. return;
  78. case ClientPinAvailability::kSupportedAndPinSet:
  79. if (options_.skip_pin_touch) {
  80. ObtainTokenFromPIN();
  81. return;
  82. }
  83. authenticator_->GetTouch(base::BindOnce(
  84. &AuthTokenRequester::ObtainTokenFromPIN, weak_factory_.GetWeakPtr()));
  85. return;
  86. case ClientPinAvailability::kSupportedButPinNotSet:
  87. if (options_.skip_pin_touch) {
  88. ObtainTokenFromNewPIN();
  89. return;
  90. }
  91. authenticator_->GetTouch(
  92. base::BindOnce(&AuthTokenRequester::ObtainTokenFromNewPIN,
  93. weak_factory_.GetWeakPtr()));
  94. return;
  95. }
  96. }
  97. void AuthTokenRequester::ObtainTokenFromInternalUV() {
  98. authenticator_->GetUvRetries(base::BindOnce(
  99. &AuthTokenRequester::OnGetUVRetries, weak_factory_.GetWeakPtr()));
  100. }
  101. void AuthTokenRequester::OnGetUVRetries(
  102. CtapDeviceResponseCode status,
  103. absl::optional<pin::RetriesResponse> response) {
  104. if (status != CtapDeviceResponseCode::kSuccess) {
  105. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  106. authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
  107. absl::nullopt);
  108. return;
  109. }
  110. internal_uv_locked_ = response->retries == 0;
  111. if (response->retries == 0) {
  112. // The authenticator was locked prior to calling
  113. // ObtainTokenFromInternalUV(). Fall back to PIN if able.
  114. if (authenticator_->Options()->client_pin_availability ==
  115. ClientPinAvailability::kSupportedAndPinSet) {
  116. if (options_.skip_pin_touch) {
  117. ObtainTokenFromPIN();
  118. return;
  119. }
  120. authenticator_->GetTouch(base::BindOnce(
  121. &AuthTokenRequester::ObtainTokenFromPIN, weak_factory_.GetWeakPtr()));
  122. return;
  123. }
  124. authenticator_->GetTouch(base::BindOnce(
  125. &AuthTokenRequester::NotifyAuthenticatorSelectedAndFailWithResult,
  126. weak_factory_.GetWeakPtr(),
  127. Result::kPostTouchAuthenticatorInternalUVLock));
  128. return;
  129. }
  130. if (is_internal_uv_retry_) {
  131. delegate_->PromptForInternalUVRetry(response->retries);
  132. }
  133. authenticator_->GetUvToken({std::begin(options_.token_permissions),
  134. std::end(options_.token_permissions)},
  135. options_.rp_id,
  136. base::BindOnce(&AuthTokenRequester::OnGetUVToken,
  137. weak_factory_.GetWeakPtr()));
  138. }
  139. void AuthTokenRequester::OnGetUVToken(
  140. CtapDeviceResponseCode status,
  141. absl::optional<pin::TokenResponse> response) {
  142. if (!base::Contains(
  143. std::set<CtapDeviceResponseCode>{
  144. CtapDeviceResponseCode::kCtap2ErrUvInvalid,
  145. CtapDeviceResponseCode::kCtap2ErrOperationDenied,
  146. CtapDeviceResponseCode::kCtap2ErrUvBlocked,
  147. CtapDeviceResponseCode::kSuccess},
  148. status)) {
  149. // The request was rejected outright, no touch occurred.
  150. FIDO_LOG(ERROR) << "Ignoring status " << static_cast<int>(status)
  151. << " from " << authenticator_->GetDisplayName();
  152. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  153. authenticator_, Result::kPreTouchAuthenticatorResponseInvalid,
  154. absl::nullopt);
  155. return;
  156. }
  157. if (!NotifyAuthenticatorSelected()) {
  158. return;
  159. }
  160. if (status == CtapDeviceResponseCode::kCtap2ErrOperationDenied) {
  161. // The user explicitly denied to the operation on an authenticator with
  162. // a display.
  163. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  164. authenticator_, Result::kPostTouchAuthenticatorOperationDenied,
  165. absl::nullopt);
  166. return;
  167. }
  168. if (status == CtapDeviceResponseCode::kCtap2ErrUvInvalid) {
  169. // The attempt failed, but a retry is possible.
  170. is_internal_uv_retry_ = true;
  171. ObtainTokenFromInternalUV();
  172. return;
  173. }
  174. if (status == CtapDeviceResponseCode::kCtap2ErrUvBlocked) {
  175. // Fall back to PIN if able.
  176. if (authenticator_->Options()->client_pin_availability ==
  177. ClientPinAvailability::kSupportedAndPinSet) {
  178. internal_uv_locked_ = true;
  179. ObtainTokenFromPIN();
  180. return;
  181. }
  182. // This can be returned pre-touch if the authenticator was already locked at
  183. // the time GetUvToken() was called. However, we checked the number of
  184. // remaining retries just before that to handle that case.
  185. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  186. authenticator_, Result::kPostTouchAuthenticatorInternalUVLock,
  187. absl::nullopt);
  188. return;
  189. }
  190. DCHECK_EQ(status, CtapDeviceResponseCode::kSuccess);
  191. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  192. authenticator_, Result::kSuccess, *response);
  193. }
  194. void AuthTokenRequester::ObtainTokenFromPIN() {
  195. if (NotifyAuthenticatorSelected()) {
  196. authenticator_->GetPinRetries(base::BindOnce(
  197. &AuthTokenRequester::OnGetPINRetries, weak_factory_.GetWeakPtr()));
  198. }
  199. }
  200. void AuthTokenRequester::OnGetPINRetries(
  201. CtapDeviceResponseCode status,
  202. absl::optional<pin::RetriesResponse> response) {
  203. if (status != CtapDeviceResponseCode::kSuccess) {
  204. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  205. authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
  206. absl::nullopt);
  207. return;
  208. }
  209. if (response->retries == 0) {
  210. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  211. authenticator_, Result::kPostTouchAuthenticatorPINHardLock,
  212. absl::nullopt);
  213. return;
  214. }
  215. pin_retries_ = response->retries;
  216. pin::PINEntryError error;
  217. if (pin_invalid_) {
  218. pin_invalid_ = false;
  219. error = pin::PINEntryError::kWrongPIN;
  220. } else if (internal_uv_locked_) {
  221. error = pin::PINEntryError::kInternalUvLocked;
  222. } else {
  223. error = pin::PINEntryError::kNoError;
  224. }
  225. delegate_->CollectPIN(
  226. pin::PINEntryReason::kChallenge, error,
  227. authenticator_->CurrentMinPINLength(), pin_retries_,
  228. base::BindOnce(&AuthTokenRequester::HavePIN, weak_factory_.GetWeakPtr()));
  229. }
  230. void AuthTokenRequester::HavePIN(std::u16string pin16) {
  231. pin::PINEntryError error = pin::ValidatePIN(
  232. pin16, authenticator_->CurrentMinPINLength(), current_pin_);
  233. if (error != pin::PINEntryError::kNoError) {
  234. delegate_->CollectPIN(pin::PINEntryReason::kChallenge, error,
  235. authenticator_->CurrentMinPINLength(), pin_retries_,
  236. base::BindOnce(&AuthTokenRequester::HavePIN,
  237. weak_factory_.GetWeakPtr()));
  238. return;
  239. }
  240. std::string pin = base::UTF16ToUTF8(pin16);
  241. authenticator_->GetPINToken(pin,
  242. {std::begin(options_.token_permissions),
  243. std::end(options_.token_permissions)},
  244. options_.rp_id,
  245. base::BindOnce(&AuthTokenRequester::OnGetPINToken,
  246. weak_factory_.GetWeakPtr(), pin));
  247. return;
  248. }
  249. void AuthTokenRequester::OnGetPINToken(
  250. std::string pin,
  251. CtapDeviceResponseCode status,
  252. absl::optional<pin::TokenResponse> response) {
  253. if (status == CtapDeviceResponseCode::kCtap2ErrPinInvalid) {
  254. pin_invalid_ = true;
  255. ObtainTokenFromPIN();
  256. return;
  257. }
  258. if (status != CtapDeviceResponseCode::kSuccess) {
  259. Result ret;
  260. switch (status) {
  261. case CtapDeviceResponseCode::kCtap2ErrPinPolicyViolation:
  262. // The user needs to set a new PIN before they can use the device.
  263. current_pin_ = pin;
  264. delegate_->CollectPIN(pin::PINEntryReason::kChange,
  265. pin::PINEntryError::kNoError,
  266. authenticator_->NewMinPINLength(),
  267. /*attempts=*/0,
  268. base::BindOnce(&AuthTokenRequester::HaveNewPIN,
  269. weak_factory_.GetWeakPtr()));
  270. return;
  271. case CtapDeviceResponseCode::kCtap2ErrPinAuthBlocked:
  272. ret = Result::kPostTouchAuthenticatorPINSoftLock;
  273. break;
  274. case CtapDeviceResponseCode::kCtap2ErrPinBlocked:
  275. ret = Result::kPostTouchAuthenticatorPINHardLock;
  276. break;
  277. default:
  278. ret = Result::kPostTouchAuthenticatorResponseInvalid;
  279. break;
  280. }
  281. delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, ret,
  282. absl::nullopt);
  283. return;
  284. }
  285. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  286. authenticator_, Result::kSuccess, std::move(*response));
  287. }
  288. void AuthTokenRequester::ObtainTokenFromNewPIN() {
  289. if (NotifyAuthenticatorSelected()) {
  290. delegate_->CollectPIN(pin::PINEntryReason::kSet,
  291. pin::PINEntryError::kNoError,
  292. authenticator_->NewMinPINLength(),
  293. /*attempts=*/0,
  294. base::BindOnce(&AuthTokenRequester::HaveNewPIN,
  295. weak_factory_.GetWeakPtr()));
  296. }
  297. }
  298. void AuthTokenRequester::HaveNewPIN(std::u16string pin16) {
  299. pin::PINEntryError error =
  300. pin::ValidatePIN(pin16, authenticator_->NewMinPINLength(), current_pin_);
  301. if (error != pin::PINEntryError::kNoError) {
  302. delegate_->CollectPIN(
  303. current_pin_ ? pin::PINEntryReason::kChange : pin::PINEntryReason::kSet,
  304. error, authenticator_->NewMinPINLength(),
  305. /*attempts=*/0,
  306. base::BindOnce(&AuthTokenRequester::HaveNewPIN,
  307. weak_factory_.GetWeakPtr()));
  308. return;
  309. }
  310. std::string pin = base::UTF16ToUTF8(pin16);
  311. if (current_pin_) {
  312. authenticator_->ChangePIN(*current_pin_, pin,
  313. base::BindOnce(&AuthTokenRequester::OnSetPIN,
  314. weak_factory_.GetWeakPtr(), pin));
  315. return;
  316. }
  317. authenticator_->SetPIN(pin, base::BindOnce(&AuthTokenRequester::OnSetPIN,
  318. weak_factory_.GetWeakPtr(), pin));
  319. return;
  320. }
  321. void AuthTokenRequester::OnSetPIN(std::string pin,
  322. CtapDeviceResponseCode status,
  323. absl::optional<pin::EmptyResponse> response) {
  324. if (status != CtapDeviceResponseCode::kSuccess) {
  325. delegate_->HavePINUVAuthTokenResultForAuthenticator(
  326. authenticator_, Result::kPostTouchAuthenticatorResponseInvalid,
  327. absl::nullopt);
  328. return;
  329. }
  330. // Having just set the PIN, we need to immediately turn around and use it to
  331. // get a PIN token.
  332. authenticator_->GetPINToken(std::move(pin),
  333. {std::begin(options_.token_permissions),
  334. std::end(options_.token_permissions)},
  335. options_.rp_id,
  336. base::BindOnce(&AuthTokenRequester::OnGetPINToken,
  337. weak_factory_.GetWeakPtr(), pin));
  338. }
  339. bool AuthTokenRequester::NotifyAuthenticatorSelected() {
  340. if (!authenticator_selected_result_.has_value()) {
  341. authenticator_selected_result_ =
  342. delegate_->AuthenticatorSelectedForPINUVAuthToken(authenticator_);
  343. }
  344. return *authenticator_selected_result_;
  345. }
  346. void AuthTokenRequester::NotifyAuthenticatorSelectedAndFailWithResult(
  347. Result result) {
  348. if (NotifyAuthenticatorSelected()) {
  349. delegate_->HavePINUVAuthTokenResultForAuthenticator(authenticator_, result,
  350. absl::nullopt);
  351. }
  352. }
  353. } // namespace device