| // Copyright 2020 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 "security_manager.h" |
| |
| #include <zircon/assert.h> |
| #include <zircon/status.h> |
| |
| #include <memory> |
| #include <optional> |
| #include <type_traits> |
| #include <utility> |
| #include <variant> |
| |
| #include "lib/async/default.h" |
| #include "lib/fit/function.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/device_address.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/log.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/random.h" |
| #include "src/connectivity/bluetooth/core/bt-host/common/uint128.h" |
| #include "src/connectivity/bluetooth/core/bt-host/gap/gap.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci-spec/link_key.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/connection.h" |
| #include "src/connectivity/bluetooth/core/bt-host/hci/status.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/packet.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/phase_2_secure_connections.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/security_request_phase.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/smp.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/status.h" |
| #include "src/connectivity/bluetooth/core/bt-host/sm/types.h" |
| #include "util.h" |
| |
| namespace bt::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 |
| |
| class SecurityManagerImpl final : public SecurityManager, |
| public PairingPhase::Listener, |
| public PairingChannel::Handler { |
| public: |
| ~SecurityManagerImpl() override; |
| SecurityManagerImpl(fxl::WeakPtr<hci::Connection> link, fbl::RefPtr<l2cap::Channel> smp, |
| IOCapability io_capability, fxl::WeakPtr<Delegate> delegate, |
| BondableMode bondable_mode, gap::LeSecurityMode security_mode); |
| // SecurityManager overrides: |
| bool AssignLongTermKey(const LTK& ltk) override; |
| void UpgradeSecurity(SecurityLevel level, PairingCallback callback) override; |
| void Reset(IOCapability io_capability) override; |
| void Abort(ErrorCode ecode) override; |
| |
| private: |
| // 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; |
| }; |
| |
| // Called when we receive a peer security request as initiator, will start Phase 1. |
| void OnSecurityRequest(AuthReqField auth_req); |
| |
| // Called when we receive a peer pairing request as responder, will start Phase 1. |
| void OnPairingRequest(const PairingRequestParams& req_params); |
| |
| // Pulls the next PendingRequest off |request_queue_| and starts a security upgrade to that |
| // |level| by either sending a Pairing Request as initiator or a Security Request as responder. |
| void UpgradeSecurityInternal(); |
| |
| // Called when the feature exchange (Phase 1) completes and the relevant features of both sides |
| // have been resolved into `features`. `preq` and `pres` need to be retained for cryptographic |
| // calculations in Phase 2. Causes a state transition from Phase 1 to Phase 2 |
| void OnFeatureExchange(PairingFeatures features, PairingRequestParams preq, |
| PairingResponseParams pres); |
| |
| // Called when Phase 2 generates an encryption key, so the link can be encrypted with it. |
| void OnPhase2EncryptionKey(const UInt128& new_key); |
| |
| // Check if encryption using `current_ltk` will satisfy the current security requirements. |
| static bool CurrentLtkInsufficientlySecureForEncryption( |
| std::optional<LTK> current_ltk, SecurityRequestPhase* security_request_phase, |
| gap::LeSecurityMode mode); |
| |
| // Called when the encryption state of the LE link changes. |
| void OnEncryptionChange(hci::Status status, bool enabled); |
| |
| // Called when the link is encrypted at the end of pairing Phase 2. |
| void EndPhase2(); |
| |
| // Cleans up pairing state, updates the current security level, and notifies parties that |
| // requested security of the link's updated security properties. |
| void OnPairingComplete(PairingData data); |
| |
| // After a call to UpgradeSecurity results in an increase of the link security level (through |
| // pairing completion or SMP Security Requested encryption), this method notifies all the |
| // callbacks associated with SecurityUpgrade requests. |
| void NotifySecurityCallbacks(); |
| |
| // Assign the current security properties and notify the delegate of the |
| // change. |
| void SetSecurityProperties(const SecurityProperties& sec); |
| |
| // Directly assigns the current |ltk_| and the underlying |le_link_|'s link key. This function |
| // does not initiate link layer encryption and can be called during and outside of pairing. |
| void OnNewLongTermKey(const LTK& ltk); |
| |
| // PairingPhase::Listener overrides: |
| void OnPairingFailed(Status status) override; |
| std::optional<IdentityInfo> OnIdentityRequest() override; |
| void ConfirmPairing(ConfirmCallback confirm) override; |
| void DisplayPasskey(uint32_t passkey, Delegate::DisplayMethod method, |
| ConfirmCallback cb) override; |
| void RequestPasskey(PasskeyResponseCallback respond) override; |
| |
| // PairingChannel::Handler overrides. SecurityManagerImpl is only the fallback handler, meaning |
| // these methods are only called by PairingChannel when no security upgrade is in progress: |
| void OnRxBFrame(ByteBufferPtr sdu) override; |
| void OnChannelClosed() override; |
| |
| // Starts the SMP timer. Stops and cancels any in-progress timers. |
| void StartNewTimer(); |
| // Stops and resets the SMP Pairing Timer. |
| void StopTimer(); |
| // Called when the pairing timer expires, forcing the pairing process to stop |
| void OnPairingTimeout(); |
| |
| // Returns a std::pair<InitiatorAddress, ResponderAddress>. Will assert if called outside active |
| // pairing or before Phase 1 is complete. |
| std::pair<DeviceAddress, DeviceAddress> LEPairingAddresses(); |
| |
| // Puts the class into a non-pairing state. |
| void ResetState(); |
| |
| // Returns true if the pairing state machine is currently in Phase 2 of pairing. |
| bool InPhase2() const { |
| return std::holds_alternative<Phase2Legacy>(current_phase_) || |
| std::holds_alternative<Phase2SecureConnections>(current_phase_); |
| } |
| |
| bool SecurityUpgradeInProgress() const { |
| return !std::holds_alternative<std::monostate>(current_phase_); |
| } |
| |
| // Validates that both SM and the link have stored LTKs, and that these values match. Disconnects |
| // the link if it finds an issue. Should only be called when an LTK is expected to exist. |
| Status ValidateExistingLocalLtk(); |
| |
| // The ID that will be assigned to the next pairing operation. |
| PairingProcedureId next_pairing_id_; |
| |
| // The higher-level class acting as a delegate for operations outside of SMP. |
| fxl::WeakPtr<Delegate> delegate_; |
| |
| // Data for the currently registered LE-U link, if any. |
| fxl::WeakPtr<hci::Connection> le_link_; |
| |
| // The IO capabilities of the device |
| IOCapability io_cap_; |
| |
| // The current LTK assigned to this connection. This can be assigned directly |
| // by calling AssignLongTermKey() or as a result of a pairing procedure. |
| std::optional<LTK> ltk_; |
| |
| // If a pairing is in progress and Phase 1 (feature exchange) has completed, this will store the |
| // result of that feature exchange. Otherwise, this will be std::nullopt. |
| std::optional<PairingFeatures> features_; |
| |
| // The pending security requests added via UpgradeSecurity(). |
| std::queue<PendingRequest> request_queue_; |
| |
| // Fixed SMP Channel used to send/receive packets |
| std::unique_ptr<PairingChannel> sm_chan_; |
| |
| // The role of the local device in pairing. |
| Role role_; |
| |
| async::TaskClosureMethod<SecurityManagerImpl, &SecurityManagerImpl::OnPairingTimeout> |
| timeout_task_{this}; |
| |
| // The presence of a particular phase in this variant indicates that a security upgrade is in |
| // progress at the stored phase. No security upgrade is in progress if std::monostate is present. |
| std::variant<std::monostate, SecurityRequestPhase, std::unique_ptr<Phase1>, Phase2Legacy, |
| Phase2SecureConnections, Phase3> |
| current_phase_; |
| |
| fxl::WeakPtrFactory<SecurityManagerImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(SecurityManagerImpl); |
| }; |
| |
| SecurityManagerImpl::PendingRequest::PendingRequest(SecurityLevel level, PairingCallback callback) |
| : level(level), callback(std::move(callback)) {} |
| |
| SecurityManagerImpl::~SecurityManagerImpl() { |
| if (le_link_) { |
| le_link_->set_encryption_change_callback({}); |
| } |
| } |
| |
| SecurityManagerImpl::SecurityManagerImpl( |
| fxl::WeakPtr<hci::Connection> link, fbl::RefPtr<l2cap::Channel> smp, IOCapability io_capability, |
| fxl::WeakPtr<Delegate> delegate, BondableMode bondable_mode, gap::LeSecurityMode security_mode) |
| : SecurityManager(bondable_mode, security_mode), |
| next_pairing_id_(0), |
| delegate_(std::move(delegate)), |
| le_link_(std::move(link)), |
| io_cap_(io_capability), |
| sm_chan_(std::make_unique<PairingChannel>( |
| smp, fit::bind_member(this, &SecurityManagerImpl::StartNewTimer))), |
| role_(le_link_->role() == hci::Connection::Role::kMaster ? Role::kInitiator |
| : Role::kResponder), |
| weak_ptr_factory_(this) { |
| ZX_ASSERT(delegate_); |
| ZX_ASSERT(le_link_); |
| ZX_ASSERT(smp); |
| ZX_ASSERT(le_link_->handle() == smp->link_handle()); |
| ZX_ASSERT(le_link_->ll_type() == hci::Connection::LinkType::kLE); |
| ZX_ASSERT(smp->id() == l2cap::kLESMPChannelId); |
| // `current_phase_` is default constructed into std::monostate in the initializer list as no |
| // security upgrade is in progress upon construction. |
| |
| // Set up HCI encryption event. |
| le_link_->set_encryption_change_callback( |
| fit::bind_member(this, &SecurityManagerImpl::OnEncryptionChange)); |
| sm_chan_->SetChannelHandler(weak_ptr_factory_.GetWeakPtr()); |
| } |
| |
| void SecurityManagerImpl::OnSecurityRequest(AuthReqField auth_req) { |
| ZX_ASSERT(!SecurityUpgradeInProgress()); |
| |
| if (role_ != Role::kInitiator) { |
| bt_log(DEBUG, "sm", "Received spurious Security Request while not acting as SM initiator"); |
| return; |
| } |
| |
| 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. |
| if (ltk_ && (ltk_->security().level() >= requested_level) && |
| (!(auth_req & AuthReq::kSC) || ltk_->security().secure_connections())) { |
| if (bt_is_error(ValidateExistingLocalLtk(), ERROR, "sm", |
| "disconnecting link as it cannot be encrypted with LTK status")) { |
| return; |
| } |
| le_link_->StartEncryption(); |
| return; |
| } |
| // V5.1 Vol. 3 Part H Section 3.4: "Upon [...] reception of the Security Request command, the |
| // Security Manager Timer shall be [...] restarted." |
| StartNewTimer(); |
| // Initiate pairing. |
| UpgradeSecurity(requested_level, [](Status status, const auto& security) { |
| bt_log(DEBUG, "sm", "security request resolved - %s %s", bt_str(status), bt_str(security)); |
| }); |
| } |
| |
| void SecurityManagerImpl::UpgradeSecurity(SecurityLevel level, PairingCallback callback) { |
| if (SecurityUpgradeInProgress()) { |
| bt_log(TRACE, "sm", "LE security upgrade in progress; request for %s security queued", |
| LevelToString(level)); |
| request_queue_.emplace(level, std::move(callback)); |
| return; |
| } |
| |
| if (level <= security().level()) { |
| callback(Status(), security()); |
| return; |
| } |
| |
| // Secure Connections only mode only permits Secure Connections authenticated pairing with a 128- |
| // bit encryption key, so we force all security upgrade requests to that level. |
| if (security_mode() == gap::LeSecurityMode::SecureConnectionsOnly) { |
| level = SecurityLevel::kSecureAuthenticated; |
| } |
| |
| // |request_queue| must be empty if there is no active security upgrade request, which is |
| // equivalent to being in idle phase with no pending security request. |
| ZX_ASSERT(request_queue_.empty()); |
| request_queue_.emplace(level, std::move(callback)); |
| UpgradeSecurityInternal(); |
| } |
| |
| void SecurityManagerImpl::OnPairingRequest(const PairingRequestParams& req_params) { |
| // Only the initiator may send the Pairing Request (V5.0 Vol. 3 Part H 3.5.1). |
| if (role_ != Role::kResponder) { |
| bt_log(DEBUG, "sm", "rejecting \"Pairing Request\" as initiator"); |
| sm_chan_->SendMessage(kPairingFailed, ErrorCode::kCommandNotSupported); |
| return; |
| } |
| // V5.1 Vol. 3 Part H Section 3.4: "Upon [...] reception of the Pairing Request command, the |
| // Security Manager Timer shall be reset and started." |
| StartNewTimer(); |
| |
| // We only require authentication as Responder if there is a pending Security Request for it. |
| SecurityRequestPhase* security_req_phase = std::get_if<SecurityRequestPhase>(¤t_phase_); |
| auto required_level = security_req_phase ? security_req_phase->pending_security_request() |
| : SecurityLevel::kEncrypted; |
| |
| // Secure Connections only mode only permits Secure Connections authenticated pairing with a 128- |
| // bit encryption key, so we force all security upgrade requests to that level. |
| if (security_mode() == gap::LeSecurityMode::SecureConnectionsOnly) { |
| required_level = SecurityLevel::kSecureAuthenticated; |
| } |
| |
| current_phase_ = Phase1::CreatePhase1Responder( |
| sm_chan_->GetWeakPtr(), weak_ptr_factory_.GetWeakPtr(), req_params, io_cap_, bondable_mode(), |
| required_level, fit::bind_member(this, &SecurityManagerImpl::OnFeatureExchange)); |
| std::get<std::unique_ptr<Phase1>>(current_phase_)->Start(); |
| } |
| |
| void SecurityManagerImpl::UpgradeSecurityInternal() { |
| ZX_ASSERT_MSG(!SecurityUpgradeInProgress(), |
| "cannot upgrade security while security upgrade already in progress!"); |
| const PendingRequest& next_req = request_queue_.front(); |
| if (next_req.level >= SecurityLevel::kAuthenticated && |
| io_cap_ == IOCapability::kNoInputNoOutput) { |
| bt_log(WARN, "sm", |
| "cannot fulfill authenticated security request as IOCapabilities are NoInputNoOutput"); |
| next_req.callback(Status(ErrorCode::kAuthenticationRequirements), security()); |
| request_queue_.pop(); |
| if (!request_queue_.empty()) { |
| UpgradeSecurityInternal(); |
| } |
| return; |
| } |
| |
| // V5.1 Vol. 3 Part H Section 3.4: "Upon transmission of the Pairing Request command [...] the |
| // [SM] Timer shall be [..] started" and "Upon transmission of the Security Request command [...] |
| // the [SM] Timer shall be reset and restarted". |
| StartNewTimer(); |
| if (role_ == Role::kInitiator) { |
| current_phase_ = Phase1::CreatePhase1Initiator( |
| sm_chan_->GetWeakPtr(), weak_ptr_factory_.GetWeakPtr(), io_cap_, bondable_mode(), |
| next_req.level, fit::bind_member(this, &SecurityManagerImpl::OnFeatureExchange)); |
| std::get<std::unique_ptr<Phase1>>(current_phase_)->Start(); |
| } else { |
| current_phase_.emplace<SecurityRequestPhase>( |
| sm_chan_->GetWeakPtr(), weak_ptr_factory_.GetWeakPtr(), next_req.level, bondable_mode(), |
| fit::bind_member(this, &SecurityManagerImpl::OnPairingRequest)); |
| std::get<SecurityRequestPhase>(current_phase_).Start(); |
| } |
| } |
| |
| void SecurityManagerImpl::OnFeatureExchange(PairingFeatures features, PairingRequestParams preq, |
| PairingResponseParams pres) { |
| ZX_ASSERT(std::holds_alternative<std::unique_ptr<Phase1>>(current_phase_)); |
| bt_log(TRACE, "sm", "obtained LE Pairing features"); |
| next_pairing_id_++; |
| features_ = features; |
| |
| const auto [initiator_addr, responder_addr] = LEPairingAddresses(); |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| if (!features.secure_connections) { |
| auto preq_pdu = util::NewPdu(sizeof(PairingRequestParams)), |
| pres_pdu = util::NewPdu(sizeof(PairingResponseParams)); |
| PacketWriter preq_writer(kPairingRequest, preq_pdu.get()), |
| pres_writer(kPairingResponse, pres_pdu.get()); |
| *preq_writer.mutable_payload<PairingRequestParams>() = preq; |
| *pres_writer.mutable_payload<PairingRequestParams>() = pres; |
| current_phase_.emplace<Phase2Legacy>( |
| sm_chan_->GetWeakPtr(), self, role_, features, *preq_pdu, *pres_pdu, initiator_addr, |
| responder_addr, fit::bind_member(this, &SecurityManagerImpl::OnPhase2EncryptionKey)); |
| std::get<Phase2Legacy>(current_phase_).Start(); |
| } else { |
| current_phase_.emplace<Phase2SecureConnections>( |
| sm_chan_->GetWeakPtr(), self, role_, features, preq, pres, initiator_addr, responder_addr, |
| fit::bind_member(this, &SecurityManagerImpl::OnPhase2EncryptionKey)); |
| std::get<Phase2SecureConnections>(current_phase_).Start(); |
| } |
| } |
| |
| void SecurityManagerImpl::OnPhase2EncryptionKey(const UInt128& new_key) { |
| ZX_ASSERT(le_link_); |
| ZX_ASSERT(features_); |
| ZX_ASSERT(InPhase2()); |
| // EDiv and Rand values are 0 for Phase 2 keys generated by Legacy or Secure Connections (Vol 3, |
| // Part H, 2.4.4 / 2.4.4.1). Secure Connections generates an LTK, while Legacy generates an STK. |
| auto new_link_key = hci::LinkKey(new_key, 0, 0); |
| |
| if (features_->secure_connections) { |
| OnNewLongTermKey(LTK(FeaturesToProperties(*features_), new_link_key)); |
| } else { |
| // `set_le_ltk` sets the encryption key of the LE link (which is the STK for Legacy), not the |
| // long-term key that results from pairing (which is generated in Phase 3 for Legacy). |
| le_link_->set_le_ltk(new_link_key); |
| } |
| // If we're the initiator, we encrypt the link. If we're the responder, we wait for the initiator |
| // to encrypt the link with the new key.|le_link_| will respond to the HCI "LTK request" event |
| // with the `new_link_key` assigned above, which should trigger OnEncryptionChange. |
| if (role_ == Role::kInitiator) { |
| if (!le_link_->StartEncryption()) { |
| bt_log(ERROR, "sm", "failed to start encryption"); |
| Abort(ErrorCode::kUnspecifiedReason); |
| } |
| } |
| } |
| |
| bool SecurityManagerImpl::CurrentLtkInsufficientlySecureForEncryption( |
| std::optional<LTK> current_ltk, SecurityRequestPhase* security_request_phase, |
| gap::LeSecurityMode mode) { |
| SecurityLevel current_ltk_sec = |
| current_ltk ? current_ltk->security().level() : SecurityLevel::kNoSecurity; |
| return (security_request_phase && |
| security_request_phase->pending_security_request() > current_ltk_sec) || |
| (mode == gap::LeSecurityMode::SecureConnectionsOnly && |
| current_ltk_sec != SecurityLevel::kSecureAuthenticated); |
| } |
| |
| void SecurityManagerImpl::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_ASSERT(delegate_); |
| delegate_->OnAuthenticationFailure(status); |
| } |
| |
| if (!status || !enabled) { |
| bt_log(WARN, "sm", "encryption of link (handle: %#.4x) %s%s!", le_link_->handle(), |
| !status ? fxl::StringPrintf("failed with %s", bt_str(status)).c_str() : "disabled", |
| SecurityUpgradeInProgress() ? "" : " during security upgrade"); |
| SetSecurityProperties(sm::SecurityProperties()); |
| if (SecurityUpgradeInProgress()) { |
| Abort(ErrorCode::kUnspecifiedReason); |
| } |
| return; |
| } |
| |
| SecurityRequestPhase* security_request_phase = std::get_if<SecurityRequestPhase>(¤t_phase_); |
| if (CurrentLtkInsufficientlySecureForEncryption(ltk_, security_request_phase, security_mode())) { |
| bt_log(WARN, "sm", "peer encrypted link with insufficiently secure key, disconnecting"); |
| delegate_->OnAuthenticationFailure(hci::Status(HostError::kInsufficientSecurity)); |
| sm_chan_->SignalLinkError(); |
| return; |
| } |
| |
| if (!SecurityUpgradeInProgress() || security_request_phase) { |
| bt_log(DEBUG, "sm", "encryption enabled while not pairing"); |
| if (bt_is_error(ValidateExistingLocalLtk(), ERROR, "sm", |
| "disconnecting link as it cannot be encrypted with LTK status")) { |
| return; |
| } |
| // If encryption is enabled while not pairing, we update the security properties to those of |
| // `ltk_`. Otherwise, we let the EndPhase2 pairing function determine the security properties. |
| SetSecurityProperties(ltk_->security()); |
| if (security_request_phase) { |
| ZX_ASSERT(role_ == Role::kResponder); |
| ZX_ASSERT(!request_queue_.empty()); |
| NotifySecurityCallbacks(); |
| } |
| return; |
| } |
| |
| if (InPhase2()) { |
| bt_log(DEBUG, "sm", "link encrypted with phase 2 generated key"); |
| EndPhase2(); |
| } |
| } |
| |
| void SecurityManagerImpl::EndPhase2() { |
| ZX_ASSERT(features_.has_value()); |
| ZX_ASSERT(InPhase2()); |
| |
| SetSecurityProperties(FeaturesToProperties(*features_)); |
| // If there are no keys to distribute, don't bother creating Phase 3 |
| if (!HasKeysToDistribute(*features_)) { |
| OnPairingComplete(PairingData()); |
| return; |
| } |
| auto self = weak_ptr_factory_.GetWeakPtr(); |
| current_phase_.emplace<Phase3>(sm_chan_->GetWeakPtr(), self, role_, *features_, security(), |
| fit::bind_member(this, &SecurityManagerImpl::OnPairingComplete)); |
| std::get<Phase3>(current_phase_).Start(); |
| } |
| |
| void SecurityManagerImpl::OnPairingComplete(PairingData pairing_data) { |
| // We must either be in Phase3 or Phase 2 with no keys to distribute if pairing has completed. |
| if (!std::holds_alternative<Phase3>(current_phase_)) { |
| ZX_ASSERT(InPhase2()); |
| ZX_ASSERT(!HasKeysToDistribute(*features_)); |
| } |
| ZX_ASSERT(delegate_); |
| ZX_ASSERT(features_.has_value()); |
| bt_log(DEBUG, "sm", "LE pairing complete"); |
| delegate_->OnPairingComplete(Status()); |
| // In Secure Connections, the LTK will be generated in Phase 2, not exchanged in Phase 3, so |
| // we want to ensure that it is still put in the pairing_data. |
| if (features_->secure_connections) { |
| ZX_ASSERT(ltk_.has_value()); |
| pairing_data.peer_ltk = pairing_data.local_ltk = ltk_; |
| } else { |
| // The SM-internal LTK is used to validate future encryption events on the existing link. |
| // Encryption with LTKs generated by LE legacy pairing uses the key received by the link-layer |
| // master - so as initiator, this is the peer key, and as responder, this is the local key. |
| const std::optional<LTK>& new_ltk = |
| role_ == Role::kInitiator ? pairing_data.peer_ltk : pairing_data.local_ltk; |
| if (new_ltk.has_value()) { |
| OnNewLongTermKey(*new_ltk); |
| } |
| } |
| |
| if (features_->generate_ct_key.has_value()) { |
| // If we are generating the CT key, we must be using secure connections, and as such the peer |
| // and local LTKs will be equivalent. |
| ZX_ASSERT(features_->secure_connections); |
| ZX_ASSERT(pairing_data.peer_ltk == pairing_data.local_ltk); |
| std::optional<UInt128> ct_key_value = |
| util::LeLtkToBrEdrLinkKey(ltk_->key().value(), features_->generate_ct_key.value()); |
| if (ct_key_value) { |
| pairing_data.cross_transport_key = |
| sm::LTK(ltk_->security(), hci::LinkKey(*ct_key_value, 0, 0)); |
| } else { |
| bt_log(WARN, "sm", "failed to generate cross-transport key"); |
| } |
| } |
| |
| if (features_->will_bond) { |
| delegate_->OnNewPairingData(pairing_data); |
| } else { |
| bt_log( |
| INFO, "gap-le", " %s pairing complete in non-bondable mode with [%s%s%s%s%s]", |
| features_->secure_connections ? "secure connections" : "legacy", |
| pairing_data.peer_ltk ? "peer_ltk " : "", pairing_data.local_ltk ? "local_ltk " : "", |
| pairing_data.irk ? "irk " : "", |
| pairing_data.identity_address |
| ? fxl::StringPrintf("(identity: %s) ", bt_str(*pairing_data.identity_address)).c_str() |
| : "", |
| pairing_data.csrk ? "csrk " : ""); |
| } |
| // So we can pair again if need be. |
| ResetState(); |
| |
| NotifySecurityCallbacks(); |
| } |
| |
| void SecurityManagerImpl::NotifySecurityCallbacks() { |
| // Separate out the requests that are satisfied by the current security level from those 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 <= security().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(), security()); |
| satisfied.pop(); |
| } |
| |
| if (!request_queue_.empty()) { |
| UpgradeSecurityInternal(); |
| } |
| } |
| |
| void SecurityManagerImpl::Reset(IOCapability io_capability) { |
| Abort(ErrorCode::kUnspecifiedReason); |
| io_cap_ = io_capability; |
| ResetState(); |
| } |
| |
| void SecurityManagerImpl::ResetState() { |
| StopTimer(); |
| features_.reset(); |
| sm_chan_->SetChannelHandler(weak_ptr_factory_.GetWeakPtr()); |
| current_phase_ = std::monostate{}; |
| } |
| |
| bool SecurityManagerImpl::AssignLongTermKey(const LTK& ltk) { |
| if (SecurityUpgradeInProgress()) { |
| bt_log(DEBUG, "sm", "Cannot directly assign LTK while pairing is in progress"); |
| return false; |
| } |
| |
| OnNewLongTermKey(ltk); |
| |
| // The initiatior starts encryption when it receives a new LTK from GAP. |
| if (role_ == Role::kInitiator && !le_link_->StartEncryption()) { |
| bt_log(ERROR, "sm", "Failed to initiate authentication procedure"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| void SecurityManagerImpl::SetSecurityProperties(const SecurityProperties& sec) { |
| if (sec != security()) { |
| bt_log(DEBUG, "sm", "security properties changed - handle: %#.4x, new: %s, old: %s", |
| le_link_->handle(), bt_str(sec), bt_str(security())); |
| set_security(sec); |
| delegate_->OnNewSecurityProperties(security()); |
| } |
| } |
| |
| void SecurityManagerImpl::Abort(ErrorCode ecode) { |
| std::visit( |
| [=](auto& arg) { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<std::unique_ptr<Phase1>, T>) { |
| arg->Abort(ecode); |
| } else if constexpr (std::is_base_of_v<PairingPhase, T>) { |
| arg.Abort(ecode); |
| } else { |
| bt_log(DEBUG, "sm", "Attempted to abort security upgrade while not in progress"); |
| } |
| }, |
| current_phase_); |
| // "Abort" should trigger OnPairingFailed. |
| } |
| |
| std::optional<IdentityInfo> SecurityManagerImpl::OnIdentityRequest() { |
| // This is called by the bearer to determine if we have local identity |
| // information to distribute. |
| ZX_ASSERT(delegate_); |
| return delegate_->OnIdentityInformationRequest(); |
| } |
| |
| void SecurityManagerImpl::ConfirmPairing(ConfirmCallback confirm) { |
| ZX_ASSERT(delegate_); |
| delegate_->ConfirmPairing([id = next_pairing_id_, self = weak_ptr_factory_.GetWeakPtr(), |
| cb = std::move(confirm)](bool confirm) { |
| if (!self || self->next_pairing_id_ != id) { |
| bt_log(TRACE, "sm", "ignoring user confirmation for expired pairing: id = %lu", id); |
| return; |
| } |
| cb(confirm); |
| }); |
| } |
| |
| void SecurityManagerImpl::DisplayPasskey(uint32_t passkey, Delegate::DisplayMethod method, |
| ConfirmCallback confirm) { |
| ZX_ASSERT(delegate_); |
| delegate_->DisplayPasskey(passkey, method, |
| [id = next_pairing_id_, self = weak_ptr_factory_.GetWeakPtr(), method, |
| cb = std::move(confirm)](bool confirm) { |
| if (!self || self->next_pairing_id_ != id) { |
| bt_log(TRACE, "sm", |
| "ignoring %s response for expired pairing: id = %lu", |
| util::DisplayMethodToString(method).c_str(), id); |
| return; |
| } |
| cb(confirm); |
| }); |
| } |
| |
| void SecurityManagerImpl::RequestPasskey(PasskeyResponseCallback respond) { |
| ZX_ASSERT(delegate_); |
| delegate_->RequestPasskey([id = next_pairing_id_, self = weak_ptr_factory_.GetWeakPtr(), |
| cb = std::move(respond)](int64_t passkey) { |
| if (!self || self->next_pairing_id_ != id) { |
| bt_log(TRACE, "sm", "ignoring passkey input response for expired pairing: id = %lu", id); |
| return; |
| } |
| cb(passkey); |
| }); |
| } |
| |
| void SecurityManagerImpl::OnRxBFrame(ByteBufferPtr sdu) { |
| fit::result<ValidPacketReader, ErrorCode> maybe_reader = ValidPacketReader::ParseSdu(sdu); |
| if (maybe_reader.is_error()) { |
| bt_log(INFO, "sm", "dropped SMP packet: %s", bt_str(Status(maybe_reader.error()))); |
| return; |
| } |
| ValidPacketReader reader = maybe_reader.value(); |
| Code smp_code = reader.code(); |
| |
| if (smp_code == kPairingRequest) { |
| OnPairingRequest(reader.payload<PairingRequestParams>()); |
| } else if (smp_code == kSecurityRequest) { |
| OnSecurityRequest(reader.payload<AuthReqField>()); |
| } else { |
| bt_log(INFO, "sm", "dropped unexpected SMP code %#.2X when not pairing", smp_code); |
| } |
| } |
| |
| void SecurityManagerImpl::OnChannelClosed() { |
| bt_log(DEBUG, "sm", "SMP channel closed while not pairing"); |
| } |
| |
| void SecurityManagerImpl::OnPairingFailed(Status status) { |
| std::string phase_status = std::visit( |
| [=](auto& arg) { |
| using T = std::decay_t<decltype(arg)>; |
| std::string s; |
| if constexpr (std::is_same_v<std::unique_ptr<Phase1>, T>) { |
| s = arg->ToString(); |
| } else if constexpr (std::is_base_of_v<PairingPhase, T>) { |
| s = arg.ToString(); |
| } else { |
| ZX_PANIC("security upgrade cannot fail when current_phase_ is std::monostate!"); |
| } |
| return s; |
| }, |
| current_phase_); |
| bt_log(ERROR, "sm", "LE pairing failed: %s. Current pairing phase: %s", status.ToString().c_str(), |
| phase_status.c_str()); |
| StopTimer(); |
| // TODO(fxbug.dev/910): implement "waiting interval" to prevent repeated attempts |
| // as described in Vol 3, Part H, 2.3.6. |
| |
| ZX_ASSERT(delegate_); |
| delegate_->OnPairingComplete(status); |
| |
| auto requests = std::move(request_queue_); |
| while (!requests.empty()) { |
| requests.front().callback(status, security()); |
| requests.pop(); |
| } |
| |
| if (SecurityUpgradeInProgress()) { |
| ZX_ASSERT(le_link_); |
| le_link_->set_le_ltk(hci::LinkKey()); |
| } |
| ResetState(); |
| // Reset state before potentially disconnecting link to avoid causing pairing phase to fail twice. |
| if (!status && status.error() == HostError::kTimedOut) { |
| // Per v5.2 Vol. 3 Part H 3.4, after a pairing timeout "No further SMP commands shall be sent |
| // over the L2CAP Security Manager Channel. A new Pairing process shall only be performed when a |
| // new physical link has been established." |
| bt_log(WARN, "sm", "pairing timed out! disconnecting link"); |
| sm_chan_->SignalLinkError(); |
| } |
| } |
| |
| void SecurityManagerImpl::StartNewTimer() { |
| if (timeout_task_.is_pending()) { |
| ZX_ASSERT(timeout_task_.Cancel() == ZX_OK); |
| } |
| timeout_task_.PostDelayed(async_get_default_dispatcher(), kPairingTimeout); |
| } |
| |
| void SecurityManagerImpl::StopTimer() { |
| if (timeout_task_.is_pending()) { |
| zx_status_t status = timeout_task_.Cancel(); |
| if (status != ZX_OK) { |
| bt_log(TRACE, "sm", "smp: failed to stop timer: %s", zx_status_get_string(status)); |
| } |
| } |
| } |
| |
| void SecurityManagerImpl::OnPairingTimeout() { |
| std::visit( |
| [=](auto& arg) { |
| using T = std::decay_t<decltype(arg)>; |
| if constexpr (std::is_same_v<std::unique_ptr<Phase1>, T>) { |
| arg->OnFailure(Status(HostError::kTimedOut)); |
| } else if constexpr (std::is_base_of_v<PairingPhase, T>) { |
| arg.OnFailure(Status(HostError::kTimedOut)); |
| } else { |
| ZX_PANIC("cannot timeout when current_phase_ is std::monostate!"); |
| } |
| }, |
| current_phase_); |
| } |
| |
| std::pair<DeviceAddress, DeviceAddress> SecurityManagerImpl::LEPairingAddresses() { |
| ZX_ASSERT(SecurityUpgradeInProgress()); |
| const DeviceAddress *initiator = &le_link_->local_address(), |
| *responder = &le_link_->peer_address(); |
| if (role_ == Role::kResponder) { |
| std::swap(initiator, responder); |
| } |
| return std::make_pair(*initiator, *responder); |
| } |
| |
| void SecurityManagerImpl::OnNewLongTermKey(const LTK& ltk) { |
| ltk_ = ltk; |
| le_link_->set_le_ltk(ltk.key()); |
| } |
| |
| Status SecurityManagerImpl::ValidateExistingLocalLtk() { |
| auto err = HostError::kNoError; |
| if (!ltk_.has_value() || !le_link_->ltk().has_value()) { |
| // The LTKs should always be present when this method is called. |
| err = HostError::kNotFound; |
| } else if (!(*le_link_->ltk() == ltk_->key())) { |
| // As only SM should ever change the LE Link encryption key, these two values should always be |
| // in sync, i.e. something in the system is acting unreliably if they get out of sync. |
| err = HostError::kNotReliable; |
| } |
| Status status(err); |
| if (!status) { |
| // SM does not own the link, so although the checks above should never fail, disconnecting the |
| // link (vs. ASSERTing these checks) is safer against non-SM code potentially touching the key. |
| delegate_->OnAuthenticationFailure(hci::Status(hci::StatusCode::kPinOrKeyMissing)); |
| sm_chan_->SignalLinkError(); |
| } |
| return status; |
| } |
| |
| std::unique_ptr<SecurityManager> SecurityManager::Create(fxl::WeakPtr<hci::Connection> link, |
| fbl::RefPtr<l2cap::Channel> smp, |
| IOCapability io_capability, |
| fxl::WeakPtr<Delegate> delegate, |
| BondableMode bondable_mode, |
| gap::LeSecurityMode security_mode) { |
| return std::unique_ptr<SecurityManagerImpl>( |
| new SecurityManagerImpl(std::move(link), std::move(smp), io_capability, std::move(delegate), |
| bondable_mode, security_mode)); |
| } |
| |
| SecurityManager::SecurityManager(BondableMode bondable_mode, gap::LeSecurityMode security_mode) |
| : bondable_mode_(bondable_mode), security_mode_(security_mode) {} |
| |
| } // namespace bt::sm |