fido_cable_device.cc 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405
  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/cable/fido_cable_device.h"
  5. #include <utility>
  6. #include "base/bind.h"
  7. #include "base/numerics/safe_math.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/strings/string_piece.h"
  10. #include "base/threading/thread_task_runner_handle.h"
  11. #include "components/device_event_log/device_event_log.h"
  12. #include "device/fido/cable/fido_ble_frames.h"
  13. #include "device/fido/cable/fido_ble_uuids.h"
  14. #include "device/fido/cable/v2_handshake.h"
  15. #include "device/fido/fido_constants.h"
  16. #include "device/fido/fido_parsing_utils.h"
  17. namespace device {
  18. namespace {
  19. // Maximum size of EncryptionData::read_sequence_num or
  20. // EncryptionData::write_sequence_num allowed. If we encounter
  21. // counter larger than |kMaxCounter| FidoCableDevice should error out.
  22. constexpr uint32_t kMaxCounter = (1 << 24) - 1;
  23. absl::optional<std::vector<uint8_t>> ConstructV1Nonce(
  24. base::span<const uint8_t> nonce,
  25. bool is_sender_client,
  26. uint32_t counter) {
  27. if (counter > kMaxCounter)
  28. return absl::nullopt;
  29. auto constructed_nonce = fido_parsing_utils::Materialize(nonce);
  30. constructed_nonce.push_back(is_sender_client ? 0x00 : 0x01);
  31. constructed_nonce.push_back(counter >> 16 & 0xFF);
  32. constructed_nonce.push_back(counter >> 8 & 0xFF);
  33. constructed_nonce.push_back(counter & 0xFF);
  34. return constructed_nonce;
  35. }
  36. } // namespace
  37. FidoCableDevice::EncryptionData::EncryptionData() = default;
  38. FidoCableDevice::EncryptionData::~EncryptionData() = default;
  39. FidoCableDevice::FidoCableDevice(BluetoothAdapter* adapter,
  40. std::string address) {
  41. connection_ = std::make_unique<FidoBleConnection>(
  42. adapter, std::move(address), BluetoothUUID(kGoogleCableUUID128),
  43. base::BindRepeating(&FidoCableDevice::OnStatusMessage,
  44. weak_factory_.GetWeakPtr()));
  45. }
  46. FidoCableDevice::FidoCableDevice(std::unique_ptr<FidoBleConnection> connection)
  47. : connection_(std::move(connection)) {}
  48. FidoCableDevice::~FidoCableDevice() = default;
  49. // static
  50. std::string FidoCableDevice::GetIdForAddress(const std::string& address) {
  51. return "ble-" + address;
  52. }
  53. std::string FidoCableDevice::GetAddress() {
  54. return connection_->address();
  55. }
  56. void FidoCableDevice::Connect() {
  57. if (state_ != State::kInit)
  58. return;
  59. StartTimeout();
  60. state_ = State::kConnecting;
  61. connection_->Connect(base::BindOnce(&FidoCableDevice::OnConnected,
  62. weak_factory_.GetWeakPtr()));
  63. }
  64. void FidoCableDevice::SendPing(std::vector<uint8_t> data,
  65. DeviceCallback callback) {
  66. AddToPendingFrames(FidoBleDeviceCommand::kPing, std::move(data),
  67. std::move(callback));
  68. }
  69. FidoBleConnection::ReadCallback FidoCableDevice::GetReadCallbackForTesting() {
  70. return base::BindRepeating(&FidoCableDevice::OnStatusMessage,
  71. weak_factory_.GetWeakPtr());
  72. }
  73. void FidoCableDevice::set_observer(FidoCableDevice::Observer* observer) {
  74. DCHECK(!observer_);
  75. observer_ = observer;
  76. }
  77. void FidoCableDevice::Cancel(CancelToken token) {
  78. if (current_token_ && *current_token_ == token) {
  79. transaction_->Cancel();
  80. return;
  81. }
  82. for (auto it = pending_frames_.begin(); it != pending_frames_.end(); it++) {
  83. if (it->token != token) {
  84. continue;
  85. }
  86. auto callback = std::move(it->callback);
  87. pending_frames_.erase(it);
  88. std::vector<uint8_t> cancel_reply = {
  89. static_cast<uint8_t>(CtapDeviceResponseCode::kCtap2ErrKeepAliveCancel)};
  90. std::move(callback).Run(
  91. FidoBleFrame(FidoBleDeviceCommand::kMsg, std::move(cancel_reply)));
  92. break;
  93. }
  94. }
  95. std::string FidoCableDevice::GetId() const {
  96. return GetIdForAddress(connection_->address());
  97. }
  98. FidoTransportProtocol FidoCableDevice::DeviceTransport() const {
  99. return FidoTransportProtocol::kHybrid;
  100. }
  101. FidoDevice::CancelToken FidoCableDevice::DeviceTransact(
  102. std::vector<uint8_t> command,
  103. DeviceCallback callback) {
  104. if (!encryption_data_ || !EncryptOutgoingMessage(&command)) {
  105. base::ThreadTaskRunnerHandle::Get()->PostTask(
  106. FROM_HERE, base::BindOnce(std::move(callback), absl::nullopt));
  107. state_ = State::kDeviceError;
  108. FIDO_LOG(ERROR) << "Failed to encrypt outgoing caBLE message.";
  109. return 0;
  110. }
  111. FIDO_LOG(DEBUG) << "Sending encrypted message to caBLE client";
  112. return AddToPendingFrames(FidoBleDeviceCommand::kMsg, std::move(command),
  113. std::move(callback));
  114. }
  115. void FidoCableDevice::OnResponseFrame(FrameCallback callback,
  116. absl::optional<FidoBleFrame> frame) {
  117. // The request is done, time to reset |transaction_|.
  118. ResetTransaction();
  119. state_ = frame ? State::kReady : State::kDeviceError;
  120. if (frame && frame->command() != FidoBleDeviceCommand::kControl) {
  121. if (!encryption_data_ || !DecryptIncomingMessage(&frame.value())) {
  122. state_ = State::kDeviceError;
  123. frame = absl::nullopt;
  124. }
  125. }
  126. auto self = GetWeakPtr();
  127. std::move(callback).Run(std::move(frame));
  128. // Executing callbacks may free |this|. Check |self| first.
  129. if (self)
  130. Transition();
  131. }
  132. void FidoCableDevice::ResetTransaction() {
  133. transaction_.reset();
  134. current_token_.reset();
  135. }
  136. void FidoCableDevice::Transition() {
  137. switch (state_) {
  138. case State::kInit:
  139. Connect();
  140. break;
  141. case State::kReady:
  142. if (!pending_frames_.empty()) {
  143. PendingFrame pending(std::move(pending_frames_.front()));
  144. pending_frames_.pop_front();
  145. current_token_ = pending.token;
  146. SendRequestFrame(std::move(pending.frame), std::move(pending.callback));
  147. }
  148. break;
  149. case State::kConnecting:
  150. case State::kBusy:
  151. break;
  152. case State::kMsgError:
  153. case State::kDeviceError:
  154. auto self = GetWeakPtr();
  155. // Executing callbacks may free |this|. Check |self| first.
  156. while (self && !pending_frames_.empty()) {
  157. // Respond to any pending frames.
  158. FrameCallback cb = std::move(pending_frames_.front().callback);
  159. pending_frames_.pop_front();
  160. std::move(cb).Run(absl::nullopt);
  161. }
  162. break;
  163. }
  164. }
  165. FidoDevice::CancelToken FidoCableDevice::AddToPendingFrames(
  166. FidoBleDeviceCommand cmd,
  167. std::vector<uint8_t> request,
  168. DeviceCallback callback) {
  169. const auto token = next_cancel_token_++;
  170. pending_frames_.emplace_back(
  171. FidoBleFrame(cmd, std::move(request)),
  172. base::BindOnce(&FidoCableDevice::OnBleResponseReceived,
  173. weak_factory_.GetWeakPtr(), std::move(callback)),
  174. token);
  175. Transition();
  176. return token;
  177. }
  178. void FidoCableDevice::SendHandshakeMessage(
  179. std::vector<uint8_t> handshake_message,
  180. DeviceCallback callback) {
  181. AddToPendingFrames(FidoBleDeviceCommand::kControl,
  182. std::move(handshake_message), std::move(callback));
  183. }
  184. void FidoCableDevice::SetV1EncryptionData(
  185. base::span<const uint8_t, 32> session_key,
  186. base::span<const uint8_t, 8> nonce) {
  187. // Encryption data must be set at most once during Cable handshake protocol.
  188. DCHECK(!encryption_data_);
  189. encryption_data_.emplace();
  190. encryption_data_->read_key = fido_parsing_utils::Materialize(session_key);
  191. encryption_data_->write_key = fido_parsing_utils::Materialize(session_key);
  192. encryption_data_->nonce = fido_parsing_utils::Materialize(nonce);
  193. }
  194. void FidoCableDevice::SetSequenceNumbersForTesting(uint32_t read_seq,
  195. uint32_t write_seq) {
  196. encryption_data_->write_sequence_num = write_seq;
  197. encryption_data_->read_sequence_num = read_seq;
  198. }
  199. base::WeakPtr<FidoDevice> FidoCableDevice::GetWeakPtr() {
  200. return weak_factory_.GetWeakPtr();
  201. }
  202. FidoCableDevice::PendingFrame::PendingFrame(FidoBleFrame in_frame,
  203. FrameCallback in_callback,
  204. CancelToken in_token)
  205. : frame(std::move(in_frame)),
  206. callback(std::move(in_callback)),
  207. token(in_token) {}
  208. FidoCableDevice::PendingFrame::PendingFrame(PendingFrame&&) = default;
  209. FidoCableDevice::PendingFrame::~PendingFrame() = default;
  210. void FidoCableDevice::OnConnected(bool success) {
  211. if (state_ != State::kConnecting) {
  212. return;
  213. }
  214. StopTimeout();
  215. if (observer_) {
  216. observer_->FidoCableDeviceConnected(this, success);
  217. }
  218. if (!success) {
  219. FIDO_LOG(ERROR) << "FidoCableDevice::Connect() failed";
  220. state_ = State::kDeviceError;
  221. Transition();
  222. return;
  223. }
  224. FIDO_LOG(EVENT) << "FidoCableDevice connected";
  225. DCHECK_EQ(State::kConnecting, state_);
  226. StartTimeout();
  227. connection_->ReadControlPointLength(base::BindOnce(
  228. &FidoCableDevice::OnReadControlPointLength, weak_factory_.GetWeakPtr()));
  229. }
  230. void FidoCableDevice::OnStatusMessage(std::vector<uint8_t> data) {
  231. if (transaction_)
  232. transaction_->OnResponseFragment(std::move(data));
  233. }
  234. void FidoCableDevice::OnReadControlPointLength(
  235. absl::optional<uint16_t> length) {
  236. if (state_ == State::kDeviceError) {
  237. return;
  238. }
  239. StopTimeout();
  240. if (length) {
  241. control_point_length_ = *length;
  242. state_ = State::kReady;
  243. } else {
  244. state_ = State::kDeviceError;
  245. }
  246. Transition();
  247. }
  248. void FidoCableDevice::SendRequestFrame(FidoBleFrame frame,
  249. FrameCallback callback) {
  250. state_ = State::kBusy;
  251. transaction_.emplace(connection_.get(), control_point_length_);
  252. transaction_->WriteRequestFrame(
  253. std::move(frame),
  254. base::BindOnce(&FidoCableDevice::OnResponseFrame,
  255. weak_factory_.GetWeakPtr(), std::move(callback)));
  256. }
  257. void FidoCableDevice::StartTimeout() {
  258. timer_.Start(FROM_HERE, kDeviceTimeout, this, &FidoCableDevice::OnTimeout);
  259. }
  260. void FidoCableDevice::StopTimeout() {
  261. timer_.Stop();
  262. }
  263. void FidoCableDevice::OnTimeout() {
  264. FIDO_LOG(ERROR) << "FIDO Cable device timeout for " << GetId();
  265. state_ = State::kDeviceError;
  266. if (observer_) {
  267. observer_->FidoCableDeviceTimeout(this);
  268. }
  269. Transition();
  270. }
  271. void FidoCableDevice::OnBleResponseReceived(
  272. DeviceCallback callback,
  273. absl::optional<FidoBleFrame> frame) {
  274. if (!frame || !frame->IsValid()) {
  275. state_ = State::kDeviceError;
  276. std::move(callback).Run(absl::nullopt);
  277. return;
  278. }
  279. if (frame->command() == FidoBleDeviceCommand::kError) {
  280. ProcessBleDeviceError(frame->data());
  281. std::move(callback).Run(absl::nullopt);
  282. return;
  283. }
  284. std::move(callback).Run(frame->data());
  285. }
  286. void FidoCableDevice::ProcessBleDeviceError(base::span<const uint8_t> data) {
  287. if (data.size() != 1) {
  288. FIDO_LOG(ERROR) << "Unknown BLE error received: "
  289. << base::HexEncode(data.data(), data.size());
  290. state_ = State::kDeviceError;
  291. return;
  292. }
  293. switch (static_cast<FidoBleFrame::ErrorCode>(data[0])) {
  294. case FidoBleFrame::ErrorCode::INVALID_CMD:
  295. case FidoBleFrame::ErrorCode::INVALID_PAR:
  296. case FidoBleFrame::ErrorCode::INVALID_LEN:
  297. state_ = State::kMsgError;
  298. break;
  299. default:
  300. FIDO_LOG(ERROR) << "BLE error received: " << static_cast<int>(data[0]);
  301. state_ = State::kDeviceError;
  302. }
  303. }
  304. bool FidoCableDevice::EncryptOutgoingMessage(
  305. std::vector<uint8_t>* message_to_encrypt) {
  306. const auto nonce =
  307. ConstructV1Nonce(encryption_data_->nonce, /*is_sender_client=*/true,
  308. encryption_data_->write_sequence_num++);
  309. if (!nonce)
  310. return false;
  311. crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
  312. aes_key.Init(encryption_data_->write_key);
  313. DCHECK_EQ(nonce->size(), aes_key.NonceLength());
  314. const uint8_t additional_data[1] = {
  315. base::strict_cast<uint8_t>(FidoBleDeviceCommand::kMsg)};
  316. std::vector<uint8_t> ciphertext =
  317. aes_key.Seal(*message_to_encrypt, *nonce, additional_data);
  318. message_to_encrypt->swap(ciphertext);
  319. return true;
  320. }
  321. bool FidoCableDevice::DecryptIncomingMessage(FidoBleFrame* incoming_frame) {
  322. const auto nonce =
  323. ConstructV1Nonce(encryption_data_->nonce, /*is_sender_client=*/false,
  324. encryption_data_->read_sequence_num);
  325. if (!nonce)
  326. return false;
  327. crypto::Aead aes_key(crypto::Aead::AES_256_GCM);
  328. aes_key.Init(encryption_data_->read_key);
  329. DCHECK_EQ(nonce->size(), aes_key.NonceLength());
  330. const uint8_t additional_data[1] = {
  331. base::strict_cast<uint8_t>(incoming_frame->command())};
  332. absl::optional<std::vector<uint8_t>> plaintext =
  333. aes_key.Open(incoming_frame->data(), *nonce, additional_data);
  334. if (!plaintext) {
  335. FIDO_LOG(ERROR) << "Failed to decrypt caBLE message.";
  336. return false;
  337. }
  338. encryption_data_->read_sequence_num++;
  339. incoming_frame->data().swap(*plaintext);
  340. return true;
  341. }
  342. } // namespace device