blob: e854efec4b92f99ae49f50f465e11e62abcaa8f3 [file] [log] [blame]
/*
*
* 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/classic_pairing_handler.h"
namespace bluetooth {
namespace security {
namespace pairing {
void ClassicPairingHandler::OnRegistrationComplete(
l2cap::classic::FixedChannelManager::RegistrationResult result,
std::unique_ptr<l2cap::classic::FixedChannelService> fixed_channel_service) {
fixed_channel_service_ = std::move(fixed_channel_service);
fixed_channel_manager_->ConnectServices(
GetRecord()->GetDevice().GetAddress(),
common::Bind(&ClassicPairingHandler::OnConnectionFail, common::Unretained(this)), security_handler_);
}
void ClassicPairingHandler::OnUnregistered() {
std::move(complete_callback_).Run(GetRecord()->GetDevice().GetAddress());
}
void ClassicPairingHandler::OnConnectionOpen(std::unique_ptr<l2cap::classic::FixedChannel> fixed_channel) {
ASSERT(fixed_channel_ == nullptr);
fixed_channel_ = std::move(fixed_channel);
fixed_channel_->Acquire();
fixed_channel_->RegisterOnCloseCallback(
security_handler_, common::BindOnce(&ClassicPairingHandler::OnConnectionClose, common::Unretained(this)));
}
void ClassicPairingHandler::OnConnectionFail(l2cap::classic::FixedChannelManager::ConnectionResult result) {
Cancel();
}
void ClassicPairingHandler::OnConnectionClose(hci::ErrorCode error_code) {
// Called when the connection gets closed
LOG_ERROR("Connection closed due to: %s", hci::ErrorCodeText(error_code).c_str());
ASSERT(fixed_channel_ != nullptr);
Cancel();
}
void ClassicPairingHandler::Initiate(bool locally_initiated, hci::IoCapability io_capability,
hci::OobDataPresent oob_present,
hci::AuthenticationRequirements auth_requirements) {
locally_initiated_ = locally_initiated;
local_io_capability_ = io_capability;
local_oob_present_ = oob_present;
local_authentication_requirements_ = auth_requirements;
// TODO(optedoblivion): Read OOB data
// if host and controller support secure connections used HCIREADLOCALOOBEXTENDEDDATA vs HCIREADLOCALOOBDATA
fixed_channel_manager_->RegisterService(
l2cap::kClassicPairingTriggerCid, security_policy_,
common::Bind(&ClassicPairingHandler::OnRegistrationComplete, common::Unretained(this)),
common::Bind(&ClassicPairingHandler::OnConnectionOpen, common::Unretained(this)), security_handler_);
}
void ClassicPairingHandler::Cancel() {
if (fixed_channel_ != nullptr) {
fixed_channel_->Release();
}
if (fixed_channel_service_ != nullptr) {
fixed_channel_service_->Unregister(common::Bind(&ClassicPairingHandler::OnUnregistered, common::Unretained(this)),
security_handler_);
}
}
void ClassicPairingHandler::OnReceive(hci::ChangeConnectionLinkKeyCompleteView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received unsupported event: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
}
void ClassicPairingHandler::OnReceive(hci::MasterLinkKeyCompleteView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received unsupported event: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
}
void ClassicPairingHandler::OnReceive(hci::PinCodeRequestView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
}
void ClassicPairingHandler::OnReceive(hci::LinkKeyRequestView packet) {
ASSERT(packet.IsValid());
// TODO(optedoblivion): Add collision detection here
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
if (GetRecord()->IsBonded() || GetRecord()->IsPaired()) {
auto packet =
hci::LinkKeyRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress(), GetRecord()->GetLinkKey());
this->GetChannel()->SendCommand(std::move(packet));
} else {
auto packet = hci::LinkKeyRequestNegativeReplyBuilder::Create(GetRecord()->GetDevice().GetAddress());
this->GetChannel()->SendCommand(std::move(packet));
}
}
void ClassicPairingHandler::OnReceive(hci::LinkKeyNotificationView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
GetRecord()->SetLinkKey(packet.GetLinkKey(), packet.GetKeyType());
}
void ClassicPairingHandler::OnReceive(hci::IoCapabilityRequestView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
hci::IoCapability io_capability = local_io_capability_;
hci::OobDataPresent oob_present = hci::OobDataPresent::NOT_PRESENT;
hci::AuthenticationRequirements authentication_requirements = local_authentication_requirements_;
auto reply_packet = hci::IoCapabilityRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress(), io_capability,
oob_present, authentication_requirements);
this->GetChannel()->SendCommand(std::move(reply_packet));
}
void ClassicPairingHandler::OnReceive(hci::IoCapabilityResponseView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
// Using local variable until device database pointer is ready
remote_io_capability_ = packet.GetIoCapability();
// TODO(optedoblivion): device->SetIoCapability(packet.GetIoCapability);
}
void ClassicPairingHandler::OnReceive(hci::SimplePairingCompleteView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
Cancel();
}
void ClassicPairingHandler::OnReceive(hci::ReturnLinkKeysView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
}
void ClassicPairingHandler::OnReceive(hci::EncryptionChangeView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
}
void ClassicPairingHandler::OnReceive(hci::EncryptionKeyRefreshCompleteView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
}
void ClassicPairingHandler::OnReceive(hci::RemoteOobDataRequestView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
}
void ClassicPairingHandler::OnReceive(hci::UserPasskeyNotificationView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
}
void ClassicPairingHandler::OnReceive(hci::KeypressNotificationView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
LOG_INFO("Notification Type: %s", hci::KeypressNotificationTypeText(packet.GetNotificationType()).c_str());
switch (packet.GetNotificationType()) {
case hci::KeypressNotificationType::ENTRY_STARTED:
// Get ready to keep track of key input
break;
case hci::KeypressNotificationType::DIGIT_ENTERED:
// Append digit to key
break;
case hci::KeypressNotificationType::DIGIT_ERASED:
// erase last digit from key
break;
case hci::KeypressNotificationType::CLEARED:
// erase all digits from key
break;
case hci::KeypressNotificationType::ENTRY_COMPLETED:
// set full key to security record
break;
}
}
/**
* Here we decide what type of pairing authentication method we will use
*
* The table is on pg 2133 of the Core v5.1 spec.
*/
void ClassicPairingHandler::OnReceive(hci::UserConfirmationRequestView packet) {
ASSERT(packet.IsValid());
LOG_INFO("Received: %s", hci::EventCodeText(packet.GetEventCode()).c_str());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
// if locally_initialized, use default, otherwise us remote io caps
hci::IoCapability initiator_io_capability = (locally_initiated_) ? local_io_capability_ : remote_io_capability_;
hci::IoCapability responder_io_capability = (!locally_initiated_) ? local_io_capability_ : remote_io_capability_;
// TODO(optedoblivion): Check for TEMPORARY pairing case
switch (initiator_io_capability) {
case hci::IoCapability::DISPLAY_ONLY:
switch (responder_io_capability) {
case hci::IoCapability::DISPLAY_ONLY:
// NumericComparison, Both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
case hci::IoCapability::DISPLAY_YES_NO:
// NumericComparison, Initiator auto confirm, Responder display
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
LOG_INFO("Numeric Comparison: A auto confirm");
// Unauthenticated
break;
case hci::IoCapability::KEYBOARD_ONLY:
// PassKey Entry, Initiator display, Responder input
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::NO_INPUT_NO_OUTPUT:
// NumericComparison, Both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
}
break;
case hci::IoCapability::DISPLAY_YES_NO:
switch (responder_io_capability) {
case hci::IoCapability::DISPLAY_ONLY:
// NumericComparison, Initiator display, Responder auto confirm
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Unauthenticated
break;
case hci::IoCapability::DISPLAY_YES_NO:
// NumericComparison Both Display, Both confirm
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::KEYBOARD_ONLY:
// PassKey Entry, Initiator display, Responder input
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::NO_INPUT_NO_OUTPUT:
// NumericComparison, auto confirm Responder, Yes/No confirm Initiator. Don't show confirmation value
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Unauthenticated
break;
}
break;
case hci::IoCapability::KEYBOARD_ONLY:
switch (responder_io_capability) {
case hci::IoCapability::DISPLAY_ONLY:
// PassKey Entry, Responder display, Initiator input
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::DISPLAY_YES_NO:
// PassKey Entry, Responder display, Initiator input
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::KEYBOARD_ONLY:
// PassKey Entry, both input
// TODO(optedoblivion): Notify UI
LOG_INFO("Notify UI");
// Authenticated
break;
case hci::IoCapability::NO_INPUT_NO_OUTPUT:
// NumericComparison, both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
}
break;
case hci::IoCapability::NO_INPUT_NO_OUTPUT:
switch (responder_io_capability) {
case hci::IoCapability::DISPLAY_ONLY:
// NumericComparison, both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
case hci::IoCapability::DISPLAY_YES_NO:
// NumericComparison, Initiator auto confirm, Responder Yes/No confirm, no show conf val
LOG_INFO("Numeric Comparison: A auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
case hci::IoCapability::KEYBOARD_ONLY:
// NumericComparison, both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
case hci::IoCapability::NO_INPUT_NO_OUTPUT:
// NumericComparison, both auto confirm
LOG_INFO("Numeric Comparison: A and B auto confirm");
GetChannel()->SendCommand(
hci::UserConfirmationRequestReplyBuilder::Create(GetRecord()->GetDevice().GetAddress()));
// Unauthenticated
break;
}
break;
}
}
void ClassicPairingHandler::OnReceive(hci::UserPasskeyRequestView packet) {
ASSERT(packet.IsValid());
ASSERT_LOG(GetRecord()->GetDevice().GetAddress() == packet.GetBdAddr(), "Address mismatch");
}
} // namespace pairing
} // namespace security
} // namespace bluetooth