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