1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 //     https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 
15 #pragma once
16 #include <unordered_map>
17 
18 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
19 #include "pw_bluetooth_sapphire/internal/host/l2cap/channel.h"
20 #include "pw_bluetooth_sapphire/internal/host/l2cap/scoped_channel.h"
21 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
22 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
23 
24 namespace bt::sm {
25 
26 // Bridge class for the SMP L2CAP channel, which implements SM-specific
27 // functionality on top of existing L2CAP functionality. Besides this
28 // SM-specific functionality, also allows runtime modification of L2CAP event
29 // callbacks by changing the PairingChannel::Handler pointer.
30 
31 class PairingChannel {
32  public:
33   // Interface for receiving L2CAP channel events.
34   class Handler {
35    public:
36     virtual ~Handler() = default;
37     virtual void OnRxBFrame(ByteBufferPtr) = 0;
38     virtual void OnChannelClosed() = 0;
39 
40     using WeakPtr = WeakSelf<Handler>::WeakPtr;
41   };
42 
43   // Initializes this PairingChannel with the L2CAP SMP fixed channel that this
44   // class wraps and the specified timer reset method. For use in production
45   // code.
46   PairingChannel(l2cap::Channel::WeakPtr chan, fit::closure timer_resetter);
47 
48   // Initializes this PairingChannel with a no-op timer reset method. Only for
49   // use in tests of classes which do not depend on the timer reset behavior.
50   explicit PairingChannel(l2cap::Channel::WeakPtr chan);
51 
52   // For setting the new handler, expected to be used when switching phases.
53   // PairingChannel is not fully initialized until SetChannelHandler has been
54   // called with a valid Handler. This two-phase initialization exists because
55   // concrete Handlers are expected to depend on PairingChannels.
56   void SetChannelHandler(Handler::WeakPtr new_handler);
57 
58   // Wrapper which encapsulates some of the boilerplate involved in sending an
59   // SMP object.
60   template <typename PayloadType>
SendMessage(Code message_code,const PayloadType & payload)61   void SendMessage(Code message_code, const PayloadType& payload) {
62     SendMessageNoTimerReset(message_code, payload);
63     reset_timer_();
64   }
65 
66   // This method exists for situations when we send messages while not pairing
67   // (e.g. rejection of pairing), where we do not want to reset the SMP timer
68   // upon transmission.
69   template <typename PayloadType>
SendMessageNoTimerReset(Code message_code,const PayloadType & payload)70   void SendMessageNoTimerReset(Code message_code, const PayloadType& payload) {
71     auto kExpectedSize = kCodeToPayloadSize.find(message_code);
72     PW_CHECK(kExpectedSize != kCodeToPayloadSize.end());
73     PW_CHECK(sizeof(PayloadType) == kExpectedSize->second);
74     auto pdu = util::NewPdu(sizeof(PayloadType));
75     PacketWriter writer(message_code, pdu.get());
76     *writer.mutable_payload<PayloadType>() = payload;
77     chan_->Send(std::move(pdu));
78   }
79 
80   using WeakPtr = WeakSelf<PairingChannel>::WeakPtr;
GetWeakPtr()81   PairingChannel::WeakPtr GetWeakPtr() { return weak_self_.GetWeakPtr(); }
82 
SupportsSecureConnections()83   bool SupportsSecureConnections() const {
84     return chan_->max_rx_sdu_size() >= kLeSecureConnectionsMtu &&
85            chan_->max_tx_sdu_size() >= kLeSecureConnectionsMtu;
86   }
87 
SignalLinkError()88   void SignalLinkError() { chan_->SignalLinkError(); }
link_type()89   bt::LinkType link_type() const { return chan_->link_type(); }
90   ~PairingChannel() = default;
91 
92  private:
93   // Used to delegate the L2CAP callbacks to the current handler
94   void OnRxBFrame(ByteBufferPtr ptr);
95   void OnChannelClosed();
96 
97   // The L2CAP Channel this class wraps. Uses a ScopedChannel because a
98   // PairingChannel is expected to own the lifetime of the underlying L2CAP
99   // channel.
100   l2cap::ScopedChannel chan_;
101 
102   // Per v5.2 Vol. 3 Part H 3.4, "The Security Manager Timer shall be reset when
103   // an L2CAP SMP command is queued for transmission". This closure signals this
104   // reset to occur.
105   fit::closure reset_timer_;
106 
107   // L2CAP channel events are delegated to this handler.
108   Handler::WeakPtr handler_;
109 
110   WeakSelf<PairingChannel> weak_self_;
111   BT_DISALLOW_COPY_AND_ASSIGN_ALLOW_MOVE(PairingChannel);
112 };
113 
114 }  // namespace bt::sm
115