blob: a614ef6811c07a24123e3bc38ec2e13b25bd9e27 [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 "pairing_state.h"
#include "src/connectivity/bluetooth/core/bt-host/common/log.h"
#include "src/connectivity/bluetooth/core/bt-host/common/random.h"
#include "util.h"
namespace bt {
using common::ByteBuffer;
using common::DeviceAddress;
using common::HostError;
using common::MutableBufferView;
using common::UInt128;
namespace sm {
namespace {
SecurityProperties FeaturesToProperties(const PairingFeatures& features) {
return SecurityProperties(features.method == PairingMethod::kJustWorks
? SecurityLevel::kEncrypted
: SecurityLevel::kAuthenticated,
features.encryption_key_size,
features.secure_connections);
}
} // namespace
PairingState::LegacyState::LegacyState(uint64_t id)
: id(id),
stk_encrypted(false),
obtained_remote_keys(0u),
sent_local_keys(false),
has_tk(false),
has_peer_confirm(false),
has_peer_rand(false),
sent_local_confirm(false),
sent_local_rand(false),
has_ltk(false),
has_irk(false) {}
bool PairingState::LegacyState::InPhase1() const {
return !features && !stk_encrypted;
}
bool PairingState::LegacyState::InPhase2() const {
return features && has_tk && !stk_encrypted;
}
bool PairingState::LegacyState::InPhase3() const {
return features && stk_encrypted && !KeyExchangeComplete();
}
bool PairingState::LegacyState::IsComplete() const {
return features && stk_encrypted && KeyExchangeComplete();
}
bool PairingState::LegacyState::WaitingForTK() const {
return features && !has_tk && !stk_encrypted;
}
bool PairingState::LegacyState::RequestedKeysObtained() const {
ZX_DEBUG_ASSERT(features);
// Return true if we expect no keys from the remote.
return !features->remote_key_distribution ||
(features->remote_key_distribution == obtained_remote_keys);
}
bool PairingState::LegacyState::LocalKeysSent() const {
ZX_DEBUG_ASSERT(features);
// Return true if we didn't agree to send any keys.
return !features->local_key_distribution || sent_local_keys;
}
bool PairingState::LegacyState::ShouldReceiveLTK() const {
ZX_DEBUG_ASSERT(features);
return (features->remote_key_distribution & KeyDistGen::kEncKey);
}
bool PairingState::LegacyState::ShouldReceiveIdentity() const {
ZX_DEBUG_ASSERT(features);
return (features->remote_key_distribution & KeyDistGen::kIdKey);
}
bool PairingState::LegacyState::ShouldSendLTK() const {
ZX_DEBUG_ASSERT(features);
return (features->local_key_distribution & KeyDistGen::kEncKey);
}
bool PairingState::LegacyState::ShouldSendIdentity() const {
ZX_DEBUG_ASSERT(features);
return (features->local_key_distribution & KeyDistGen::kIdKey);
}
PairingState::PendingRequest::PendingRequest(SecurityLevel level,
PairingCallback callback)
: level(level), callback(std::move(callback)) {}
PairingState::PairingState(fxl::WeakPtr<hci::Connection> link,
fbl::RefPtr<l2cap::Channel> smp,
IOCapability io_capability,
fxl::WeakPtr<Delegate> delegate)
: next_pairing_id_(0),
delegate_(delegate),
le_link_(link),
weak_ptr_factory_(this) {
ZX_DEBUG_ASSERT(delegate_);
ZX_DEBUG_ASSERT(le_link_);
ZX_DEBUG_ASSERT(smp);
ZX_DEBUG_ASSERT(link->handle() == smp->link_handle());
ZX_DEBUG_ASSERT(link->ll_type() == hci::Connection::LinkType::kLE);
ZX_DEBUG_ASSERT(smp->id() == l2cap::kLESMPChannelId);
// Set up SMP data bearer.
// TODO(armansito): Enable SC when we support it.
le_smp_ =
std::make_unique<Bearer>(std::move(smp), link->role(), false /* sc */,
io_capability, weak_ptr_factory_.GetWeakPtr());
// Set up HCI encryption event.
le_link_->set_encryption_change_callback(
fit::bind_member(this, &PairingState::OnEncryptionChange));
}
PairingState::~PairingState() {
if (le_link_) {
le_link_->set_encryption_change_callback({});
}
}
void PairingState::Reset(IOCapability io_capability) {
Abort();
le_smp_->set_io_capability(io_capability);
}
bool PairingState::AssignLongTermKey(const LTK& ltk) {
if (legacy_state_) {
bt_log(TRACE, "sm",
"Cannot directly assign LTK while pairing is in progress");
return false;
}
AssignLongTermKeyInternal(ltk);
// Try to initiate encryption if we are the master.
if (le_link_->role() == hci::Connection::Role::kMaster &&
!le_link_->StartEncryption()) {
bt_log(ERROR, "sm", "Failed to initiate authentication procedure");
return false;
}
return true;
}
void PairingState::UpgradeSecurity(SecurityLevel level,
PairingCallback callback) {
// If pairing is in progress then we queue the request.
if (legacy_state_) {
bt_log(SPEW, "sm", "LE legacy pairing in progress; request queued");
ZX_DEBUG_ASSERT(le_smp_->pairing_started());
request_queue_.emplace(level, std::move(callback));
return;
}
if (level <= le_sec_.level()) {
callback(Status(), le_sec_);
return;
}
// TODO(armansito): Support initiating a security upgrade using the SMP
// Security Request.
if (le_smp_->role() != hci::Connection::Role::kMaster) {
callback(Status(HostError::kNotSupported), SecurityProperties());
return;
}
request_queue_.emplace(level, std::move(callback));
BeginLegacyPairingPhase1(level);
}
void PairingState::SetSecurityProperties(const SecurityProperties& sec) {
if (sec != le_sec_) {
bt_log(TRACE, "sm",
"security properties changed - handle: %#.4x, new: %s, old: %s",
le_link_->handle(), sec.ToString().c_str(),
le_sec_.ToString().c_str());
le_sec_ = sec;
delegate_->OnNewSecurityProperties(le_sec_);
}
}
void PairingState::AbortLegacyPairing(ErrorCode error_code) {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(le_smp_->pairing_started());
le_smp_->Abort(error_code);
// "Abort" should trigger OnPairingFailed.
}
void PairingState::BeginLegacyPairingPhase1(SecurityLevel level) {
ZX_DEBUG_ASSERT(le_smp_->role() == hci::Connection::Role::kMaster);
ZX_DEBUG_ASSERT_MSG(!legacy_state_, "already pairing!");
if (level == SecurityLevel::kAuthenticated) {
le_smp_->set_mitm_required(true);
}
legacy_state_ = std::make_unique<LegacyState>(next_pairing_id_++);
le_smp_->InitiateFeatureExchange();
}
void PairingState::Abort() {
if (!le_smp_->pairing_started())
return;
bt_log(TRACE, "sm", "abort pairing");
if (legacy_state_) {
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
}
}
void PairingState::BeginLegacyPairingPhase2(const ByteBuffer& preq,
const ByteBuffer& pres) {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->WaitingForTK());
ZX_DEBUG_ASSERT(!legacy_state_->features->secure_connections);
ZX_DEBUG_ASSERT(!legacy_state_->has_tk);
ZX_DEBUG_ASSERT(!legacy_state_->has_peer_confirm);
ZX_DEBUG_ASSERT(!legacy_state_->has_peer_rand);
ZX_DEBUG_ASSERT(!legacy_state_->sent_local_confirm);
ZX_DEBUG_ASSERT(!legacy_state_->sent_local_rand);
// Cache |preq| and |pres|. These are used for confirm value generation.
ZX_DEBUG_ASSERT(preq.size() == legacy_state_->preq.size());
ZX_DEBUG_ASSERT(pres.size() == legacy_state_->pres.size());
preq.Copy(&legacy_state_->preq);
pres.Copy(&legacy_state_->pres);
auto self = weak_ptr_factory_.GetWeakPtr();
auto tk_callback = [self, id = legacy_state_->id](bool success, uint32_t tk) {
if (!self) {
return;
}
auto* state = self->legacy_state_.get();
if (!state || id != state->id) {
bt_log(TRACE, "sm",
"ignoring TK callback for expired pairing: (id = %lu)", id);
return;
}
if (!success) {
bt_log(TRACE, "sm", "TK delegate responded with error; aborting");
if (state->features->method == PairingMethod::kPasskeyEntryInput) {
self->AbortLegacyPairing(ErrorCode::kPasskeyEntryFailed);
} else {
self->AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
}
return;
}
ZX_DEBUG_ASSERT(state->WaitingForTK());
// Set the lower bits to |tk|.
tk = htole32(tk);
state->tk.fill(0);
std::memcpy(state->tk.data(), &tk, sizeof(tk));
state->has_tk = true;
ZX_DEBUG_ASSERT(state->InPhase2());
// We have TK so we can generate the confirm value now.
const DeviceAddress *ia, *ra;
self->LEPairingAddresses(&ia, &ra);
state->local_rand = common::RandomUInt128();
util::C1(state->tk, state->local_rand, state->preq, state->pres, *ia, *ra,
&state->local_confirm);
// If we are the initiator then we just generated the "Mconfirm" value. We
// start the exchange by sending this value to the peer. Otherwise this is
// the "Sconfirm" value and we either:
// a. send it now if the peer has sent us its confirm value while we were
// waiting for the TK.
// b. send it later when we receive Mconfirm.
if (state->features->initiator || state->has_peer_confirm) {
self->LegacySendConfirmValue();
}
};
ZX_DEBUG_ASSERT(delegate_);
delegate_->OnTemporaryKeyRequest(legacy_state_->features->method,
std::move(tk_callback));
}
void PairingState::LegacySendConfirmValue() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->InPhase2());
ZX_DEBUG_ASSERT(!legacy_state_->sent_local_confirm);
legacy_state_->sent_local_confirm = true;
le_smp_->SendConfirmValue(legacy_state_->local_confirm);
}
void PairingState::LegacySendRandomValue() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->InPhase2());
ZX_DEBUG_ASSERT(!legacy_state_->sent_local_rand);
legacy_state_->sent_local_rand = true;
le_smp_->SendRandomValue(legacy_state_->local_rand);
}
void PairingState::EndLegacyPairingPhase2() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->InPhase2());
// Update the current security level. Even though the link is encrypted with
// the STK (i.e. a temporary key) it provides a level of security.
legacy_state_->stk_encrypted = true;
SetSecurityProperties(FeaturesToProperties(*legacy_state_->features));
if (legacy_state_->InPhase3()) {
if (legacy_state_->features->initiator &&
!legacy_state_->RequestedKeysObtained()) {
ZX_DEBUG_ASSERT(le_smp_->role() == hci::Connection::Role::kMaster);
bt_log(TRACE, "sm", "waiting to receive keys from the responder");
return;
}
if (!legacy_state_->LocalKeysSent() && !SendLocalKeys()) {
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
}
// If there are no keys left to exchange then we're done with pairing. Since
// we're only encrypted with the STK, the pairing will be short-term (this is
// the case if the "bonding" flag was not set).
if (legacy_state_->IsComplete()) {
CompleteLegacyPairing();
}
}
bool PairingState::SendLocalKeys() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->InPhase3());
ZX_DEBUG_ASSERT(!legacy_state_->LocalKeysSent());
if (legacy_state_->ShouldSendLTK()) {
// Generate completely random values for LTK, EDiv, and Rand.
hci::LinkKey key(common::RandomUInt128(), common::Random<uint64_t>(),
common::Random<uint16_t>());
// Assign the link key to make it available when the master initiates
// encryption. The security properties of the LTK are based on the current
// properties under which it gets exchanged.
AssignLongTermKeyInternal(LTK(le_sec_, key));
le_smp_->SendEncryptionKey(key);
}
if (legacy_state_->ShouldSendIdentity()) {
ZX_DEBUG_ASSERT(delegate_);
auto id_info = delegate_->OnIdentityInformationRequest();
if (!id_info) {
bt_log(TRACE, "sm",
"local identity information required but no longer "
"available; abort pairing");
return false;
}
le_smp_->SendIdentityInfo(*id_info);
}
legacy_state_->sent_local_keys = true;
return true;
}
void PairingState::CompleteLegacyPairing() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->IsComplete());
ZX_DEBUG_ASSERT(le_smp_->pairing_started());
le_smp_->StopTimer();
// The security properties of all keys are determined by the security
// properties of the link used to distribute them. This is already reflected
// by |le_sec_|.
PairingData pairing_data;
if (ltk_) {
pairing_data.ltk = *ltk_;
}
if (legacy_state_->has_irk) {
// If there is an IRK there must also be an identity address.
pairing_data.irk = Key(le_sec_, legacy_state_->irk);
pairing_data.identity_address = legacy_state_->identity_address;
}
bt_log(TRACE, "sm", "LE legacy pairing complete");
legacy_state_ = nullptr;
// TODO(armansito): Report CSRK when we support it.
ZX_DEBUG_ASSERT(delegate_);
delegate_->OnPairingComplete(Status());
delegate_->OnNewPairingData(pairing_data);
// Separate out the requests that are satisfied by the current security level
// from the ones that require a higher level. We'll retry pairing for the
// latter.
std::queue<PendingRequest> satisfied;
std::queue<PendingRequest> unsatisfied;
while (!request_queue_.empty()) {
auto& request = request_queue_.front();
if (request.level <= le_sec_.level()) {
satisfied.push(std::move(request));
} else {
unsatisfied.push(std::move(request));
}
request_queue_.pop();
}
request_queue_ = std::move(unsatisfied);
// Notify the satisfied requests with success.
while (!satisfied.empty()) {
satisfied.front().callback(Status(), le_sec_);
satisfied.pop();
}
if (!unsatisfied.empty()) {
BeginLegacyPairingPhase1(unsatisfied.front().level);
}
}
void PairingState::OnPairingFailed(Status status) {
bt_log(ERROR, "sm", "LE pairing failed: %s", status.ToString().c_str());
// TODO(NET-1201): implement "waiting interval" to prevent repeated attempts
// as described in Vol 3, Part H, 2.3.6.
ZX_DEBUG_ASSERT(delegate_);
delegate_->OnPairingComplete(status);
auto requests = std::move(request_queue_);
while (!requests.empty()) {
requests.front().callback(status, le_sec_);
requests.pop();
}
if (legacy_state_) {
ZX_DEBUG_ASSERT(le_link_);
le_link_->set_link_key(hci::LinkKey());
legacy_state_ = nullptr;
}
}
void PairingState::OnFeatureExchange(const PairingFeatures& features,
const ByteBuffer& preq,
const ByteBuffer& pres) {
bt_log(SPEW, "sm", "obtained LE Pairing features");
if (!features.initiator) {
if (legacy_state_) {
ZX_DEBUG_ASSERT(legacy_state_->features);
// Reject if the peer sent a new pairing request while pairing is already
// in progress.
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
legacy_state_ = std::make_unique<LegacyState>(next_pairing_id_++);
}
ZX_DEBUG_ASSERT(legacy_state_);
legacy_state_->features = features;
BeginLegacyPairingPhase2(preq, pres);
}
void PairingState::OnPairingConfirm(const UInt128& confirm) {
// TODO(armansito): Have separate subroutines to handle this event for legacy
// and secure connections.
if (!legacy_state_) {
bt_log(TRACE, "sm", "ignoring confirm value received while not pairing");
return;
}
// Allow this command if:
// a. we are in Phase 2, or
// b. we are the responder but still waiting for a TK.
// Reject pairing if neither of these is true.
if (!legacy_state_->InPhase2() &&
(!legacy_state_->WaitingForTK() || legacy_state_->features->initiator)) {
bt_log(ERROR, "sm",
"abort pairing due to confirm value received outside phase 2");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// abort pairing if we received a second confirm value from the peer. The
// specification defines a certain order for the phase 2 commands.
if (legacy_state_->has_peer_confirm) {
bt_log(ERROR, "sm", "already received confirm value! aborting");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// The confirm value shouldn't be sent after the random value. (See Vol 3,
// Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of events.
if (legacy_state_->has_peer_rand) {
bt_log(ERROR, "sm",
"\"Pairing Confirm\" expected before \"Pairing Random\"");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
legacy_state_->peer_confirm = confirm;
legacy_state_->has_peer_confirm = true;
if (legacy_state_->features->initiator) {
// We MUST have a TK and have previously generated an Mconfirm.
ZX_DEBUG_ASSERT(legacy_state_->has_tk);
// We are the master and have previously sent Mconfirm and just received
// Sconfirm. We now send Mrand for the peer to compare.
ZX_DEBUG_ASSERT(le_smp_->role() == hci::Connection::Role::kMaster);
LegacySendRandomValue();
} else {
// We are the responder and have just received Mconfirm.
ZX_DEBUG_ASSERT(le_smp_->role() != hci::Connection::Role::kMaster);
if (!legacy_state_->WaitingForTK()) {
LegacySendConfirmValue();
}
}
}
void PairingState::OnPairingRandom(const UInt128& random) {
// TODO(armansito): Have separate subroutines to handle this event for legacy
// and secure connections.
if (!legacy_state_) {
bt_log(TRACE, "sm", "ignoring confirm value received while not pairing");
return;
}
if (!legacy_state_->InPhase2()) {
bt_log(ERROR, "sm",
"abort pairing due to confirm value received outside phase 2");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// We must have a TK and sent a confirm value by now (this is implied by
// InPhase2() above).
ZX_DEBUG_ASSERT(legacy_state_->has_tk);
// abort pairing if we received a second random value from the peer. The
// specification defines a certain order for the phase 2 commands.
if (legacy_state_->has_peer_rand) {
bt_log(ERROR, "sm", "already received random value! aborting");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// The random value shouldn't be sent before the confirm value. (See Vol 3,
// Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of events.
if (!legacy_state_->has_peer_confirm) {
bt_log(ERROR, "sm", "\"Pairing Rand\" expected after \"Pairing Confirm\"");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// Check that the order of the SMP commands is correct.
if (legacy_state_->features->initiator) {
ZX_DEBUG_ASSERT(le_smp_->role() == hci::Connection::Role::kMaster);
// The initiator distributes both values before the responder sends Srandom.
if (!legacy_state_->sent_local_rand || !legacy_state_->sent_local_confirm) {
bt_log(ERROR, "sm", "\"Pairing Random\" received in wrong order!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
} else {
ZX_DEBUG_ASSERT(le_smp_->role() != hci::Connection::Role::kMaster);
// We cannot have sent the Srand without receiving Mrand first.
ZX_DEBUG_ASSERT(!legacy_state_->sent_local_rand);
// We need to send Sconfirm before the master sends Mrand.
if (!legacy_state_->sent_local_confirm) {
bt_log(ERROR, "sm", "\"Pairing Random\" received in wrong order!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
}
legacy_state_->peer_rand = random;
legacy_state_->has_peer_rand = true;
// We have both confirm and rand values from the peer. Generate it locally and
// compare.
const DeviceAddress *ia, *ra;
LEPairingAddresses(&ia, &ra);
UInt128 peer_confirm;
util::C1(legacy_state_->tk, legacy_state_->peer_rand, legacy_state_->preq,
legacy_state_->pres, *ia, *ra, &peer_confirm);
if (peer_confirm != legacy_state_->peer_confirm) {
bt_log(ERROR, "sm", "%sconfirm value does not match!",
legacy_state_->features->initiator ? "S" : "M");
AbortLegacyPairing(ErrorCode::kConfirmValueFailed);
return;
}
ZX_DEBUG_ASSERT(le_link_);
// Generate the STK.
UInt128 stk;
UInt128* initiator_rand = &legacy_state_->local_rand;
UInt128* responder_rand = &legacy_state_->peer_rand;
if (!legacy_state_->features->initiator) {
std::swap(initiator_rand, responder_rand);
}
util::S1(legacy_state_->tk, *responder_rand, *initiator_rand, &stk);
// Mask the key based on the requested encryption key size.
uint8_t key_size = legacy_state_->features->encryption_key_size;
if (key_size < 16) {
MutableBufferView view(stk.data() + key_size, 16 - key_size);
view.SetToZeros();
}
// EDiv and Rand values are set to 0 to generate the STK (Vol 3, Part H,
// 2.4.4.1).
le_link_->set_link_key(hci::LinkKey(stk, 0, 0));
if (legacy_state_->features->initiator) {
// Initiate link layer encryption with STK.
if (!le_link_->StartEncryption()) {
bt_log(ERROR, "sm", "failed to start encryption");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
}
} else {
// Send Srand and wait for the master to encrypt the link with the STK.
// |le_link_| will respond to the LE LTK request event with the STK that it
// got assigned above.
LegacySendRandomValue();
}
}
void PairingState::OnLongTermKey(const common::UInt128& ltk) {
if (!legacy_state_) {
bt_log(TRACE, "sm", "ignoring LTK received while not in legacy pairing");
return;
}
if (!legacy_state_->InPhase3()) {
// The link MUST be encrypted with the STK for the transfer of the LTK to be
// secure.
bt_log(ERROR, "sm", "abort pairing due to LTK received outside phase 3");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
if (!legacy_state_->ShouldReceiveLTK()) {
bt_log(ERROR, "sm", "received unexpected LTK");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// abort pairing if we received a second LTK from the peer.
if (legacy_state_->has_ltk) {
bt_log(ERROR, "sm", "already received LTK! aborting");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
ZX_DEBUG_ASSERT(!(legacy_state_->obtained_remote_keys & KeyDistGen::kEncKey));
legacy_state_->ltk_bytes = ltk;
legacy_state_->has_ltk = true;
// Wait to receive EDiv and Rand
}
void PairingState::OnMasterIdentification(uint16_t ediv, uint64_t random) {
if (!legacy_state_) {
bt_log(TRACE, "sm",
"ignoring ediv/rand received while not in legacy pairing");
return;
}
if (!legacy_state_->InPhase3()) {
// The link MUST be encrypted with the STK for the transfer of the LTK to be
// secure.
bt_log(ERROR, "sm",
"abort pairing due to ediv/rand received outside phase 3");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
ZX_DEBUG_ASSERT(legacy_state_->stk_encrypted);
if (!legacy_state_->ShouldReceiveLTK()) {
bt_log(ERROR, "sm", "received unexpected ediv/rand");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// EDIV and Rand must be sent AFTER the LTK (Vol 3, Part H, 3.6.1).
if (!legacy_state_->has_ltk) {
bt_log(ERROR, "sm", "received EDIV and Rand before LTK!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
if (legacy_state_->obtained_remote_keys & KeyDistGen::kEncKey) {
bt_log(ERROR, "sm", "already received EDIV and Rand!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// The security properties of the LTK are determined by the current link
// properties (i.e. the properties of the STK).
AssignLongTermKeyInternal(
LTK(le_sec_, hci::LinkKey(legacy_state_->ltk_bytes, random, ediv)));
legacy_state_->obtained_remote_keys |= KeyDistGen::kEncKey;
// "EncKey" received. Complete pairing if possible.
OnExpectedKeyReceived();
}
void PairingState::OnIdentityResolvingKey(const common::UInt128& irk) {
if (!legacy_state_) {
bt_log(TRACE, "sm", "ignoring IRK received while not in legacy pairing!");
return;
}
if (!legacy_state_->InPhase3()) {
// The link must be encrypted with the STK for the transfer of the IRK to be
// secure.
bt_log(ERROR, "sm", "abort pairing due to IRK received outside phase 3");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
if (!legacy_state_->ShouldReceiveIdentity()) {
bt_log(ERROR, "sm", "received unexpected IRK");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// Abort if we receive an IRK more than once.
if (legacy_state_->has_irk) {
bt_log(ERROR, "sm", "already received IRK! aborting");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
ZX_DEBUG_ASSERT(!(legacy_state_->obtained_remote_keys & KeyDistGen::kIdKey));
legacy_state_->irk = irk;
legacy_state_->has_irk = true;
// Wait to receive identity address
}
void PairingState::OnIdentityAddress(
const common::DeviceAddress& identity_address) {
if (!legacy_state_) {
bt_log(TRACE, "sm",
"ignoring identity address received while not in legacy pairing");
return;
}
if (!legacy_state_->InPhase3()) {
// The link must be encrypted with the STK for the transfer of the address
// to be secure.
bt_log(ERROR, "sm",
"abort pairing due to identity address received outside phase 3");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
if (!legacy_state_->ShouldReceiveIdentity()) {
bt_log(ERROR, "sm", "received unexpected identity address");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// The identity address must be sent after the IRK (Vol 3, Part H, 3.6.1).
if (!legacy_state_->has_irk) {
bt_log(ERROR, "sm", "received identity address before the IRK!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
if (legacy_state_->obtained_remote_keys & KeyDistGen::kIdKey) {
bt_log(ERROR, "sm", "already received identity information!");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
// Store the identity address and mark all identity info as received.
legacy_state_->identity_address = identity_address;
legacy_state_->obtained_remote_keys |= KeyDistGen::kIdKey;
// "IdKey" received. Complete pairing if possible.
OnExpectedKeyReceived();
}
void PairingState::OnSecurityRequest(AuthReqField auth_req) {
ZX_DEBUG_ASSERT(!legacy_state_);
ZX_DEBUG_ASSERT(le_link_);
ZX_DEBUG_ASSERT(le_link_->role() == hci::Connection::Role::kMaster);
SecurityLevel requested_level;
if (auth_req & AuthReq::kMITM) {
requested_level = SecurityLevel::kAuthenticated;
} else {
requested_level = SecurityLevel::kEncrypted;
}
// If we already have a LTK and its security properties satisfy the request,
// then we start link layer encryption (which will either encrypt the link or
// perform a key refresh). See Vol 3, Part H, Figure 2.7 for the algorithm.
// TODO(armansito): This should compare the peer's SC requirement against the
// LTK's security properties. Since we currently don't support LE Secure
// Connections we assume that no local LTK ever satisfies this. If the peer
// requests SC, then we'll initiate pairing as normal and let the peer accept
// or reject the request.
if (ltk_ && (ltk_->security().level() >= requested_level) &&
!(auth_req & AuthReq::kSC)) {
// The existing key satisfies the security requirement.
bt_log(TRACE, "sm", "responding to security request using existing LTK");
ZX_DEBUG_ASSERT(le_link_->ltk());
ZX_DEBUG_ASSERT(*le_link_->ltk() == ltk_->key());
le_link_->StartEncryption();
return;
}
// Initiate pairing.
UpgradeSecurity(requested_level, [](Status status, const auto& security) {
bt_log(TRACE, "sm", "security request resolved - %s %s",
status.ToString().c_str(), security.ToString().c_str());
});
}
bool PairingState::HasIdentityInformation() {
// This is called by the bearer to determine if we have local identity
// information to distribute during a feature exchange. We ask the delegate to
// see if it currently has this data to determine whether to continue.
ZX_DEBUG_ASSERT(delegate_);
return static_cast<bool>(delegate_->OnIdentityInformationRequest());
}
void PairingState::OnEncryptionChange(hci::Status status, bool enabled) {
// First notify the delegate in case of failure.
if (bt_is_error(status, ERROR, "sm", "link layer authentication failed")) {
ZX_DEBUG_ASSERT(delegate_);
delegate_->OnAuthenticationFailure(status);
}
if (!enabled) {
bt_log(TRACE, "sm", "encryption disabled (handle: %#.4x)",
le_link_->handle());
SetSecurityProperties(sm::SecurityProperties());
} else if (!legacy_state_) {
bt_log(TRACE, "sm", "encryption enabled while not pairing");
// If encryption was enabled while not pairing, then the link key must match
// |ltk_|. We update the security properties based on that key. Otherwise,
// we let the pairing handlers below determine the security properties (i.e.
// EndLegacyPairingPhase2() and CompleteLegacyPairing().
ZX_DEBUG_ASSERT(le_link_->ltk());
ZX_DEBUG_ASSERT(ltk_);
ZX_DEBUG_ASSERT(*le_link_->ltk() == ltk_->key());
SetSecurityProperties(ltk_->security());
}
// Nothing to do if no pairing is in progress.
if (!legacy_state_) {
return;
}
if (!status || !enabled) {
bt_log(ERROR, "sm", "failed to encrypt link during pairing");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
return;
}
ZX_DEBUG_ASSERT(le_smp_->pairing_started());
if (legacy_state_->InPhase2()) {
bt_log(TRACE, "sm", "link encrypted with STK");
EndLegacyPairingPhase2();
}
}
void PairingState::OnExpectedKeyReceived() {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->stk_encrypted);
if (!legacy_state_->RequestedKeysObtained()) {
ZX_DEBUG_ASSERT(legacy_state_->InPhase3());
bt_log(TRACE, "sm", "more keys pending");
return;
}
if (legacy_state_->features->initiator && !legacy_state_->LocalKeysSent()) {
SendLocalKeys();
}
// We are no longer in Phase 3.
ZX_DEBUG_ASSERT(!legacy_state_->InPhase3());
// Complete pairing now if we don't need to wait for encryption using the LTK.
// Otherwise we'll mark it as complete when the link is encrypted with it.
if (legacy_state_->IsComplete()) {
CompleteLegacyPairing();
return;
}
if (ltk_ && legacy_state_->features->initiator) {
ZX_DEBUG_ASSERT(le_link_->ltk());
ZX_DEBUG_ASSERT(*le_link_->ltk() == ltk_->key());
if (!le_link_->StartEncryption()) {
bt_log(ERROR, "sm", "failed to start encryption");
AbortLegacyPairing(ErrorCode::kUnspecifiedReason);
}
}
}
void PairingState::LEPairingAddresses(const DeviceAddress** out_initiator,
const DeviceAddress** out_responder) {
ZX_DEBUG_ASSERT(legacy_state_);
ZX_DEBUG_ASSERT(legacy_state_->features);
if (legacy_state_->features->initiator) {
*out_initiator = &le_link_->local_address();
*out_responder = &le_link_->peer_address();
} else {
*out_initiator = &le_link_->peer_address();
*out_responder = &le_link_->local_address();
}
}
void PairingState::AssignLongTermKeyInternal(const LTK& ltk) {
ltk_ = ltk;
le_link_->set_link_key(ltk.key());
}
} // namespace sm
} // namespace bt