You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
248 lines
9.8 KiB
248 lines
9.8 KiB
/******************************************************************************
|
|
*
|
|
* 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 {
|
|
|
|
LegacyStage1ResultOrFailure PairingHandlerLe::DoLegacyStage1(const InitialInformations& i,
|
|
const PairingRequestView& pairing_request,
|
|
const PairingResponseView& pairing_response) {
|
|
if (((pairing_request.GetAuthReq() | pairing_response.GetAuthReq()) & AuthReqMaskMitm) == 0) {
|
|
// If both devices have not set MITM option, Just Works shall be used
|
|
return LegacyJustWorks();
|
|
}
|
|
|
|
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 LegacyOutOfBand(i);
|
|
}
|
|
|
|
const auto& iom = pairing_request.GetIoCapability();
|
|
const auto& ios = pairing_response.GetIoCapability();
|
|
|
|
if (iom == IoCapability::NO_INPUT_NO_OUTPUT || ios == IoCapability::NO_INPUT_NO_OUTPUT) {
|
|
return LegacyJustWorks();
|
|
}
|
|
|
|
if ((iom == IoCapability::DISPLAY_ONLY || iom == IoCapability::DISPLAY_YES_NO) &&
|
|
(ios == IoCapability::DISPLAY_ONLY || ios == IoCapability::DISPLAY_YES_NO)) {
|
|
return LegacyJustWorks();
|
|
}
|
|
|
|
// This if() should not be needed, these are only combinations left.
|
|
if (iom == IoCapability::KEYBOARD_DISPLAY || iom == IoCapability::KEYBOARD_ONLY ||
|
|
ios == IoCapability::KEYBOARD_DISPLAY || ios == IoCapability::KEYBOARD_ONLY) {
|
|
IoCapability my_iocaps = IAmCentral(i) ? iom : ios;
|
|
IoCapability remote_iocaps = IAmCentral(i) ? ios : iom;
|
|
return LegacyPasskeyEntry(i, my_iocaps, remote_iocaps);
|
|
}
|
|
|
|
// We went through all possble combinations.
|
|
LOG_ALWAYS_FATAL("This should never happen");
|
|
}
|
|
|
|
LegacyStage1ResultOrFailure PairingHandlerLe::LegacyJustWorks() {
|
|
LOG_INFO("Legacy Just Works start");
|
|
return Octet16{0};
|
|
}
|
|
|
|
LegacyStage1ResultOrFailure PairingHandlerLe::LegacyPasskeyEntry(const InitialInformations& i,
|
|
const IoCapability& my_iocaps,
|
|
const IoCapability& remote_iocaps) {
|
|
bool i_am_displaying = false;
|
|
if (my_iocaps == IoCapability::DISPLAY_ONLY || my_iocaps == IoCapability::DISPLAY_YES_NO) {
|
|
i_am_displaying = true;
|
|
} else if (
|
|
IAmCentral(i) && remote_iocaps == IoCapability::KEYBOARD_DISPLAY && my_iocaps == IoCapability::KEYBOARD_DISPLAY) {
|
|
i_am_displaying = true;
|
|
} else if (my_iocaps == IoCapability::KEYBOARD_DISPLAY && remote_iocaps == IoCapability::KEYBOARD_ONLY) {
|
|
i_am_displaying = true;
|
|
}
|
|
|
|
LOG_INFO("Passkey Entry start %s", i_am_displaying ? "displaying" : "accepting");
|
|
|
|
uint32_t passkey;
|
|
if (i_am_displaying) {
|
|
// generate passkey in a valid range
|
|
passkey = GenerateRandom();
|
|
passkey &= 0x0fffff; /* maximum 20 significant bits */
|
|
constexpr uint32_t PASSKEY_MAX = 999999;
|
|
if (passkey > PASSKEY_MAX) passkey >>= 1;
|
|
|
|
ConfirmationData data(i.remote_connection_address, i.remote_name, passkey);
|
|
i.user_interface_handler->Post(
|
|
common::BindOnce(&UI::DisplayConfirmValue, common::Unretained(i.user_interface), data));
|
|
} else {
|
|
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<PairingEvent> response = WaitUiPasskey();
|
|
if (!response) return PairingFailure("Passkey did not arrive!");
|
|
|
|
passkey = response->ui_value;
|
|
}
|
|
|
|
Octet16 tk{0};
|
|
tk[0] = (uint8_t)(passkey);
|
|
tk[1] = (uint8_t)(passkey >> 8);
|
|
tk[2] = (uint8_t)(passkey >> 16);
|
|
tk[3] = (uint8_t)(passkey >> 24);
|
|
|
|
LOG_INFO("Passkey Entry finish");
|
|
return tk;
|
|
}
|
|
|
|
LegacyStage1ResultOrFailure PairingHandlerLe::LegacyOutOfBand(const InitialInformations& i) {
|
|
return i.remote_oob_data->security_manager_tk_value;
|
|
}
|
|
|
|
StkOrFailure PairingHandlerLe::DoLegacyStage2(const InitialInformations& i, const PairingRequestView& pairing_request,
|
|
const PairingResponseView& pairing_response, const Octet16& tk) {
|
|
LOG_INFO("Legacy Step 2 start");
|
|
std::vector<uint8_t> preq(pairing_request.begin(), pairing_request.end());
|
|
std::vector<uint8_t> pres(pairing_response.begin(), pairing_response.end());
|
|
|
|
Octet16 mrand, srand;
|
|
if (IAmCentral(i)) {
|
|
mrand = GenerateRandom<16>();
|
|
|
|
// LOG(INFO) << +(IAmCentral(i)) << " tk = " << base::HexEncode(tk.data(), tk.size());
|
|
// LOG(INFO) << +(IAmCentral(i)) << " mrand = " << base::HexEncode(mrand.data(), mrand.size());
|
|
// LOG(INFO) << +(IAmCentral(i)) << " pres = " << base::HexEncode(pres.data(), pres.size());
|
|
// LOG(INFO) << +(IAmCentral(i)) << " preq = " << base::HexEncode(preq.data(), preq.size());
|
|
|
|
Octet16 mconfirm = crypto_toolbox::c1(
|
|
tk,
|
|
mrand,
|
|
preq.data(),
|
|
pres.data(),
|
|
(uint8_t)i.my_connection_address.GetAddressType(),
|
|
i.my_connection_address.GetAddress().data(),
|
|
(uint8_t)i.remote_connection_address.GetAddressType(),
|
|
i.remote_connection_address.GetAddress().data());
|
|
|
|
// LOG(INFO) << +(IAmCentral(i)) << " mconfirm = " << base::HexEncode(mconfirm.data(), mconfirm.size());
|
|
|
|
LOG_INFO("Central sends Mconfirm");
|
|
SendL2capPacket(i, PairingConfirmBuilder::Create(mconfirm));
|
|
|
|
LOG_INFO("Central waits for the Sconfirm");
|
|
auto sconfirm_pkt = WaitPairingConfirm();
|
|
if (std::holds_alternative<PairingFailure>(sconfirm_pkt)) {
|
|
return std::get<PairingFailure>(sconfirm_pkt);
|
|
}
|
|
Octet16 sconfirm = std::get<PairingConfirmView>(sconfirm_pkt).GetConfirmValue();
|
|
|
|
LOG_INFO("Central sends Mrand");
|
|
SendL2capPacket(i, PairingRandomBuilder::Create(mrand));
|
|
|
|
LOG_INFO("Central waits for Srand");
|
|
auto random_pkt = WaitPairingRandom();
|
|
if (std::holds_alternative<PairingFailure>(random_pkt)) {
|
|
return std::get<PairingFailure>(random_pkt);
|
|
}
|
|
srand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
|
|
|
|
// LOG(INFO) << +(IAmCentral(i)) << " srand = " << base::HexEncode(srand.data(), srand.size());
|
|
|
|
Octet16 sconfirm_generated = crypto_toolbox::c1(
|
|
tk,
|
|
srand,
|
|
preq.data(),
|
|
pres.data(),
|
|
(uint8_t)i.my_connection_address.GetAddressType(),
|
|
i.my_connection_address.GetAddress().data(),
|
|
(uint8_t)i.remote_connection_address.GetAddressType(),
|
|
i.remote_connection_address.GetAddress().data());
|
|
|
|
if (sconfirm != sconfirm_generated) {
|
|
LOG_INFO("sconfirm does not match generated value");
|
|
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
|
|
return PairingFailure("sconfirm does not match generated value");
|
|
}
|
|
} else {
|
|
srand = GenerateRandom<16>();
|
|
|
|
std::vector<uint8_t> preq(pairing_request.begin(), pairing_request.end());
|
|
std::vector<uint8_t> pres(pairing_response.begin(), pairing_response.end());
|
|
|
|
Octet16 sconfirm = crypto_toolbox::c1(
|
|
tk,
|
|
srand,
|
|
preq.data(),
|
|
pres.data(),
|
|
(uint8_t)i.remote_connection_address.GetAddressType(),
|
|
i.remote_connection_address.GetAddress().data(),
|
|
(uint8_t)i.my_connection_address.GetAddressType(),
|
|
i.my_connection_address.GetAddress().data());
|
|
|
|
LOG_INFO("Peripheral waits for the Mconfirm");
|
|
auto mconfirm_pkt = WaitPairingConfirm();
|
|
if (std::holds_alternative<PairingFailure>(mconfirm_pkt)) {
|
|
return std::get<PairingFailure>(mconfirm_pkt);
|
|
}
|
|
Octet16 mconfirm = std::get<PairingConfirmView>(mconfirm_pkt).GetConfirmValue();
|
|
|
|
LOG_INFO("Peripheral sends Sconfirm");
|
|
SendL2capPacket(i, PairingConfirmBuilder::Create(sconfirm));
|
|
|
|
LOG_INFO("Peripheral waits for Mrand");
|
|
auto random_pkt = WaitPairingRandom();
|
|
if (std::holds_alternative<PairingFailure>(random_pkt)) {
|
|
return std::get<PairingFailure>(random_pkt);
|
|
}
|
|
mrand = std::get<PairingRandomView>(random_pkt).GetRandomValue();
|
|
|
|
Octet16 mconfirm_generated = crypto_toolbox::c1(
|
|
tk,
|
|
mrand,
|
|
preq.data(),
|
|
pres.data(),
|
|
(uint8_t)i.remote_connection_address.GetAddressType(),
|
|
i.remote_connection_address.GetAddress().data(),
|
|
(uint8_t)i.my_connection_address.GetAddressType(),
|
|
i.my_connection_address.GetAddress().data());
|
|
|
|
if (mconfirm != mconfirm_generated) {
|
|
LOG_INFO("mconfirm does not match generated value");
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::CONFIRM_VALUE_FAILED));
|
|
return PairingFailure("mconfirm does not match generated value");
|
|
}
|
|
|
|
LOG_INFO("Peripheral sends Srand");
|
|
SendL2capPacket(i, PairingRandomBuilder::Create(srand));
|
|
}
|
|
|
|
LOG_INFO("Legacy stage 2 finish");
|
|
|
|
/* STK */
|
|
return crypto_toolbox::s1(tk, mrand, srand);
|
|
}
|
|
} // namespace security
|
|
} // namespace bluetooth
|