blob: 0c8701044a2a410da785a708bf05d7bba0be06dd [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 "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/util.h"
#include <array>
#include <cstdint>
#include <gtest/gtest.h>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/device_address.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/uint128.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/uint256.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci-spec/constants.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/error.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/sm/smp.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h"
namespace bt::sm::util {
namespace {
TEST(UtilTest, ConvertSmIoCapabilityToHci) {
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::DISPLAY_ONLY,
IOCapabilityForHci(IOCapability::kDisplayOnly));
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::DISPLAY_YES_NO,
IOCapabilityForHci(IOCapability::kDisplayYesNo));
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::KEYBOARD_ONLY,
IOCapabilityForHci(IOCapability::kKeyboardOnly));
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::NO_INPUT_NO_OUTPUT,
IOCapabilityForHci(IOCapability::kNoInputNoOutput));
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::DISPLAY_YES_NO,
IOCapabilityForHci(IOCapability::kKeyboardDisplay));
// Test remaining invalid values for sm::IOCapability.
for (int i = 0x05; i < 0xff; i++) {
EXPECT_EQ(pw::bluetooth::emboss::IoCapability::NO_INPUT_NO_OUTPUT,
IOCapabilityForHci(static_cast<IOCapability>(i)));
}
}
TEST(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(UtilTest, SelectPairingMethodOOB) {
// In SC OOB is selected if either device has OOB data.
EXPECT_EQ(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/true,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/true,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
EXPECT_NE(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// In legacy OOB is selected if both devices have OOB data.
EXPECT_EQ(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/true,
/*peer_oob=*/true,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
EXPECT_NE(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/true,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
EXPECT_NE(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/true,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
EXPECT_NE(PairingMethod::kOutOfBand,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
}
TEST(UtilTest, SelectPairingMethodNoMITM) {
// The pairing method should be "Just Works" if neither device requires MITM
// protection, regardless of other parameters.
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/false,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Shouldn't default to "Just Works" if at least one device requires MITM
// protection.
EXPECT_NE(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
}
// Tests all combinations that result in the "Just Works" pairing method.
TEST(UtilTest, SelectPairingMethodJustWorks) {
// Local: DisplayOnly
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayOnly,
/*peer_ioc=*/IOCapability::kDisplayOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayOnly,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayOnly,
/*peer_ioc=*/IOCapability::kNoInputNoOutput,
/*local_initiator=*/true));
// Local: DisplayYesNo
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kDisplayOnly,
/*local_initiator=*/true));
// 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(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kNoInputNoOutput,
/*local_initiator=*/true));
// Local: KeyboardOnly
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardOnly,
/*peer_ioc=*/IOCapability::kNoInputNoOutput,
/*local_initiator=*/true));
// Local: NoInputNoOutput. Always "Just Works".
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kNoInputNoOutput,
/*peer_ioc=*/IOCapability::kDisplayOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kNoInputNoOutput,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kNoInputNoOutput,
/*peer_ioc=*/IOCapability::kKeyboardOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kNoInputNoOutput,
/*peer_ioc=*/IOCapability::kNoInputNoOutput,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kNoInputNoOutput,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Local: KeyboardDisplay
EXPECT_EQ(PairingMethod::kJustWorks,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kNoInputNoOutput,
/*local_initiator=*/true));
}
// Tests all combinations that result in the "Passkey Entry (input)" pairing
// method.
TEST(UtilTest, SelectPairingMethodPasskeyEntryInput) {
// Local: KeyboardOnly
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardOnly,
/*peer_ioc=*/IOCapability::kDisplayOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardOnly,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardOnly,
/*peer_ioc=*/IOCapability::kKeyboardOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardOnly,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Local: KeyboardDisplay
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kDisplayOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
// If both devices have the KeyboardDisplay capability then the responder
// inputs.
EXPECT_EQ(PairingMethod::kPasskeyEntryInput,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/false));
}
// Tests all combinations that result in the "Passkey Entry (display)" pairing
// method.
TEST(UtilTest, SelectPairingMethodPasskeyEntryDisplay) {
// Local: DisplayOnly
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayOnly,
/*peer_ioc=*/IOCapability::kKeyboardOnly,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayOnly,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Local: DisplayYesNo
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kKeyboardOnly,
/*local_initiator=*/true));
// If the peer has a display then use "Passkey Entry" only for LE Legacy
// pairing.
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Local: KeyboardDisplay
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardOnly,
/*local_initiator=*/true));
// If both devices have the KeyboardDisplay capability then the initiator
// displays.
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay,
SelectPairingMethod(/*secure_connections=*/false,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
}
// 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(UtilTest, SelectPairingMethodNumericComparison) {
// Local: DisplayYesNo
EXPECT_EQ(PairingMethod::kNumericComparison,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kNumericComparison,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kDisplayYesNo,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
// Local: KeyboardDisplay
EXPECT_EQ(PairingMethod::kNumericComparison,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kDisplayYesNo,
/*local_initiator=*/true));
EXPECT_EQ(PairingMethod::kNumericComparison,
SelectPairingMethod(/*secure_connections=*/true,
/*local_oob=*/false,
/*peer_oob=*/false,
/*mitm_required=*/true,
/*local_ioc=*/IOCapability::kKeyboardDisplay,
/*peer_ioc=*/IOCapability::kKeyboardDisplay,
/*local_initiator=*/true));
}
// Tests "c1" using the sample data from Vol 3, Part H, 2.2.3.
TEST(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 StaticByteBuffer preq(0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07);
const StaticByteBuffer pres(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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