diff options
Diffstat (limited to 'src/bluetooth/qlowenergycontroller_bluez.cpp')
-rw-r--r-- | src/bluetooth/qlowenergycontroller_bluez.cpp | 115 |
1 files changed, 110 insertions, 5 deletions
diff --git a/src/bluetooth/qlowenergycontroller_bluez.cpp b/src/bluetooth/qlowenergycontroller_bluez.cpp index 1562c3f9..06586192 100644 --- a/src/bluetooth/qlowenergycontroller_bluez.cpp +++ b/src/bluetooth/qlowenergycontroller_bluez.cpp @@ -38,13 +38,16 @@ ** ****************************************************************************/ +#include "lecmacverifier_p.h" #include "qlowenergycontroller_p.h" #include "qbluetoothsocket_p.h" #include "qleadvertiser_p.h" #include "bluez/bluez_data_p.h" #include "bluez/hcimanager_p.h" +#include <QtCore/QFileInfo> #include <QtCore/QLoggingCategory> +#include <QtCore/QSettings> #include <QtBluetooth/QBluetoothLocalDevice> #include <QtBluetooth/QBluetoothSocket> #include <QtBluetooth/QLowEnergyCharacteristicData> @@ -60,8 +63,6 @@ #include <sys/socket.h> #include <unistd.h> -#define ATTRIBUTE_CHANNEL_ID 4 - #define ATT_DEFAULT_LE_MTU 23 #define ATT_MAX_LE_MTU 0x200 @@ -271,6 +272,7 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() connect(hciManager, SIGNAL(encryptionChangedEvent(QBluetoothAddress,bool)), this, SLOT(encryptionChangedEvent(QBluetoothAddress,bool))); hciManager->monitorEvent(HciManager::LeMetaEvent); + hciManager->monitorAclPackets(); connect(hciManager, &HciManager::connectionComplete, [this](quint16 handle) { connectionHandle = handle; qCDebug(QT_BT_BLUEZ) << "received connection complete event, handle:" << handle; @@ -281,11 +283,22 @@ QLowEnergyControllerPrivate::QLowEnergyControllerPrivate() emit q_ptr->connectionUpdated(params); } ); + connect(hciManager, &HciManager::signatureResolvingKeyReceived, + [this](quint16 handle, const quint128 &csrk) { + if (handle == connectionHandle) { + qCDebug(QT_BT_BLUEZ) << "received new signature resolving key" + << QByteArray(reinterpret_cast<const char *>(csrk.data), + sizeof csrk).toHex(); + signingData.insert(remoteDevice.toUInt64(), SigningData(csrk)); + } + } + ); } QLowEnergyControllerPrivate::~QLowEnergyControllerPrivate() { closeServerSocket(); + delete cmacVerifier; } class ServerSocket @@ -337,6 +350,7 @@ private: int m_socket = -1; }; + void QLowEnergyControllerPrivate::startAdvertising(const QLowEnergyAdvertisingParameters ¶ms, const QLowEnergyAdvertisingData &advertisingData, const QLowEnergyAdvertisingData &scanResponseData) @@ -2462,9 +2476,32 @@ void QLowEnergyControllerPrivate::handleWriteRequestOrCommand(const QByteArray & qCWarning(QT_BT_BLUEZ) << "Ignoring signed write on encrypted link."; return; } - // const QByteArray signature = packet.right(12); - qCWarning(QT_BT_BLUEZ) << "signed write not implemented, ignoring."; - return; // TODO: Check signature and continue if it's valid. Check and update sign counter. + const auto signingDataIt = signingData.find(remoteDevice.toUInt64()); + if (signingDataIt == signingData.constEnd()) { + qCWarning(QT_BT_BLUEZ) << "No CSRK found for peer device, ignoring signed write"; + return; + } + + const quint32 signCounter = getBtData<quint32>(packet.data() + packet.count() - 12); + if (signCounter < signingDataIt.value().counter + 1) { + qCWarning(QT_BT_BLUEZ) << "Client's' sign counter" << signCounter + << "not greater than local sign counter" + << signingDataIt.value().counter + << "; ignoring signed write command."; + return; + } + + const quint64 macFromClient = getBtData<quint64>(packet.data() + packet.count() - 8); + const bool signatureCorrect = verifyMac(packet.left(packet.count() - 12), + signingDataIt.value().key, signCounter, macFromClient); + if (!signatureCorrect) { + qCWarning(QT_BT_BLUEZ) << "Signed Write packet has wrong signature, disconnecting"; + disconnectFromDevice(); // Recommended by spec v4.2, Vol 3, part C, 10.4.2 + return; + } + + signingDataIt.value().counter = signCounter; + storeSignCounter(); valueLength = packet.count() - 15; } else { valueLength = packet.count() - 3; @@ -2678,6 +2715,7 @@ void QLowEnergyControllerPrivate::handleConnectionRequest() l2cpSocket->setSocketDescriptor(clientSocket, QBluetoothServiceInfo::L2capProtocol, QBluetoothSocket::ConnectedState, QIODevice::ReadWrite | QIODevice::Unbuffered); restoreClientConfigurations(); + loadSigningDataIfNecessary(); setState(QLowEnergyController::ConnectedState); } @@ -2773,6 +2811,64 @@ void QLowEnergyControllerPrivate::restoreClientConfigurations() sendNextIndication(); } +void QLowEnergyControllerPrivate::loadSigningDataIfNecessary() +{ + const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64()); + if (signingDataIt != signingData.constEnd()) + return; // We are up to date for this device. + const QString settingsFilePath = keySettingsFilePath(); + if (!QFileInfo(settingsFilePath).exists()) { + qCDebug(QT_BT_BLUEZ) << "No settings found for peer device."; + return; + } + QSettings settings(settingsFilePath, QSettings::IniFormat); + settings.beginGroup(QLatin1String("RemoteSignatureKey")); + const QByteArray keyString = settings.value(QLatin1String("Key")).toByteArray(); + if (keyString.isEmpty()) { + qCDebug(QT_BT_BLUEZ) << "No remote signature key found in settings file"; + return; + } + const QByteArray keyData = QByteArray::fromHex(keyString); + if (keyData.count() != int(sizeof(quint128))) { + qCWarning(QT_BT_BLUEZ) << "Remote signature key in settings file has invalid size" + << keyString.count(); + return; + } + qCDebug(QT_BT_BLUEZ) << "CSRK of peer device is" << keyString; + const quint32 counter = settings.value(QLatin1String("Counter"), 0).toUInt(); + quint128 csrk; + using namespace std; + memcpy(csrk.data, keyData.constData(), keyData.count()); + signingData.insert(remoteDevice.toUInt64(), SigningData(csrk, counter - 1)); +} + +void QLowEnergyControllerPrivate::storeSignCounter() +{ + const auto signingDataIt = signingData.constFind(remoteDevice.toUInt64()); + if (signingDataIt == signingData.constEnd()) + return; + const QString settingsFilePath = keySettingsFilePath(); + if (!QFileInfo(settingsFilePath).exists()) + return; + QSettings settings(settingsFilePath, QSettings::IniFormat); + if (!settings.isWritable()) + return; + settings.beginGroup(QLatin1String("RemoteSignatureKey")); + const QString counterKey = QLatin1String("Counter"); + if (!settings.allKeys().contains(counterKey)) + return; + const quint32 counterValue = signingDataIt.value().counter + 1; + if (counterValue == settings.value(counterKey).toUInt()) + return; + settings.setValue(counterKey, counterValue); +} + +QString QLowEnergyControllerPrivate::keySettingsFilePath() const +{ + return QString::fromLatin1("/var/lib/bluetooth/%1/%2/info") + .arg(localAdapter.toString(), remoteDevice.toString()); +} + static QByteArray uuidToByteArray(const QBluetoothUuid &uuid) { QByteArray ba; @@ -2999,4 +3095,13 @@ int QLowEnergyControllerPrivate::checkReadPermissions(QVector<Attribute> &attrib return 0; } +bool QLowEnergyControllerPrivate::verifyMac(const QByteArray &message, const quint128 &csrk, + quint32 signCounter, quint64 expectedMac) +{ + if (!cmacVerifier) + cmacVerifier = new LeCmacVerifier; + return cmacVerifier->verify(LeCmacVerifier::createFullMessage(message, signCounter), csrk, + expectedMac); +} + QT_END_NAMESPACE |