pin.cc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. // Copyright 2019 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/pin.h"
  5. #include <numeric>
  6. #include <string>
  7. #include <utility>
  8. #include "base/i18n/char_iterator.h"
  9. #include "base/strings/string_util.h"
  10. #include "base/strings/utf_string_conversions.h"
  11. #include "components/cbor/reader.h"
  12. #include "components/cbor/values.h"
  13. #include "components/cbor/writer.h"
  14. #include "device/fido/fido_constants.h"
  15. #include "device/fido/pin_internal.h"
  16. #include "third_party/boringssl/src/include/openssl/aes.h"
  17. #include "third_party/boringssl/src/include/openssl/ec.h"
  18. #include "third_party/boringssl/src/include/openssl/nid.h"
  19. #include "third_party/boringssl/src/include/openssl/sha.h"
  20. namespace device {
  21. namespace pin {
  22. namespace {
  23. uint8_t PermissionsToByte(base::span<const pin::Permissions> permissions) {
  24. return std::accumulate(permissions.begin(), permissions.end(), 0,
  25. [](uint8_t byte, pin::Permissions flag) {
  26. return byte |= static_cast<uint8_t>(flag);
  27. });
  28. }
  29. } // namespace
  30. // HasAtLeastFourCodepoints returns true if |pin| is UTF-8 encoded and contains
  31. // four or more code points. This reflects the "4 Unicode characters"
  32. // requirement in CTAP2.
  33. static bool HasAtLeastFourCodepoints(const std::string& pin) {
  34. base::i18n::UTF8CharIterator it(pin);
  35. return it.Advance() && it.Advance() && it.Advance() && it.Advance();
  36. }
  37. PINEntryError ValidatePIN(const std::string& pin,
  38. uint32_t min_pin_length,
  39. absl::optional<std::string> current_pin) {
  40. if (pin.size() < min_pin_length) {
  41. return PINEntryError::kTooShort;
  42. }
  43. if (pin.size() > kMaxBytes || pin.back() == 0 || !base::IsStringUTF8(pin)) {
  44. return PINEntryError::kInvalidCharacters;
  45. }
  46. if (!HasAtLeastFourCodepoints(pin)) {
  47. return PINEntryError::kTooShort;
  48. }
  49. if (pin == current_pin) {
  50. return pin::PINEntryError::kSameAsCurrentPIN;
  51. }
  52. return PINEntryError::kNoError;
  53. }
  54. PINEntryError ValidatePIN(const std::u16string& pin16,
  55. uint32_t min_pin_length,
  56. absl::optional<std::string> current_pin) {
  57. std::string pin;
  58. if (!base::UTF16ToUTF8(pin16.c_str(), pin16.size(), &pin)) {
  59. return pin::PINEntryError::kInvalidCharacters;
  60. }
  61. return ValidatePIN(std::move(pin), min_pin_length, std::move(current_pin));
  62. }
  63. // EncodePINCommand returns a CTAP2 PIN command for the operation |subcommand|.
  64. // Additional elements of the top-level CBOR map can be added with the optional
  65. // |add_additional| callback.
  66. static std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  67. EncodePINCommand(
  68. PINUVAuthProtocol protocol_version,
  69. Subcommand subcommand,
  70. std::function<void(cbor::Value::MapValue*)> add_additional = nullptr) {
  71. cbor::Value::MapValue map;
  72. map.emplace(static_cast<int>(RequestKey::kProtocol),
  73. static_cast<uint8_t>(protocol_version));
  74. map.emplace(static_cast<int>(RequestKey::kSubcommand),
  75. static_cast<int>(subcommand));
  76. if (add_additional) {
  77. add_additional(&map);
  78. }
  79. return std::make_pair(CtapRequestCommand::kAuthenticatorClientPin,
  80. cbor::Value(std::move(map)));
  81. }
  82. RetriesResponse::RetriesResponse() = default;
  83. // static
  84. absl::optional<RetriesResponse> RetriesResponse::ParsePinRetries(
  85. const absl::optional<cbor::Value>& cbor) {
  86. return RetriesResponse::Parse(std::move(cbor),
  87. static_cast<int>(ResponseKey::kRetries));
  88. }
  89. // static
  90. absl::optional<RetriesResponse> RetriesResponse::ParseUvRetries(
  91. const absl::optional<cbor::Value>& cbor) {
  92. return RetriesResponse::Parse(std::move(cbor),
  93. static_cast<int>(ResponseKey::kUvRetries));
  94. }
  95. // static
  96. absl::optional<RetriesResponse> RetriesResponse::Parse(
  97. const absl::optional<cbor::Value>& cbor,
  98. const int retries_key) {
  99. if (!cbor || !cbor->is_map()) {
  100. return absl::nullopt;
  101. }
  102. const auto& response_map = cbor->GetMap();
  103. auto it = response_map.find(cbor::Value(retries_key));
  104. if (it == response_map.end() || !it->second.is_unsigned()) {
  105. return absl::nullopt;
  106. }
  107. const int64_t retries = it->second.GetUnsigned();
  108. if (retries > INT_MAX) {
  109. return absl::nullopt;
  110. }
  111. RetriesResponse ret;
  112. ret.retries = static_cast<int>(retries);
  113. return ret;
  114. }
  115. KeyAgreementResponse::KeyAgreementResponse() = default;
  116. // static
  117. absl::optional<KeyAgreementResponse> KeyAgreementResponse::Parse(
  118. const absl::optional<cbor::Value>& cbor) {
  119. if (!cbor || !cbor->is_map()) {
  120. return absl::nullopt;
  121. }
  122. const auto& response_map = cbor->GetMap();
  123. // The ephemeral key is encoded as a COSE structure.
  124. auto it = response_map.find(
  125. cbor::Value(static_cast<int>(ResponseKey::kKeyAgreement)));
  126. if (it == response_map.end() || !it->second.is_map()) {
  127. return absl::nullopt;
  128. }
  129. const auto& cose_key = it->second.GetMap();
  130. return ParseFromCOSE(cose_key);
  131. }
  132. // static
  133. absl::optional<KeyAgreementResponse> KeyAgreementResponse::ParseFromCOSE(
  134. const cbor::Value::MapValue& cose_key) {
  135. // The COSE key must be a P-256 point. See
  136. // https://tools.ietf.org/html/rfc8152#section-7.1
  137. for (const auto& pair : std::vector<std::pair<int, int>>({
  138. {1 /* key type */, 2 /* elliptic curve, uncompressed */},
  139. {3 /* algorithm */, -25 /* ECDH, ephemeral–static, HKDF-SHA-256 */},
  140. {-1 /* curve */, 1 /* P-256 */},
  141. })) {
  142. auto it = cose_key.find(cbor::Value(pair.first));
  143. if (it == cose_key.end() || !it->second.is_integer() ||
  144. it->second.GetInteger() != pair.second) {
  145. return absl::nullopt;
  146. }
  147. }
  148. // See https://tools.ietf.org/html/rfc8152#section-13.1.1
  149. const auto& x_it = cose_key.find(cbor::Value(-2));
  150. const auto& y_it = cose_key.find(cbor::Value(-3));
  151. if (x_it == cose_key.end() || y_it == cose_key.end() ||
  152. !x_it->second.is_bytestring() || !y_it->second.is_bytestring()) {
  153. return absl::nullopt;
  154. }
  155. const auto& x = x_it->second.GetBytestring();
  156. const auto& y = y_it->second.GetBytestring();
  157. KeyAgreementResponse ret;
  158. if (x.size() != sizeof(ret.x) || y.size() != sizeof(ret.y)) {
  159. return absl::nullopt;
  160. }
  161. memcpy(ret.x, x.data(), sizeof(ret.x));
  162. memcpy(ret.y, y.data(), sizeof(ret.y));
  163. bssl::UniquePtr<EC_GROUP> group(
  164. EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
  165. // Check that the point is on the curve.
  166. auto point = PointFromKeyAgreementResponse(group.get(), ret);
  167. if (!point) {
  168. return absl::nullopt;
  169. }
  170. return ret;
  171. }
  172. std::array<uint8_t, kP256X962Length> KeyAgreementResponse::X962() const {
  173. std::array<uint8_t, kP256X962Length> ret;
  174. static_assert(ret.size() == 1 + sizeof(this->x) + sizeof(this->y),
  175. "Bad length for return type");
  176. ret[0] = POINT_CONVERSION_UNCOMPRESSED;
  177. memcpy(&ret[1], this->x, sizeof(this->x));
  178. memcpy(&ret[1 + sizeof(this->x)], this->y, sizeof(this->y));
  179. return ret;
  180. }
  181. SetRequest::SetRequest(PINUVAuthProtocol protocol,
  182. const std::string& pin,
  183. const KeyAgreementResponse& peer_key)
  184. : protocol_(protocol), peer_key_(peer_key) {
  185. DCHECK_EQ(ValidatePIN(pin), PINEntryError::kNoError);
  186. memset(pin_, 0, sizeof(pin_));
  187. memcpy(pin_, pin.data(), pin.size());
  188. }
  189. cbor::Value::MapValue EncodeCOSEPublicKey(
  190. base::span<const uint8_t, kP256X962Length> x962) {
  191. cbor::Value::MapValue cose_key;
  192. cose_key.emplace(1 /* key type */, 2 /* uncompressed elliptic curve */);
  193. cose_key.emplace(3 /* algorithm */,
  194. -25 /* ECDH, ephemeral–static, HKDF-SHA-256 */);
  195. cose_key.emplace(-1 /* curve */, 1 /* P-256 */);
  196. cose_key.emplace(-2 /* x */, x962.subspan(1, 32));
  197. cose_key.emplace(-3 /* y */, x962.subspan(33, 32));
  198. return cose_key;
  199. }
  200. ChangeRequest::ChangeRequest(PINUVAuthProtocol protocol,
  201. const std::string& old_pin,
  202. const std::string& new_pin,
  203. const KeyAgreementResponse& peer_key)
  204. : protocol_(protocol), peer_key_(peer_key) {
  205. uint8_t digest[SHA256_DIGEST_LENGTH];
  206. SHA256(reinterpret_cast<const uint8_t*>(old_pin.data()), old_pin.size(),
  207. digest);
  208. memcpy(old_pin_hash_, digest, sizeof(old_pin_hash_));
  209. DCHECK_EQ(ValidatePIN(new_pin), PINEntryError::kNoError);
  210. memset(new_pin_, 0, sizeof(new_pin_));
  211. memcpy(new_pin_, new_pin.data(), new_pin.size());
  212. }
  213. // static
  214. absl::optional<EmptyResponse> EmptyResponse::Parse(
  215. const absl::optional<cbor::Value>& cbor) {
  216. // Yubikeys can return just the status byte, and no CBOR bytes, for the empty
  217. // response, which will end up here with |cbor| being |nullopt|. This seems
  218. // wrong, but is handled. (The response should, instead, encode an empty CBOR
  219. // map.)
  220. if (cbor && (!cbor->is_map() || !cbor->GetMap().empty())) {
  221. return absl::nullopt;
  222. }
  223. EmptyResponse ret;
  224. return ret;
  225. }
  226. TokenResponse::TokenResponse(PINUVAuthProtocol protocol)
  227. : protocol_(protocol) {}
  228. TokenResponse::~TokenResponse() = default;
  229. TokenResponse::TokenResponse(const TokenResponse&) = default;
  230. TokenResponse& TokenResponse::operator=(const TokenResponse&) = default;
  231. absl::optional<TokenResponse> TokenResponse::Parse(
  232. PINUVAuthProtocol protocol,
  233. base::span<const uint8_t> shared_key,
  234. const absl::optional<cbor::Value>& cbor) {
  235. if (!cbor || !cbor->is_map()) {
  236. return absl::nullopt;
  237. }
  238. const auto& response_map = cbor->GetMap();
  239. auto it =
  240. response_map.find(cbor::Value(static_cast<int>(ResponseKey::kPINToken)));
  241. if (it == response_map.end() || !it->second.is_bytestring()) {
  242. return absl::nullopt;
  243. }
  244. const auto& encrypted_token = it->second.GetBytestring();
  245. if (encrypted_token.size() % AES_BLOCK_SIZE != 0) {
  246. return absl::nullopt;
  247. }
  248. std::vector<uint8_t> token =
  249. ProtocolVersion(protocol).Decrypt(shared_key, encrypted_token);
  250. // The token must have the correct size for the given protocol.
  251. switch (protocol) {
  252. case PINUVAuthProtocol::kV1:
  253. // In CTAP2.1, V1 tokens are fixed at 16 or 32 bytes. But in CTAP2.0 they
  254. // may be any multiple of 16 bytes. We don't know the CTAP version, so
  255. // only enforce the latter.
  256. if (token.empty() || token.size() % AES_BLOCK_SIZE != 0) {
  257. return absl::nullopt;
  258. }
  259. break;
  260. case PINUVAuthProtocol::kV2:
  261. if (token.size() != 32u) {
  262. return absl::nullopt;
  263. }
  264. break;
  265. }
  266. TokenResponse ret(protocol);
  267. ret.token_ = std::move(token);
  268. return ret;
  269. }
  270. std::pair<PINUVAuthProtocol, std::vector<uint8_t>> TokenResponse::PinAuth(
  271. base::span<const uint8_t> client_data_hash) const {
  272. return {protocol_,
  273. ProtocolVersion(protocol_).Authenticate(token_, client_data_hash)};
  274. }
  275. // static
  276. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  277. AsCTAPRequestValuePair(const PinRetriesRequest& request) {
  278. return EncodePINCommand(request.protocol, Subcommand::kGetRetries);
  279. }
  280. // static
  281. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  282. AsCTAPRequestValuePair(const UvRetriesRequest& request) {
  283. return EncodePINCommand(request.protocol, Subcommand::kGetUvRetries);
  284. }
  285. // static
  286. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  287. AsCTAPRequestValuePair(const KeyAgreementRequest& request) {
  288. return EncodePINCommand(request.protocol, Subcommand::kGetKeyAgreement);
  289. }
  290. // static
  291. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  292. AsCTAPRequestValuePair(const SetRequest& request) {
  293. // See
  294. // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#settingNewPin
  295. std::vector<uint8_t> shared_key;
  296. const Protocol& pin_protocol = ProtocolVersion(request.protocol_);
  297. auto cose_key = EncodeCOSEPublicKey(
  298. pin_protocol.Encapsulate(request.peer_key_, &shared_key));
  299. static_assert((sizeof(request.pin_) % AES_BLOCK_SIZE) == 0,
  300. "pin_ is not a multiple of the AES block size");
  301. std::vector<uint8_t> encrypted_pin =
  302. pin_protocol.Encrypt(shared_key, request.pin_);
  303. std::vector<uint8_t> pin_auth =
  304. pin_protocol.Authenticate(shared_key, encrypted_pin);
  305. return EncodePINCommand(
  306. request.protocol_, Subcommand::kSetPIN,
  307. [&cose_key, &encrypted_pin, &pin_auth](cbor::Value::MapValue* map) {
  308. map->emplace(static_cast<int>(RequestKey::kKeyAgreement),
  309. std::move(cose_key));
  310. map->emplace(static_cast<int>(RequestKey::kNewPINEnc),
  311. std::move(encrypted_pin));
  312. map->emplace(static_cast<int>(RequestKey::kPINAuth),
  313. std::move(pin_auth));
  314. });
  315. }
  316. // static
  317. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  318. AsCTAPRequestValuePair(const ChangeRequest& request) {
  319. // See
  320. // https://fidoalliance.org/specs/fido-v2.0-rd-20180702/fido-client-to-authenticator-protocol-v2.0-rd-20180702.html#changingExistingPin
  321. std::vector<uint8_t> shared_key;
  322. const Protocol& pin_protocol = ProtocolVersion(request.protocol_);
  323. auto cose_key = EncodeCOSEPublicKey(
  324. pin_protocol.Encapsulate(request.peer_key_, &shared_key));
  325. static_assert((sizeof(request.new_pin_) % AES_BLOCK_SIZE) == 0,
  326. "new_pin_ is not a multiple of the AES block size");
  327. std::vector<uint8_t> encrypted_pin =
  328. pin_protocol.Encrypt(shared_key, request.new_pin_);
  329. static_assert((sizeof(request.old_pin_hash_) % AES_BLOCK_SIZE) == 0,
  330. "old_pin_hash_ is not a multiple of the AES block size");
  331. std::vector<uint8_t> old_pin_hash_enc =
  332. pin_protocol.Encrypt(shared_key, request.old_pin_hash_);
  333. std::vector<uint8_t> ciphertexts_concat(encrypted_pin.size() +
  334. old_pin_hash_enc.size());
  335. memcpy(ciphertexts_concat.data(), encrypted_pin.data(), encrypted_pin.size());
  336. memcpy(ciphertexts_concat.data() + encrypted_pin.size(),
  337. old_pin_hash_enc.data(), old_pin_hash_enc.size());
  338. std::vector<uint8_t> pin_auth =
  339. pin_protocol.Authenticate(shared_key, ciphertexts_concat);
  340. return EncodePINCommand(
  341. request.protocol_, Subcommand::kChangePIN,
  342. [&cose_key, &encrypted_pin, &old_pin_hash_enc,
  343. &pin_auth](cbor::Value::MapValue* map) {
  344. map->emplace(static_cast<int>(RequestKey::kKeyAgreement),
  345. std::move(cose_key));
  346. map->emplace(static_cast<int>(RequestKey::kPINHashEnc),
  347. std::move(old_pin_hash_enc));
  348. map->emplace(static_cast<int>(RequestKey::kNewPINEnc),
  349. std::move(encrypted_pin));
  350. map->emplace(static_cast<int>(RequestKey::kPINAuth),
  351. std::move(pin_auth));
  352. });
  353. }
  354. // static
  355. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  356. AsCTAPRequestValuePair(const ResetRequest&) {
  357. return std::make_pair(CtapRequestCommand::kAuthenticatorReset, absl::nullopt);
  358. }
  359. TokenRequest::TokenRequest(PINUVAuthProtocol protocol,
  360. const KeyAgreementResponse& peer_key)
  361. : protocol_(protocol),
  362. public_key_(
  363. ProtocolVersion(protocol_).Encapsulate(peer_key, &shared_key_)) {}
  364. TokenRequest::~TokenRequest() = default;
  365. TokenRequest::TokenRequest(TokenRequest&& other) = default;
  366. const std::vector<uint8_t>& TokenRequest::shared_key() const {
  367. return shared_key_;
  368. }
  369. PinTokenRequest::PinTokenRequest(PINUVAuthProtocol protocol,
  370. const std::string& pin,
  371. const KeyAgreementResponse& peer_key)
  372. : TokenRequest(protocol, peer_key) {
  373. uint8_t digest[SHA256_DIGEST_LENGTH];
  374. SHA256(reinterpret_cast<const uint8_t*>(pin.data()), pin.size(), digest);
  375. memcpy(pin_hash_, digest, sizeof(pin_hash_));
  376. }
  377. PinTokenRequest::~PinTokenRequest() = default;
  378. PinTokenRequest::PinTokenRequest(PinTokenRequest&& other) = default;
  379. // static
  380. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  381. AsCTAPRequestValuePair(const PinTokenRequest& request) {
  382. static_assert((sizeof(request.pin_hash_) % AES_BLOCK_SIZE) == 0,
  383. "pin_hash_ is not a multiple of the AES block size");
  384. std::vector<uint8_t> encrypted_pin =
  385. ProtocolVersion(request.protocol_)
  386. .Encrypt(request.shared_key_, request.pin_hash_);
  387. return EncodePINCommand(
  388. request.protocol_, Subcommand::kGetPINToken,
  389. [&request, &encrypted_pin](cbor::Value::MapValue* map) {
  390. map->emplace(static_cast<int>(RequestKey::kKeyAgreement),
  391. EncodeCOSEPublicKey(request.public_key_));
  392. map->emplace(static_cast<int>(RequestKey::kPINHashEnc),
  393. std::move(encrypted_pin));
  394. });
  395. }
  396. PinTokenWithPermissionsRequest::PinTokenWithPermissionsRequest(
  397. PINUVAuthProtocol protocol,
  398. const std::string& pin,
  399. const KeyAgreementResponse& peer_key,
  400. base::span<const pin::Permissions> permissions,
  401. const absl::optional<std::string> rp_id)
  402. : PinTokenRequest(protocol, pin, peer_key),
  403. permissions_(PermissionsToByte(permissions)),
  404. rp_id_(rp_id) {}
  405. // static
  406. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  407. AsCTAPRequestValuePair(const PinTokenWithPermissionsRequest& request) {
  408. std::vector<uint8_t> encrypted_pin =
  409. ProtocolVersion(request.protocol_)
  410. .Encrypt(request.shared_key_, request.pin_hash_);
  411. return EncodePINCommand(
  412. request.protocol_, Subcommand::kGetPinUvAuthTokenUsingPinWithPermissions,
  413. [&request, &encrypted_pin](cbor::Value::MapValue* map) {
  414. map->emplace(static_cast<int>(RequestKey::kKeyAgreement),
  415. EncodeCOSEPublicKey(request.public_key_));
  416. map->emplace(static_cast<int>(RequestKey::kPINHashEnc),
  417. std::move(encrypted_pin));
  418. map->emplace(static_cast<int>(RequestKey::kPermissions),
  419. std::move(request.permissions_));
  420. if (request.rp_id_) {
  421. map->emplace(static_cast<int>(RequestKey::kPermissionsRPID),
  422. *request.rp_id_);
  423. }
  424. });
  425. }
  426. PinTokenWithPermissionsRequest::~PinTokenWithPermissionsRequest() = default;
  427. PinTokenWithPermissionsRequest::PinTokenWithPermissionsRequest(
  428. PinTokenWithPermissionsRequest&& other) = default;
  429. UvTokenRequest::UvTokenRequest(PINUVAuthProtocol protocol,
  430. const KeyAgreementResponse& peer_key,
  431. absl::optional<std::string> rp_id,
  432. base::span<const pin::Permissions> permissions)
  433. : TokenRequest(protocol, peer_key),
  434. rp_id_(rp_id),
  435. permissions_(PermissionsToByte(permissions)) {}
  436. UvTokenRequest::~UvTokenRequest() = default;
  437. UvTokenRequest::UvTokenRequest(UvTokenRequest&& other) = default;
  438. // static
  439. std::pair<CtapRequestCommand, absl::optional<cbor::Value>>
  440. AsCTAPRequestValuePair(const UvTokenRequest& request) {
  441. return EncodePINCommand(
  442. request.protocol_, Subcommand::kGetUvToken,
  443. [&request](cbor::Value::MapValue* map) {
  444. map->emplace(static_cast<int>(RequestKey::kKeyAgreement),
  445. EncodeCOSEPublicKey(request.public_key_));
  446. map->emplace(static_cast<int>(RequestKey::kPermissions),
  447. request.permissions_);
  448. if (request.rp_id_) {
  449. map->emplace(static_cast<int>(RequestKey::kPermissionsRPID),
  450. *request.rp_id_);
  451. }
  452. });
  453. }
  454. static std::vector<uint8_t> ConcatSalts(
  455. base::span<const uint8_t, 32> salt1,
  456. const absl::optional<std::array<uint8_t, 32>>& salt2) {
  457. const size_t salts_size =
  458. salt1.size() + (salt2.has_value() ? salt2->size() : 0);
  459. std::vector<uint8_t> salts(salts_size);
  460. memcpy(salts.data(), salt1.data(), salt1.size());
  461. if (salt2.has_value()) {
  462. memcpy(salts.data() + salt1.size(), salt2->data(), salt2->size());
  463. }
  464. return salts;
  465. }
  466. HMACSecretRequest::HMACSecretRequest(
  467. PINUVAuthProtocol protocol,
  468. const KeyAgreementResponse& peer_key,
  469. base::span<const uint8_t, 32> salt1,
  470. const absl::optional<std::array<uint8_t, 32>>& salt2)
  471. : protocol_(protocol),
  472. public_key_x962(
  473. ProtocolVersion(protocol_).Encapsulate(peer_key, &shared_key_)),
  474. encrypted_salts(
  475. ProtocolVersion(protocol_).Encrypt(shared_key_,
  476. ConcatSalts(salt1, salt2))),
  477. salts_auth(ProtocolVersion(protocol_).Authenticate(shared_key_,
  478. encrypted_salts)) {}
  479. HMACSecretRequest::~HMACSecretRequest() = default;
  480. HMACSecretRequest::HMACSecretRequest(const HMACSecretRequest& other) = default;
  481. absl::optional<std::vector<uint8_t>> HMACSecretRequest::Decrypt(
  482. base::span<const uint8_t> ciphertext) {
  483. if (ciphertext.size() != this->encrypted_salts.size()) {
  484. return absl::nullopt;
  485. }
  486. return pin::ProtocolVersion(protocol_).Decrypt(shared_key_, ciphertext);
  487. }
  488. } // namespace pin
  489. } // namespace device