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