blob: 7f0e00172e131e104aad01cd8cfd95a75c868da6 [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 "bearer.h"
#include "garnet/drivers/bluetooth/lib/common/test_helpers.h"
#include "garnet/drivers/bluetooth/lib/l2cap/fake_channel_test.h"
#include "lib/fxl/macros.h"
namespace btlib {
namespace sm {
namespace {
using common::BufferView;
using common::ByteBuffer;
using common::ContainersEqual;
using common::CreateStaticByteBuffer;
using common::DeviceAddress;
using common::DynamicByteBuffer;
using common::HostError;
using common::StaticByteBuffer;
using common::UInt128;
class SMP_BearerTest : public l2cap::testing::FakeChannelTest,
public Bearer::Listener {
public:
SMP_BearerTest() : weak_ptr_factory_(this) {}
~SMP_BearerTest() override = default;
protected:
void SetUp() override { NewBearer(); }
void TearDown() override { bearer_ = nullptr; }
void NewBearer(
hci::Connection::Role role = hci::Connection::Role::kMaster,
hci::Connection::LinkType ll_type = hci::Connection::LinkType::kLE,
bool sc_supported = false,
IOCapability io_capability = IOCapability::kNoInputNoOutput) {
l2cap::ChannelId cid = ll_type == hci::Connection::LinkType::kLE
? l2cap::kLESMPChannelId
: l2cap::kSMPChannelId;
ChannelOptions options(cid);
options.link_type = ll_type;
fake_chan_ = CreateFakeChannel(options);
bearer_ =
std::make_unique<Bearer>(fake_chan_, role, sc_supported, io_capability,
weak_ptr_factory_.GetWeakPtr());
}
// Bearer::Listener override:
void OnPairingFailed(Status error) override {
pairing_error_count_++;
last_error_ = error;
}
// Bearer::Listener override:
void OnFeatureExchange(const PairingFeatures& features,
const ByteBuffer& preq,
const ByteBuffer& pres) override {
feature_exchange_count_++;
features_ = features;
preq_ = DynamicByteBuffer(preq);
pres_ = DynamicByteBuffer(pres);
}
// Bearer::Listener override:
void OnPairingConfirm(const UInt128& confirm) override {
confirm_value_count_++;
confirm_value_ = confirm;
}
// Bearer::Listener override:
void OnPairingRandom(const UInt128& random) override {
random_value_count_++;
random_value_ = random;
}
// Bearer::Listener override:
void OnLongTermKey(const UInt128& ltk) override {
ltk_count_++;
ltk_ = ltk;
}
// Bearer::Listener override:
void OnMasterIdentification(uint16_t ediv, uint64_t random) override {
master_id_count_++;
ediv_ = ediv;
rand_ = random;
}
// Bearer::Listener override:
void OnIdentityResolvingKey(const UInt128& irk) override {
irk_count_++;
irk_ = irk;
}
// Bearer::Listener override:
void OnIdentityAddress(const DeviceAddress& address) override {
identity_addr_count_++;
identity_addr_ = address;
}
Bearer* bearer() const { return bearer_.get(); }
l2cap::testing::FakeChannel* fake_chan() const { return fake_chan_.get(); }
int pairing_error_count() const { return pairing_error_count_; }
Status last_error() const { return last_error_; }
int feature_exchange_count() const { return feature_exchange_count_; }
const PairingFeatures& features() const { return features_; }
const ByteBuffer& preq() const { return preq_; }
const ByteBuffer& pres() const { return pres_; }
int confirm_value_count() const { return confirm_value_count_; }
int random_value_count() const { return random_value_count_; }
int ltk_count() const { return ltk_count_; }
int master_id_count() const { return master_id_count_; }
int irk_count() const { return irk_count_; }
int identity_addr_count() const { return identity_addr_count_; }
const UInt128& confirm_value() const { return confirm_value_; }
const UInt128& random_value() const { return random_value_; }
const UInt128& ltk() const { return ltk_; }
uint16_t ediv() const { return ediv_; }
uint64_t rand() const { return rand_; }
const UInt128& irk() const { return irk_; }
const DeviceAddress& identity_addr() const { return identity_addr_; }
private:
fbl::RefPtr<l2cap::testing::FakeChannel> fake_chan_;
std::unique_ptr<Bearer> bearer_;
int pairing_error_count_ = 0;
Status last_error_;
int feature_exchange_count_ = 0;
PairingFeatures features_;
DynamicByteBuffer pres_, preq_;
int confirm_value_count_ = 0;
int random_value_count_ = 0;
int ltk_count_ = 0;
int master_id_count_ = 0;
int irk_count_ = 0;
int identity_addr_count_ = 0;
UInt128 confirm_value_;
UInt128 random_value_;
UInt128 ltk_;
uint16_t ediv_ = 0;
uint64_t rand_ = 0;
UInt128 irk_;
DeviceAddress identity_addr_;
fxl::WeakPtrFactory<SMP_BearerTest> weak_ptr_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(SMP_BearerTest);
};
TEST_F(SMP_BearerTest, PacketsWhileIdle) {
int tx_count = 0;
fake_chan()->SetSendCallback([&](auto) { tx_count++; }, dispatcher());
// Packets received while idle should have no side effect.
fake_chan()->Receive(BufferView()); // empty invalid buffer
fake_chan()->Receive(StaticByteBuffer<kLEMTU + 1>()); // exceeds MTU
fake_chan()->Receive(CreateStaticByteBuffer(kPairingFailed));
fake_chan()->Receive(CreateStaticByteBuffer(kPairingResponse));
RunLoopFor(zx::sec(kPairingTimeout));
EXPECT_EQ(0, tx_count);
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
// Abort should have no effect either.
bearer()->Abort(ErrorCode::kPairingNotSupported);
// Unrecognized packets should result in a PairingFailed packet.
fake_chan()->Receive(CreateStaticByteBuffer(0xFF));
RunLoopUntilIdle();
EXPECT_EQ(1, tx_count);
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
}
TEST_F(SMP_BearerTest, FeatureExchangeErrorSlave) {
NewBearer(hci::Connection::Role::kSlave);
EXPECT_FALSE(bearer()->InitiateFeatureExchange());
}
TEST_F(SMP_BearerTest, FeatureExchangeStartDefaultParams) {
// clang-format off
const auto kExpected = CreateStaticByteBuffer(
0x01, // code: "Pairing Request"
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
// clang-format on
int tx_count = 0;
fake_chan()->SetSendCallback(
[&](auto pdu) {
tx_count++;
EXPECT_TRUE(ContainersEqual(kExpected, *pdu));
},
dispatcher());
EXPECT_TRUE(bearer()->InitiateFeatureExchange());
RunLoopUntilIdle();
EXPECT_EQ(1, tx_count);
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_FALSE(bearer()->InitiateFeatureExchange());
}
TEST_F(SMP_BearerTest, FeatureExchangeStartCustomParams) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kLE,
true /* sc_supported */, IOCapability::kDisplayYesNo);
bearer()->set_oob_available(true);
bearer()->set_mitm_required(true);
// clang-format off
const auto kExpected = CreateStaticByteBuffer(
0x01, // code: "Pairing Request"
0x01, // IO cap.: DisplayYesNo
0x01, // OOB: present
0b00001101, // AuthReq: Bonding, SC, MITM
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
// clang-format on
int tx_count = 0;
fake_chan()->SetSendCallback(
[&](auto pdu) {
tx_count++;
EXPECT_TRUE(ContainersEqual(kExpected, *pdu));
},
dispatcher());
EXPECT_TRUE(bearer()->InitiateFeatureExchange());
RunLoopUntilIdle();
EXPECT_EQ(1, tx_count);
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_FALSE(bearer()->InitiateFeatureExchange());
}
TEST_F(SMP_BearerTest, FeatureExchangeTimeout) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
RunLoopFor(zx::sec(kPairingTimeout));
EXPECT_EQ(HostError::kTimedOut, last_error().error());
EXPECT_TRUE(fake_chan()->link_error());
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(1, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
}
TEST_F(SMP_BearerTest, Abort) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
bearer()->Abort(ErrorCode::kPairingNotSupported);
EXPECT_EQ(ErrorCode::kPairingNotSupported, last_error().protocol_error());
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_FALSE(fake_chan()->link_error());
EXPECT_EQ(1, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
// Timer should have stopped.
RunLoopFor(zx::sec(kPairingTimeout));
EXPECT_EQ(1, pairing_error_count());
}
TEST_F(SMP_BearerTest, FeatureExchangePairingFailed) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
fake_chan()->Receive(CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x05 // reason: Pairing Not Supported
));
RunLoopUntilIdle();
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(1, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
EXPECT_EQ(ErrorCode::kPairingNotSupported, last_error().protocol_error());
}
// Pairing should fail if MITM is required but the pairing method cannot provide
// it (due to I/O capabilities).
TEST_F(SMP_BearerTest, FeatureExchangeFailureAuthenticationRequirements) {
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x04, // AuthReq: MITM required
0x07, // encr. key size: 7 (default min)
0x01, // initiator keys: enc key only
0x01 // responder keys: enc key only
);
const auto kFailure = CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x03 // reason: Authentication requirements
);
// clang-format on
// Initiate the request in a loop task for Expect to detect it.
async::PostTask(dispatcher(),
[this] { bearer()->InitiateFeatureExchange(); });
ASSERT_TRUE(Expect(kRequest));
ASSERT_TRUE(bearer()->pairing_started());
// We should receive a pairing response and reply back with Pairing Failed.
EXPECT_TRUE(ReceiveAndExpect(kResponse, kFailure));
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(1, pairing_error_count());
EXPECT_TRUE(last_error().is_protocol_error());
EXPECT_EQ(ErrorCode::kAuthenticationRequirements,
last_error().protocol_error());
EXPECT_EQ(0, feature_exchange_count());
}
TEST_F(SMP_BearerTest, FeatureExchangePairingResponseJustWorks) {
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, MITM not required
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x00, // AuthReq: MITM not required
0x07, // encr. key size: 7 (default min)
0x01, // initiator keys: enc key only
0x01 // responder keys: enc key only
);
// clang-format on
// Initiate the request in a loop task for Expect to detect it.
async::PostTask(dispatcher(),
[this] { bearer()->InitiateFeatureExchange(); });
ASSERT_TRUE(Expect(kRequest));
ASSERT_TRUE(bearer()->pairing_started());
fake_chan()->Receive(kResponse);
RunLoopUntilIdle();
// Pairing should continue until explicitly stopped.
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(1, feature_exchange_count());
EXPECT_TRUE(features().initiator);
EXPECT_FALSE(features().secure_connections);
EXPECT_EQ(PairingMethod::kJustWorks, features().method);
EXPECT_EQ(7, features().encryption_key_size);
EXPECT_TRUE(KeyDistGen::kEncKey & features().local_key_distribution);
EXPECT_TRUE(KeyDistGen::kEncKey & features().remote_key_distribution);
EXPECT_TRUE(ContainersEqual(kRequest, preq()));
EXPECT_TRUE(ContainersEqual(kResponse, pres()));
}
// One of the devices requires MITM protection and the I/O capabilities can
// provide it.
TEST_F(SMP_BearerTest, FeatureExchangePairingResponseMITM) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kLE,
false /* sc_supported */, IOCapability::kDisplayYesNo);
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Request
0x01, // IO cap.: DisplayYesNo
0x00, // OOB: not present
0x01, // AuthReq: bonding, MITM not required
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x02, // IO cap.: KeyboardOnly
0x00, // OOB: not present
0x04, // AuthReq: MITM required
0x07, // encr. key size: 7 (default min)
0x01, // initiator keys: enc key only
0x01 // responder keys: enc key only
);
// clang-format on
// Initiate the request in a loop task for Expect to detect it.
async::PostTask(dispatcher(),
[this] { bearer()->InitiateFeatureExchange(); });
ASSERT_TRUE(Expect(kRequest));
ASSERT_TRUE(bearer()->pairing_started());
fake_chan()->Receive(kResponse);
RunLoopUntilIdle();
// Pairing should continue until explicitly stopped.
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(1, feature_exchange_count());
EXPECT_TRUE(features().initiator);
EXPECT_FALSE(features().secure_connections);
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay, features().method);
EXPECT_EQ(7, features().encryption_key_size);
EXPECT_TRUE(KeyDistGen::kEncKey & features().local_key_distribution);
EXPECT_TRUE(KeyDistGen::kEncKey & features().remote_key_distribution);
EXPECT_TRUE(ContainersEqual(kRequest, preq()));
EXPECT_TRUE(ContainersEqual(kResponse, pres()));
}
TEST_F(SMP_BearerTest, FeatureExchangeEncryptionKeySize) {
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM
0x10, // encr. key size: 16 (default max)
0x00, // initiator keys: none
0x03 // responder keys: enc key and identity info
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x04, // AuthReq: MITM required
0x02, // encr. key size: 2 (too small)
0x01, // initiator keys: enc key only
0x01 // responder keys: enc key only
);
const auto kFailure =
CreateStaticByteBuffer(0x05, // code: Pairing Failed
0x06 // reason: Encryption Key Size
);
// clang-format on
// Initiate the request in a loop task for Expect to detect it.
async::PostTask(dispatcher(), [this] {
bearer()->InitiateFeatureExchange();
EXPECT_TRUE(bearer()->pairing_started());
});
ASSERT_TRUE(Expect(kRequest));
ASSERT_TRUE(bearer()->pairing_started());
// We should receive a pairing response and reply back with Pairing Failed.
EXPECT_TRUE(ReceiveAndExpect(kResponse, kFailure));
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(1, pairing_error_count());
EXPECT_EQ(0, feature_exchange_count());
EXPECT_EQ(ErrorCode::kEncryptionKeySize, last_error().protocol_error());
}
TEST_F(SMP_BearerTest, FeatureExchangeResponderErrorMaster) {
const auto kRequest =
CreateStaticByteBuffer(0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x00, // AuthReq: no auth. request by default
0x10, // encr. key size: 16 (default max)
0x01, // initiator key dist.: encr. key only
0x01 // responder key dist.: encr. key only
);
const auto kFailure =
CreateStaticByteBuffer(0x05, // code: Pairing Failed
0x07 // reason: Command Not Supported
);
NewBearer(hci::Connection::Role::kMaster);
EXPECT_TRUE(ReceiveAndExpect(kRequest, kFailure));
EXPECT_FALSE(bearer()->pairing_started());
}
TEST_F(SMP_BearerTest, FeatureExchangeResponderMalformedRequest) {
const auto kMalformedRequest =
CreateStaticByteBuffer(0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x00, // AuthReq: no auth. request by default
0x10, // encr. key size: 16 (default max)
0x01 // initiator key dist.: encr. key only
// Missing last byte
);
const auto kFailure =
CreateStaticByteBuffer(0x05, // code: Pairing Failed
0x0A // reason: Invalid Parameters
);
NewBearer(hci::Connection::Role::kMaster);
EXPECT_TRUE(ReceiveAndExpect(kMalformedRequest, kFailure));
EXPECT_FALSE(bearer()->pairing_started());
}
// Tests that the pairing timer gets reset when a second Pairing Request is
// received.
TEST_F(SMP_BearerTest, FeatureExchangeResponderTimerRestarted) {
NewBearer(hci::Connection::Role::kSlave);
constexpr uint64_t kThresholdSeconds = 1;
const auto kRequest =
CreateStaticByteBuffer(0x01, // code: Pairing Request
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x00, // AuthReq: no auth. request by default
0x10, // encr. key size: 16 (default max)
0x01, // initiator key dist.: encr. key only
0x01 // responder key dist.: encr. key only
);
fake_chan()->Receive(kRequest);
RunLoopUntilIdle();
ASSERT_TRUE(bearer()->pairing_started());
// Advance the time to 1 second behind the end of the pairing timeout.
RunLoopFor(zx::sec(kPairingTimeout - kThresholdSeconds));
// The timer should not have expired.
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_FALSE(fake_chan()->link_error());
EXPECT_EQ(0, pairing_error_count());
EXPECT_TRUE(last_error().is_success());
// Send a second request which should restart the timer.
fake_chan()->Receive(kRequest);
RunLoopUntilIdle();
EXPECT_TRUE(bearer()->pairing_started());
// The old timeout should not expire when advance to 1 second behind the new
// timeout.
RunLoopFor(zx::sec(kPairingTimeout - kThresholdSeconds));
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_EQ(0, pairing_error_count());
RunLoopFor(zx::sec(kThresholdSeconds));
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(1, pairing_error_count());
EXPECT_EQ(HostError::kTimedOut, last_error().error());
}
// Pairing should fail if MITM is required but the pairing method cannot provide
// it (due to I/O capabilities).
TEST_F(SMP_BearerTest,
FeatureExchangeResponderFailedAuthenticationRequirements) {
NewBearer(hci::Connection::Role::kSlave);
const auto kRequest =
CreateStaticByteBuffer(0x01, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x04, // AuthReq: MITM required
0x07, // encr. key size: 7 (default min)
0x01, // initiator key dist.: encr. key only
0x01 // responder key dist.: encr. key only
);
const auto kFailure =
CreateStaticByteBuffer(0x05, // code: Pairing Failed
0x03 // reason: Authentication requirements
);
EXPECT_TRUE(ReceiveAndExpect(kRequest, kFailure));
EXPECT_EQ(1, pairing_error_count());
EXPECT_FALSE(bearer()->pairing_started());
EXPECT_EQ(ErrorCode::kAuthenticationRequirements,
last_error().protocol_error());
}
TEST_F(SMP_BearerTest, FeatureExchangeResponderJustWorks) {
NewBearer(hci::Connection::Role::kSlave);
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x00, // AuthReq: MITM not required
0x07, // encr. key size: 7 (default min)
0x02, // initiator keys: identity only
0x03 // responder keys: enc key and identity
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x03, // IO cap.: NoInputNoOutput
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM
0x10, // encr. key size: 16 (default max)
0x02, // initiator keys: identity only
0x00 // responder keys: none
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kRequest, kResponse));
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(1, feature_exchange_count());
EXPECT_FALSE(features().initiator);
EXPECT_FALSE(features().secure_connections);
EXPECT_EQ(PairingMethod::kJustWorks, features().method);
EXPECT_EQ(7, features().encryption_key_size);
EXPECT_TRUE(ContainersEqual(kRequest, preq()));
EXPECT_TRUE(ContainersEqual(kResponse, pres()));
// We don't support generating local keys yet.
EXPECT_EQ(0u, features().local_key_distribution);
// The remote should send us identity information since we requested it and it
// promised it.
EXPECT_TRUE(KeyDistGen::kIdKey & features().remote_key_distribution);
}
TEST_F(SMP_BearerTest, FeatureExchangeResponderMITM) {
NewBearer(hci::Connection::Role::kSlave, hci::Connection::LinkType::kLE,
false /* sc_supported */, IOCapability::kDisplayYesNo);
// clang-format off
const auto kRequest = CreateStaticByteBuffer(
0x01, // code: Pairing Request
0x02, // IO cap.: KeyboardOnly
0x00, // OOB: not present
0x04, // AuthReq: MITM required
0x07, // encr. key size: 7 (default min)
0x02, // initiator keys: identity only
0x03 // responder keys: enc key and identity
);
const auto kResponse = CreateStaticByteBuffer(
0x02, // code: Pairing Response
0x01, // IO cap.: DisplayYesNo
0x00, // OOB: not present
0x01, // AuthReq: bonding, no MITM
0x10, // encr. key size: 16 (default max)
0x02, // initiator keys: identity only
0x00 // responder keys: none
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kRequest, kResponse));
EXPECT_TRUE(bearer()->pairing_started());
EXPECT_EQ(0, pairing_error_count());
EXPECT_EQ(1, feature_exchange_count());
EXPECT_FALSE(features().initiator);
EXPECT_FALSE(features().secure_connections);
EXPECT_EQ(PairingMethod::kPasskeyEntryDisplay, features().method);
EXPECT_EQ(7, features().encryption_key_size);
EXPECT_TRUE(ContainersEqual(kRequest, preq()));
EXPECT_TRUE(ContainersEqual(kResponse, pres()));
// We don't support generating local keys yet.
EXPECT_EQ(0u, features().local_key_distribution);
// The remote should send us identity information since we requested it and it
// promised it.
EXPECT_TRUE(KeyDistGen::kIdKey & features().remote_key_distribution);
}
TEST_F(SMP_BearerTest, UnsupportedCommandDuringPairing) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
const auto kExpected =
CreateStaticByteBuffer(0x05, // code: Pairing Failed
0x07 // reason: Command Not Supported
);
ReceiveAndExpect(CreateStaticByteBuffer(0xFF), kExpected);
EXPECT_FALSE(bearer()->pairing_started());
}
TEST_F(SMP_BearerTest, StopTimer) {
const auto kResponse =
CreateStaticByteBuffer(0x02, // code: Pairing Response
0x00, // IO cap.: DisplayOnly
0x00, // OOB: not present
0x00, // AuthReq: MITM not required
0x07, // encr. key size: 7 (default min)
0x01, // initiator key dist.: encr. key only
0x01 // responder key dist.: encr. key only
);
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
fake_chan()->Receive(kResponse);
RunLoopUntilIdle();
// Pairing should continue until explicitly stopped.
EXPECT_TRUE(bearer()->pairing_started());
bearer()->StopTimer();
EXPECT_FALSE(bearer()->pairing_started());
RunLoopFor(zx::sec(kPairingTimeout));
EXPECT_EQ(0, pairing_error_count());
}
TEST_F(SMP_BearerTest, SendConfirmValueNotPairing) {
ASSERT_FALSE(bearer()->pairing_started());
UInt128 confirm;
EXPECT_FALSE(bearer()->SendConfirmValue(confirm));
}
TEST_F(SMP_BearerTest, SendConfirmValueNotLE) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kACL,
false /* sc_supported */, IOCapability::kDisplayYesNo);
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 confirm;
EXPECT_FALSE(bearer()->SendConfirmValue(confirm));
}
TEST_F(SMP_BearerTest, SendConfirmValue) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 confirm{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
// Initiate the request in a loop task so it runs after the call to Expect
// below.
async::PostTask(dispatcher(),
[&] { EXPECT_TRUE(bearer()->SendConfirmValue(confirm)); });
// clang-format off
EXPECT_TRUE(Expect(CreateStaticByteBuffer(
// code: Pairing Confirm
0x03,
// Confirm value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
)));
// clang-format on
}
TEST_F(SMP_BearerTest, OnPairingConfirmNotPairing) {
// clang-format off
const auto kPairingConfirm = CreateStaticByteBuffer(
// code: Pairing Confirm
0x03,
// Confirm value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kPairingConfirm);
RunLoopUntilIdle();
EXPECT_EQ(0, confirm_value_count());
}
TEST_F(SMP_BearerTest, OnPairingConfirmNotLE) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kACL,
false /* sc_supported */, IOCapability::kDisplayYesNo);
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kPairingConfirm = CreateStaticByteBuffer(
// code: Pairing Confirm
0x03,
// Confirm value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
const auto kPairingFailed = CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x07 // reason: Command Not Supported
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kPairingConfirm, kPairingFailed));
EXPECT_EQ(0, confirm_value_count());
}
TEST_F(SMP_BearerTest, OnPairingConfirmMalformed) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kMalformedPairingConfirm = CreateStaticByteBuffer(
// code: Pairing Confirm
0x03,
// Confirm value (1 octet too short)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
const auto kPairingFailed = CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x0A // reason: Invalid Parameters
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kMalformedPairingConfirm, kPairingFailed));
EXPECT_EQ(0, confirm_value_count());
}
TEST_F(SMP_BearerTest, OnPairingConfirmCallback) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 kExpected{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
// clang-format off
const auto kPairingConfirm = CreateStaticByteBuffer(
// code: Pairing Confirm
0x03,
// Confirm value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kPairingConfirm);
RunLoopUntilIdle();
EXPECT_EQ(1, confirm_value_count());
EXPECT_EQ(kExpected, confirm_value());
}
TEST_F(SMP_BearerTest, SendRandomValueNotPairing) {
ASSERT_FALSE(bearer()->pairing_started());
UInt128 rand;
EXPECT_FALSE(bearer()->SendRandomValue(rand));
}
TEST_F(SMP_BearerTest, SendRandomValueNotLE) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kACL,
false /* sc_supported */, IOCapability::kDisplayYesNo);
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 rand;
EXPECT_FALSE(bearer()->SendRandomValue(rand));
}
TEST_F(SMP_BearerTest, SendRandomValue) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 rand{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
// Initiate the request in a loop task so it runs after the call to Expect
// below.
async::PostTask(dispatcher(),
[&] { EXPECT_TRUE(bearer()->SendRandomValue(rand)); });
// clang-format off
EXPECT_TRUE(Expect(CreateStaticByteBuffer(
// code: Pairing Random
0x04,
// Confirm value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
)));
// clang-format on
}
TEST_F(SMP_BearerTest, OnPairingRandomNotPairing) {
// clang-format off
const auto kPairingRandom = CreateStaticByteBuffer(
// code: Pairing Random
0x04,
// Random value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kPairingRandom);
RunLoopUntilIdle();
EXPECT_EQ(0, random_value_count());
}
TEST_F(SMP_BearerTest, OnPairingRandomNotLE) {
NewBearer(hci::Connection::Role::kMaster, hci::Connection::LinkType::kACL,
false /* sc_supported */, IOCapability::kDisplayYesNo);
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kPairingRandom = CreateStaticByteBuffer(
// code: Pairing Random
0x04,
// Random value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
const auto kPairingFailed = CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x07 // reason: Command Not Supported
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kPairingRandom, kPairingFailed));
EXPECT_EQ(0, random_value_count());
}
TEST_F(SMP_BearerTest, OnPairingRandomMalformed) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kMalformedPairingRandom = CreateStaticByteBuffer(
// code: Pairing Random
0x04,
// Random value (1 octet too short)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
const auto kPairingFailed = CreateStaticByteBuffer(
0x05, // code: Pairing Failed
0x0A // reason: Invalid Parameters
);
// clang-format on
EXPECT_TRUE(ReceiveAndExpect(kMalformedPairingRandom, kPairingFailed));
EXPECT_EQ(0, random_value_count());
}
TEST_F(SMP_BearerTest, OnPairingRandomCallback) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 kExpected{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
// clang-format off
const auto kPairingRandom = CreateStaticByteBuffer(
// code: Pairing Random
0x04,
// Random value
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kPairingRandom);
RunLoopUntilIdle();
EXPECT_EQ(1, random_value_count());
EXPECT_EQ(kExpected, random_value());
}
TEST_F(SMP_BearerTest, OnIdentityInformationNotPairing) {
// clang-format off
const auto kIdentityInfo = CreateStaticByteBuffer(
// code: Identity Information
0x08,
// IRK
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kIdentityInfo);
RunLoopUntilIdle();
EXPECT_EQ(0, irk_count());
}
TEST_F(SMP_BearerTest, OnIdentityInformationMalformed) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kIdentityInfo = CreateStaticByteBuffer(
// code: Identity Information
0x08,
// IRK (1 octet too short)
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15
);
// clang-format on
fake_chan()->Receive(kIdentityInfo);
RunLoopUntilIdle();
EXPECT_EQ(0, irk_count());
}
TEST_F(SMP_BearerTest, OnIdentityInformationCallback) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
UInt128 kExpected{{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}};
// clang-format off
const auto kIdentityInfo = CreateStaticByteBuffer(
// code: Identity Information
0x08,
// IRK
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
);
// clang-format on
fake_chan()->Receive(kIdentityInfo);
RunLoopUntilIdle();
EXPECT_EQ(kExpected, irk());
}
TEST_F(SMP_BearerTest, OnIdentityAddressInformationNotPairing) {
// clang-format off
const auto kIdentityAddr = CreateStaticByteBuffer(
0x09, // code: Identity Address Information
0x00, // type: public
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF // BD_ADDR
);
// clang-format on
fake_chan()->Receive(kIdentityAddr);
RunLoopUntilIdle();
EXPECT_EQ(0, identity_addr_count());
}
TEST_F(SMP_BearerTest, OnIdentityAddressInformationMalformed) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
// clang-format off
const auto kIdentityAddr = CreateStaticByteBuffer(
0x09, // code: Identity Address Information
0x00, // type: public
0xAA, 0xBB, 0xCC, 0xDD, 0xEE // BD_ADDR (1 byte too short)
);
// clang-format on
fake_chan()->Receive(kIdentityAddr);
RunLoopUntilIdle();
EXPECT_EQ(0, identity_addr_count());
}
TEST_F(SMP_BearerTest, OnIdentityAddressInformationCallbackPublic) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
DeviceAddress kExpected(DeviceAddress::Type::kLEPublic, "FF:EE:DD:CC:BB:AA");
// clang-format off
const auto kIdentityAddr = CreateStaticByteBuffer(
0x09, // code: Identity Address Information
0x00, // type: public
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF // BD_ADDR
);
// clang-format on
fake_chan()->Receive(kIdentityAddr);
RunLoopUntilIdle();
EXPECT_EQ(1, identity_addr_count());
EXPECT_EQ(kExpected, identity_addr());
}
TEST_F(SMP_BearerTest, OnIdentityAddressInformationCallbackRandom) {
bearer()->InitiateFeatureExchange();
ASSERT_TRUE(bearer()->pairing_started());
DeviceAddress kExpected(DeviceAddress::Type::kLERandom, "FF:EE:DD:CC:BB:AA");
// clang-format off
const auto kIdentityAddr = CreateStaticByteBuffer(
0x09, // code: Identity Address Information
0x01, // type: random
0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF // BD_ADDR
);
// clang-format on
fake_chan()->Receive(kIdentityAddr);
RunLoopUntilIdle();
EXPECT_EQ(1, identity_addr_count());
EXPECT_EQ(kExpected, identity_addr());
}
} // namespace
} // namespace sm
} // namespace btlib