• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "pw_bluetooth_sapphire/internal/host/sm/phase_2_legacy.h"
16 
17 #include <optional>
18 
19 #include "pw_bluetooth_sapphire/internal/host/common/assert.h"
20 #include "pw_bluetooth_sapphire/internal/host/common/log.h"
21 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
22 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h"
23 #include "pw_bluetooth_sapphire/internal/host/hci/connection.h"
24 #include "pw_bluetooth_sapphire/internal/host/sm/delegate.h"
25 #include "pw_bluetooth_sapphire/internal/host/sm/packet.h"
26 #include "pw_bluetooth_sapphire/internal/host/sm/pairing_phase.h"
27 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
28 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
29 #include "pw_bluetooth_sapphire/internal/host/sm/util.h"
30 
31 namespace bt::sm {
32 namespace {
33 // We do not support OOB pairing & Legacy pairing does not permit Numeric
34 // Comparison.
IsSupportedLegacyMethod(PairingMethod method)35 bool IsSupportedLegacyMethod(PairingMethod method) {
36   return (method == PairingMethod::kJustWorks ||
37           method == PairingMethod::kPasskeyEntryDisplay ||
38           method == PairingMethod::kPasskeyEntryInput);
39 }
40 }  // namespace
41 
Phase2Legacy(PairingChannel::WeakPtr chan,Listener::WeakPtr listener,Role role,PairingFeatures features,const ByteBuffer & preq,const ByteBuffer & pres,const DeviceAddress & initiator_add,const DeviceAddress & responder_add,OnPhase2KeyGeneratedCallback cb)42 Phase2Legacy::Phase2Legacy(PairingChannel::WeakPtr chan,
43                            Listener::WeakPtr listener,
44                            Role role,
45                            PairingFeatures features,
46                            const ByteBuffer& preq,
47                            const ByteBuffer& pres,
48                            const DeviceAddress& initiator_add,
49                            const DeviceAddress& responder_add,
50                            OnPhase2KeyGeneratedCallback cb)
51     : PairingPhase(std::move(chan), std::move(listener), role),
52       sent_local_confirm_(false),
53       sent_local_rand_(false),
54       tk_(std::nullopt),
55       local_confirm_(std::nullopt),
56       peer_confirm_(std::nullopt),
57       local_rand_(std::nullopt),
58       peer_rand_(std::nullopt),
59       features_(features),
60       initiator_addr_(initiator_add),
61       responder_addr_(responder_add),
62       on_stk_ready_(std::move(cb)),
63       weak_self_(this) {
64   // Cache |preq| and |pres|. These are used for confirm value generation.
65   BT_ASSERT(preq.size() == preq_.size());
66   BT_ASSERT(pres.size() == pres_.size());
67   BT_ASSERT_MSG(IsSupportedLegacyMethod(features.method),
68                 "unsupported legacy pairing method!");
69   preq.Copy(&preq_);
70   pres.Copy(&pres_);
71   SetPairingChannelHandler(*this);
72 }
73 
Start()74 void Phase2Legacy::Start() {
75   BT_ASSERT(!has_failed());
76   BT_ASSERT(!features_.secure_connections);
77   BT_ASSERT(!tk_.has_value());
78   BT_ASSERT(!peer_confirm_.has_value());
79   BT_ASSERT(!peer_rand_.has_value());
80   BT_ASSERT(!sent_local_confirm_);
81   BT_ASSERT(!sent_local_rand_);
82   MakeTemporaryKeyRequest();
83 }
84 
MakeTemporaryKeyRequest()85 void Phase2Legacy::MakeTemporaryKeyRequest() {
86   bt_log(DEBUG,
87          "sm",
88          "TK request - method: %s",
89          sm::util::PairingMethodToString(features_.method).c_str());
90   BT_ASSERT(listener().is_alive());
91   auto self = weak_self_.GetWeakPtr();
92   if (features_.method == sm::PairingMethod::kPasskeyEntryInput) {
93     // The TK will be provided by the user.
94     listener()->RequestPasskey([self](int64_t passkey) {
95       if (!self.is_alive()) {
96         return;
97       }
98       bool success = passkey >= 0;
99       self->HandleTemporaryKey(success ? std::optional<uint32_t>(passkey)
100                                        : std::nullopt);
101     });
102     return;
103   }
104 
105   if (features_.method == sm::PairingMethod::kPasskeyEntryDisplay) {
106     // Randomly generate a 6 digit passkey.
107     uint32_t passkey;
108     random_generator()->GetInt<uint32_t>(passkey,
109                                          /*exclusive_upper_bound=*/1'000'000);
110     listener()->DisplayPasskey(
111         passkey,
112         Delegate::DisplayMethod::kPeerEntry,
113         [passkey, self](bool confirm) {
114           if (!self.is_alive()) {
115             return;
116           }
117           self->HandleTemporaryKey(confirm ? std::optional<uint32_t>(passkey)
118                                            : std::nullopt);
119         });
120     return;
121   }
122 
123   // TODO(fxbug.dev/42138242): Support providing a TK out of band.
124   BT_ASSERT(features_.method == sm::PairingMethod::kJustWorks);
125   listener()->ConfirmPairing([self](bool confirm) {
126     if (!self.is_alive()) {
127       return;
128     }
129     self->HandleTemporaryKey(confirm ? std::optional<uint32_t>(0)
130                                      : std::nullopt);
131   });
132 }
133 
HandleTemporaryKey(std::optional<uint32_t> maybe_tk)134 void Phase2Legacy::HandleTemporaryKey(std::optional<uint32_t> maybe_tk) {
135   if (!maybe_tk.has_value()) {
136     bt_log(INFO, "sm", "temporary key listener responded with error; aborting");
137     if (features_.method == PairingMethod::kPasskeyEntryInput) {
138       Abort(ErrorCode::kPasskeyEntryFailed);
139     } else {
140       Abort(ErrorCode::kUnspecifiedReason);
141     }
142     return;
143   }
144   uint32_t tk = *maybe_tk;
145   tk_ = UInt128{0};
146   // Set the lower bits to |tk|.
147   tk = htole32(tk);
148   std::memcpy(tk_.value().data(), &tk, sizeof(tk));
149 
150   // We have TK so we can generate the confirm value now.
151   local_rand_ = Random<UInt128>();
152   local_confirm_ = UInt128();
153   util::C1(tk_.value(),
154            local_rand_.value(),
155            preq_,
156            pres_,
157            initiator_addr_,
158            responder_addr_,
159            &(local_confirm_.value()));
160 
161   // If we are the initiator then we just generated the "Mconfirm" value. We
162   // start the exchange by sending this value to the peer. Otherwise this is the
163   // "Sconfirm" value and we either:
164   //    a. send it now if the peer has sent us its confirm value while we were
165   //    waiting for the TK.
166   //    b. send it later when we receive Mconfirm.
167   if (role() == Role::kInitiator || peer_confirm_.has_value()) {
168     SendConfirmValue();
169   }
170 }
171 
SendConfirmValue()172 void Phase2Legacy::SendConfirmValue() {
173   BT_ASSERT(!sent_local_confirm_);
174   BT_ASSERT(local_confirm_.has_value());
175   // Only allowed on the LE transport.
176   if (sm_chan().link_type() != bt::LinkType::kLE) {
177     bt_log(DEBUG,
178            "sm",
179            "attempted to send confirm value over BR/EDR, not sending");
180     return;
181   }
182 
183   sm_chan().SendMessage(kPairingConfirm, *local_confirm_);
184   sent_local_confirm_ = true;
185 }
186 
OnPairingConfirm(PairingConfirmValue confirm)187 void Phase2Legacy::OnPairingConfirm(PairingConfirmValue confirm) {
188   if (fit::result result = CanReceivePairingConfirm(); result.is_error()) {
189     Abort(result.error_value());
190     return;
191   }
192 
193   peer_confirm_ = confirm;
194 
195   if (role() == Role::kInitiator) {
196     // We MUST have a TK and have previously generated an Mconfirm - this was
197     // implicitly checked in CanReceivePairingConfirm by checking whether we've
198     // sent the confirm value.
199     BT_ASSERT(tk_.has_value());
200     BT_ASSERT(sent_local_confirm_);
201 
202     // We have sent Mconfirm and just received Sconfirm. We now send Mrand for
203     // the peer to compare.
204     SendRandomValue();
205   } else if (tk_.has_value()) {
206     // We are the responder and have just received Mconfirm. If we already have
207     // a TK, we now send the local confirm to the peer. If not,
208     // HandleTemporaryKey will take care of that.
209     SendConfirmValue();
210   }
211 }
212 
SendRandomValue()213 void Phase2Legacy::SendRandomValue() {
214   BT_ASSERT(!sent_local_rand_);
215   // This is always generated in the TK callback, which must have been called by
216   // now as the random are sent after the confirm values, and the TK must exist
217   // in order to send the confirm.
218   BT_ASSERT(local_rand_.has_value());
219 
220   // Only allowed on the LE transport.
221   if (sm_chan().link_type() != bt::LinkType::kLE) {
222     bt_log(
223         WARN, "sm", "attempted to send confirm value over BR/EDR, not sending");
224     return;
225   }
226 
227   sm_chan().SendMessage(kPairingRandom, *local_rand_);
228   sent_local_rand_ = true;
229 }
230 
OnPairingRandom(PairingRandomValue rand)231 void Phase2Legacy::OnPairingRandom(PairingRandomValue rand) {
232   if (fit::result result = CanReceivePairingRandom(); result.is_error()) {
233     Abort(result.error_value());
234     return;
235   }
236   // These should have been checked in CanReceivePairingRandom
237   BT_ASSERT(local_rand_.has_value());
238   BT_ASSERT(tk_.has_value());
239   BT_ASSERT(peer_confirm_.has_value());
240 
241   peer_rand_ = rand;
242 
243   // We have the peer's confirm and rand. Verify the peer confirm to validate
244   // the authentication.
245   UInt128 peer_confirm_check;
246   util::C1(tk_.value(),
247            peer_rand_.value(),
248            preq_,
249            pres_,
250            initiator_addr_,
251            responder_addr_,
252            &peer_confirm_check);
253   if (peer_confirm_check != peer_confirm_) {
254     bt_log(WARN,
255            "sm",
256            "%sconfirm value does not match!",
257            role() == Role::kInitiator ? "S" : "M");
258     Abort(ErrorCode::kConfirmValueFailed);
259     return;
260   }
261 
262   // Generate the STK.
263   UInt128 stk;
264   auto [initiator_rand, responder_rand] =
265       util::MapToRoles(*local_rand_, *peer_rand_, role());
266   util::S1(tk_.value(), responder_rand, initiator_rand, &stk);
267 
268   // Mask the key based on the requested encryption key size.
269   uint8_t key_size = features_.encryption_key_size;
270   if (key_size < kMaxEncryptionKeySize) {
271     MutableBufferView view(stk.data() + key_size,
272                            kMaxEncryptionKeySize - key_size);
273     view.SetToZeros();
274   }
275 
276   // We've generated the STK, so Phase 2 is now over if we're the initiator.
277   on_stk_ready_(stk);
278 
279   // As responder, we choose to notify the STK to the higher layer before
280   // sending our SRand. We expect the peer initiator to request encryption
281   // immediately after receiving SRand, and we want to ensure the STK is
282   // available at the hci::Connection layer when this occurs.
283   if (role() == Role::kResponder) {
284     SendRandomValue();
285   }
286 }
287 
CanReceivePairingConfirm() const288 fit::result<ErrorCode> Phase2Legacy::CanReceivePairingConfirm() const {
289   // Only allowed on the LE transport.
290   if (sm_chan().link_type() != bt::LinkType::kLE) {
291     bt_log(DEBUG, "sm", "\"Confirm value\" over BR/EDR not supported!");
292     return fit::error(ErrorCode::kCommandNotSupported);
293   }
294 
295   // Per the message sequence charts in V5.1 Vol. 3 Part H Appendix C.2.1,
296   // reject the pairing confirm value and abort if
297   //    a. we are the initiator, and have not yet sent our confirm value.
298   //    b. we are the responder, and have already sent our confirm value.
299   if ((role() == Role::kInitiator && !sent_local_confirm_) ||
300       (role() == Role::kResponder && sent_local_confirm_)) {
301     bt_log(
302         WARN, "sm", "abort pairing due to confirm value received out of order");
303     return fit::error(ErrorCode::kUnspecifiedReason);
304   }
305 
306   // Legacy pairing only allows for one confirm/random exchange per pairing.
307   if (peer_confirm_.has_value()) {
308     bt_log(WARN, "sm", "already received confirm value! aborting");
309     return fit::error(ErrorCode::kUnspecifiedReason);
310   }
311 
312   // The confirm value shouldn't be sent after the random value. (See spec V5.0
313   // Vol 3, Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of
314   // events).
315   if (peer_rand_.has_value() || sent_local_rand_) {
316     bt_log(
317         WARN, "sm", "\"Pairing Confirm\" must come before \"Pairing Random\"");
318     return fit::error(ErrorCode::kUnspecifiedReason);
319   }
320 
321   return fit::ok();
322 }
323 
CanReceivePairingRandom() const324 fit::result<ErrorCode> Phase2Legacy::CanReceivePairingRandom() const {
325   // Only allowed on the LE transport.
326   if (sm_chan().link_type() != bt::LinkType::kLE) {
327     bt_log(DEBUG, "sm", "\"Random value\" over BR/EDR not supported!");
328     return fit::error(ErrorCode::kCommandNotSupported);
329   }
330 
331   if (!tk_.has_value()) {
332     bt_log(
333         WARN, "sm", "abort pairing, random value received before user input");
334     return fit::error(ErrorCode::kUnspecifiedReason);
335   }
336 
337   // V5.0 Vol 3, Part H, 2.3.5.5 dictates that there should be exactly one
338   // pairing random value received by each peer in Legacy Pairing Phase 2.
339   if (peer_rand_.has_value()) {
340     bt_log(WARN, "sm", "already received random value! aborting");
341     return fit::error(ErrorCode::kUnspecifiedReason);
342   }
343 
344   // The random value shouldn't be sent before the confirm value. See V5.0 Vol
345   // 3, Part H, 2.3.5.5 and Appendix C.2.1.1 for the specific order of events.
346   if (!peer_confirm_.has_value()) {
347     bt_log(WARN, "sm", "\"Pairing Rand\" expected after \"Pairing Confirm\"");
348     return fit::error(ErrorCode::kUnspecifiedReason);
349   }
350 
351   if (role() == Role::kInitiator) {
352     // The initiator distributes both values before the responder sends Srandom.
353     if (!sent_local_rand_ || !sent_local_confirm_) {
354       bt_log(WARN, "sm", "\"Pairing Random\" received in wrong order!");
355       return fit::error(ErrorCode::kUnspecifiedReason);
356     }
357   } else {
358     // We know we have not received Mrand, and should not have sent Srand
359     // without receiving Mrand.
360     BT_ASSERT(!sent_local_rand_);
361 
362     // We need to send Sconfirm before the initiator sends Mrand.
363     if (!sent_local_confirm_) {
364       bt_log(WARN, "sm", "\"Pairing Random\" received in wrong order!");
365       return fit::error(ErrorCode::kUnspecifiedReason);
366     }
367   }
368 
369   return fit::ok();
370 }
371 
OnRxBFrame(ByteBufferPtr sdu)372 void Phase2Legacy::OnRxBFrame(ByteBufferPtr sdu) {
373   fit::result<ErrorCode, ValidPacketReader> maybe_reader =
374       ValidPacketReader::ParseSdu(sdu);
375   if (maybe_reader.is_error()) {
376     Abort(maybe_reader.error_value());
377     return;
378   }
379   ValidPacketReader reader = maybe_reader.value();
380   Code smp_code = reader.code();
381 
382   if (smp_code == kPairingFailed) {
383     OnFailure(Error(reader.payload<ErrorCode>()));
384   } else if (smp_code == kPairingConfirm) {
385     OnPairingConfirm(reader.payload<PairingConfirmValue>());
386   } else if (smp_code == kPairingRandom) {
387     OnPairingRandom(reader.payload<PairingRandomValue>());
388   } else {
389     bt_log(INFO,
390            "sm",
391            "received unexpected code %#.2X when in Pairing Legacy Phase 2",
392            smp_code);
393     Abort(ErrorCode::kUnspecifiedReason);
394   }
395 }
396 
397 }  // namespace bt::sm
398