fido_cable_handshake_handler_unittest.cc 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  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_handshake_handler.h"
  5. #include <array>
  6. #include <limits>
  7. #include <memory>
  8. #include <string>
  9. #include <utility>
  10. #include "base/bind.h"
  11. #include "base/memory/raw_ptr.h"
  12. #include "base/test/task_environment.h"
  13. #include "base/threading/sequenced_task_runner_handle.h"
  14. #include "components/cbor/reader.h"
  15. #include "components/cbor/values.h"
  16. #include "components/cbor/writer.h"
  17. #include "crypto/hkdf.h"
  18. #include "crypto/hmac.h"
  19. #include "device/bluetooth/test/bluetooth_test.h"
  20. #include "device/bluetooth/test/mock_bluetooth_adapter.h"
  21. #include "device/fido/cable/fido_ble_frames.h"
  22. #include "device/fido/cable/fido_cable_device.h"
  23. #include "device/fido/cable/mock_fido_ble_connection.h"
  24. #include "device/fido/fido_constants.h"
  25. #include "device/fido/fido_parsing_utils.h"
  26. #include "device/fido/test_callback_receiver.h"
  27. #include "testing/gmock/include/gmock/gmock.h"
  28. #include "testing/gtest/include/gtest/gtest.h"
  29. #include "third_party/abseil-cpp/absl/types/optional.h"
  30. namespace device {
  31. namespace {
  32. using ::testing::_;
  33. using ::testing::Invoke;
  34. using ::testing::Test;
  35. using TestDeviceCallbackReceiver =
  36. test::ValueCallbackReceiver<absl::optional<std::vector<uint8_t>>>;
  37. using NiceMockBluetoothAdapter = ::testing::NiceMock<MockBluetoothAdapter>;
  38. // Sufficiently large test control point length as we are not interested
  39. // in testing fragmentations of BLE messages. All Cable messages are encrypted
  40. // and decrypted per request frame, not fragment.
  41. constexpr auto kControlPointLength = std::numeric_limits<uint16_t>::max();
  42. constexpr std::array<uint8_t, 16> kAuthenticatorSessionRandom = {{
  43. 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
  44. 0x01, 0x02, 0x03, 0x04,
  45. }};
  46. constexpr std::array<uint8_t, 32> kTestSessionPreKey = {{
  47. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  48. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  49. 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
  50. }};
  51. constexpr std::array<uint8_t, 32> kIncorrectSessionPreKey = {{
  52. 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
  53. 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
  54. 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee,
  55. }};
  56. constexpr std::array<uint8_t, 8> kTestNonce = {{
  57. 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x09, 0x08,
  58. }};
  59. constexpr std::array<uint8_t, 8> kIncorrectNonce = {{
  60. 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
  61. }};
  62. constexpr std::array<uint8_t, 50> kValidAuthenticatorHello = {{
  63. // Map(2)
  64. 0xA2,
  65. // Key(0)
  66. 0x00,
  67. // Text(28)
  68. 0x78, 0x1C,
  69. // "caBLE v1 authenticator hello"
  70. 0x63, 0x61, 0x42, 0x4C, 0x45, 0x20, 0x76, 0x31, 0x20, 0x61, 0x75, 0x74,
  71. 0x68, 0x65, 0x6E, 0x74, 0x69, 0x63, 0x61, 0x74, 0x6F, 0x72, 0x20, 0x68,
  72. 0x65, 0x6C, 0x6C, 0x6F,
  73. // Key(1)
  74. 0x01,
  75. // Bytes(16)
  76. 0x50,
  77. // Authenticator random session
  78. 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
  79. 0x01, 0x02, 0x03, 0x04,
  80. }};
  81. constexpr std::array<uint8_t, 43> kInvalidAuthenticatorHello = {{
  82. // Map(2)
  83. 0xA2,
  84. // Key(0)
  85. 0x00,
  86. // Text(21)
  87. 0x75,
  88. // "caBLE INVALID MESSAGE"
  89. 0x63, 0x61, 0x42, 0x4C, 0x45, 0x20, 0x49, 0x4E, 0x56, 0x41, 0x4C, 0x49,
  90. 0x44, 0x20, 0x4D, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45,
  91. // Key(1)
  92. 0x01,
  93. // Bytes(16)
  94. 0x50,
  95. // Authenticator random session
  96. 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04, 0x01, 0x02, 0x03, 0x04,
  97. 0x01, 0x02, 0x03, 0x04,
  98. }};
  99. constexpr char kIncorrectHandshakeKey[] = "INCORRECT_HANDSHAKE_KEY_12345678";
  100. // Returns the expected encryption key that should be constructed given that
  101. // the client random nonce is |client_random_nonce| and other determining
  102. // factors (i.e. authenticator session random, session pre key, and nonce) are
  103. // |kAuthenticatorSessionRandom|, |kTestSessionPreKey|, and |kTestNonce|,
  104. // respectively.
  105. std::vector<uint8_t> GetExpectedEncryptionKey(
  106. base::span<const uint8_t> client_random_nonce) {
  107. std::vector<uint8_t> nonce_message =
  108. fido_parsing_utils::Materialize(kTestNonce);
  109. fido_parsing_utils::Append(&nonce_message, client_random_nonce);
  110. fido_parsing_utils::Append(&nonce_message, kAuthenticatorSessionRandom);
  111. return crypto::HkdfSha256(kTestSessionPreKey,
  112. crypto::SHA256Hash(nonce_message),
  113. kCableDeviceEncryptionKeyInfo, 32);
  114. }
  115. // Given a hello message and handshake key from the authenticator, construct
  116. // a handshake message by concatenating hello message and its mac message
  117. // derived from |handshake_key|.
  118. std::vector<uint8_t> ConstructAuthenticatorHelloReply(
  119. base::span<const uint8_t> hello_msg,
  120. base::StringPiece handshake_key) {
  121. auto reply = fido_parsing_utils::Materialize(hello_msg);
  122. crypto::HMAC hmac(crypto::HMAC::SHA256);
  123. if (!hmac.Init(handshake_key))
  124. return std::vector<uint8_t>();
  125. std::array<uint8_t, 32> authenticator_hello_mac;
  126. if (!hmac.Sign(fido_parsing_utils::ConvertToStringPiece(hello_msg),
  127. authenticator_hello_mac.data(),
  128. authenticator_hello_mac.size())) {
  129. return std::vector<uint8_t>();
  130. }
  131. fido_parsing_utils::Append(
  132. &reply, base::make_span(authenticator_hello_mac).first(16));
  133. return reply;
  134. }
  135. // Constructs incoming handshake message from the authenticator into a BLE
  136. // control fragment.
  137. std::vector<uint8_t> ConstructSerializedOutgoingFragment(
  138. base::span<const uint8_t> data) {
  139. if (data.empty())
  140. return std::vector<uint8_t>();
  141. FidoBleFrame response_frame(FidoBleDeviceCommand::kControl,
  142. fido_parsing_utils::Materialize(data));
  143. const auto response_fragment =
  144. std::get<0>(response_frame.ToFragments(kControlPointLength));
  145. std::vector<uint8_t> outgoing_message;
  146. response_fragment.Serialize(&outgoing_message);
  147. return outgoing_message;
  148. }
  149. // Authenticator abstraction that handles logic related to validating handshake
  150. // messages from the client and sending rely handshake message back to the
  151. // client. Session key and nonce are assumed to be |kTestSessionPreKey| and
  152. // |kTestNonce| respectively.
  153. class FakeCableAuthenticator {
  154. public:
  155. FakeCableAuthenticator() {
  156. handshake_key_ = crypto::HkdfSha256(
  157. fido_parsing_utils::ConvertToStringPiece(kTestSessionPreKey),
  158. fido_parsing_utils::ConvertToStringPiece(kTestNonce),
  159. kCableHandshakeKeyInfo, 32);
  160. }
  161. // Receives handshake message from the client, check its validity and if the
  162. // handshake message is valid, store |client_session_random| embedded in the
  163. // handshake message.
  164. bool ConfirmClientHandshakeMessage(
  165. base::span<const uint8_t> handshake_message) {
  166. if (handshake_message.size() <= 16)
  167. return false;
  168. crypto::HMAC hmac(crypto::HMAC::SHA256);
  169. if (!hmac.Init(handshake_key_))
  170. return false;
  171. // Handshake message from client should be concatenation of client hello
  172. // message (42 bytes) with message authentication code (16 bytes).
  173. if (handshake_message.size() != 58)
  174. return false;
  175. const auto client_hello = handshake_message.first(42);
  176. if (!hmac.VerifyTruncated(
  177. fido_parsing_utils::ConvertToStringPiece(client_hello),
  178. fido_parsing_utils::ConvertToStringPiece(
  179. handshake_message.subspan(42)))) {
  180. return false;
  181. }
  182. const auto& client_hello_cbor = cbor::Reader::Read(client_hello);
  183. if (!client_hello_cbor)
  184. return false;
  185. const auto& message_map = client_hello_cbor->GetMap();
  186. auto hello_message_it = message_map.find(cbor::Value(0));
  187. auto client_random_nonce_it = message_map.find(cbor::Value(1));
  188. if (hello_message_it == message_map.end() ||
  189. client_random_nonce_it == message_map.end())
  190. return false;
  191. if (!hello_message_it->second.is_string() ||
  192. hello_message_it->second.GetString() != kCableClientHelloMessage) {
  193. return false;
  194. }
  195. if (!client_random_nonce_it->second.is_bytestring() ||
  196. client_random_nonce_it->second.GetBytestring().size() != 16) {
  197. return false;
  198. }
  199. client_session_random_ =
  200. std::move(client_random_nonce_it->second.GetBytestring());
  201. return true;
  202. }
  203. std::vector<uint8_t> RelyWithAuthenticatorHandShakeMessage(
  204. base::span<const uint8_t> handshake_message) {
  205. if (!ConfirmClientHandshakeMessage(handshake_message))
  206. return std::vector<uint8_t>();
  207. return ConstructAuthenticatorHelloReply(kValidAuthenticatorHello,
  208. handshake_key_);
  209. }
  210. private:
  211. std::string handshake_key_;
  212. std::vector<uint8_t> client_session_random_;
  213. std::vector<uint8_t> authenticator_session_random_ =
  214. fido_parsing_utils::Materialize(kAuthenticatorSessionRandom);
  215. };
  216. } // namespace
  217. class FidoCableHandshakeHandlerTest : public Test {
  218. public:
  219. FidoCableHandshakeHandlerTest() {
  220. auto connection = std::make_unique<MockFidoBleConnection>(
  221. adapter_.get(), BluetoothTestBase::kTestDeviceAddress1);
  222. connection_ = connection.get();
  223. device_ = std::make_unique<FidoCableDevice>(std::move(connection));
  224. connection_->read_callback() = device_->GetReadCallbackForTesting();
  225. }
  226. std::unique_ptr<FidoCableV1HandshakeHandler> CreateHandshakeHandler(
  227. std::array<uint8_t, 8> nonce,
  228. std::array<uint8_t, 32> session_pre_key) {
  229. return std::make_unique<FidoCableV1HandshakeHandler>(device_.get(), nonce,
  230. session_pre_key);
  231. }
  232. void ConnectWithLength(uint16_t length) {
  233. EXPECT_CALL(*connection(), ConnectPtr).WillOnce(Invoke([](auto* callback) {
  234. std::move(*callback).Run(true);
  235. }));
  236. EXPECT_CALL(*connection(), ReadControlPointLengthPtr(_))
  237. .WillOnce(Invoke([length](auto* cb) { std::move(*cb).Run(length); }));
  238. device()->Connect();
  239. }
  240. FidoCableDevice* device() { return device_.get(); }
  241. MockFidoBleConnection* connection() { return connection_; }
  242. FakeCableAuthenticator* authenticator() { return &authenticator_; }
  243. TestDeviceCallbackReceiver& callback_receiver() { return callback_receiver_; }
  244. protected:
  245. base::test::TaskEnvironment task_environment_;
  246. private:
  247. scoped_refptr<MockBluetoothAdapter> adapter_ =
  248. base::MakeRefCounted<NiceMockBluetoothAdapter>();
  249. FakeCableAuthenticator authenticator_;
  250. raw_ptr<MockFidoBleConnection> connection_;
  251. std::unique_ptr<FidoCableDevice> device_;
  252. TestDeviceCallbackReceiver callback_receiver_;
  253. };
  254. // Checks that outgoing handshake message from the client is a BLE frame with
  255. // Control command type.
  256. MATCHER(IsControlFrame, "") {
  257. return !arg.empty() &&
  258. arg[0] == base::strict_cast<uint8_t>(FidoBleDeviceCommand::kControl);
  259. }
  260. TEST_F(FidoCableHandshakeHandlerTest, HandShakeSuccess) {
  261. ConnectWithLength(kControlPointLength);
  262. EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
  263. .WillOnce(Invoke([this](const auto& data, auto* cb) {
  264. base::SequencedTaskRunnerHandle::Get()->PostTask(
  265. FROM_HERE, base::BindOnce(std::move(*cb), true));
  266. const auto client_ble_handshake_message =
  267. base::make_span(data).subspan(3);
  268. base::SequencedTaskRunnerHandle::Get()->PostTask(
  269. FROM_HERE,
  270. base::BindOnce(
  271. connection()->read_callback(),
  272. ConstructSerializedOutgoingFragment(
  273. authenticator()->RelyWithAuthenticatorHandShakeMessage(
  274. client_ble_handshake_message))));
  275. }));
  276. auto handshake_handler =
  277. CreateHandshakeHandler(kTestNonce, kTestSessionPreKey);
  278. handshake_handler->InitiateCableHandshake(callback_receiver().callback());
  279. callback_receiver().WaitForCallback();
  280. const auto& value = callback_receiver().value();
  281. ASSERT_TRUE(value);
  282. EXPECT_TRUE(handshake_handler->ValidateAuthenticatorHandshakeMessage(*value));
  283. EXPECT_EQ(GetExpectedEncryptionKey(handshake_handler->client_session_random_),
  284. handshake_handler->GetEncryptionKeyAfterSuccessfulHandshake(
  285. kAuthenticatorSessionRandom));
  286. }
  287. TEST_F(FidoCableHandshakeHandlerTest, HandShakeWithIncorrectSessionPreKey) {
  288. ConnectWithLength(kControlPointLength);
  289. EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
  290. .WillOnce(Invoke([this](const auto& data, auto* cb) {
  291. base::SequencedTaskRunnerHandle::Get()->PostTask(
  292. FROM_HERE, base::BindOnce(std::move(*cb), true));
  293. const auto client_ble_handshake_message =
  294. base::make_span(data).subspan(3);
  295. base::SequencedTaskRunnerHandle::Get()->PostTask(
  296. FROM_HERE,
  297. base::BindOnce(
  298. connection()->read_callback(),
  299. ConstructSerializedOutgoingFragment(
  300. authenticator()->RelyWithAuthenticatorHandShakeMessage(
  301. client_ble_handshake_message))));
  302. }));
  303. auto handshake_handler =
  304. CreateHandshakeHandler(kTestNonce, kIncorrectSessionPreKey);
  305. handshake_handler->InitiateCableHandshake(callback_receiver().callback());
  306. callback_receiver().WaitForCallback();
  307. EXPECT_FALSE(callback_receiver().value());
  308. }
  309. TEST_F(FidoCableHandshakeHandlerTest, HandshakeFailWithIncorrectNonce) {
  310. ConnectWithLength(kControlPointLength);
  311. EXPECT_CALL(*connection(), WriteControlPointPtr(IsControlFrame(), _))
  312. .WillOnce(Invoke([this](const auto& data, auto* cb) {
  313. base::SequencedTaskRunnerHandle::Get()->PostTask(
  314. FROM_HERE, base::BindOnce(std::move(*cb), true));
  315. const auto client_ble_handshake_message =
  316. base::make_span(data).subspan(3);
  317. base::SequencedTaskRunnerHandle::Get()->PostTask(
  318. FROM_HERE,
  319. base::BindOnce(
  320. connection()->read_callback(),
  321. ConstructSerializedOutgoingFragment(
  322. authenticator()->RelyWithAuthenticatorHandShakeMessage(
  323. client_ble_handshake_message))));
  324. }));
  325. auto handshake_handler =
  326. CreateHandshakeHandler(kIncorrectNonce, kTestSessionPreKey);
  327. handshake_handler->InitiateCableHandshake(callback_receiver().callback());
  328. callback_receiver().WaitForCallback();
  329. EXPECT_FALSE(callback_receiver().value());
  330. }
  331. TEST_F(FidoCableHandshakeHandlerTest,
  332. HandshakeFailWithIncorrectAuthenticatorResponse) {
  333. auto handshake_handler =
  334. CreateHandshakeHandler(kTestNonce, kTestSessionPreKey);
  335. EXPECT_NE(kIncorrectHandshakeKey, handshake_handler->handshake_key_);
  336. const auto authenticator_reply_with_invalid_key =
  337. ConstructAuthenticatorHelloReply(kValidAuthenticatorHello,
  338. kIncorrectHandshakeKey);
  339. EXPECT_FALSE(handshake_handler->ValidateAuthenticatorHandshakeMessage(
  340. authenticator_reply_with_invalid_key));
  341. const auto authenticator_reply_with_invalid_hello_msg =
  342. ConstructAuthenticatorHelloReply(kInvalidAuthenticatorHello,
  343. handshake_handler->handshake_key_);
  344. EXPECT_FALSE(handshake_handler->ValidateAuthenticatorHandshakeMessage(
  345. authenticator_reply_with_invalid_hello_msg));
  346. }
  347. } // namespace device