diff --git a/nuki/bluez/bluetoothgattcharacteristic.cpp b/nuki/bluez/bluetoothgattcharacteristic.cpp index 69ccd658..422bd246 100644 --- a/nuki/bluez/bluetoothgattcharacteristic.cpp +++ b/nuki/bluez/bluetoothgattcharacteristic.cpp @@ -336,7 +336,7 @@ QDebug operator<<(QDebug debug, BluetoothGattCharacteristic *characteristic) debug.noquote().nospace() << " B"; if (characteristic->properties().testFlag(BluetoothGattCharacteristic::Read)) - debug.noquote().nospace() << " R "; + debug.noquote().nospace() << " R"; if (characteristic->properties().testFlag(BluetoothGattCharacteristic::WriteNoResponse)) debug.noquote().nospace() << " WNR"; diff --git a/nuki/nuki.cpp b/nuki/nuki.cpp index 2ee562dc..1c5cef66 100644 --- a/nuki/nuki.cpp +++ b/nuki/nuki.cpp @@ -37,13 +37,6 @@ #include #include -static QBluetoothUuid initializationServiceUuid = QBluetoothUuid(QUuid("a92ee000-5501-11e4-916c-0800200c9a66")); -static QBluetoothUuid pairingServiceUuid = QBluetoothUuid(QUuid("a92ee100-5501-11e4-916c-0800200c9a66")); -static QBluetoothUuid pairingDataCharacteristicUuid = QBluetoothUuid(QUuid("a92ee101-5501-11e4-916c-0800200c9a66")); -static QBluetoothUuid keyturnerServiceUuid = QBluetoothUuid(QUuid("a92ee200-5501-11e4-916c-0800200c9a66")); -static QBluetoothUuid keyturnerDataCharacteristicUuid = QBluetoothUuid(QUuid("a92ee201-5501-11e4-916c-0800200c9a66")); -static QBluetoothUuid keyturnerUserDataCharacteristicUuid = QBluetoothUuid(QUuid("a92ee202-5501-11e4-916c-0800200c9a66")); - Nuki::Nuki(Thing *thing, BluetoothDevice *bluetoothDevice, QObject *parent) : QObject(parent), m_thing(thing), @@ -204,40 +197,12 @@ void Nuki::executeCurrentAction() bool Nuki::enableNotificationsIndications(BluetoothGattCharacteristic *characteristic) { - qCDebug(dcNuki()) << "Start enable notifications/indications for" << characteristic; - - // Get the client configuration descriptor - // https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Descriptors/org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml - BluetoothGattDescriptor *clientConfiguration = characteristic->getDescriptor(QBluetoothUuid::ClientCharacteristicConfiguration); - if (!clientConfiguration) { - qCWarning(dcNuki()) << "Could not start notification/indications for" << characteristic << "because the client configuration descriptor is missing"; + qCDebug(dcNuki()) << "Enable notifications on" << characteristic; + if (!characteristic->startNotifications()) { + qCDebug(dcNuki()) << "Failed to start notifications on" << characteristic; return false; } -// qCDebug(dcNuki()) << "Enable notifications on" << characteristic; -// if (!characteristic->startNotifications()) { -// qCDebug(dcNuki()) << "Failed to start notifications on" << characteristic; -// return false; -// } - - if (characteristic->properties().testFlag(BluetoothGattCharacteristic::Indicate)) { - qCDebug(dcNuki()) << "Enable indications on" << characteristic; - QByteArray configuration; - QDataStream stream(&configuration, QIODevice::WriteOnly); - stream.setByteOrder(QDataStream::LittleEndian); - stream << static_cast(2); - if (!clientConfiguration->writeValue(configuration)) { - qCWarning(dcNuki()) << "Failed to write client configuration descriptor on" << characteristic; - return false; - } - - } else { - qCWarning(dcNuki()) << "Could not enable notifications. Access properties do not allow indicate or notify" << characteristic; - return false; - } - - clientConfiguration->readValue(); - return true; } @@ -451,12 +416,12 @@ bool Nuki::init() return false; } - if (!m_bluetoothDevice->hasService(pairingServiceUuid)) { + if (!m_bluetoothDevice->hasService(pairingServiceUuid())) { qCWarning(dcNuki()) << "Could not find pairing service on device" << m_bluetoothDevice; return false; } - if (!m_bluetoothDevice->hasService(keyturnerServiceUuid)) { + if (!m_bluetoothDevice->hasService(keyturnerServiceUuid())) { qCWarning(dcNuki()) << "Could not find key turner service on device" << m_bluetoothDevice; return false; } @@ -467,25 +432,25 @@ bool Nuki::init() connect(m_deviceInformationService, &BluetoothGattService::characteristicReadFinished, this, &Nuki::onDeviceInfoCharacteristicReadFinished); // Keyturner service - m_keyturnerService = m_bluetoothDevice->getService(keyturnerServiceUuid); - if (!m_keyturnerService->hasCharacteristic(keyturnerUserDataCharacteristicUuid)) { + m_keyturnerService = m_bluetoothDevice->getService(keyturnerServiceUuid()); + if (!m_keyturnerService->hasCharacteristic(keyturnerUserDataCharacteristicUuid())) { qCWarning(dcNuki()) << "Could not find user data characteristc on device" << m_bluetoothDevice; return false; } // Set key turner characteristics for data and user data - if (!m_keyturnerService->hasCharacteristic(keyturnerDataCharacteristicUuid)) { + if (!m_keyturnerService->hasCharacteristic(keyturnerDataCharacteristicUuid())) { qCWarning(dcNuki()) << "Could not find data characteristc on device" << m_bluetoothDevice; return false; } // Enable notifications/indications - m_keyturnerUserDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerUserDataCharacteristicUuid); + m_keyturnerUserDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerUserDataCharacteristicUuid()); if (!enableNotificationsIndications(m_keyturnerUserDataCharacteristic)) { qCWarning(dcNuki()) << "Could not enable notifications/indications for user data characteristic."; return false; } - m_keyturnerDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerDataCharacteristicUuid); + m_keyturnerDataCharacteristic = m_keyturnerService->getCharacteristic(keyturnerDataCharacteristicUuid()); if (!enableNotificationsIndications(m_keyturnerDataCharacteristic)) { qCWarning(dcNuki()) << "Could not enable notifications/indications for key turner data characteristic."; return false; @@ -494,13 +459,13 @@ bool Nuki::init() // Pairing service - m_pairingService = m_bluetoothDevice->getService(pairingServiceUuid); - if (!m_pairingService->hasCharacteristic(pairingDataCharacteristicUuid)) { + m_pairingService = m_bluetoothDevice->getService(pairingServiceUuid()); + if (!m_pairingService->hasCharacteristic(pairingDataCharacteristicUuid())) { qCWarning(dcNuki()) << "Could not find pairing data characteristc on device" << m_bluetoothDevice; return false; } - m_pairingDataCharacteristic = m_pairingService->getCharacteristic(pairingDataCharacteristicUuid); + m_pairingDataCharacteristic = m_pairingService->getCharacteristic(pairingDataCharacteristicUuid()); if (!enableNotificationsIndications(m_pairingDataCharacteristic)) { qCWarning(dcNuki()) << "Could not enable notifications for pairing characteristic."; return false; @@ -590,8 +555,6 @@ void Nuki::setAvailable(bool available) if (m_available) { executeCurrentAction(); - - } else { // Finish any running actions finishCurrentAction(false); diff --git a/nuki/nuki.h b/nuki/nuki.h index 3754c647..8d664260 100644 --- a/nuki/nuki.h +++ b/nuki/nuki.h @@ -50,7 +50,6 @@ class Nuki : public QObject { - Q_OBJECT public: @@ -78,6 +77,12 @@ public: void clearSettings(); + static QBluetoothUuid initializationServiceUuid() { return QBluetoothUuid(QUuid("a92ee000-5501-11e4-916c-0800200c9a66")); } + static QBluetoothUuid pairingServiceUuid() { return QBluetoothUuid(QUuid("a92ee100-5501-11e4-916c-0800200c9a66")); } + static QBluetoothUuid pairingDataCharacteristicUuid() { return QBluetoothUuid(QUuid("a92ee101-5501-11e4-916c-0800200c9a66")); } + static QBluetoothUuid keyturnerServiceUuid() { return QBluetoothUuid(QUuid("a92ee200-5501-11e4-916c-0800200c9a66")); } + static QBluetoothUuid keyturnerDataCharacteristicUuid() { return QBluetoothUuid(QUuid("a92ee201-5501-11e4-916c-0800200c9a66")); } + static QBluetoothUuid keyturnerUserDataCharacteristicUuid() { return QBluetoothUuid(QUuid("a92ee202-5501-11e4-916c-0800200c9a66")); } private: Thing *m_thing = nullptr; diff --git a/nuki/nukiauthenticator.cpp b/nuki/nukiauthenticator.cpp index fed0ae8d..cbbd2a6f 100644 --- a/nuki/nukiauthenticator.cpp +++ b/nuki/nukiauthenticator.cpp @@ -58,6 +58,7 @@ NukiAuthenticator::NukiAuthenticator(const QBluetoothHostInfo &hostInfo, Bluetoo // Check if we have authentication data for this thing and set initial state loadData(); if (isValid()) { + qCDebug(dcNuki()) << "Found valid authroization data for" << hostInfo.address().toString(); setState(AuthenticationStateAuthenticated); } else { setState(AuthenticationStateUnauthenticated); @@ -81,7 +82,7 @@ bool NukiAuthenticator::isValid() const return !m_privateKey.isEmpty() && !m_publicKey.isEmpty() && !m_publicKeyNuki.isEmpty() && - !m_authorizationId == 0 && + m_authorizationId != 0 && !m_authorizationIdRawData.isEmpty() && !m_uuid.isEmpty(); } @@ -208,7 +209,6 @@ void NukiAuthenticator::setState(NukiAuthenticator::AuthenticationState state) switch (m_state) { case AuthenticationStateUnauthenticated: - resetExpectedData(); break; case AuthenticationStateAuthenticated: qCDebug(dcNuki()) << "Device" << m_hostInfo.address().toString() << "authenticated."; @@ -218,47 +218,37 @@ void NukiAuthenticator::setState(NukiAuthenticator::AuthenticationState state) if (m_debug) qCDebug(dcNuki()) << " Authorization ID:" << NukiUtils::convertByteArrayToHexStringCompact(m_authorizationIdRawData) << m_authorizationId; break; case AuthenticationStateRequestPublicKey: - resetExpectedData(NukiUtils::CommandPublicKey, 2); requestPublicKey(); break; case AuthenticationStateGenerateKeyPair: - resetExpectedData(); generateKeyPair(); setState(AuthenticationStateSendPublicKey); break; case AuthenticationStateSendPublicKey: - resetExpectedData(NukiUtils::CommandChallenge, 2); sendPublicKey(); setState(AuthenticationStateReadChallenge); break; case AuthenticationStateReadChallenge: - resetExpectedData(NukiUtils::CommandChallenge, 2); break; case AuthenticationStateAutorization: sendAuthorizationAuthenticator(); setState(AuthenticationStateReadSecondChallenge); break; case AuthenticationStateReadSecondChallenge: - resetExpectedData(NukiUtils::CommandChallenge, 2); break; case AuthenticationStateAuthenticateData: - resetExpectedData(); sendAuthenticateData(); setState(AuthenticationStateAuthorizationId); break; case AuthenticationStateAuthorizationId: - resetExpectedData(NukiUtils::CommandAuthorizationId, 5); break; case AuthenticationStateAuthorizationIdConfirm: - resetExpectedData(); sendAuthoizationIdConfirm(); setState(AuthenticationStateStatus); break; case AuthenticationStateStatus: - resetExpectedData(NukiUtils::CommandStatus); break; case AuthenticationStateError: - resetExpectedData(); emit errorOccured(m_error); emit authenticationProcessFinished(false); break; @@ -268,14 +258,6 @@ void NukiAuthenticator::setState(NukiAuthenticator::AuthenticationState state) } } -void NukiAuthenticator::resetExpectedData(NukiUtils::Command command, int expectedCount) -{ - m_currentReceivingCommand = command; - m_currentReceivingCurrentCount = 0; - m_currentReceivingExpectedCount = expectedCount; - m_currentReceivingData.clear(); -} - bool NukiAuthenticator::createAuthenticator(const QByteArray content) { // Create shared key @@ -469,7 +451,6 @@ void NukiAuthenticator::loadData() void NukiAuthenticator::onPairingDataCharacteristicChanged(const QByteArray &value) { - if (m_debug) qCDebug(dcNuki()) << "Authenticator data received: <--" << NukiUtils::convertByteArrayToHexStringCompact(value); // Process pairing characteristic data QByteArray data = QByteArray(value); @@ -478,12 +459,9 @@ void NukiAuthenticator::onPairingDataCharacteristicChanged(const QByteArray &val quint16 command; stream >> command; - // Check if we are collecting data for multi part notification - if (m_currentReceivingCurrentCount > 0) { - command = m_currentReceivingCommand; - } + m_currentReceivingData = value; - if (m_debug) qCDebug(dcNuki()) << static_cast(command); + if (m_debug) qCDebug(dcNuki()) << "Authenticator data received: <--" << static_cast(command) << "|" << NukiUtils::convertByteArrayToHexStringCompact(value); switch (command) { case NukiUtils::CommandErrorReport: @@ -500,81 +478,70 @@ void NukiAuthenticator::onPairingDataCharacteristicChanged(const QByteArray &val setState(AuthenticationStateError); break; case NukiUtils::CommandPublicKey: - m_currentReceivingCurrentCount++; - m_currentReceivingData.append(value); - if (m_currentReceivingCurrentCount == m_currentReceivingExpectedCount) { - if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { - qCWarning(dcNuki()) << "Invalid CRC CCITT value for public key message."; - // FIXME: check what to do if crc is invalid - } - - qCDebug(dcNuki()) << "Authenticator: Nuki public key message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); - m_publicKeyNuki = m_currentReceivingData.mid(2, 32); - if (m_debug) qCDebug(dcNuki()) << "Authenticator: --> Nuki public key:" << NukiUtils::convertByteArrayToHexStringCompact(m_publicKeyNuki); - - setState(AuthenticationStateGenerateKeyPair); + if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { + qCWarning(dcNuki()) << "Invalid CRC CCITT value for public key message."; + // FIXME: check what to do if crc is invalid } + + qCDebug(dcNuki()) << "Authenticator: Nuki public key message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); + m_publicKeyNuki = m_currentReceivingData.mid(2, 32); + if (m_debug) qCDebug(dcNuki()) << "Authenticator: --> Nuki public key:" << NukiUtils::convertByteArrayToHexStringCompact(m_publicKeyNuki); + + setState(AuthenticationStateGenerateKeyPair); break; case NukiUtils::CommandChallenge: - m_currentReceivingCurrentCount++; - m_currentReceivingData.append(value); - if (m_currentReceivingCurrentCount == m_currentReceivingExpectedCount) { - qCDebug(dcNuki()) << "Authenticator: Nuki challenge message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); - if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { - qCWarning(dcNuki()) << "Invalid CRC CCITT value for challenge message."; - // FIXME: check what to do if crc is invalid - } + qCDebug(dcNuki()) << "Authenticator: Nuki challenge message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); + if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { + qCWarning(dcNuki()) << "Invalid CRC CCITT value for challenge message."; + // FIXME: check what to do if crc is invalid + } - m_nonceNuki = m_currentReceivingData.mid(2, 32); - if (m_debug) qCDebug(dcNuki()) << "Authenticator: --> Nuki nonce:" << NukiUtils::convertByteArrayToHexStringCompact(m_nonceNuki); - // Check if this was from the first challenge read or the second - if (m_state == AuthenticationStateReadChallenge) { - setState(AuthenticationStateAutorization); - } else if (m_state == AuthenticationStateReadSecondChallenge) { - setState(AuthenticationStateAuthenticateData); - } else { - qCWarning(dcNuki()) << "Received a challenge without expecting one."; - setState(AuthenticationStateError); - } + m_nonceNuki = m_currentReceivingData.mid(2, 32); + if (m_debug) qCDebug(dcNuki()) << "Authenticator: --> Nuki nonce:" << NukiUtils::convertByteArrayToHexStringCompact(m_nonceNuki); + // Check if this was from the first challenge read or the second + if (m_state == AuthenticationStateReadChallenge) { + setState(AuthenticationStateAutorization); + } else if (m_state == AuthenticationStateReadSecondChallenge) { + setState(AuthenticationStateAuthenticateData); + } else { + qCWarning(dcNuki()) << "Received a challenge without expecting one."; + setState(AuthenticationStateError); } break; - case NukiUtils::CommandAuthorizationId: - m_currentReceivingCurrentCount++; - m_currentReceivingData.append(value); - if (m_currentReceivingCurrentCount == m_currentReceivingExpectedCount) { - qCDebug(dcNuki()) << "Authenticator: Nuki authorization ID message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); + case NukiUtils::CommandAuthorizationId: { + qCDebug(dcNuki()) << "Authenticator: Nuki authorization ID message received" << (m_debug ? NukiUtils::convertByteArrayToHexStringCompact(m_currentReceivingData) : ""); - if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { - qCWarning(dcNuki()) << "Invalid CRC CCITT value for challenge message."; - // FIXME: check what to do if crc is invalid - } - - // Parse data - QByteArray message = m_currentReceivingData.mid(2, m_currentReceivingData.count() - 4); - QByteArray authenticator = message.left(32); - Q_ASSERT_X(authenticator.count() == 32, "data length", "Nuki nonce has not the correct length."); - - // Read authorization ID - m_authorizationIdRawData = message.mid(32, 4); - QDataStream stream(&m_authorizationIdRawData, QIODevice::ReadOnly); - stream.setByteOrder(QDataStream::LittleEndian); - stream >> m_authorizationId; - - m_uuid = message.mid(36, 16); - Q_ASSERT_X(m_uuid.count() == 16, "data length", "UUIS has not the correct length."); - - m_nonceNuki = message.mid(52, 32); - Q_ASSERT_X(m_nonceNuki.count() == 32, "data length", "Nuki nonce has not the correct length."); - - if (m_debug) qCDebug(dcNuki()) << " Full message :" << NukiUtils::convertByteArrayToHexStringCompact(message); - if (m_debug) qCDebug(dcNuki()) << " Authenticator :" << NukiUtils::convertByteArrayToHexStringCompact(authenticator); - if (m_debug) qCDebug(dcNuki()) << " Authorization ID:" << NukiUtils::convertByteArrayToHexStringCompact(m_authorizationIdRawData) << m_authorizationId; - if (m_debug) qCDebug(dcNuki()) << " UUID data :" << NukiUtils::convertByteArrayToHexStringCompact(m_uuid); - if (m_debug) qCDebug(dcNuki()) << " Nuki nonce :" << NukiUtils::convertByteArrayToHexStringCompact(m_nonceNuki); - - setState(AuthenticationStateAuthorizationIdConfirm); + if (!NukiUtils::validateMessageCrc(m_currentReceivingData)) { + qCWarning(dcNuki()) << "Invalid CRC CCITT value for challenge message."; + // FIXME: check what to do if crc is invalid } + + // Parse data + QByteArray message = m_currentReceivingData.mid(2, m_currentReceivingData.count() - 4); + QByteArray authenticator = message.left(32); + Q_ASSERT_X(authenticator.count() == 32, "data length", "Nuki nonce has not the correct length."); + + // Read authorization ID + m_authorizationIdRawData = message.mid(32, 4); + QDataStream stream(&m_authorizationIdRawData, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream >> m_authorizationId; + + m_uuid = message.mid(36, 16); + Q_ASSERT_X(m_uuid.count() == 16, "data length", "UUID has not the correct length."); + + m_nonceNuki = message.mid(52, 32); + Q_ASSERT_X(m_nonceNuki.count() == 32, "data length", "Nuki nonce has not the correct length."); + + if (m_debug) qCDebug(dcNuki()) << " Full message :" << NukiUtils::convertByteArrayToHexStringCompact(message); + if (m_debug) qCDebug(dcNuki()) << " Authenticator :" << NukiUtils::convertByteArrayToHexStringCompact(authenticator); + if (m_debug) qCDebug(dcNuki()) << " Authorization ID:" << NukiUtils::convertByteArrayToHexStringCompact(m_authorizationIdRawData) << m_authorizationId; + if (m_debug) qCDebug(dcNuki()) << " UUID data :" << NukiUtils::convertByteArrayToHexStringCompact(m_uuid); + if (m_debug) qCDebug(dcNuki()) << " Nuki nonce :" << NukiUtils::convertByteArrayToHexStringCompact(m_nonceNuki); + + setState(AuthenticationStateAuthorizationIdConfirm); break; + } case NukiUtils::CommandStatus: { quint8 status; stream >> status; @@ -604,7 +571,6 @@ void NukiAuthenticator::onPairingDataCharacteristicChanged(const QByteArray &val } default: qCWarning(dcNuki()) << "Authenticator: Unhandled command identifier for parining charateristic" << NukiUtils::convertUint16ToHexString(command); - resetExpectedData(); break; } } diff --git a/nuki/nukiauthenticator.h b/nuki/nukiauthenticator.h index 679489bc..740d7e02 100644 --- a/nuki/nukiauthenticator.h +++ b/nuki/nukiauthenticator.h @@ -110,7 +110,6 @@ private: // State machine void setState(AuthenticationState state); - void resetExpectedData(NukiUtils::Command command = NukiUtils::CommandRequestData, int expectedCount = 1); // Helper methods bool createAuthenticator(const QByteArray content); diff --git a/nuki/nukicontroller.cpp b/nuki/nukicontroller.cpp index 21ffde80..895f6dad 100644 --- a/nuki/nukicontroller.cpp +++ b/nuki/nukicontroller.cpp @@ -88,6 +88,23 @@ bool NukiController::readLockState() return true; } +bool NukiController::readConfiguration() +{ + if (m_state != NukiControllerStateIdle) { + // TODO: maybe queue commands + qCWarning(dcNuki()) << "Controller: Could not read lock state, Nuki is currenty busy"; + return false; + } + + if (!m_nukiAuthenticator->isValid()) { + qCWarning(dcNuki()) << "Invalid authenticator. Please authenticate the thing first."; + return false; + } + + setState(NukiControllerStateReadingConfiguration); + return true; +} + bool NukiController::lock() { if (m_state != NukiControllerStateIdle) { @@ -154,6 +171,15 @@ void NukiController::setState(NukiController::NukiControllerState state) case NukiControllerStateReadingLockStates: sendReadLockStateRequest(); break; + case NukiControllerStateReadingConfigurationRequestChallange: + sendRequestChallengeRequest(); + break; + case NukiControllerStateReadingConfigurationExecute: + sendReadConfigurationRequest(); + setState(NukiControllerStateReadingConfiguration); + break; + case NukiControllerStateReadingConfiguration: + break; case NukiControllerStateLockActionRequestChallange: sendRequestChallengeRequest(); break; @@ -235,6 +261,12 @@ void NukiController::processNukiStatesData(const QByteArray &data) emit nukiStatesChanged(); } +void NukiController::processNukiConfigData(const QByteArray &data) +{ + qCDebug(dcNuki()) << "Processing config data from nuki" << data; + +} + void NukiController::processNukiErrorReport(const QByteArray &data) { qint8 errorCode; @@ -280,6 +312,20 @@ void NukiController::processUserDataNotification(const QByteArray nonce, quint32 if (command == NukiUtils::CommandNukiStates) { processNukiStatesData(payload); emit readNukiStatesFinished(true); + setState(NukiControllerStateReadingConfigurationRequestChallange); + return; + } + break; + case NukiControllerStateReadingConfigurationRequestChallange: + if (command == NukiUtils::CommandChallenge) { + m_nukiNonce = payload; + setState(NukiControllerStateReadingConfigurationExecute); + return; + } + break; + case NukiControllerStateReadingConfiguration: + if (command == NukiUtils::CommandConfig) { + processNukiConfigData(payload); setState(NukiControllerStateIdle); return; } @@ -424,6 +470,45 @@ void NukiController::sendReadLockStateRequest() m_userDataCharacteristic->writeCharacteristic(message); } +void NukiController::sendReadConfigurationRequest() +{ + qCDebug(dcNuki()) << "Controller: Reading configurations"; + + // Create data for encryption + QByteArray payload; + QDataStream stream(&payload, QIODevice::WriteOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream << static_cast(NukiUtils::CommandRequestConfig); + for (int i = 0; i < m_nukiNonce.length(); i++) { + stream << static_cast(m_nukiNonce.at(i)); + } + + // Create unencrypted PDATA + QByteArray unencryptedMessage = NukiUtils::createRequestMessageForUnencryptedForEncryption(m_nukiAuthenticator->authorizationId(), NukiUtils::CommandRequestData, payload); + + // Encrypt PDATA + QByteArray nonce = m_nukiAuthenticator->generateNonce(crypto_box_NONCEBYTES); + QByteArray encryptedMessage = m_nukiAuthenticator->encryptData(unencryptedMessage, nonce); + + // Create ADATA + QByteArray header; + header.append(nonce); + header.append(m_nukiAuthenticator->authorizationIdRawData()); + header.append(NukiUtils::converUint16ToByteArrayLittleEndian(static_cast(encryptedMessage.length()))); + + // Message ADATA + PDATA + QByteArray message; + message.append(header); + message.append(encryptedMessage); + + // Send data + qCDebug(dcNuki()) << "Controller: Sending get config request"; + if (m_debug) qCDebug(dcNuki()) << " Nonce :" << NukiUtils::convertByteArrayToHexStringCompact(nonce); + if (m_debug) qCDebug(dcNuki()) << " Header :" << NukiUtils::convertByteArrayToHexStringCompact(header); + if (m_debug) qCDebug(dcNuki()) << "Controller: -->" << NukiUtils::convertByteArrayToHexStringCompact(message); + m_userDataCharacteristic->writeCharacteristic(message); +} + void NukiController::sendRequestChallengeRequest() { qCDebug(dcNuki()) << "Controller: Request challenge"; @@ -508,47 +593,27 @@ void NukiController::onUserDataCharacteristicChanged(const QByteArray &value) { if (m_debug) qCDebug(dcNuki()) << "Controller: Data received: <--" << NukiUtils::convertByteArrayToHexStringCompact(value); - if (m_messageBufferCounter <= 0) { - // New data arrived - m_messageBuffer.append(value); - m_messageBufferCounter++; - } else { - // We are currently collecting - m_messageBuffer.append(value); - m_messageBufferCounter++; + m_messageBuffer = value; - // In the second buffer message is the complete message length - if (m_messageBufferCounter == 2) { - if (m_messageBuffer.count() < 30) { - qCWarning(dcNuki()) << "Controller: Cannot understand message. Rejecting."; - resetMessageBuffer(); - return; - } - - // Parse message length - // ADATA: 24 byte nonce, 4 byte autorization, 2 byte encrypted message length - m_messageBufferAData = m_messageBuffer.left(30); - m_messageBufferPData = m_messageBuffer.right(m_messageBuffer.count() - 30); - - m_messageBufferNonce = m_messageBufferAData.left(24); - QByteArray messageInformation = m_messageBufferAData.right(6); - - QDataStream stream(&messageInformation, QIODevice::ReadOnly); - stream.setByteOrder(QDataStream::LittleEndian); - stream >> m_messageBufferIdentifier >> m_messageBufferLength; - if (m_messageBufferPData.count() == m_messageBufferLength) { - processUserDataNotification(m_messageBufferNonce, m_messageBufferIdentifier, m_messageBufferPData); - resetMessageBuffer(); - } - - } else { - // We already know the message length and are still collecting p data - m_messageBufferPData.append(value); - if (m_messageBufferPData.count() == m_messageBufferLength) { - // Message finished - processUserDataNotification(m_messageBufferNonce, m_messageBufferIdentifier, m_messageBufferPData); - resetMessageBuffer(); - } - } + if (m_messageBuffer.count() < 30) { + qCWarning(dcNuki()) << "Controller: Cannot understand message. Rejecting."; + resetMessageBuffer(); + return; } + + // Parse message length + // ADATA: 24 byte nonce, 4 byte autorization, 2 byte encrypted message length + m_messageBufferAData = m_messageBuffer.left(30); + m_messageBufferPData = m_messageBuffer.right(m_messageBuffer.count() - 30); + m_messageBufferNonce = m_messageBufferAData.left(24); + QByteArray messageInformation = m_messageBufferAData.right(6); + + QDataStream stream(&messageInformation, QIODevice::ReadOnly); + stream.setByteOrder(QDataStream::LittleEndian); + stream >> m_messageBufferIdentifier >> m_messageBufferLength; + if (m_messageBufferPData.count() == m_messageBufferLength) { + processUserDataNotification(m_messageBufferNonce, m_messageBufferIdentifier, m_messageBufferPData); + resetMessageBuffer(); + } + } diff --git a/nuki/nukicontroller.h b/nuki/nukicontroller.h index ebc4b37f..2414f60f 100644 --- a/nuki/nukicontroller.h +++ b/nuki/nukicontroller.h @@ -49,6 +49,11 @@ public: // Read state NukiControllerStateReadingLockStates, + // Read configuration + NukiControllerStateReadingConfigurationRequestChallange, + NukiControllerStateReadingConfigurationExecute, + NukiControllerStateReadingConfiguration, + // Lock action NukiControllerStateLockActionRequestChallange, NukiControllerStateLockActionExecute, @@ -80,6 +85,7 @@ public: // Actions bool readLockState(); + bool readConfiguration(); bool lock(); bool unlock(); bool unlatch(); @@ -119,11 +125,13 @@ private: // Data processors void processNukiStatesData(const QByteArray &data); + void processNukiConfigData(const QByteArray &data); void processNukiErrorReport(const QByteArray &data); void processUserDataNotification(const QByteArray nonce, quint32 authorizationIdentifier, const QByteArray &privateData); // State action methods void sendReadLockStateRequest(); + void sendReadConfigurationRequest(); void sendRequestChallengeRequest(); void sendLockActionRequest(NukiUtils::LockAction lockAction, quint8 flag = 0); diff --git a/nuki/nukiutils.h b/nuki/nukiutils.h index 315c911e..1abf36c1 100644 --- a/nuki/nukiutils.h +++ b/nuki/nukiutils.h @@ -143,9 +143,9 @@ public: CommandOpeningsClosingsSummary = 0x0010, CommandBatteryReport = 0x0011, CommandErrorReport = 0x0012, - CommandSetConG = 0x0013, - CommandRequestConG = 0x0014, - CommandConG = 0x0015, + CommandSetConfig = 0x0013, + CommandRequestConfig = 0x0014, + CommandConfig = 0x0015, CommandSetSecurityPIN = 0x0019, CommandRequestCalibration = 0x001A, CommandRequestReboot = 0x001D, @@ -190,7 +190,7 @@ public: // Message helper static QByteArray createRequestMessageForUnencrypted(NukiUtils::Command command, const QByteArray &payload); - static QByteArray createRequestMessageForUnencryptedForEncryption(quint32 authenticationId, NukiUtils::Command command, const QByteArray &payload); + static QByteArray createRequestMessageForUnencryptedForEncryption(quint32 authenticationId, NukiUtils::Command command, const QByteArray &payload = QByteArray()); };