| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "util.h" |
| |
| #include <openssl/aes.h> |
| |
| #include "garnet/drivers/bluetooth/lib/hci/util.h" |
| #include "lib/fxl/logging.h" |
| |
| namespace btlib { |
| |
| using common::BufferView; |
| using common::ByteBuffer; |
| using common::DeviceAddress; |
| using common::UInt128; |
| |
| namespace sm { |
| namespace util { |
| namespace { |
| |
| constexpr size_t kPreqSize = 7; |
| |
| // Swap the endianness of a 128-bit integer. |in| and |out| should not be backed |
| // by the same buffer. |
| void Swap128(const UInt128& in, UInt128* out) { |
| FXL_DCHECK(out); |
| for (size_t i = 0; i < in.size(); ++i) { |
| (*out)[i] = in[in.size() - i - 1]; |
| } |
| } |
| |
| // XOR two 128-bit integers and return the result in |out|. It is possible to |
| // pass a pointer to one of the inputs as |out|. |
| void Xor128(const UInt128& int1, const UInt128& int2, UInt128* out) { |
| FXL_DCHECK(out); |
| |
| uint64_t lower1 = *reinterpret_cast<const uint64_t*>(int1.data()); |
| uint64_t upper1 = *reinterpret_cast<const uint64_t*>(int1.data() + 8); |
| |
| uint64_t lower2 = *reinterpret_cast<const uint64_t*>(int2.data()); |
| uint64_t upper2 = *reinterpret_cast<const uint64_t*>(int2.data() + 8); |
| |
| uint64_t lower_res = lower1 ^ lower2; |
| uint64_t upper_res = upper1 ^ upper2; |
| |
| std::memcpy(out->data(), &lower_res, 8); |
| std::memcpy(out->data() + 8, &upper_res, 8); |
| } |
| |
| } // namespace |
| |
| PairingMethod SelectPairingMethod(bool sec_conn, bool local_oob, bool peer_oob, |
| bool mitm_required, IOCapability local_ioc, |
| IOCapability peer_ioc) { |
| if ((sec_conn && (local_oob || peer_oob)) || |
| (!sec_conn && local_oob && peer_oob)) { |
| return PairingMethod::kOutOfBand; |
| } |
| |
| // If neither device requires MITM protection or if the peer has not I/O |
| // capable, we select Just Works. |
| if (!mitm_required || peer_ioc == IOCapability::kNoInputNoOutput) { |
| return PairingMethod::kJustWorks; |
| } |
| |
| // Select the pairing method by comparing I/O capabilities. The switch |
| // statement will return if an authenticated entry method is selected. |
| // Otherwise, we'll break out and default to Just Works below. |
| switch (local_ioc) { |
| case IOCapability::kNoInputNoOutput: |
| break; |
| |
| case IOCapability::kDisplayOnly: |
| switch (peer_ioc) { |
| case IOCapability::kKeyboardOnly: |
| case IOCapability::kKeyboardDisplay: |
| return PairingMethod::kPasskeyEntry; |
| default: |
| break; |
| } |
| break; |
| |
| case IOCapability::kDisplayYesNo: |
| switch (peer_ioc) { |
| case IOCapability::kDisplayYesNo: |
| return sec_conn ? PairingMethod::kNumericComparison |
| : PairingMethod::kJustWorks; |
| case IOCapability::kKeyboardDisplay: |
| return sec_conn ? PairingMethod::kNumericComparison |
| : PairingMethod::kPasskeyEntry; |
| case IOCapability::kKeyboardOnly: |
| return PairingMethod::kPasskeyEntry; |
| default: |
| break; |
| } |
| break; |
| |
| case IOCapability::kKeyboardOnly: |
| return PairingMethod::kPasskeyEntry; |
| |
| case IOCapability::kKeyboardDisplay: |
| if (peer_ioc == IOCapability::kKeyboardOnly || |
| peer_ioc == IOCapability::kDisplayOnly) { |
| return PairingMethod::kPasskeyEntry; |
| } |
| |
| return sec_conn ? PairingMethod::kNumericComparison |
| : PairingMethod::kPasskeyEntry; |
| } |
| |
| return PairingMethod::kJustWorks; |
| } |
| |
| void Encrypt(const common::UInt128& key, const common::UInt128& plaintext_data, |
| common::UInt128* out_encrypted_data) { |
| // Swap the bytes since "the most significant octet of key corresponds to |
| // key[0], the most significant octet of plaintextData corresponds to in[0] |
| // and the most significant octet of encryptedData corresponds to out[0] using |
| // the notation specified in FIPS-197" for the security function "e" (Vol 3, |
| // Part H, 2.2.1). |
| UInt128 be_k, be_pt, be_enc; |
| Swap128(key, &be_k); |
| Swap128(plaintext_data, &be_pt); |
| |
| AES_KEY k; |
| AES_set_encrypt_key(be_k.data(), 128, &k); |
| AES_encrypt(be_pt.data(), be_enc.data(), &k); |
| |
| Swap128(be_enc, out_encrypted_data); |
| } |
| |
| void 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) { |
| FXL_DCHECK(preq.size() == kPreqSize); |
| FXL_DCHECK(pres.size() == kPreqSize); |
| FXL_DCHECK(out_confirm_value); |
| |
| UInt128 p1, p2; |
| |
| // Calculate p1 = pres || preq || rat’ || iat’ |
| hci::LEAddressType iat = hci::AddressTypeToHCI(initiator_addr.type()); |
| hci::LEAddressType rat = hci::AddressTypeToHCI(responder_addr.type()); |
| p1[0] = static_cast<uint8_t>(iat); |
| p1[1] = static_cast<uint8_t>(rat); |
| std::memcpy(p1.data() + 2, preq.data(), preq.size()); |
| std::memcpy(p1.data() + 2 + preq.size(), pres.data(), pres.size()); |
| |
| // Calculate p2 = padding || ia || ra |
| BufferView ia = initiator_addr.value().bytes(); |
| BufferView ra = responder_addr.value().bytes(); |
| std::memcpy(p2.data(), ra.data(), ra.size()); |
| std::memcpy(p2.data() + ra.size(), ia.data(), ia.size()); |
| std::memset(p2.data() + ra.size() + ia.size(), 0, |
| p2.size() - ra.size() - ia.size()); |
| |
| // Calculate the confirm value: e(tk, e(tk, rand XOR p1) XOR p2) |
| UInt128 tmp; |
| Xor128(rand, p1, &p1); |
| Encrypt(tk, p1, &tmp); |
| Xor128(tmp, p2, &tmp); |
| Encrypt(tk, tmp, out_confirm_value); |
| } |
| |
| void S1(const common::UInt128& tk, const common::UInt128& r1, |
| const common::UInt128& r2, common::UInt128* out_stk) { |
| FXL_DCHECK(out_stk); |
| |
| UInt128 r_prime; |
| |
| // Take the lower 64-bits of r1 and r2 and concatanate them to produce |
| // r’ = r1’ || r2’, where r2' contains the LSB and r1' the MSB. |
| std::memcpy(r_prime.data(), r2.data(), 8); |
| std::memcpy(r_prime.data() + 8, r1.data(), 8); |
| |
| // Calculate the STK: e(tk, r’) |
| Encrypt(tk, r_prime, out_stk); |
| } |
| |
| } // namespace util |
| } // namespace sm |
| } // namespace btlib |