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.
478 lines
21 KiB
478 lines
21 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"
|
|
|
|
namespace bluetooth {
|
|
namespace security {
|
|
|
|
MyOobData PairingHandlerLe::GenerateOobData() {
|
|
MyOobData data{};
|
|
std::tie(data.private_key, data.public_key) = GenerateECDHKeyPair();
|
|
|
|
data.r = bluetooth::os::GenerateRandom<16>();
|
|
data.c = crypto_toolbox::f4(data.public_key.x.data(), data.public_key.x.data(), data.r, 0);
|
|
return data;
|
|
}
|
|
|
|
void PairingHandlerLe::PairingMain(InitialInformations i) {
|
|
LOG_INFO("Pairing Started");
|
|
|
|
if (i.remotely_initiated) {
|
|
LOG_INFO("Was remotely initiated, presenting user with the accept prompt");
|
|
i.user_interface_handler->Post(common::BindOnce(&UI::DisplayPairingPrompt, common::Unretained(i.user_interface),
|
|
i.remote_connection_address, i.remote_name));
|
|
|
|
// If pairing was initiated by remote device, wait for the user to accept
|
|
// the request from the UI.
|
|
LOG_INFO("Waiting for the prompt response");
|
|
std::optional<PairingEvent> pairingAccepted = WaitUiPairingAccept();
|
|
if (!pairingAccepted || pairingAccepted->ui_value == 0) {
|
|
LOG_INFO("User either did not accept the remote pairing, or the prompt timed out");
|
|
// TODO: Uncomment this one once we find a way to attempt to send packet when the link is down
|
|
// SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
|
|
i.OnPairingFinished(PairingFailure("User either did not accept the remote pairing, or the prompt timed out"));
|
|
return;
|
|
}
|
|
|
|
LOG_INFO("Pairing prompt accepted");
|
|
}
|
|
|
|
/************************************************ PHASE 1 *********************************************************/
|
|
Phase1ResultOrFailure phase_1_result = ExchangePairingFeature(i);
|
|
if (std::holds_alternative<PairingFailure>(phase_1_result)) {
|
|
LOG_WARN("Pairing failed in phase 1");
|
|
// We already send pairing fialed in lower layer. Which one should do that ? how about disconneciton?
|
|
// SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
|
|
// TODO: disconnect?
|
|
i.OnPairingFinished(std::get<PairingFailure>(phase_1_result));
|
|
return;
|
|
}
|
|
|
|
auto [pairing_request, pairing_response] = std::get<Phase1Result>(phase_1_result);
|
|
|
|
uint8_t key_size =
|
|
std::min(pairing_request.GetMaximumEncryptionKeySize(), pairing_response.GetMaximumEncryptionKeySize());
|
|
if (key_size < 7 || key_size > 16) {
|
|
LOG_WARN("Resulting key size is bad %d", key_size);
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::ENCRYPTION_KEY_SIZE));
|
|
i.OnPairingFinished(PairingFailure("Resulting key size is bad", PairingFailedReason::ENCRYPTION_KEY_SIZE));
|
|
return;
|
|
}
|
|
if (key_size != 16) {
|
|
LOG_WARN("Resulting key size is less than 16 octets!");
|
|
}
|
|
|
|
/************************************************ PHASE 2 *********************************************************/
|
|
bool isSecureConnections = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskSc;
|
|
if (isSecureConnections) {
|
|
// 2.3.5.6 LE Secure Connections pairing phase 2
|
|
LOG_INFO("Pairing Phase 2 LE Secure connections Started");
|
|
|
|
/*
|
|
TODO: what to do with this piece of spec ?
|
|
If Secure Connections pairing has been initiated over BR/EDR, the
|
|
following fields of the SM Pairing Request PDU are reserved for future use:
|
|
• the IO Capability field,
|
|
• the OOB data flag field, and
|
|
• all bits in the Auth Req field except the CT2 bit.
|
|
*/
|
|
|
|
OobDataFlag remote_have_oob_data =
|
|
IAmCentral(i) ? pairing_response.GetOobDataFlag() : pairing_request.GetOobDataFlag();
|
|
|
|
auto key_exchange_result = ExchangePublicKeys(i, remote_have_oob_data);
|
|
if (std::holds_alternative<PairingFailure>(key_exchange_result)) {
|
|
LOG_ERROR("Public key exchange failed");
|
|
i.OnPairingFinished(std::get<PairingFailure>(key_exchange_result));
|
|
return;
|
|
}
|
|
auto [PKa, PKb, dhkey] = std::get<KeyExchangeResult>(key_exchange_result);
|
|
|
|
// Public key exchange finished, Diffie-Hellman key computed.
|
|
|
|
Stage1ResultOrFailure stage1result = DoSecureConnectionsStage1(i, PKa, PKb, pairing_request, pairing_response);
|
|
if (std::holds_alternative<PairingFailure>(stage1result)) {
|
|
i.OnPairingFinished(std::get<PairingFailure>(stage1result));
|
|
return;
|
|
}
|
|
|
|
Stage2ResultOrFailure stage_2_result = DoSecureConnectionsStage2(i, PKa, PKb, pairing_request, pairing_response,
|
|
std::get<Stage1Result>(stage1result), dhkey);
|
|
if (std::holds_alternative<PairingFailure>(stage_2_result)) {
|
|
i.OnPairingFinished(std::get<PairingFailure>(stage_2_result));
|
|
return;
|
|
}
|
|
|
|
Octet16 ltk = std::get<Octet16>(stage_2_result);
|
|
// Mask the key
|
|
std::fill(ltk.begin() + key_size, ltk.end(), 0x00);
|
|
|
|
if (IAmCentral(i)) {
|
|
LOG_INFO("Sending start encryption request");
|
|
SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, ltk);
|
|
} else {
|
|
auto ltk_req = WaitLeLongTermKeyRequest();
|
|
SendHciLeLongTermKeyReply(i, i.connection_handle, ltk);
|
|
}
|
|
} else {
|
|
// 2.3.5.5 LE legacy pairing phase 2
|
|
LOG_INFO("Pairing Phase 2 LE legacy pairing Started");
|
|
|
|
LegacyStage1ResultOrFailure stage1result = DoLegacyStage1(i, pairing_request, pairing_response);
|
|
if (std::holds_alternative<PairingFailure>(stage1result)) {
|
|
LOG_ERROR("Phase 1 failed");
|
|
i.OnPairingFinished(std::get<PairingFailure>(stage1result));
|
|
return;
|
|
}
|
|
|
|
Octet16 tk = std::get<Octet16>(stage1result);
|
|
StkOrFailure stage2result = DoLegacyStage2(i, pairing_request, pairing_response, tk);
|
|
if (std::holds_alternative<PairingFailure>(stage2result)) {
|
|
LOG_ERROR("stage 2 failed");
|
|
i.OnPairingFinished(std::get<PairingFailure>(stage2result));
|
|
return;
|
|
}
|
|
|
|
Octet16 stk = std::get<Octet16>(stage2result);
|
|
// Mask the key
|
|
std::fill(stk.begin() + key_size, stk.end(), 0x00);
|
|
if (IAmCentral(i)) {
|
|
LOG_INFO("Sending start encryption request");
|
|
SendHciLeStartEncryption(i, i.connection_handle, {0}, {0}, stk);
|
|
} else {
|
|
auto ltk_req = WaitLeLongTermKeyRequest();
|
|
SendHciLeLongTermKeyReply(i, i.connection_handle, stk);
|
|
}
|
|
}
|
|
|
|
/************************************************ PHASE 3 *********************************************************/
|
|
LOG_INFO("Waiting for encryption changed");
|
|
auto encryption_change_result = WaitEncryptionChanged();
|
|
if (std::holds_alternative<PairingFailure>(encryption_change_result)) {
|
|
i.OnPairingFinished(std::get<PairingFailure>(encryption_change_result));
|
|
return;
|
|
} else if (std::holds_alternative<EncryptionChangeView>(encryption_change_result)) {
|
|
EncryptionChangeView encryption_changed = std::get<EncryptionChangeView>(encryption_change_result);
|
|
if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS ||
|
|
encryption_changed.GetEncryptionEnabled() != hci::EncryptionEnabled::ON) {
|
|
i.OnPairingFinished(PairingFailure("Encryption change failed"));
|
|
return;
|
|
}
|
|
} else if (std::holds_alternative<EncryptionKeyRefreshCompleteView>(encryption_change_result)) {
|
|
EncryptionKeyRefreshCompleteView encryption_changed =
|
|
std::get<EncryptionKeyRefreshCompleteView>(encryption_change_result);
|
|
if (encryption_changed.GetStatus() != hci::ErrorCode::SUCCESS) {
|
|
i.OnPairingFinished(PairingFailure("Encryption key refresh failed"));
|
|
return;
|
|
}
|
|
} else {
|
|
i.OnPairingFinished(PairingFailure("Unknown case of encryption change result"));
|
|
return;
|
|
}
|
|
LOG_INFO("Encryption change finished successfully");
|
|
|
|
DistributedKeysOrFailure keyExchangeStatus = DistributeKeys(i, pairing_response, isSecureConnections);
|
|
if (std::holds_alternative<PairingFailure>(keyExchangeStatus)) {
|
|
i.OnPairingFinished(std::get<PairingFailure>(keyExchangeStatus));
|
|
LOG_ERROR("Key exchange failed");
|
|
return;
|
|
}
|
|
|
|
// If it's secure connections pairing, do cross-transport key derivation
|
|
DistributedKeys distributed_keys = std::get<DistributedKeys>(keyExchangeStatus);
|
|
if ((pairing_response.GetAuthReq() & AuthReqMaskSc) && distributed_keys.remote_ltk.has_value()) {
|
|
bool use_h7 = (pairing_response.GetAuthReq() & AuthReqMaskCt2);
|
|
Octet16 link_key = crypto_toolbox::ltk_to_link_key(*(distributed_keys.remote_ltk), use_h7);
|
|
distributed_keys.remote_link_key = link_key;
|
|
}
|
|
|
|
// bool bonding = pairing_request.GetAuthReq() & pairing_response.GetAuthReq() & AuthReqMaskBondingFlag;
|
|
|
|
i.OnPairingFinished(PairingResult{
|
|
.connection_address = i.remote_connection_address,
|
|
.distributed_keys = distributed_keys,
|
|
.key_size = key_size,
|
|
});
|
|
|
|
LOG_INFO("Pairing finished successfully.");
|
|
}
|
|
|
|
Phase1ResultOrFailure PairingHandlerLe::ExchangePairingFeature(const InitialInformations& i) {
|
|
LOG_INFO("Phase 1 start");
|
|
|
|
if (IAmCentral(i)) {
|
|
// Send Pairing Request
|
|
const auto& x = i.myPairingCapabilities;
|
|
auto pairing_request_builder =
|
|
PairingRequestBuilder::Create(x.io_capability, x.oob_data_flag, x.auth_req, x.maximum_encryption_key_size,
|
|
x.initiator_key_distribution, x.responder_key_distribution);
|
|
// basically pairing_request = myPairingCapabilities;
|
|
|
|
// Convert builder to view
|
|
std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
|
|
BitInserter it(*packet_bytes);
|
|
pairing_request_builder->Serialize(it);
|
|
PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
|
|
auto temp_cmd_view = CommandView::Create(packet_bytes_view);
|
|
auto pairing_request = PairingRequestView::Create(temp_cmd_view);
|
|
ASSERT(pairing_request.IsValid());
|
|
|
|
LOG_INFO("Sending Pairing Request");
|
|
SendL2capPacket(i, std::move(pairing_request_builder));
|
|
|
|
LOG_INFO("Waiting for Pairing Response");
|
|
auto response = WaitPairingResponse();
|
|
|
|
/* There is a potential collision where the peripheral initiates the pairing at the same time we initiate it, by
|
|
* sending security request. */
|
|
if (std::holds_alternative<PairingFailure>(response) &&
|
|
std::get<PairingFailure>(response).received_code_ == Code::SECURITY_REQUEST) {
|
|
LOG_INFO("Received security request, waiting for Pairing Response again...");
|
|
response = WaitPairingResponse();
|
|
}
|
|
|
|
if (std::holds_alternative<PairingFailure>(response)) {
|
|
// TODO: should the failure reason be different in some cases ? How about
|
|
// when we lost connection ? Don't send anything at all, or have L2CAP
|
|
// layer ignore it?
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
|
|
return std::get<PairingFailure>(response);
|
|
}
|
|
|
|
auto pairing_response = std::get<PairingResponseView>(response);
|
|
|
|
LOG_INFO("Phase 1 finish");
|
|
return Phase1Result{pairing_request, pairing_response};
|
|
} else {
|
|
std::optional<PairingRequestView> pairing_request;
|
|
|
|
if (i.remotely_initiated) {
|
|
if (!i.pairing_request.has_value()) {
|
|
return PairingFailure("You must pass PairingRequest as a initial information to peripheral!");
|
|
}
|
|
|
|
pairing_request = i.pairing_request.value();
|
|
|
|
if (!pairing_request->IsValid()) return PairingFailure("Malformed PairingRequest");
|
|
} else {
|
|
SendL2capPacket(i, SecurityRequestBuilder::Create(i.myPairingCapabilities.auth_req));
|
|
|
|
LOG_INFO("Waiting for Pairing Request");
|
|
auto request = WaitPairingRequest();
|
|
if (std::holds_alternative<PairingFailure>(request)) {
|
|
LOG_INFO("%s", std::get<PairingFailure>(request).message.c_str());
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::UNSPECIFIED_REASON));
|
|
return std::get<PairingFailure>(request);
|
|
}
|
|
|
|
pairing_request = std::get<PairingRequestView>(request);
|
|
}
|
|
|
|
uint8_t key_size = pairing_request->GetMaximumEncryptionKeySize();
|
|
if (key_size < 7 || key_size > 16) {
|
|
LOG_WARN("Resulting key size is bad %d", key_size);
|
|
SendL2capPacket(i, PairingFailedBuilder::Create(PairingFailedReason::ENCRYPTION_KEY_SIZE));
|
|
return PairingFailure("Resulting key size is bad", PairingFailedReason::ENCRYPTION_KEY_SIZE);
|
|
}
|
|
|
|
// Send Pairing Request
|
|
const auto& x = i.myPairingCapabilities;
|
|
// basically pairing_response_builder = my_first_packet;
|
|
// We are not allowed to enable bits that the remote did not allow us to set in initiator_key_dist and
|
|
// responder_key_distribution
|
|
auto pairing_response_builder =
|
|
PairingResponseBuilder::Create(x.io_capability, x.oob_data_flag, x.auth_req, x.maximum_encryption_key_size,
|
|
x.initiator_key_distribution & pairing_request->GetInitiatorKeyDistribution(),
|
|
x.responder_key_distribution & pairing_request->GetResponderKeyDistribution());
|
|
|
|
// Convert builder to view
|
|
std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
|
|
BitInserter it(*packet_bytes);
|
|
pairing_response_builder->Serialize(it);
|
|
PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
|
|
auto temp_cmd_view = CommandView::Create(packet_bytes_view);
|
|
auto pairing_response = PairingResponseView::Create(temp_cmd_view);
|
|
ASSERT(pairing_response.IsValid());
|
|
|
|
LOG_INFO("Sending Pairing Response");
|
|
SendL2capPacket(i, std::move(pairing_response_builder));
|
|
|
|
LOG_INFO("Phase 1 finish");
|
|
return Phase1Result{pairing_request.value(), pairing_response};
|
|
}
|
|
}
|
|
|
|
DistributedKeysOrFailure PairingHandlerLe::DistributeKeys(const InitialInformations& i,
|
|
const PairingResponseView& pairing_response,
|
|
bool isSecureConnections) {
|
|
uint8_t keys_i_receive =
|
|
IAmCentral(i) ? pairing_response.GetResponderKeyDistribution() : pairing_response.GetInitiatorKeyDistribution();
|
|
uint8_t keys_i_send =
|
|
IAmCentral(i) ? pairing_response.GetInitiatorKeyDistribution() : pairing_response.GetResponderKeyDistribution();
|
|
|
|
// In Secure Connections on the LE Transport, the EncKey field shall be ignored
|
|
if (isSecureConnections) {
|
|
keys_i_send = (~KeyMaskEnc) & keys_i_send;
|
|
keys_i_receive = (~KeyMaskEnc) & keys_i_receive;
|
|
}
|
|
|
|
LOG_INFO("Key distribution start, keys_i_send=0x%02x, keys_i_receive=0x%02x", keys_i_send, keys_i_receive);
|
|
|
|
// TODO: obtain actual values, and apply key_size to the LTK
|
|
Octet16 my_ltk = bluetooth::os::GenerateRandom<16>();
|
|
uint16_t my_ediv = bluetooth::os::GenerateRandom();
|
|
std::array<uint8_t, 8> my_rand = bluetooth::os::GenerateRandom<8>();
|
|
|
|
Octet16 my_irk = i.my_identity_resolving_key;
|
|
Address my_identity_address = i.my_identity_address.GetAddress();
|
|
AddrType my_identity_address_type =
|
|
static_cast<bluetooth::security::AddrType>(i.my_identity_address.GetAddressType());
|
|
Octet16 my_signature_key{0};
|
|
|
|
if (IAmCentral(i)) {
|
|
// EncKey is unused for LE Secure Connections
|
|
DistributedKeysOrFailure keys = ReceiveKeys(keys_i_receive);
|
|
if (std::holds_alternative<PairingFailure>(keys)) {
|
|
return keys;
|
|
}
|
|
|
|
SendKeys(i, keys_i_send, my_ltk, my_ediv, my_rand, my_irk, my_identity_address, my_identity_address_type,
|
|
my_signature_key);
|
|
|
|
std::get<DistributedKeys>(keys).local_ltk = my_ltk;
|
|
std::get<DistributedKeys>(keys).local_ediv = my_ediv;
|
|
std::get<DistributedKeys>(keys).local_rand = my_rand;
|
|
LOG_INFO("Key distribution finish");
|
|
return keys;
|
|
} else {
|
|
SendKeys(i, keys_i_send, my_ltk, my_ediv, my_rand, my_irk, my_identity_address, my_identity_address_type,
|
|
my_signature_key);
|
|
|
|
DistributedKeysOrFailure keys = ReceiveKeys(keys_i_receive);
|
|
if (std::holds_alternative<PairingFailure>(keys)) {
|
|
return keys;
|
|
}
|
|
|
|
std::get<DistributedKeys>(keys).local_ltk = my_ltk;
|
|
std::get<DistributedKeys>(keys).local_ediv = my_ediv;
|
|
std::get<DistributedKeys>(keys).local_rand = my_rand;
|
|
LOG_INFO("Key distribution finish");
|
|
return keys;
|
|
}
|
|
}
|
|
|
|
DistributedKeysOrFailure PairingHandlerLe::ReceiveKeys(const uint8_t& keys_i_receive) {
|
|
std::optional<Octet16> ltk; /* Legacy only */
|
|
std::optional<uint16_t> ediv; /* Legacy only */
|
|
std::optional<std::array<uint8_t, 8>> rand; /* Legacy only */
|
|
std::optional<hci::AddressWithType> identity_address;
|
|
std::optional<Octet16> irk;
|
|
std::optional<Octet16> signature_key;
|
|
|
|
if (keys_i_receive & KeyMaskEnc) {
|
|
{
|
|
auto packet = WaitEncryptionInformation();
|
|
if (std::holds_alternative<PairingFailure>(packet)) {
|
|
LOG_ERROR(" Was expecting Encryption Information but did not receive!");
|
|
return std::get<PairingFailure>(packet);
|
|
}
|
|
ltk = std::get<EncryptionInformationView>(packet).GetLongTermKey();
|
|
}
|
|
|
|
{
|
|
auto packet = WaitCentralIdentification();
|
|
if (std::holds_alternative<PairingFailure>(packet)) {
|
|
LOG_ERROR(" Was expecting Central Identification but did not receive!");
|
|
return std::get<PairingFailure>(packet);
|
|
}
|
|
ediv = std::get<CentralIdentificationView>(packet).GetEdiv();
|
|
rand = std::get<CentralIdentificationView>(packet).GetRand();
|
|
}
|
|
}
|
|
|
|
if (keys_i_receive & KeyMaskId) {
|
|
auto packet = WaitIdentityInformation();
|
|
if (std::holds_alternative<PairingFailure>(packet)) {
|
|
LOG_ERROR(" Was expecting Identity Information but did not receive!");
|
|
return std::get<PairingFailure>(packet);
|
|
}
|
|
|
|
LOG_INFO("Received Identity Information");
|
|
irk = std::get<IdentityInformationView>(packet).GetIdentityResolvingKey();
|
|
|
|
auto iapacket = WaitIdentityAddressInformation();
|
|
if (std::holds_alternative<PairingFailure>(iapacket)) {
|
|
LOG_ERROR(
|
|
"Was expecting Identity Address Information but did "
|
|
"not receive!");
|
|
return std::get<PairingFailure>(iapacket);
|
|
}
|
|
LOG_INFO("Received Identity Address Information");
|
|
auto iapacketview = std::get<IdentityAddressInformationView>(iapacket);
|
|
identity_address = hci::AddressWithType(iapacketview.GetBdAddr(), iapacketview.GetAddrType() == AddrType::PUBLIC
|
|
? hci::AddressType::PUBLIC_DEVICE_ADDRESS
|
|
: hci::AddressType::RANDOM_DEVICE_ADDRESS);
|
|
}
|
|
|
|
if (keys_i_receive & KeyMaskSign) {
|
|
auto packet = WaitSigningInformation();
|
|
if (std::holds_alternative<PairingFailure>(packet)) {
|
|
LOG_ERROR(" Was expecting Signing Information but did not receive!");
|
|
return std::get<PairingFailure>(packet);
|
|
}
|
|
|
|
LOG_INFO("Received Signing Information");
|
|
signature_key = std::get<SigningInformationView>(packet).GetSignatureKey();
|
|
}
|
|
|
|
return DistributedKeys{.remote_ltk = ltk,
|
|
.remote_ediv = ediv,
|
|
.remote_rand = rand,
|
|
.remote_identity_address = identity_address,
|
|
.remote_irk = irk,
|
|
.remote_signature_key = signature_key};
|
|
}
|
|
|
|
void PairingHandlerLe::SendKeys(const InitialInformations& i, const uint8_t& keys_i_send, Octet16 ltk, uint16_t ediv,
|
|
std::array<uint8_t, 8> rand, Octet16 irk, Address identity_address,
|
|
AddrType identity_addres_type, Octet16 signature_key) {
|
|
if (keys_i_send & KeyMaskEnc) {
|
|
LOG_INFO("Sending Encryption Information");
|
|
SendL2capPacket(i, EncryptionInformationBuilder::Create(ltk));
|
|
LOG_INFO("Sending Central Identification");
|
|
SendL2capPacket(i, CentralIdentificationBuilder::Create(ediv, rand));
|
|
}
|
|
|
|
if (keys_i_send & KeyMaskId) {
|
|
LOG_INFO("Sending Identity Information");
|
|
SendL2capPacket(i, IdentityInformationBuilder::Create(irk));
|
|
LOG_INFO("Sending Identity Address Information");
|
|
SendL2capPacket(i, IdentityAddressInformationBuilder::Create(identity_addres_type, identity_address));
|
|
}
|
|
|
|
if (keys_i_send & KeyMaskSign) {
|
|
LOG_INFO("Sending Signing Information");
|
|
SendL2capPacket(i, SigningInformationBuilder::Create(signature_key));
|
|
}
|
|
}
|
|
|
|
} // namespace security
|
|
} // namespace bluetooth
|