| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "util.h" |
| |
| #include <array> |
| #include <cstdint> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/device_address.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/test_helpers.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uint128.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uint256.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/constants.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/smp.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/status.h" |
| |
| namespace bt::sm::util { |
| namespace { |
| |
| TEST(SMP_UtilTest, ConvertSmIoCapabilityToHci) { |
| EXPECT_EQ(hci::IOCapability::kDisplayOnly, IOCapabilityForHci(IOCapability::kDisplayOnly)); |
| EXPECT_EQ(hci::IOCapability::kDisplayYesNo, IOCapabilityForHci(IOCapability::kDisplayYesNo)); |
| EXPECT_EQ(hci::IOCapability::kKeyboardOnly, IOCapabilityForHci(IOCapability::kKeyboardOnly)); |
| EXPECT_EQ(hci::IOCapability::kNoInputNoOutput, |
| IOCapabilityForHci(IOCapability::kNoInputNoOutput)); |
| EXPECT_EQ(hci::IOCapability::kDisplayYesNo, IOCapabilityForHci(IOCapability::kKeyboardDisplay)); |
| |
| // Test remaining invalid values for sm::IOCapability. |
| for (int i = 0x05; i < 0xff; i++) { |
| EXPECT_EQ(hci::IOCapability::kNoInputNoOutput, |
| IOCapabilityForHci(static_cast<IOCapability>(i))); |
| } |
| } |
| |
| TEST(SMP_UtilTest, MapToRolesCorrectly) { |
| UInt128 local_val = {1}, peer_val = {2}; |
| auto [initiator_val, responder_val] = MapToRoles(local_val, peer_val, Role::kInitiator); |
| EXPECT_EQ(local_val, initiator_val); |
| EXPECT_EQ(peer_val, responder_val); |
| |
| std::tie(initiator_val, responder_val) = MapToRoles(local_val, peer_val, Role::kResponder); |
| EXPECT_EQ(local_val, responder_val); |
| EXPECT_EQ(peer_val, initiator_val); |
| } |
| |
| TEST(SMP_UtilTest, SelectPairingMethodOOB) { |
| // In SC OOB is selected if either device has OOB data. |
| EXPECT_EQ( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(true /* sc */, true /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, true /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| EXPECT_NE( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // In legacy OOB is selected if both devices have OOB data. |
| EXPECT_EQ( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(false /* sc */, true /* local_oob */, true /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| EXPECT_NE( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, true /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| EXPECT_NE( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(false /* sc */, true /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| EXPECT_NE( |
| PairingMethod::kOutOfBand, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| } |
| |
| TEST(SMP_UtilTest, SelectPairingMethodNoMITM) { |
| // The pairing method should be "Just Works" if neither device requires MITM |
| // protection, regardless of other parameters. |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| false /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Shouldn't default to "Just Works" if at least one device requires MITM |
| // protection. |
| EXPECT_NE( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| } |
| |
| // Tests all combinations that result in the "Just Works" pairing method. |
| TEST(SMP_UtilTest, SelectPairingMethodJustWorks) { |
| // Local: DisplayOnly |
| EXPECT_EQ(PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayOnly /* local */, |
| IOCapability::kDisplayOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayOnly /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayOnly /* local */, |
| IOCapability::kNoInputNoOutput /* peer */, true /* local_initiator */)); |
| |
| // Local: DisplayYesNo |
| EXPECT_EQ(PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kDisplayOnly /* peer */, true /* local_initiator */)); |
| // If both devices are DisplayYesNo, then "Just Works" is selected for LE |
| // legacy pairing (i.e. at least one device doesn't support Secure |
| // Connections). |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kNoInputNoOutput /* peer */, true /* local_initiator */)); |
| |
| // Local: KeyboardOnly |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardOnly /* local */, |
| IOCapability::kNoInputNoOutput /* peer */, true /* local_initiator */)); |
| |
| // Local: NoInputNoOutput. Always "Just Works". |
| EXPECT_EQ(PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kNoInputNoOutput /* local */, |
| IOCapability::kDisplayOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kNoInputNoOutput /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kNoInputNoOutput /* local */, |
| IOCapability::kKeyboardOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kNoInputNoOutput /* local */, |
| IOCapability::kNoInputNoOutput /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kNoInputNoOutput /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Local: KeyboardDisplay |
| EXPECT_EQ( |
| PairingMethod::kJustWorks, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kNoInputNoOutput /* peer */, true /* local_initiator */)); |
| } |
| |
| // Tests all combinations that result in the "Passkey Entry (input)" pairing |
| // method. |
| TEST(SMP_UtilTest, SelectPairingMethodPasskeyEntryInput) { |
| // Local: KeyboardOnly |
| EXPECT_EQ(PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardOnly /* local */, |
| IOCapability::kDisplayOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardOnly /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardOnly /* local */, |
| IOCapability::kKeyboardOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardOnly /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Local: KeyboardDisplay |
| EXPECT_EQ(PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kDisplayOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| |
| // If both devices have the KeyboardDisplay capability then the responder |
| // inputs. |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryInput, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, false /* local_initiator */)); |
| } |
| |
| // Tests all combinations that result in the "Passkey Entry (display)" pairing |
| // method. |
| TEST(SMP_UtilTest, SelectPairingMethodPasskeyEntryDisplay) { |
| // Local: DisplayOnly |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayOnly /* local */, |
| IOCapability::kKeyboardOnly /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayOnly /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Local: DisplayYesNo |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kKeyboardOnly /* peer */, true /* local_initiator */)); |
| // If the peer has a display then use "Passkey Entry" only for LE Legacy |
| // pairing. |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Local: KeyboardDisplay |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardOnly /* peer */, true /* local_initiator */)); |
| |
| // If both devices have the KeyboardDisplay capability then the initiator |
| // displays. |
| EXPECT_EQ( |
| PairingMethod::kPasskeyEntryDisplay, |
| SelectPairingMethod(false /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| } |
| |
| // Tests all combinations that result in the "Numeric Comparison" pairing |
| // method. This will be selected in certain I/O capability combinations only if |
| // both devices support Secure Connections. |
| TEST(SMP_UtilTest, SelectPairingMethodNumericComparison) { |
| // Local: DisplayYesNo |
| EXPECT_EQ( |
| PairingMethod::kNumericComparison, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kNumericComparison, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kDisplayYesNo /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| |
| // Local: KeyboardDisplay |
| EXPECT_EQ( |
| PairingMethod::kNumericComparison, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kDisplayYesNo /* peer */, true /* local_initiator */)); |
| EXPECT_EQ( |
| PairingMethod::kNumericComparison, |
| SelectPairingMethod(true /* sc */, false /* local_oob */, false /* peer_oob */, |
| true /* mitm */, IOCapability::kKeyboardDisplay /* local */, |
| IOCapability::kKeyboardDisplay /* peer */, true /* local_initiator */)); |
| } |
| |
| // Tests "c1" using the sample data from Vol 3, Part H, 2.2.3. |
| TEST(SMP_UtilTest, C1) { |
| const UInt128 tk{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; |
| const UInt128 r{{0xE0, 0x2E, 0x70, 0xC6, 0x4E, 0x27, 0x88, 0x63, 0x0E, 0x6F, 0xAD, 0x56, 0x21, |
| 0xD5, 0x83, 0x57}}; |
| const auto preq = CreateStaticByteBuffer(0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07); |
| const auto pres = CreateStaticByteBuffer(0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05); |
| const DeviceAddress initiator_addr(DeviceAddress::Type::kLERandom, |
| {0xA6, 0xA5, 0xA4, 0xA3, 0xA2, 0xA1}); |
| const DeviceAddress responder_addr(DeviceAddress::Type::kLEPublic, |
| {0xB6, 0xB5, 0xB4, 0xB3, 0xB2, 0xB1}); |
| |
| const UInt128 kExpected{{0x86, 0x3B, 0xF1, 0xBE, 0xC5, 0x4D, 0xA7, 0xD2, 0xEA, 0x88, 0x89, 0x87, |
| 0xEF, 0x3F, 0x1E, 0x1E}}; |
| |
| UInt128 result; |
| C1(tk, r, preq, pres, initiator_addr, responder_addr, &result); |
| EXPECT_TRUE(ContainersEqual(kExpected, result)); |
| } |
| |
| // Tests "s1" using the sample data from Vol 3, Part H, 2.2.4. |
| TEST(SMP_UtilTest, S1) { |
| const UInt128 tk{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; |
| const UInt128 r1{{0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x09, 0xA0, 0xB0, 0xC0, 0xD0, |
| 0xE0, 0xF0, 0x00}}; |
| const UInt128 r2{{0x00, 0xFF, 0xEE, 0xDD, 0xCC, 0xBB, 0xAA, 0x99, 0x08, 0x07, 0x06, 0x05, 0x04, |
| 0x03, 0x02, 0x01}}; |
| |
| const UInt128 kExpected{{0x62, 0xA0, 0x6D, 0x79, 0xAE, 0x16, 0x42, 0x5B, 0x9B, 0xF4, 0xB0, 0xE8, |
| 0xF0, 0xE1, 0x1F, 0x9A}}; |
| |
| UInt128 result; |
| S1(tk, r1, r2, &result); |
| EXPECT_TRUE(ContainersEqual(kExpected, result)); |
| } |
| |
| // Test "ah" using the sample data from Vol 3, Part H, Appendix D.7. |
| TEST(SMP_UtilTest, Ah) { |
| const UInt128 irk{{0x9B, 0x7D, 0x39, 0x0A, 0xA6, 0x10, 0x10, 0x34, 0x05, 0xAD, 0xC8, 0x57, 0xA3, |
| 0x34, 0x02, 0xEC}}; |
| const uint32_t prand = 0x708194; |
| const uint32_t kExpected = 0x0DFBAA; |
| |
| EXPECT_EQ(kExpected, Ah(irk, prand)); |
| } |
| |
| TEST(SMP_UtilTest, IrkCanResolveRpa) { |
| // Using the sample data from Vol 3, Part H, Appendix D.7. |
| const UInt128 kIRK{{0x9B, 0x7D, 0x39, 0x0A, 0xA6, 0x10, 0x10, 0x34, 0x05, 0xAD, 0xC8, 0x57, 0xA3, |
| 0x34, 0x02, 0xEC}}; |
| const DeviceAddress kStaticRandom(DeviceAddress::Type::kLERandom, |
| {0xA9, 0xFB, 0x0D, 0x94, 0x81, 0xF0}); |
| const DeviceAddress kNonResolvable(DeviceAddress::Type::kLERandom, |
| {0xA9, 0xFB, 0x0D, 0x94, 0x81, 0x00}); |
| const DeviceAddress kNonMatchingResolvable(DeviceAddress::Type::kLERandom, |
| {0xA9, 0xFB, 0x0D, 0x94, 0x81, 0x70}); |
| const DeviceAddress kMatchingResolvable(DeviceAddress::Type::kLERandom, |
| {0xAA, 0xFB, 0x0D, 0x94, 0x81, 0x70}); |
| |
| ASSERT_FALSE(kStaticRandom.IsResolvablePrivate()); |
| ASSERT_FALSE(kNonResolvable.IsResolvablePrivate()); |
| ASSERT_TRUE(kNonMatchingResolvable.IsResolvablePrivate()); |
| ASSERT_TRUE(kMatchingResolvable.IsResolvablePrivate()); |
| |
| EXPECT_FALSE(IrkCanResolveRpa(kIRK, kStaticRandom)); |
| EXPECT_FALSE(IrkCanResolveRpa(kIRK, kNonResolvable)); |
| EXPECT_FALSE(IrkCanResolveRpa(kIRK, kNonMatchingResolvable)); |
| EXPECT_TRUE(IrkCanResolveRpa(kIRK, kMatchingResolvable)); |
| } |
| |
| TEST(SMP_UtilTest, GenerateRpa) { |
| const UInt128 irk{ |
| {'s', 'o', 'm', 'e', ' ', 'r', 'a', 'n', 'd', 'o', 'm', ' ', 'd', 'a', 't', 'a'}}; |
| |
| DeviceAddress rpa = GenerateRpa(irk); |
| |
| EXPECT_EQ(DeviceAddress::Type::kLERandom, rpa.type()); |
| EXPECT_TRUE(rpa.IsResolvablePrivate()); |
| |
| // It should be possible to resolve the RPA with the IRK used to generate it. |
| EXPECT_TRUE(IrkCanResolveRpa(irk, rpa)); |
| } |
| |
| TEST(SMP_UtilTest, GenerateRandomAddress) { |
| DeviceAddress addr = GenerateRandomAddress(false); |
| EXPECT_EQ(DeviceAddress::Type::kLERandom, addr.type()); |
| EXPECT_TRUE(addr.IsNonResolvablePrivate()); |
| |
| addr = GenerateRandomAddress(true); |
| EXPECT_EQ(DeviceAddress::Type::kLERandom, addr.type()); |
| EXPECT_TRUE(addr.IsStaticRandom()); |
| } |
| |
| // Using the sample data from Vol 3, Part H, Appendix D.1. |
| TEST(SMP_UtilTest, AesCmac) { |
| const UInt128 key{0x3C, 0x4F, 0xCF, 0x09, 0x88, 0x15, 0xF7, 0xAB, |
| 0xA6, 0xD2, 0xAE, 0x28, 0x16, 0x15, 0x7E, 0x2B}; |
| |
| // D.1.1 Example 1: Len = 0 |
| const BufferView kMsg0; |
| const UInt128 kMsg0ExpectedCmac = {0x46, 0x67, 0x75, 0x9B, 0x12, 0x7D, 0xA3, 0x7F, |
| 0x28, 0x37, 0x59, 0xE9, 0x29, 0x69, 0x1D, 0xBB}; |
| |
| // D.1.2 Example 2: Len = 16 |
| const StaticByteBuffer<16> kMsg16{0x2A, 0x17, 0x93, 0x73, 0x11, 0x7E, 0x3D, 0xE9, |
| 0x96, 0x9F, 0x40, 0x2E, 0xE2, 0xBE, 0xC1, 0x6B}; |
| const UInt128 kMsg16ExpectedCmac{0x7C, 0x28, 0x4A, 0xD0, 0x9D, 0xDD, 0x9B, 0xF7, |
| 0x44, 0x41, 0x4D, 0x6B, 0xB4, 0x16, 0x0A, 0x07}; |
| |
| // D.1.3 Example 3: Len = 40 |
| const StaticByteBuffer<40> kMsg40{0x11, 0xE4, 0x5C, 0xA3, 0x46, 0x1C, 0xC8, 0x30, 0x51, 0x8E, |
| 0xAF, 0x45, 0xAC, 0x6F, 0xB7, 0x9E, 0x9C, 0xAC, 0x03, 0x1E, |
| 0x57, 0x8A, 0x2D, 0xAE, 0x2A, 0x17, 0x93, 0x73, 0x11, 0x7E, |
| 0x3D, 0xE9, 0x96, 0x9F, 0x40, 0x2E, 0xE2, 0xBE, 0xC1, 0x6B}; |
| const UInt128 kMsg40ExpectedCmac{0x27, 0xC8, 0x97, 0x14, 0x61, 0x32, 0xCA, 0x30, |
| 0x30, 0xE6, 0x9A, 0xDE, 0x47, 0x67, 0xA6, 0xDF}; |
| |
| // D.1.4 Example 4: Len = 64 |
| const StaticByteBuffer<64> kMsg64{ |
| 0x10, 0x37, 0x6C, 0xE6, 0x7B, 0x41, 0x2B, 0xAD, 0x17, 0x9B, 0x4F, 0xDF, 0x45, |
| 0x24, 0x9F, 0xF6, 0xEF, 0x52, 0x0A, 0x1A, 0x19, 0xC1, 0xFB, 0xE5, 0x11, 0xE4, |
| 0x5C, 0xA3, 0x46, 0x1C, 0xC8, 0x30, 0x51, 0x8E, 0xAF, 0x45, 0xAC, 0x6F, 0xB7, |
| 0x9E, 0x9C, 0xAC, 0x03, 0x1E, 0x57, 0x8A, 0x2D, 0xAE, 0x2A, 0x17, 0x93, 0x73, |
| 0x11, 0x7E, 0x3D, 0xE9, 0x96, 0x9F, 0x40, 0x2E, 0xE2, 0xBE, 0xC1, 0x6B}; |
| const UInt128 kMsg64ExpectedCmac{0xFE, 0x3C, 0x36, 0x79, 0x17, 0x74, 0x49, 0xFC, |
| 0x92, 0x9D, 0x3B, 0x7E, 0xBF, 0xBE, 0xF0, 0x51}; |
| |
| std::optional<UInt128> cmac_output{}; |
| |
| cmac_output = AesCmac(key, kMsg0); |
| ASSERT_TRUE(cmac_output.has_value()); |
| EXPECT_EQ(kMsg0ExpectedCmac, *cmac_output); |
| |
| cmac_output = AesCmac(key, kMsg16); |
| ASSERT_TRUE(cmac_output.has_value()); |
| EXPECT_EQ(kMsg16ExpectedCmac, *cmac_output); |
| |
| cmac_output = AesCmac(key, kMsg40); |
| ASSERT_TRUE(cmac_output.has_value()); |
| EXPECT_EQ(kMsg40ExpectedCmac, *cmac_output); |
| |
| cmac_output = AesCmac(key, kMsg64); |
| ASSERT_TRUE(cmac_output.has_value()); |
| EXPECT_EQ(kMsg64ExpectedCmac, *cmac_output); |
| } |
| |
| // Using the sample data from Vol 3, Part H, Appendix D.2. |
| TEST(SMP_UtilTest, F4) { |
| const UInt256 kU{0xE6, 0x9D, 0x35, 0x0E, 0x48, 0x01, 0x03, 0xCC, 0xDB, 0xFD, 0xF4, |
| 0xAC, 0x11, 0x91, 0xF4, 0xEF, 0xB9, 0xA5, 0xF9, 0xE9, 0xA7, 0x83, |
| 0x2C, 0x5E, 0x2C, 0xBE, 0x97, 0xF2, 0xD2, 0x03, 0xB0, 0x20}; |
| const UInt256 kV{0xFD, 0xC5, 0x7F, 0xF4, 0x49, 0xDD, 0x4F, 0x6B, 0xFB, 0x7C, 0x9D, |
| 0xF1, 0xC2, 0x9A, 0xCB, 0x59, 0x2A, 0xE7, 0xD4, 0xEE, 0xFB, 0xFC, |
| 0x0A, 0x90, 0x9A, 0xBB, 0xF6, 0x32, 0x3D, 0x8B, 0x18, 0x55}; |
| const UInt128 kX{0xAB, 0xAE, 0x2B, 0x71, 0xEC, 0xB2, 0xFF, 0xFF, |
| 0x3E, 0x73, 0x77, 0xD1, 0x54, 0x84, 0xCB, 0xD5}; |
| const uint8_t kZ = 0x00; |
| const UInt128 kExpectedF4{0x2D, 0x87, 0x74, 0xA9, 0xBE, 0xA1, 0xED, 0xF1, |
| 0x1C, 0xBD, 0xA9, 0x07, 0xF1, 0x16, 0xC9, 0xF2}; |
| |
| std::optional<UInt128> f4_out = F4(kU, kV, kX, kZ); |
| ASSERT_TRUE(f4_out.has_value()); |
| EXPECT_EQ(kExpectedF4, *f4_out); |
| } |
| |
| // Using the sample data from Vol 3, Part H, Appendix D.3. |
| TEST(SMP_UtilTest, F5) { |
| const UInt256 kDhKey{0x98, 0xA6, 0xBF, 0x73, 0xF3, 0x34, 0x8D, 0x86, 0xF1, 0x66, 0xF8, |
| 0xB4, 0x13, 0x6B, 0x79, 0x99, 0x9B, 0x7D, 0x39, 0x0A, 0xA6, 0x10, |
| 0x10, 0x34, 0x05, 0xAD, 0xC8, 0x57, 0xA3, 0x34, 0x02, 0xEC}; |
| const UInt128 kInitiatorNonce{0xAB, 0xAE, 0x2B, 0x71, 0xEC, 0xB2, 0xFF, 0xFF, |
| 0x3E, 0x73, 0x77, 0xD1, 0x54, 0x84, 0xCB, 0xD5}; |
| const UInt128 kResponderNonce{0xCF, 0xC4, 0x3D, 0xFF, 0xF7, 0x83, 0x65, 0x21, |
| 0x6E, 0x5F, 0xA7, 0x25, 0xCC, 0xE7, 0xE8, 0xA6}; |
| const DeviceAddress kInitiatorAddr(DeviceAddress::Type::kLEPublic, |
| {0xCE, 0xBF, 0x37, 0x37, 0x12, 0x56}); |
| const DeviceAddress kResponderAddr(DeviceAddress::Type::kLEPublic, |
| {0xC1, 0xCF, 0x2D, 0x70, 0x13, 0xA7}); |
| const UInt128 kExpectedMacKey{0x20, 0x6E, 0x63, 0xCE, 0x20, 0x6A, 0x3F, 0xFD, |
| 0x02, 0x4A, 0x08, 0xA1, 0x76, 0xF1, 0x65, 0x29}; |
| const UInt128 kExpectedLtk{0x38, 0x0A, 0x75, 0x94, 0xB5, 0x22, 0x05, 0x98, |
| 0x23, 0xCD, 0xD7, 0x69, 0x11, 0x79, 0x86, 0x69}; |
| |
| std::optional<F5Results> results = |
| F5(kDhKey, kInitiatorNonce, kResponderNonce, kInitiatorAddr, kResponderAddr); |
| |
| ASSERT_TRUE(results.has_value()); |
| EXPECT_EQ(kExpectedMacKey, results->mac_key); |
| EXPECT_EQ(kExpectedLtk, results->ltk); |
| } |
| |
| // Using the sample data from Vol 3, Part H, Appendix D.4 |
| TEST(SMP_UtilTest, F6) { |
| const UInt128 kMacKey{0x20, 0x6E, 0x63, 0xCE, 0x20, 0x6A, 0x3F, 0xFD, |
| 0x02, 0x4A, 0x08, 0xA1, 0x76, 0xF1, 0x65, 0x29}; |
| const UInt128 kN1{0xAB, 0xAE, 0x2B, 0x71, 0xEC, 0xB2, 0xFF, 0xFF, |
| 0x3E, 0x73, 0x77, 0xD1, 0x54, 0x84, 0xCB, 0xD5}; |
| const UInt128 kN2{0xCF, 0xC4, 0x3D, 0xFF, 0xF7, 0x83, 0x65, 0x21, |
| 0x6E, 0x5F, 0xA7, 0x25, 0xCC, 0xE7, 0xE8, 0xA6}; |
| const UInt128 kR{0xC8, 0x0F, 0x2D, 0x0C, 0xD2, 0x42, 0xDA, 0x08, |
| 0x54, 0xBB, 0x53, 0xB4, 0x3B, 0x34, 0xA3, 0x12}; |
| const AuthReqField auth_req = 0x01; |
| const auto oob = static_cast<OOBDataFlag>(0x01); |
| const auto io_cap = static_cast<IOCapability>(0x02); |
| const DeviceAddress a1(DeviceAddress::Type::kLEPublic, {0xCE, 0xBF, 0x37, 0x37, 0x12, 0x56}); |
| const DeviceAddress a2(DeviceAddress::Type::kLEPublic, {0xC1, 0xCF, 0x2D, 0x70, 0x13, 0xA7}); |
| const UInt128 kExpectedF6Out{0x61, 0x8F, 0x95, 0xDA, 0x09, 0x0B, 0x6C, 0xD2, |
| 0xC5, 0xE8, 0xD0, 0x9C, 0x98, 0x73, 0xC4, 0xE3}; |
| |
| std::optional<UInt128> f6_out = F6(kMacKey, kN1, kN2, kR, auth_req, oob, io_cap, a1, a2); |
| |
| ASSERT_TRUE(f6_out.has_value()); |
| EXPECT_EQ(kExpectedF6Out, *f6_out); |
| } |
| |
| // Using the sample data from Vol 3, Part H, Appendix D.5 |
| TEST(SMP_UtilTest, G2) { |
| const UInt256 kInitiatorPubKeyX{0xE6, 0x9D, 0x35, 0x0E, 0x48, 0x01, 0x03, 0xCC, 0xDB, 0xFD, 0xF4, |
| 0xAC, 0x11, 0x91, 0xF4, 0xEF, 0xB9, 0xA5, 0xF9, 0xE9, 0xA7, 0x83, |
| 0x2C, 0x5E, 0x2C, 0xBE, 0x97, 0xF2, 0xD2, 0x03, 0xB0, 0x20}; |
| const UInt256 kResponderPubKeyX{0xFD, 0xC5, 0x7F, 0xF4, 0x49, 0xDD, 0x4F, 0x6B, 0xFB, 0x7C, 0x9D, |
| 0xF1, 0xC2, 0x9A, 0xCB, 0x59, 0x2A, 0xE7, 0xD4, 0xEE, 0xFB, 0xFC, |
| 0x0A, 0x90, 0x9A, 0xBB, 0xF6, 0x32, 0x3D, 0x8B, 0x18, 0x55}; |
| const UInt128 kInitiatorNonce{0xAB, 0xAE, 0x2B, 0x71, 0xEC, 0xB2, 0xFF, 0xFF, |
| 0x3E, 0x73, 0x77, 0xD1, 0x54, 0x84, 0xCB, 0xD5}; |
| const UInt128 kResponderNonce{0xCF, 0xC4, 0x3D, 0xFF, 0xF7, 0x83, 0x65, 0x21, |
| 0x6E, 0x5F, 0xA7, 0x25, 0xCC, 0xE7, 0xE8, 0xA6}; |
| const uint32_t kExpectedG2Out = 0x2f9ed5ba; |
| |
| std::optional<uint32_t> g2_out = |
| G2(kInitiatorPubKeyX, kResponderPubKeyX, kInitiatorNonce, kResponderNonce); |
| |
| ASSERT_TRUE(g2_out.has_value()); |
| EXPECT_EQ(kExpectedG2Out, *g2_out); |
| } |
| |
| // Using the sample data from v5.2 Vol. 3, Part H, Appendix D.6 |
| TEST(SMP_UtilTest, H6) { |
| const UInt128 kKeyBytes = {0x9B, 0x7D, 0x39, 0x0A, 0xA6, 0x10, 0x10, 0x34, |
| 0x05, 0xAD, 0xC8, 0x57, 0xA3, 0x34, 0x02, 0xEC}; |
| const uint32_t kKeyId = 0x6c656272; |
| const UInt128 kExpectedH6Out = {0x99, 0x63, 0xB1, 0x80, 0xE2, 0xA9, 0xD3, 0xE8, |
| 0x1C, 0xC9, 0x6D, 0xE7, 0x02, 0xE1, 0x9A, 0x2D}; |
| |
| std::optional<UInt128> h6_out = H6(kKeyBytes, kKeyId); |
| ASSERT_TRUE(h6_out.has_value()); |
| ASSERT_EQ(kExpectedH6Out, *h6_out); |
| } |
| |
| // Using the sample data from v5.2 Vol. 3, Part H, Appendix D.8 |
| TEST(SMP_UtilTest, H7) { |
| const UInt128 kKeyBytes = {0x9B, 0x7D, 0x39, 0x0A, 0xA6, 0x10, 0x10, 0x34, |
| 0x05, 0xAD, 0xC8, 0x57, 0xA3, 0x34, 0x02, 0xEC}; |
| const UInt128 kSalt = {0x31, 0x70, 0x6D, 0x74, 0x00, 0x00, 0x00, 0x00, |
| 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; |
| const UInt128 kExpectedH7Out = {0x11, 0x70, 0xA5, 0x75, 0x2A, 0x8C, 0x99, 0xD2, |
| 0xEC, 0xC0, 0xA3, 0xC6, 0x97, 0x35, 0x17, 0xFB}; |
| |
| std::optional<UInt128> h7_out = H7(kSalt, kKeyBytes); |
| ASSERT_TRUE(h7_out.has_value()); |
| ASSERT_EQ(kExpectedH7Out, *h7_out); |
| } |
| |
| // Using the sample data from v5.2 Vol. 3, Part H, Appendix D.9 |
| TEST(SMP_UtilTest, LtkToLinkKeyUsingH7) { |
| const UInt128 kLtkBytes = {0x64, 0xBF, 0x4F, 0x33, 0x33, 0x6C, 0x06, 0xBD, |
| 0x58, 0x4B, 0x26, 0xE3, 0xBC, 0xF9, 0x8D, 0x36}; |
| const UInt128 kExpectedLinkKeyBytes = {0x35, 0xB8, 0x47, 0x30, 0xF4, 0xF1, 0x39, 0x0A, |
| 0x53, 0x02, 0xA4, 0xDC, 0x79, 0xD3, 0x7A, 0x28}; |
| std::optional<UInt128> out_link_key = |
| LeLtkToBrEdrLinkKey(kLtkBytes, CrossTransportKeyAlgo::kUseH7); |
| |
| ASSERT_TRUE(out_link_key.has_value()); |
| EXPECT_EQ(kExpectedLinkKeyBytes, *out_link_key); |
| } |
| |
| // Using the sample data from v5.2 Vol. 3, Part H, Appendix D.10 |
| TEST(SMP_UtilTest, LtkToLinkKeyUsingH6) { |
| const UInt128 kLtkBytes = {0x64, 0xBF, 0x4F, 0x33, 0x33, 0x6C, 0x06, 0xBD, |
| 0x58, 0x4B, 0x26, 0xE3, 0xBC, 0xF9, 0x8D, 0x36}; |
| const UInt128 kExpectedLinkKeyBytes = {0xB0, 0x8F, 0x38, 0xEE, 0xAF, 0x30, 0x82, 0x0D, |
| 0xBD, 0xC1, 0x3F, 0x63, 0xEF, 0xA4, 0x1C, 0xBC}; |
| std::optional<UInt128> out_link_key = |
| LeLtkToBrEdrLinkKey(kLtkBytes, CrossTransportKeyAlgo::kUseH6); |
| |
| ASSERT_TRUE(out_link_key.has_value()); |
| EXPECT_EQ(kExpectedLinkKeyBytes, *out_link_key); |
| } |
| } // namespace |
| } // namespace bt::sm::util |