blob: f65006f8d6008f79f4371dd7d205c2a63ec2ce5d [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.
#pragma once
#include <memory>
#include <queue>
#include "garnet/drivers/bluetooth/lib/common/optional.h"
#include "garnet/drivers/bluetooth/lib/hci/link_key.h"
#include "garnet/drivers/bluetooth/lib/l2cap/channel.h"
#include "garnet/drivers/bluetooth/lib/sm/bearer.h"
#include "garnet/drivers/bluetooth/lib/sm/status.h"
#include "garnet/drivers/bluetooth/lib/sm/types.h"
#include "lib/fxl/macros.h"
namespace btlib {
namespace sm {
// Represents the pairing state of a connected peer. The peer device must be a
// LE or a BR/EDR/LE device.
class PairingState final {
public:
explicit PairingState(IOCapability io_capability);
~PairingState();
// Callback used to obtain a Temporary Key used during legacy pairing. The
// callback should return a TK via the |tk_response| parameter.
//
// If the delegate fails to obtain the TK, it should signal this by setting
// the |success| parameter to false. This will abort the pairing procedure.
//
// The delegate should use the following algorithm to provide a temporary key:
//
// 1. If |method| is kJustWorks, the |tk| should be set to 0. Depending on
// the I/O capabilities the user should be asked to confirm or reject the
// pairing.
//
// 2. If |method| is kPasskeyEntryDisplay, the |tk| should be set to a
// random integer between 0 and 999,999. This should be displayed to the
// user until the user has finished entering the passkey on the peer device.
// TODO(armansito): Notify the delegate on SMP keypress events to
// automatically dismiss the dialog.
//
// 3. If |method| is kPasskeyEntryInput, the user should be prompted to
// enter a 6-digit passkey. The |tk| should be set to this passkey.
using TKResponse = fit::function<void(bool success, uint32_t tk)>;
using TKDelegate = fit::function<void(PairingMethod method, TKResponse)>;
void set_legacy_tk_delegate(TKDelegate delegate) {
le_tk_delegate_ = std::move(delegate);
}
// Event triggered when a new LE Long Term Key is obtained for this
// connection.
using LELTKCallback = fit::function<void(const LTK& ltk)>;
void set_le_ltk_callback(LELTKCallback callback) {
le_ltk_callback_ = std::move(callback);
}
// TODO(armansito): Add events for received keys.
// TODO(armansito): Add PairingDelegate events.
// Register a LE link. This method cannot be called on the same PairingState
// instance more than once.
void RegisterLE(fxl::WeakPtr<hci::Connection> link,
fbl::RefPtr<l2cap::Channel> smp);
// Attempt to raise the security level of a the connection to the desired
// |level| and notify the result in |callback|.
//
// If the desired security properties are already satisfied, this procedure
// will succeed immediately.
//
// If a pairing procedure has already been initiated (either by us or the
// peer), the request will be queued and |callback| will be notified when the
// procedure completes. If the resulting security level does not satisfy
// |level|, pairing will be re-initiated.
//
// If no pairing is in progress then the local device will initiate pairing.
//
// If pairing fails |callback| will be called with a |status| that represents
// the error.
using PairingCallback =
fit::function<void(Status status, const SecurityProperties& sec_props)>;
void UpdateSecurity(SecurityLevel level, PairingCallback callback);
// Abort any on-going pairing procedure. TODO: note about callbacks.
void Abort();
private:
static constexpr size_t kPairingRequestSize =
sizeof(Header) + sizeof(PairingRequestParams);
// Represents the state for LE legacy pairing.
struct LegacyState final {
// |id| uniquely identifies the pairing procedure that this state object is
// tied to. This is generated by PairingState.
explicit LegacyState(unsigned int id);
// We are in Phase 1 if pairing features have not been obtained.
bool InPhase1() const;
// We are in Phase 2 if we have obtained the TK and waiting for STK
// encryption.
bool InPhase2() const;
// We are in Phase 3 if the link is encrypted with the STK but not all
// requested keys have been obtained.
bool InPhase3() const;
bool IsComplete() const;
// True if we are in the beginning of Phase 2 where we have not obtained the
// TK yet.
bool WaitingForTK() const;
// True if all keys that are expected from the remote have been received.
bool RequestedKeysObtained() const;
bool ShouldReceiveLTK() const; // True if peer should send the LTK
bool ShouldSendLTK() const; // True if we should send the LTK
// True if LTK will be exchanged and the link is yet to be encrypted with
// the LTK.
bool WaitingForEncryptionWithLTK() const;
// A unique token for this pairing state.
unsigned int id;
// The pairing features obtained during Phase 1. If invalid, we're in
// Phase 1. Otherwise, we're in Phase 2.
common::Optional<PairingFeatures> features;
// True if the link has been encrypted with the STK. This means that we're
// in Phase 3. Otherwise we're in Phase 1 or 2.
bool stk_encrypted;
// True if the link has been encrypted with the LTK. If the LTK should be
// exchanged, then pairing is considered complete when the link is
// encrypted with the LTK.
bool ltk_encrypted;
// The remote keys that have been obtained so far.
KeyDistGenField obtained_remote_keys;
// Data used to generate STK and confirm values in Phase 2.
bool has_tk;
bool has_peer_confirm;
bool has_peer_rand;
bool sent_local_confirm;
bool sent_local_rand;
common::UInt128 tk;
common::UInt128 local_confirm;
common::UInt128 peer_confirm;
common::UInt128 local_rand;
common::UInt128 peer_rand;
common::StaticByteBuffer<kPairingRequestSize> preq;
common::StaticByteBuffer<kPairingRequestSize> pres;
// Data from the peer tracked during the Phase 3. Parts of LTK are received
// in separate events.
bool has_ltk;
common::UInt128 ltk_bytes; // LTK without ediv/rand
// Keys obtained during pairing.
common::Optional<hci::LinkKey> ltk; // LTK with ediv/rand
};
// Represents a pending request to update the security level.
struct PendingRequest {
PendingRequest(SecurityLevel level, PairingCallback callback);
PendingRequest(PendingRequest&&) = default;
PendingRequest& operator=(PendingRequest&&) = default;
SecurityLevel level;
PairingCallback callback;
};
// Aborts an ongoing legacy pairing procedure.
void AbortLegacyPairing(ErrorCode error_code);
// Begin Phase 1 of LE legacy pairing with the given |level|.
void BeginLegacyPairingPhase1(SecurityLevel level);
// Begin Phase 2 of LE legacy pairing. This is called after LE pairing
// features have been exchanged and results (asynchronously) in the generation
// and encryption of a link using the STK. This follows (roughly) the
// following steps:
// 1. Asynchronously obtain the TK.
// 2. Generate the local confirm/rand values.
// 3. If initiator, start the exchange, otherwise wait for the peer to send
// its confirm value.
void BeginLegacyPairingPhase2(const common::ByteBuffer& preq,
const common::ByteBuffer& pres);
void LegacySendConfirmValue();
void LegacySendRandomValue();
// Called when the link is encrypted with the STK at the end of Legacy
// Pairing Phase 2.
void EndLegacyPairingPhase2();
// Completes the legacy pairing process by cleaning up pairing state, updates
// the current security level, and notifies parties that have requested
// security.
void CompleteLegacyPairing();
// Called when pairing features have been exchanged over the LE transport.
void OnLEPairingFeatures(const PairingFeatures& features,
const common::ByteBuffer& preq,
const common::ByteBuffer& pres);
// Called when pairing fails or is aborted over the LE transport.
void OnLEPairingFailed(Status status);
// Called when pairing confirm and random values are received.
void OnLEPairingConfirm(const common::UInt128& confirm);
void OnLEPairingRandom(const common::UInt128& random);
// Called when information about the LE legacy LTK is received.
void OnLELongTermKey(const common::UInt128& ltk);
void OnLEMasterIdentification(uint16_t ediv, uint64_t random);
// Called when the encryption state of the LE link changes.
void OnLEEncryptionChange(hci::Status status, bool enabled);
// Returns pointers to the initiator and responder addresses. This can be
// called after pairing Phase 1.
void LEPairingAddresses(common::DeviceAddress** out_initiator,
common::DeviceAddress** out_responder);
// The ID that will be assigned to the next pairing state.
unsigned int next_pairing_id_;
// Delegate callback to obtain a TK based on a selected pairing method.
TKDelegate le_tk_delegate_;
// Callbacks used to notify obtained keys during pairing.
LELTKCallback le_ltk_callback_;
// TODO(armansito): Make it possible to change I/O capabilities.
IOCapability ioc_;
// Data for the currently registered LE-U link, if any. This data is valid
// only if |le_smp_| is not nullptr.
fxl::WeakPtr<hci::Connection> le_link_;
std::unique_ptr<Bearer> le_smp_; // SMP data bearer for the LE-U link.
common::DeviceAddress le_local_addr_; // Local address used while connecting.
common::DeviceAddress le_peer_addr_; // Peer address used while connecting.
SecurityProperties le_sec_; // Current security properties of the LE-U link.
// The state of the LE legacy pairing procedure, if any.
std::unique_ptr<LegacyState> legacy_state_;
// The pending security requests added via UpdateSecurity().
std::queue<PendingRequest> request_queue_;
// TODO(armansito): Support SMP over ACL-U for LE Secure Connections.
fxl::WeakPtrFactory<PairingState> weak_ptr_factory_;
FXL_DISALLOW_COPY_AND_ASSIGN(PairingState);
};
} // namespace sm
} // namespace btlib