• 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/util.h"
16 
17 #include <pw_assert/check.h>
18 #include <pw_bytes/endian.h>
19 #include <pw_crypto/aes.h>
20 #include <pw_crypto/aes_cmac.h>
21 #include <pw_preprocessor/compiler.h>
22 
23 #include <algorithm>
24 #include <optional>
25 
26 #include "pw_bluetooth_sapphire/internal/host/common/byte_buffer.h"
27 #include "pw_bluetooth_sapphire/internal/host/common/device_address.h"
28 #include "pw_bluetooth_sapphire/internal/host/common/random.h"
29 #include "pw_bluetooth_sapphire/internal/host/common/uint128.h"
30 #include "pw_bluetooth_sapphire/internal/host/common/uint256.h"
31 #include "pw_bluetooth_sapphire/internal/host/sm/error.h"
32 #include "pw_bluetooth_sapphire/internal/host/sm/smp.h"
33 #include "pw_bluetooth_sapphire/internal/host/sm/types.h"
34 
35 namespace bt::sm::util {
36 namespace {
37 
38 constexpr size_t kPreqSize = 7;
39 constexpr uint32_t k24BitMax = 0xFFFFFF;
40 // F5 parameters are stored in little-endian
41 const auto kF5Salt = UInt128{0xBE,
42                              0x83,
43                              0x60,
44                              0x5A,
45                              0xDB,
46                              0x0B,
47                              0x37,
48                              0x60,
49                              0x38,
50                              0xA5,
51                              0xF5,
52                              0xAA,
53                              0x91,
54                              0x83,
55                              0x88,
56                              0x6C};
57 const auto kF5KeyId = std::array<uint8_t, 4>{0x65, 0x6C, 0x74, 0x62};
58 
59 using pw::crypto::aes_cmac::Cmac;
60 using pw::crypto::unsafe::aes::EncryptBlock;
61 
62 // Swap the endianness of a 128-bit integer. |in| and |out| should not be backed
63 // by the same buffer.
Swap128(const UInt128 & in,UInt128 * out)64 void Swap128(const UInt128& in, UInt128* out) {
65   PW_DCHECK(out);
66   for (size_t i = 0; i < in.size(); ++i) {
67     (*out)[i] = in[in.size() - i - 1];
68   }
69 }
70 
71 // Get a UInt128 view as a const byte span.
Bytes128(const UInt128 & value)72 pw::span<const std::byte, kUInt128Size> Bytes128(const UInt128& value) {
73   return pw::span<const std::byte, kUInt128Size>(
74       reinterpret_cast<const std::byte*>(value.data()), value.size());
75 }
76 
77 // Get a UInt128 view as a mutable byte span.
Bytes128(UInt128 * value)78 pw::span<std::byte, kUInt128Size> Bytes128(UInt128* value) {
79   return pw::span<std::byte, kUInt128Size>(
80       reinterpret_cast<std::byte*>(value->data()), value->size());
81 }
82 
83 // XOR two 128-bit integers and return the result in |out|. It is possible to
84 // pass a pointer to one of the inputs as |out|.
Xor128(const UInt128 & int1,const UInt128 & int2,UInt128 * out)85 void Xor128(const UInt128& int1, const UInt128& int2, UInt128* out) {
86   PW_DCHECK(out);
87 
88   for (size_t i = 0; i < kUInt128Size; ++i) {
89     out->at(i) = int1.at(i) ^ int2.at(i);
90   }
91 }
92 
93 // Writes |data| to |output_data_loc| & returns a view of the remainder of
94 // |output_data_loc|.
95 template <typename InputType>
WriteToBuffer(InputType data,MutableBufferView output_data_loc)96 MutableBufferView WriteToBuffer(InputType data,
97                                 MutableBufferView output_data_loc) {
98   output_data_loc.WriteObj(data);
99   return output_data_loc.mutable_view(sizeof(data));
100 }
101 
102 // Converts |addr| into the 56-bit format used by F5/F6 and writes that data to
103 // a BufferView. Returns a buffer view pointing just past the last byte written.
WriteCryptoDeviceAddr(const DeviceAddress & addr,const MutableBufferView & out)104 MutableBufferView WriteCryptoDeviceAddr(const DeviceAddress& addr,
105                                         const MutableBufferView& out) {
106   std::array<uint8_t, sizeof(addr.value()) + 1> little_endian_addr_buffer;
107   BufferView addr_bytes = addr.value().bytes();
108   std::copy(
109       addr_bytes.begin(), addr_bytes.end(), little_endian_addr_buffer.data());
110   little_endian_addr_buffer[6] = addr.IsPublic() ? 0x00 : 0x01;
111   return WriteToBuffer(little_endian_addr_buffer, out);
112 }
113 
114 }  // namespace
115 
IOCapabilityToString(IOCapability capability)116 std::string IOCapabilityToString(IOCapability capability) {
117   switch (capability) {
118     case IOCapability::kDisplayOnly:
119       return "Display Only";
120     case IOCapability::kDisplayYesNo:
121       return "Display w/ Confirmation";
122     case IOCapability::kKeyboardOnly:
123       return "Keyboard";
124     case IOCapability::kNoInputNoOutput:
125       return "No I/O";
126     case IOCapability::kKeyboardDisplay:
127       return "Keyboard w/ Display";
128     default:
129       break;
130   }
131   return "(unknown)";
132 }
133 
IOCapabilityForHci(IOCapability capability)134 pw::bluetooth::emboss::IoCapability IOCapabilityForHci(
135     IOCapability capability) {
136   switch (capability) {
137     case IOCapability::kDisplayOnly:
138       return pw::bluetooth::emboss::IoCapability::DISPLAY_ONLY;
139     case IOCapability::kDisplayYesNo:
140       return pw::bluetooth::emboss::IoCapability::DISPLAY_YES_NO;
141     case IOCapability::kKeyboardOnly:
142       return pw::bluetooth::emboss::IoCapability::KEYBOARD_ONLY;
143     case IOCapability::kNoInputNoOutput:
144       return pw::bluetooth::emboss::IoCapability::NO_INPUT_NO_OUTPUT;
145 
146     // There's no dedicated HCI "Keyboard w/ Display" IO Capability. Use
147     // DisplayYesNo for devices with keyboard input and numeric output. See Core
148     // Spec v5.0 Vol 3, Part C, Section 5.2.2.5 (Table 5.5).
149     case IOCapability::kKeyboardDisplay:
150       return pw::bluetooth::emboss::IoCapability::DISPLAY_YES_NO;
151     default:
152       break;
153   }
154   return pw::bluetooth::emboss::IoCapability::NO_INPUT_NO_OUTPUT;
155 }
156 
PairingMethodToString(PairingMethod method)157 std::string PairingMethodToString(PairingMethod method) {
158   switch (method) {
159     case PairingMethod::kJustWorks:
160       return "Just Works";
161     case PairingMethod::kPasskeyEntryInput:
162       return "Passkey Entry (input)";
163     case PairingMethod::kPasskeyEntryDisplay:
164       return "Passkey Entry (display)";
165     case PairingMethod::kNumericComparison:
166       return "Numeric Comparison";
167     case PairingMethod::kOutOfBand:
168       return "OOB";
169     default:
170       break;
171   }
172   return "(unknown)";
173 }
174 
DisplayMethodToString(Delegate::DisplayMethod method)175 std::string DisplayMethodToString(Delegate::DisplayMethod method) {
176   switch (method) {
177     case Delegate::DisplayMethod::kComparison:
178       return "Numeric Comparison";
179     case Delegate::DisplayMethod::kPeerEntry:
180       return "Peer Passkey Entry";
181     default:
182       return "(unknown)";
183   }
184 }
185 
NewPdu(size_t param_size)186 MutableByteBufferPtr NewPdu(size_t param_size) {
187   // TODO(fxbug.dev/42083692): Remove unique_ptr->DynamicByteBuffer double
188   // indirection once sufficient progress has been made on the attached bug
189   // (specifically re:l2cap::Channel::Send).
190   return std::make_unique<DynamicByteBuffer>(sizeof(Header) + param_size);
191 }
192 
SelectPairingMethod(bool sec_conn,bool local_oob,bool peer_oob,bool mitm_required,IOCapability local_ioc,IOCapability peer_ioc,bool local_initiator)193 PairingMethod SelectPairingMethod(
194     bool sec_conn,
195     bool local_oob,
196     bool peer_oob,
197     bool mitm_required,  // inclusive-language: ignore
198     IOCapability local_ioc,
199     IOCapability peer_ioc,
200     bool local_initiator) {
201   if ((sec_conn && (local_oob || peer_oob)) ||
202       (!sec_conn && local_oob && peer_oob)) {
203     return PairingMethod::kOutOfBand;
204   }
205 
206   // inclusive-language: ignore
207   // If neither device requires MITM protection or if the peer has not I/O
208   // capable, we select Just Works.
209   // inclusive-language: ignore
210   if (!mitm_required || peer_ioc == IOCapability::kNoInputNoOutput) {
211     return PairingMethod::kJustWorks;
212   }
213 
214   // Select the pairing method by comparing I/O capabilities. The switch
215   // statement will return if an authenticated entry method is selected.
216   // Otherwise, we'll break out and default to Just Works below.
217   switch (local_ioc) {
218     case IOCapability::kNoInputNoOutput:
219       break;
220 
221     case IOCapability::kDisplayOnly:
222       PW_MODIFY_DIAGNOSTICS_PUSH();
223       PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
224       switch (peer_ioc) {
225         case IOCapability::kKeyboardOnly:
226         case IOCapability::kKeyboardDisplay:
227           return PairingMethod::kPasskeyEntryDisplay;
228         case IOCapability::kDisplayOnly:
229         case IOCapability::kDisplayYesNo:
230         case IOCapability::kNoInputNoOutput:
231           break;
232       }
233       PW_MODIFY_DIAGNOSTICS_POP();
234       break;
235 
236     case IOCapability::kDisplayYesNo:
237       PW_MODIFY_DIAGNOSTICS_PUSH();
238       PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
239       switch (peer_ioc) {
240         case IOCapability::kDisplayYesNo:
241           return sec_conn ? PairingMethod::kNumericComparison
242                           : PairingMethod::kJustWorks;
243         case IOCapability::kKeyboardDisplay:
244           return sec_conn ? PairingMethod::kNumericComparison
245                           : PairingMethod::kPasskeyEntryDisplay;
246         case IOCapability::kKeyboardOnly:
247           return PairingMethod::kPasskeyEntryDisplay;
248         case IOCapability::kDisplayOnly:
249         case IOCapability::kNoInputNoOutput:
250           break;
251       }
252       PW_MODIFY_DIAGNOSTICS_POP();
253       break;
254 
255     case IOCapability::kKeyboardOnly:
256       return PairingMethod::kPasskeyEntryInput;
257 
258     case IOCapability::kKeyboardDisplay:
259       PW_MODIFY_DIAGNOSTICS_PUSH();
260       PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
261       switch (peer_ioc) {
262         case IOCapability::kKeyboardOnly:
263           return PairingMethod::kPasskeyEntryDisplay;
264         case IOCapability::kDisplayOnly:
265           return PairingMethod::kPasskeyEntryInput;
266         case IOCapability::kDisplayYesNo:
267           return sec_conn ? PairingMethod::kNumericComparison
268                           : PairingMethod::kPasskeyEntryInput;
269         case IOCapability::kKeyboardDisplay:
270         case IOCapability::kNoInputNoOutput:
271           break;
272       }
273       PW_MODIFY_DIAGNOSTICS_POP();
274 
275       // If both devices have KeyboardDisplay then use Numeric Comparison
276       // if S.C. is supported. Otherwise, the initiator always displays and the
277       // responder inputs a passkey.
278       if (sec_conn) {
279         return PairingMethod::kNumericComparison;
280       }
281       return local_initiator ? PairingMethod::kPasskeyEntryDisplay
282                              : PairingMethod::kPasskeyEntryInput;
283   }
284 
285   return PairingMethod::kJustWorks;
286 }
287 
Encrypt(const UInt128 & key,const UInt128 & plaintext_data,UInt128 * out_encrypted_data)288 void Encrypt(const UInt128& key,
289              const UInt128& plaintext_data,
290              UInt128* out_encrypted_data) {
291   // Swap the bytes since "the most significant octet of key corresponds to
292   // key[0], the most significant octet of plaintextData corresponds to in[0]
293   // and the most significant octet of encryptedData corresponds to out[0] using
294   // the notation specified in FIPS-197" for the security function "e" (Vol 3,
295   // Part H, 2.2.1).
296   UInt128 be_key, be_plaintext, be_encrypt;
297   Swap128(key, &be_key);
298   Swap128(plaintext_data, &be_plaintext);
299 
300   PW_CHECK_OK(
301       EncryptBlock(
302           Bytes128(be_key), Bytes128(be_plaintext), Bytes128(&be_encrypt)),
303       "Encryption failed.");
304 
305   Swap128(be_encrypt, out_encrypted_data);
306 }
307 
C1(const UInt128 & tk,const UInt128 & rand,const ByteBuffer & preq,const ByteBuffer & pres,const DeviceAddress & initiator_addr,const DeviceAddress & responder_addr,UInt128 * out_confirm_value)308 void C1(const UInt128& tk,
309         const UInt128& rand,
310         const ByteBuffer& preq,
311         const ByteBuffer& pres,
312         const DeviceAddress& initiator_addr,
313         const DeviceAddress& responder_addr,
314         UInt128* out_confirm_value) {
315   PW_DCHECK(preq.size() == kPreqSize);
316   PW_DCHECK(pres.size() == kPreqSize);
317   PW_DCHECK(out_confirm_value);
318 
319   UInt128 p1, p2;
320 
321   // Calculate p1 = pres || preq || rat’ || iat’
322   pw::bluetooth::emboss::LEAddressType iat =
323       DeviceAddress::DeviceAddrToLeAddr(initiator_addr.type());
324   pw::bluetooth::emboss::LEAddressType rat =
325       DeviceAddress::DeviceAddrToLeAddr(responder_addr.type());
326   p1[0] = static_cast<uint8_t>(iat);
327   p1[1] = static_cast<uint8_t>(rat);
328   std::memcpy(p1.data() + 2, preq.data(), preq.size());  // Bytes [2-8]
329   std::memcpy(p1.data() + 2 + preq.size(), pres.data(), pres.size());  // [9-15]
330 
331   // Calculate p2 = padding || ia || ra
332   BufferView ia = initiator_addr.value().bytes();
333   BufferView ra = responder_addr.value().bytes();
334   std::memcpy(p2.data(), ra.data(), ra.size());              // Lowest 6 bytes
335   std::memcpy(p2.data() + ra.size(), ia.data(), ia.size());  // Next 6 bytes
336   std::memset(p2.data() + ra.size() + ia.size(),
337               0,
338               p2.size() - ra.size() - ia.size());  // Pad 0s for the remainder
339 
340   // Calculate the confirm value: e(tk, e(tk, rand XOR p1) XOR p2)
341   UInt128 tmp;
342   Xor128(rand, p1, &p1);
343   Encrypt(tk, p1, &tmp);
344   Xor128(tmp, p2, &tmp);
345   Encrypt(tk, tmp, out_confirm_value);
346 }
347 
S1(const UInt128 & tk,const UInt128 & r1,const UInt128 & r2,UInt128 * out_stk)348 void S1(const UInt128& tk,
349         const UInt128& r1,
350         const UInt128& r2,
351         UInt128* out_stk) {
352   PW_DCHECK(out_stk);
353 
354   UInt128 r_prime;
355 
356   // Take the lower 64-bits of r1 and r2 and concatanate them to produce
357   // r’ = r1’ || r2’, where r2' contains the LSB and r1' the MSB.
358   constexpr size_t kHalfSize = sizeof(UInt128) / 2;
359   std::memcpy(r_prime.data(), r2.data(), kHalfSize);
360   std::memcpy(r_prime.data() + kHalfSize, r1.data(), kHalfSize);
361 
362   // Calculate the STK: e(tk, r’)
363   Encrypt(tk, r_prime, out_stk);
364 }
365 
Ah(const UInt128 & k,uint32_t r)366 uint32_t Ah(const UInt128& k, uint32_t r) {
367   PW_DCHECK(r <= k24BitMax);
368 
369   // r' = padding || r.
370   UInt128 r_prime;
371   r_prime.fill(0);
372   *reinterpret_cast<uint32_t*>(r_prime.data()) =
373       pw::bytes::ConvertOrderTo(cpp20::endian::little, r & k24BitMax);
374 
375   UInt128 hash128;
376   Encrypt(k, r_prime, &hash128);
377 
378   return pw::bytes::ConvertOrderFrom(
379              cpp20::endian::little,
380              *reinterpret_cast<uint32_t*>(hash128.data())) &
381          k24BitMax;
382 }
383 
IrkCanResolveRpa(const UInt128 & irk,const DeviceAddress & rpa)384 bool IrkCanResolveRpa(const UInt128& irk, const DeviceAddress& rpa) {
385   if (!rpa.IsResolvablePrivate()) {
386     return false;
387   }
388 
389   // The |rpa_hash| and |prand| values generated below should match the least
390   // and most significant 3 bytes of |rpa|, respectively.
391   BufferView rpa_bytes = rpa.value().bytes();
392 
393   // Lower 24-bits (in host order).
394   uint32_t rpa_hash = pw::bytes::ConvertOrderFrom(cpp20::endian::little,
395                                                   rpa_bytes.To<uint32_t>()) &
396                       k24BitMax;
397 
398   // Upper 24-bits (we avoid a cast to uint32_t to prevent an invalid access
399   // since the buffer would be too short).
400   BufferView prand_bytes = rpa_bytes.view(3);
401   uint32_t prand = prand_bytes[0];
402   prand |= static_cast<uint32_t>(prand_bytes[1]) << 8;
403   prand |= static_cast<uint32_t>(prand_bytes[2]) << 16;
404 
405   return Ah(irk, prand) == rpa_hash;
406 }
407 
GenerateRpa(const UInt128 & irk)408 DeviceAddress GenerateRpa(const UInt128& irk) {
409   // 24-bit prand value in little-endian order.
410   constexpr auto k24BitSize = 3;
411   uint32_t prand_le = 0;
412   static_assert(k24BitSize == sizeof(uint32_t) - 1);
413   MutableBufferView prand_bytes(&prand_le, k24BitSize);
414 
415   // The specification requires that at least one bit of the address is 1 and at
416   // least one bit is 0. We expect that zx_cprng_draw() satisfies these
417   // requirements.
418   // TODO(fxbug.dev/42099048): Maybe generate within a range to enforce this?
419   random_generator()->Get(prand_bytes.mutable_subspan());
420 
421   // Make sure that the highest two bits are 0 and 1 respectively.
422   prand_bytes[2] |= 0b01000000;
423   prand_bytes[2] &= ~0b10000000;
424 
425   // 24-bit hash value in little-endian order.
426   uint32_t hash_le = pw::bytes::ConvertOrderTo(
427       cpp20::endian::little,
428       Ah(irk, pw::bytes::ConvertOrderFrom(cpp20::endian::little, prand_le)));
429   BufferView hash_bytes(&hash_le, k24BitSize);
430 
431   // The |rpa_hash| and |prand| values generated below take up the least
432   // and most significant 3 bytes of |rpa|, respectively.
433   StaticByteBuffer<kDeviceAddressSize> addr_bytes;
434   addr_bytes.Write(hash_bytes);
435   addr_bytes.Write(prand_bytes, hash_bytes.size());
436 
437   return DeviceAddress(DeviceAddress::Type::kLERandom,
438                        DeviceAddressBytes(addr_bytes));
439 }
440 
GenerateRandomAddress(bool is_static)441 DeviceAddress GenerateRandomAddress(bool is_static) {
442   StaticByteBuffer<kDeviceAddressSize> addr_bytes;
443 
444   // The specification requires that at least one bit of the address is 1 and at
445   // least one bit is 0. We expect that zx_cprng_draw() satisfies these
446   // requirements.
447   // TODO(fxbug.dev/42099048): Maybe generate within a range to enforce this?
448   random_generator()->Get(addr_bytes.mutable_subspan());
449 
450   if (is_static) {
451     // The highest two bits of a static random address are both 1 (see Vol 3,
452     // Part B, 1.3.2.1).
453     addr_bytes[kDeviceAddressSize - 1] |= 0b11000000;
454   } else {
455     // The highest two bits of a NRPA are both 0 (see Vol 3, Part B, 1.3.2.2).
456     addr_bytes[kDeviceAddressSize - 1] &= ~0b11000000;
457   }
458 
459   return DeviceAddress(DeviceAddress::Type::kLERandom,
460                        DeviceAddressBytes(addr_bytes));
461 }
462 
AesCmac(const UInt128 & hash_key,const ByteBuffer & msg)463 std::optional<UInt128> AesCmac(const UInt128& hash_key, const ByteBuffer& msg) {
464   // Reverse little-endian input parameters to the big-endian format expected by
465   // pw_crypto.
466   UInt128 be_key;
467   Swap128(hash_key, &be_key);
468   DynamicByteBuffer be_msg(msg);
469   uint8_t* msg_begin = be_msg.mutable_data();
470   std::reverse(msg_begin, msg_begin + be_msg.size());
471   UInt128 be_out, out;
472 
473   if (!Cmac(Bytes128(be_key))
474            .Update(be_msg.subspan())
475            .Final(Bytes128(&be_out))
476            .ok()) {
477     return std::nullopt;
478   }
479 
480   Swap128(be_out, &out);
481   return out;
482 }
483 
F4(const UInt256 & u,const UInt256 & v,const UInt128 & x,const uint8_t z)484 std::optional<UInt128> F4(const UInt256& u,
485                           const UInt256& v,
486                           const UInt128& x,
487                           const uint8_t z) {
488   constexpr size_t kDataLength = 2 * kUInt256Size + 1;
489   StaticByteBuffer<kDataLength> data_to_encrypt;
490   // Write to buffer in reverse of human-readable spec format as all parameters
491   // are little-endian.
492   MutableBufferView current_view =
493       WriteToBuffer(z, data_to_encrypt.mutable_view());
494   current_view = WriteToBuffer(v, current_view);
495   current_view = WriteToBuffer(u, current_view);
496 
497   // Ensures |current_view| is at the end of data_to_encrypt
498   PW_DCHECK(current_view.size() == 0);
499   return AesCmac(x, data_to_encrypt);
500 }
501 
F5(const UInt256 & dhkey,const UInt128 & initiator_nonce,const UInt128 & responder_nonce,const DeviceAddress & initiator_addr,const DeviceAddress & responder_addr)502 std::optional<F5Results> F5(const UInt256& dhkey,
503                             const UInt128& initiator_nonce,
504                             const UInt128& responder_nonce,
505                             const DeviceAddress& initiator_addr,
506                             const DeviceAddress& responder_addr) {
507   // Get the T key value
508   StaticByteBuffer<kUInt256Size> dhkey_buffer;
509   WriteToBuffer(dhkey, dhkey_buffer.mutable_view());
510   std::optional<UInt128> maybe_cmac = AesCmac(kF5Salt, dhkey_buffer);
511   if (!maybe_cmac.has_value()) {
512     return std::nullopt;
513   }
514   UInt128 t_key = maybe_cmac.value();
515 
516   // Create the MacKey and LTK using the T Key value.
517   uint8_t counter = 0x00;
518   const std::array<uint8_t, 2> length = {0x00, 0x01};  // 256 in little-endian
519   constexpr size_t kDataLength = sizeof(counter) + kF5KeyId.size() +
520                                  2 * kUInt128Size +
521                                  2 * (1 + kDeviceAddressSize) + length.size();
522   StaticByteBuffer<kDataLength> data_to_encrypt;
523 
524   // Write to buffer in reverse of human-readable spec format as all parameters
525   // are little-endian.
526   MutableBufferView current_view =
527       WriteToBuffer(length, data_to_encrypt.mutable_view());
528   current_view = WriteCryptoDeviceAddr(responder_addr, current_view);
529   current_view = WriteCryptoDeviceAddr(initiator_addr, current_view);
530   current_view = WriteToBuffer(responder_nonce, current_view);
531   current_view = WriteToBuffer(initiator_nonce, current_view);
532   current_view = WriteToBuffer(kF5KeyId, current_view);
533   current_view = WriteToBuffer(counter, current_view);
534 
535   // Ensures |current_view| is at the end of data_to_encrypt
536   PW_DCHECK(current_view.size() == 0);
537   maybe_cmac = AesCmac(t_key, data_to_encrypt);
538   if (!maybe_cmac.has_value()) {
539     return std::nullopt;
540   }
541   F5Results results{.mac_key = *maybe_cmac, .ltk = {0}};
542 
543   // Overwrite counter value only for LTK calculation.
544   counter = 0x01;
545   data_to_encrypt.Write(&counter, 1, kDataLength - 1);
546   maybe_cmac = AesCmac(t_key, data_to_encrypt);
547   if (!maybe_cmac.has_value()) {
548     return std::nullopt;
549   }
550   results.ltk = *maybe_cmac;
551   return results;
552 }
553 
F6(const UInt128 & mackey,const UInt128 & n1,const UInt128 & n2,const UInt128 & r,AuthReqField auth_req,OOBDataFlag oob,IOCapability io_cap,const DeviceAddress & a1,const DeviceAddress & a2)554 std::optional<UInt128> F6(const UInt128& mackey,
555                           const UInt128& n1,
556                           const UInt128& n2,
557                           const UInt128& r,
558                           AuthReqField auth_req,
559                           OOBDataFlag oob,
560                           IOCapability io_cap,
561                           const DeviceAddress& a1,
562                           const DeviceAddress& a2) {
563   constexpr size_t kDataLength = 3 * kUInt128Size + sizeof(AuthReqField) +
564                                  sizeof(OOBDataFlag) + sizeof(IOCapability) +
565                                  2 * (1 + kDeviceAddressSize);
566   StaticByteBuffer<kDataLength> data_to_encrypt;
567   // Write to buffer in reverse of human-readable spec format as all parameters
568   // are little-endian.
569   MutableBufferView current_view =
570       WriteCryptoDeviceAddr(a2, data_to_encrypt.mutable_view());
571   current_view = WriteCryptoDeviceAddr(a1, current_view);
572   current_view = WriteToBuffer(static_cast<uint8_t>(io_cap), current_view);
573   current_view = WriteToBuffer(static_cast<uint8_t>(oob), current_view);
574   current_view = WriteToBuffer(auth_req, current_view);
575   current_view = WriteToBuffer(r, current_view);
576   current_view = WriteToBuffer(n2, current_view);
577   current_view = WriteToBuffer(n1, current_view);
578   // Ensures |current_view| is at the end of data_to_encrypt
579   PW_DCHECK(current_view.size() == 0);
580   return AesCmac(mackey, data_to_encrypt);
581 }
582 
G2(const UInt256 & initiator_pubkey_x,const UInt256 & responder_pubkey_x,const UInt128 & initiator_nonce,const UInt128 & responder_nonce)583 std::optional<uint32_t> G2(const UInt256& initiator_pubkey_x,
584                            const UInt256& responder_pubkey_x,
585                            const UInt128& initiator_nonce,
586                            const UInt128& responder_nonce) {
587   constexpr size_t kDataLength = 2 * kUInt256Size + kUInt128Size;
588   StaticByteBuffer<kDataLength> data_to_encrypt;
589   // Write to buffer in reverse of human-readable spec format as all parameters
590   // are little-endian.
591   MutableBufferView current_view =
592       WriteToBuffer(responder_nonce, data_to_encrypt.mutable_view());
593   current_view = WriteToBuffer(responder_pubkey_x, current_view);
594   current_view = WriteToBuffer(initiator_pubkey_x, current_view);
595   PW_DCHECK(current_view.size() == 0);
596   std::optional<UInt128> maybe_cmac = AesCmac(initiator_nonce, data_to_encrypt);
597   if (!maybe_cmac.has_value()) {
598     return std::nullopt;
599   }
600   UInt128 cmac_output = *maybe_cmac;
601   // Implements the "mod 32" part of G2 on the little-endian output of AES-CMAC.
602   return uint32_t{cmac_output[3]} << 24 | uint32_t{cmac_output[2]} << 16 |
603          uint32_t{cmac_output[1]} << 8 | uint32_t{cmac_output[0]};
604 }
605 
H6(const UInt128 & w,uint32_t key_id)606 std::optional<UInt128> H6(const UInt128& w, uint32_t key_id) {
607   StaticByteBuffer<sizeof(key_id)> data_to_encrypt;
608   data_to_encrypt.WriteObj(key_id);
609   return AesCmac(w, data_to_encrypt);
610 }
611 
H7(const UInt128 & salt,const UInt128 & w)612 std::optional<UInt128> H7(const UInt128& salt, const UInt128& w) {
613   StaticByteBuffer<kUInt128Size> data_to_encrypt;
614   data_to_encrypt.WriteObj(w);
615   return AesCmac(salt, data_to_encrypt);
616 }
617 
LeLtkToBrEdrLinkKey(const UInt128 & le_ltk,CrossTransportKeyAlgo hash_function)618 std::optional<UInt128> LeLtkToBrEdrLinkKey(
619     const UInt128& le_ltk, CrossTransportKeyAlgo hash_function) {
620   std::optional<UInt128> intermediate_key;
621   if (hash_function == CrossTransportKeyAlgo::kUseH7) {
622     const UInt128 salt = {0x31,
623                           0x70,
624                           0x6D,
625                           0x74,
626                           0x00,
627                           0x00,
628                           0x00,
629                           0x00,
630                           0x00,
631                           0x00,
632                           0x00,
633                           0x00,
634                           0x00,
635                           0x00,
636                           0x00,
637                           0x00};
638     intermediate_key = H7(salt, le_ltk);
639   } else if (hash_function == CrossTransportKeyAlgo::kUseH6) {
640     // The string "tmp1" mapped into extended ASCII per spec v5.2 Vol. 3 Part
641     // H 2.4.2.4.
642     const uint32_t tmp1_key_id = 0x746D7031;
643     intermediate_key = H6(le_ltk, tmp1_key_id);
644   } else {
645     bt_log(WARN,
646            "sm",
647            "unexpected CrossTransportKeyAlgo passed to link key generation!");
648   }
649   if (!intermediate_key.has_value()) {
650     return std::nullopt;
651   }
652   // The string "lebr" mapped into extended ASCII per spec v5.2 Vol. 3 Part
653   // H 2.4.2.4.
654   const uint32_t lebr_key_id = 0x6C656272;
655   return H6(*intermediate_key, lebr_key_id);
656 }
657 
BrEdrLinkKeyToLeLtk(const UInt128 & link_key,CrossTransportKeyAlgo hash_function)658 std::optional<UInt128> BrEdrLinkKeyToLeLtk(
659     const UInt128& link_key, CrossTransportKeyAlgo hash_function) {
660   std::optional<UInt128> intermediate_ltk;
661   if (hash_function == CrossTransportKeyAlgo::kUseH6) {
662     // The string "tmp2" mapped into ASCII per spec v6.0 Vol. 3 Part
663     // H 2.4.2.5.
664     const uint32_t tmp2_key_id = 0x746D7032;
665     intermediate_ltk = H6(link_key, tmp2_key_id);
666   } else if (hash_function == CrossTransportKeyAlgo::kUseH7) {
667     const UInt128 salt = {0x32,
668                           0x70,
669                           0x6D,
670                           0x74,
671                           0x00,
672                           0x00,
673                           0x00,
674                           0x00,
675                           0x00,
676                           0x00,
677                           0x00,
678                           0x00,
679                           0x00,
680                           0x00,
681                           0x00,
682                           0x00};
683     intermediate_ltk = H7(salt, link_key);
684   } else {
685     bt_log(ERROR,
686            "sm",
687            "unexpected CrossTransportKeyAlgo passed to link key generation!");
688   }
689   if (!intermediate_ltk.has_value()) {
690     return std::nullopt;
691   }
692   // The string "brle" mapped into ASCII per spec v6.0 Vol. 3 Part
693   // H 2.4.2.5.
694   const uint32_t brle_key_id = 0x62726C65;
695   return H6(*intermediate_ltk, brle_key_id);
696 }
697 
698 }  // namespace bt::sm::util
699