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