blob: 278eb64927ef776f449a62bf7e169cd179ae93bc [file] [log] [blame]
// 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