/****************************************************************************** * * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ #include "security/pairing_handler_le.h" #include "os/rand.h" using bluetooth::os::GenerateRandom; namespace bluetooth { namespace security { std::variant PairingHandlerLe::ExchangePublicKeys(const InitialInformations& i, OobDataFlag remote_have_oob_data) { // Generate ECDH, or use one that was used for OOB data const auto [private_key, public_key] = (remote_have_oob_data == OobDataFlag::NOT_PRESENT || !i.my_oob_data) ? GenerateECDHKeyPair() : std::make_pair(i.my_oob_data->private_key, i.my_oob_data->public_key); LOG_INFO("Public key exchange start"); std::unique_ptr myPublicKey = PairingPublicKeyBuilder::Create(public_key.x, public_key.y); if (!ValidateECDHPoint(public_key)) { LOG_ERROR("Can't validate my own public key!!!"); return PairingFailure("Can't validate my own public key"); } if (IAmCentral(i)) { // Send pairing public key LOG_INFO("Central sends out public key"); SendL2capPacket(i, std::move(myPublicKey)); } LOG_INFO(" Waiting for Public key..."); auto response = WaitPairingPublicKey(); LOG_INFO(" Received public key"); if (std::holds_alternative(response)) { return std::get(response); } EcdhPublicKey remote_public_key; auto ppkv = std::get(response); remote_public_key.x = ppkv.GetPublicKeyX(); remote_public_key.y = ppkv.GetPublicKeyY(); LOG_INFO("Received Public key from remote"); if (public_key.x == remote_public_key.x) { LOG_INFO("Remote and local public keys can't match"); return PairingFailure("Remote and local public keys match"); } // validate received public key if (!ValidateECDHPoint(remote_public_key)) { // TODO: Spec is unclear what should happend when the point is not on // the correct curve: A device that detects an invalid public key from // the peer at any point during the LE Secure Connections pairing // process shall not use the resulting LTK, if any. LOG_INFO("Can't validate remote public key"); return PairingFailure("Can't validate remote public key"); } if (!IAmCentral(i)) { LOG_INFO("Peripheral sends out public key"); // Send pairing public key SendL2capPacket(i, std::move(myPublicKey)); } LOG_INFO("Public key exchange finish"); std::array dhkey = ComputeDHKey(private_key, remote_public_key); const EcdhPublicKey& PKa = IAmCentral(i) ? public_key : remote_public_key; const EcdhPublicKey& PKb = IAmCentral(i) ? remote_public_key : public_key; return KeyExchangeResult{PKa, PKb, dhkey}; } Stage1ResultOrFailure PairingHandlerLe::DoSecureConnectionsStage1(const InitialInformations& i, const EcdhPublicKey& PKa, const EcdhPublicKey& PKb, const PairingRequestView& pairing_request, const PairingResponseView& pairing_response) { if (((pairing_request.GetAuthReq() & AuthReqMaskMitm) == 0) && ((pairing_response.GetAuthReq() & AuthReqMaskMitm) == 0)) { // If both devices have not set MITM option, Just Works shall be used return SecureConnectionsJustWorks(i, PKa, PKb); } if (pairing_request.GetOobDataFlag() == OobDataFlag::PRESENT || pairing_response.GetOobDataFlag() == OobDataFlag::PRESENT) { OobDataFlag remote_oob_flag = IAmCentral(i) ? pairing_response.GetOobDataFlag() : pairing_request.GetOobDataFlag(); OobDataFlag my_oob_flag = IAmCentral(i) ? pairing_request.GetOobDataFlag() : pairing_response.GetOobDataFlag(); return SecureConnectionsOutOfBand(i, PKa, PKb, my_oob_flag, remote_oob_flag); } const auto& iom = pairing_request.GetIoCapability(); const auto& ios = pairing_response.GetIoCapability(); if ((iom == IoCapability::KEYBOARD_DISPLAY || iom == IoCapability::DISPLAY_YES_NO) && (ios == IoCapability::KEYBOARD_DISPLAY || ios == IoCapability::DISPLAY_YES_NO)) { return SecureConnectionsNumericComparison(i, PKa, PKb); } if (iom == IoCapability::NO_INPUT_NO_OUTPUT || ios == IoCapability::NO_INPUT_NO_OUTPUT) { return SecureConnectionsJustWorks(i, PKa, PKb); } if ((iom == IoCapability::DISPLAY_ONLY || iom == IoCapability::DISPLAY_YES_NO) && (ios == IoCapability::DISPLAY_ONLY || ios == IoCapability::DISPLAY_YES_NO)) { return SecureConnectionsJustWorks(i, PKa, PKb); } IoCapability my_iocaps = IAmCentral(i) ? iom : ios; IoCapability remote_iocaps = IAmCentral(i) ? ios : iom; return SecureConnectionsPasskeyEntry(i, PKa, PKb, my_iocaps, remote_iocaps); } Stage2ResultOrFailure PairingHandlerLe::DoSecureConnectionsStage2(const InitialInformations& i, const EcdhPublicKey& PKa, const EcdhPublicKey& PKb, const PairingRequestView& pairing_request, const PairingResponseView& pairing_response, const Stage1Result stage1result, const std::array& dhkey) { LOG_INFO("Authentication stage 2 started"); auto [Na, Nb, ra, rb] = stage1result; // 2.3.5.6.5 Authentication stage 2 long term key calculation uint8_t a[7]; uint8_t b[7]; if (IAmCentral(i)) { memcpy(a, i.my_connection_address.GetAddress().data(), hci::Address::kLength); a[6] = (uint8_t)i.my_connection_address.GetAddressType(); memcpy(b, i.remote_connection_address.GetAddress().data(), hci::Address::kLength); b[6] = (uint8_t)i.remote_connection_address.GetAddressType(); } else { memcpy(a, i.remote_connection_address.GetAddress().data(), hci::Address::kLength); a[6] = (uint8_t)i.remote_connection_address.GetAddressType(); memcpy(b, i.my_connection_address.GetAddress().data(), hci::Address::kLength); b[6] = (uint8_t)i.my_connection_address.GetAddressType(); } Octet16 ltk, mac_key; crypto_toolbox::f5((uint8_t*)dhkey.data(), Na, Nb, a, b, &mac_key, <k); // DHKey exchange and check std::array iocapA{static_cast(pairing_request.GetIoCapability()), static_cast(pairing_request.GetOobDataFlag()), pairing_request.GetAuthReq()}; std::array iocapB{static_cast(pairing_response.GetIoCapability()), static_cast(pairing_response.GetOobDataFlag()), pairing_response.GetAuthReq()}; // LOG(INFO) << +(IAmCentral(i)) << " LTK = " << base::HexEncode(ltk.data(), ltk.size()); // LOG(INFO) << +(IAmCentral(i)) << " MAC_KEY = " << base::HexEncode(mac_key.data(), mac_key.size()); // LOG(INFO) << +(IAmCentral(i)) << " Na = " << base::HexEncode(Na.data(), Na.size()); // LOG(INFO) << +(IAmCentral(i)) << " Nb = " << base::HexEncode(Nb.data(), Nb.size()); // LOG(INFO) << +(IAmCentral(i)) << " ra = " << base::HexEncode(ra.data(), ra.size()); // LOG(INFO) << +(IAmCentral(i)) << " rb = " << base::HexEncode(rb.data(), rb.size()); // LOG(INFO) << +(IAmCentral(i)) << " iocapA = " << base::HexEncode(iocapA.data(), iocapA.size()); // LOG(INFO) << +(IAmCentral(i)) << " iocapB = " << base::HexEncode(iocapB.data(), iocapB.size()); // LOG(INFO) << +(IAmCentral(i)) << " a = " << base::HexEncode(a, 7); // LOG(INFO) << +(IAmCentral(i)) << " b = " << base::HexEncode(b, 7); Octet16 Ea = crypto_toolbox::f6(mac_key, Na, Nb, rb, iocapA.data(), a, b); Octet16 Eb = crypto_toolbox::f6(mac_key, Nb, Na, ra, iocapB.data(), b, a); if (IAmCentral(i)) { // send Pairing DHKey Check SendL2capPacket(i, PairingDhKeyCheckBuilder::Create(Ea)); auto response = WaitPairingDHKeyCheck(); if (std::holds_alternative(response)) { return std::get(response); } if (std::get(response).GetDhKeyCheck() != Eb) { LOG_INFO("Ea != Eb, aborting!"); SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::DHKEY_CHECK_FAILED)); return PairingFailure("Ea != Eb"); } } else { auto response = WaitPairingDHKeyCheck(); if (std::holds_alternative(response)) { return std::get(response); } if (std::get(response).GetDhKeyCheck() != Ea) { LOG_INFO("Ea != Eb, aborting!"); SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::DHKEY_CHECK_FAILED)); return PairingFailure("Ea != Eb"); } // send Pairing DHKey Check SendL2capPacket(i, PairingDhKeyCheckBuilder::Create(Eb)); } LOG_INFO("Authentication stage 2 (DHKey checks) finished"); return ltk; } Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsOutOfBand(const InitialInformations& i, const EcdhPublicKey& Pka, const EcdhPublicKey& Pkb, OobDataFlag my_oob_flag, OobDataFlag remote_oob_flag) { LOG_INFO("Out Of Band start"); Octet16 zeros{0}; Octet16 localR = (remote_oob_flag == OobDataFlag::PRESENT && i.my_oob_data) ? i.my_oob_data->r : zeros; Octet16 remoteR; if (my_oob_flag == OobDataFlag::NOT_PRESENT || (my_oob_flag == OobDataFlag::PRESENT && !i.remote_oob_data)) { /* we have send the OOB data, but not received them. remote will check if * C value is correct */ remoteR = zeros; } else { remoteR = i.remote_oob_data->le_sc_r; Octet16 remoteC = i.remote_oob_data->le_sc_c; Octet16 remoteC2; if (IAmCentral(i)) { remoteC2 = crypto_toolbox::f4((uint8_t*)Pkb.x.data(), (uint8_t*)Pkb.x.data(), remoteR, 0); } else { remoteC2 = crypto_toolbox::f4((uint8_t*)Pka.x.data(), (uint8_t*)Pka.x.data(), remoteR, 0); } if (remoteC2 != remoteC) { LOG_ERROR("C_computed != C_from_remote, aborting!"); return PairingFailure("C_computed != C_from_remote, aborting"); } } Octet16 Na, Nb, ra, rb; if (IAmCentral(i)) { ra = localR; rb = remoteR; Na = GenerateRandom<16>(); // Send Pairing Random SendL2capPacket(i, PairingRandomBuilder::Create(Na)); LOG_INFO("Central waits for Nb"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Nb = std::get(random).GetRandomValue(); } else { ra = remoteR; rb = localR; Nb = GenerateRandom<16>(); LOG_INFO("Peripheral waits for random"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Na = std::get(random).GetRandomValue(); SendL2capPacket(i, PairingRandomBuilder::Create(Nb)); } return Stage1Result{Na, Nb, ra, rb}; } Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsPasskeyEntry(const InitialInformations& i, const EcdhPublicKey& PKa, const EcdhPublicKey& PKb, IoCapability my_iocaps, IoCapability remote_iocaps) { LOG_INFO("Passkey Entry start"); Octet16 Na, Nb, ra{0}, rb{0}; uint32_t passkey; if (my_iocaps == IoCapability::DISPLAY_ONLY || remote_iocaps == IoCapability::KEYBOARD_ONLY) { // I display passkey = GenerateRandom(); passkey &= 0x0fffff; /* maximum 20 significant bytes */ constexpr uint32_t PASSKEY_MAX = 999999; while (passkey > PASSKEY_MAX) passkey >>= 1; ConfirmationData data(i.remote_connection_address, i.remote_name, passkey); i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPasskey, common::Unretained(i.user_interface), data)); } else if (my_iocaps == IoCapability::KEYBOARD_ONLY || remote_iocaps == IoCapability::DISPLAY_ONLY) { ConfirmationData data(i.remote_connection_address, i.remote_name); i.user_interface_handler->Post( common::BindOnce(&UI::DisplayEnterPasskeyDialog, common::Unretained(i.user_interface), data)); std::optional response = WaitUiPasskey(); if (!response) return PairingFailure("Passkey did not arrive!"); passkey = response->ui_value; /*TODO: shall we send "Keypress Notification" after each key ? This would * have impact on the SMP timeout*/ } else { LOG(FATAL) << "THIS SHOULD NEVER HAPPEN"; return PairingFailure("FATAL!"); } uint32_t bitmask = 0x01; for (int loop = 0; loop < 20; loop++, bitmask <<= 1) { LOG_INFO("Iteration no %d", loop); bool bit_set = ((bitmask & passkey) != 0); uint8_t ri = bit_set ? 0x81 : 0x80; Octet16 Cai, Cbi, Nai, Nbi; if (IAmCentral(i)) { Nai = GenerateRandom<16>(); Cai = crypto_toolbox::f4((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Nai, ri); // Send Pairing Confirm LOG_INFO("Central sends Cai"); SendL2capPacket(i, PairingConfirmBuilder::Create(Cai)); LOG_INFO("Central waits for the Cbi"); auto confirm = WaitPairingConfirm(); if (std::holds_alternative(confirm)) { return std::get(confirm); } Cbi = std::get(confirm).GetConfirmValue(); // Send Pairing Random SendL2capPacket(i, PairingRandomBuilder::Create(Nai)); LOG_INFO("Central waits for Nbi"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Nbi = std::get(random).GetRandomValue(); Octet16 Cbi2 = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nbi, ri); if (Cbi != Cbi2) { LOG_INFO("Cai != Cbi, aborting!"); SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED)); return PairingFailure("Cai != Cbi"); } } else { Nbi = GenerateRandom<16>(); // Compute confirm Cbi = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nbi, ri); LOG_INFO("Peripheral waits for the Cai"); auto confirm = WaitPairingConfirm(); if (std::holds_alternative(confirm)) { return std::get(confirm); } Cai = std::get(confirm).GetConfirmValue(); // Send Pairing Confirm LOG_INFO("Peripheral sends confirmation"); SendL2capPacket(i, PairingConfirmBuilder::Create(Cbi)); LOG_INFO("Peripheral waits for random"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Nai = std::get(random).GetRandomValue(); Octet16 Cai2 = crypto_toolbox::f4((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Nai, ri); if (Cai != Cai2) { LOG_INFO("Cai != Cai2, aborting!"); SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED)); return PairingFailure("Cai != Cai2"); } // Send Pairing Random SendL2capPacket(i, PairingRandomBuilder::Create(Nbi)); } if (loop == 19) { Na = Nai; Nb = Nbi; } } ra[0] = (uint8_t)(passkey); ra[1] = (uint8_t)(passkey >> 8); ra[2] = (uint8_t)(passkey >> 16); ra[3] = (uint8_t)(passkey >> 24); rb = ra; return Stage1Result{Na, Nb, ra, rb}; } Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsNumericComparison(const InitialInformations& i, const EcdhPublicKey& PKa, const EcdhPublicKey& PKb) { LOG_INFO("Numeric Comparison start"); Stage1ResultOrFailure result = SecureConnectionsJustWorks(i, PKa, PKb); if (std::holds_alternative(result)) { return std::get(result); } const auto [Na, Nb, ra, rb] = std::get(result); uint32_t number_to_display = crypto_toolbox::g2((uint8_t*)PKa.x.data(), (uint8_t*)PKb.x.data(), Na, Nb); ConfirmationData data(i.remote_connection_address, i.remote_name, number_to_display); i.user_interface_handler->Post( common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface), data)); std::optional confirmyesno = WaitUiConfirmYesNo(); if (!confirmyesno || confirmyesno->ui_value == 0) { LOG_INFO("Was expecting the user value confirm"); return PairingFailure("Was expecting the user value confirm"); } return result; } Stage1ResultOrFailure PairingHandlerLe::SecureConnectionsJustWorks(const InitialInformations& i, const EcdhPublicKey& PKa, const EcdhPublicKey& PKb) { Octet16 Cb, Na, Nb, ra, rb; ra = rb = {0}; if (IAmCentral(i)) { Na = GenerateRandom<16>(); LOG_INFO("Central waits for confirmation"); auto confirm = WaitPairingConfirm(); if (std::holds_alternative(confirm)) { return std::get(confirm); } Cb = std::get(confirm).GetConfirmValue(); // Send Pairing Random SendL2capPacket(i, PairingRandomBuilder::Create(Na)); LOG_INFO("Central waits for Random"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Nb = std::get(random).GetRandomValue(); // Compute Cb locally Octet16 Cb_local = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0); if (Cb_local != Cb) { LOG_INFO("Cb_local != Cb, aborting!"); SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED)); return PairingFailure("Cb_local != Cb"); } } else { Nb = GenerateRandom<16>(); // Compute confirm Cb = crypto_toolbox::f4((uint8_t*)PKb.x.data(), (uint8_t*)PKa.x.data(), Nb, 0); // Send Pairing Confirm LOG_INFO("Peripheral sends confirmation"); SendL2capPacket(i, PairingConfirmBuilder::Create(Cb)); LOG_INFO("Peripheral waits for random"); auto random = WaitPairingRandom(); if (std::holds_alternative(random)) { return std::get(random); } Na = std::get(random).GetRandomValue(); // Send Pairing Random SendL2capPacket(i, PairingRandomBuilder::Create(Nb)); } return Stage1Result{Na, Nb, ra, rb}; } } // namespace security } // namespace bluetooth