summaryrefslogtreecommitdiffstats
path: root/src/knx/netip/qknxnetipsecureconfiguration.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/knx/netip/qknxnetipsecureconfiguration.cpp')
-rw-r--r--src/knx/netip/qknxnetipsecureconfiguration.cpp472
1 files changed, 472 insertions, 0 deletions
diff --git a/src/knx/netip/qknxnetipsecureconfiguration.cpp b/src/knx/netip/qknxnetipsecureconfiguration.cpp
new file mode 100644
index 0000000..8b2d03b
--- /dev/null
+++ b/src/knx/netip/qknxnetipsecureconfiguration.cpp
@@ -0,0 +1,472 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtKnx module.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qknxcryptographicengine.h"
+#include "qknxnetipsecureconfiguration.h"
+
+#include <QtCore/qfile.h>
+
+#include "private/qknxkeyring_p.h"
+#include "private/qknxnetipsecureconfiguration_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QKnxPrivate
+{
+ static QKnxNetIpSecureConfiguration fromInterface(const QKnx::Ets::Keyring::QKnxInterface &iface,
+ const QKnxByteArray &pwHash, const QKnxByteArray &createdHash)
+ {
+ QKnxNetIpSecureConfiguration s;
+ s.setUserId(QKnxNetIp::SecureUserId(iface.UserID));
+ s.setPrivateKey(QKnxSecureKey::generatePrivateKey());
+ s.setHost({ QKnxAddress::Type::Individual, iface.Host });
+ s.setUserPassword(QKnxCryptographicEngine::decodeAndDecryptPassword(pwHash,
+ createdHash, iface.Password).toByteArray());
+ s.setIndividualAddress({ QKnxAddress::Type::Individual, iface.IndividualAddress });
+ s.setDeviceAuthenticationCode(QKnxCryptographicEngine::decodeAndDecryptPassword(pwHash,
+ createdHash, iface.Authentication).toByteArray());
+ return s;
+ }
+
+ static QKnxNetIpSecureConfiguration fromDevice(const QKnx::Ets::Keyring::QKnxDevice &device,
+ const QKnxByteArray &pwHash, const QKnxByteArray &createdHash)
+ {
+ QKnxNetIpSecureConfiguration s;
+ s.setUserId(QKnxNetIp::SecureUserId::Management);
+ s.setPrivateKey(QKnxSecureKey::generatePrivateKey());
+ s.setHost({ QKnxAddress::Type::Individual, device.IndividualAddress });
+ s.setUserPassword(QKnxCryptographicEngine::decodeAndDecryptPassword(pwHash,
+ createdHash, device.ManagementPassword).toByteArray());
+ s.setIndividualAddress({ QKnxAddress::Type::Individual, device.IndividualAddress });
+ s.setDeviceAuthenticationCode(QKnxCryptographicEngine::decodeAndDecryptPassword(pwHash,
+ createdHash, device.Authentication).toByteArray());
+ return s;
+ }
+
+ static QVector<QKnxNetIpSecureConfiguration> fromKeyring(QKnxNetIpSecureConfiguration::Type type,
+ const QKnxAddress &ia, const QString &filePath, const QByteArray &password, bool validate)
+ {
+ QFile file;
+ file.setFileName(filePath);
+ if (!file.open(QIODevice::ReadOnly))
+ return {};
+
+ QXmlStreamReader reader(&file);
+ QKnx::Ets::Keyring::QKnxKeyring keyring;
+
+ const auto pwHash = QKnxCryptographicEngine::keyringPasswordHash(password);
+ if (validate) {
+ if (!keyring.validate(&reader, pwHash))
+ return {};
+ file.seek(0);
+ reader.setDevice(&file);
+ }
+
+ if (!keyring.parseElement(&reader, true))
+ return {};
+ const auto createdHash = QKnxCryptographicEngine::hashSha256(keyring.Created.toUtf8());
+
+ auto iaString = ia.toString();
+ QVector<QKnxNetIpSecureConfiguration> results;
+
+ if (type == QKnxNetIpSecureConfiguration::Type::Tunneling) {
+ if (keyring.Interface.isEmpty())
+ return {};
+
+ if (!iaString.isEmpty()) { // only a single interface is requested
+ for (const auto iface : qAsConst(keyring.Interface)) {
+ if (iaString != iface.IndividualAddress)
+ continue;
+ return { QKnxPrivate::fromInterface(iface, pwHash, createdHash) };
+ }
+ } else {
+ for (const auto iface : qAsConst(keyring.Interface))
+ results.append(QKnxPrivate::fromInterface(iface, pwHash, createdHash));
+ }
+ }
+
+ if (type == QKnxNetIpSecureConfiguration::Type::DeviceManagement) {
+ if (keyring.Devices.isEmpty())
+ return {};
+
+ const auto devices = keyring.Devices.value(0).Device;
+ if (!iaString.isEmpty()) { // only a single device is requested
+ for (const auto device : devices) {
+ if (iaString != device.IndividualAddress)
+ continue;
+ return { QKnxPrivate::fromDevice(device, pwHash, createdHash) };
+ }
+ } else {
+ for (const auto device : devices)
+ results.append(QKnxPrivate::fromDevice(device, pwHash, createdHash));
+ }
+ }
+
+ return results;
+ }
+}
+
+/*!
+ \since 5.13
+ \inmodule QtKnx
+
+ \class QKnxNetIpSecureConfiguration
+ \ingroup qtknx-general-classes
+
+ \brief The QKnxNetIpSecureConfiguration class holds configuration
+ options used for the secure session authentication process.
+
+ It holds information such as secure key, user ID and password, device
+ authentication code, and so on.
+
+ This class is part of the Qt KNX module and currently available as a
+ Technology Preview, and therefore the API and functionality provided
+ by the class may be subject to change at any time without prior notice.
+*/
+
+/*!
+ \enum QKnxNetIpSecureConfiguration::Type
+
+ This enum holds the type of secure configuration that can be constructed
+ from an ETS exported keyring (*.knxkeys) file.
+
+ \value Tunneling
+ KNXnet/IP secure tunneling configuration.
+ \value DeviceManagement
+ KNXnet/IP secure device management configuration.
+*/
+
+/*!
+ Constructs a new, empty, invalid secure configuration.
+
+ \sa isNull(), isValid()
+*/
+QKnxNetIpSecureConfiguration::QKnxNetIpSecureConfiguration()
+ : d(new QKnxNetIpSecureConfigurationPrivate)
+{}
+
+/*!
+ Releases any resources held by the secure configuration.
+*/
+QKnxNetIpSecureConfiguration::~QKnxNetIpSecureConfiguration() = default;
+
+/*!
+ Constructs a vector of secure configurations for the given type
+ \a type from an ETS exported \a keyring (*.knxkeys) file that was
+ encrypted with the given password \a password. Set the \a validate
+ argument to \c true to verify that all data in the keyring file is
+ trustworthy, \c false to omit the check.
+
+ \note If an error occurred, no or invalid information for \a type
+ was found in the keyring file, the returned vector can be empty.
+*/
+QVector<QKnxNetIpSecureConfiguration> QKnxNetIpSecureConfiguration::fromKeyring(Type type,
+ const QString &keyring, const QByteArray &password, bool validate)
+{
+ return QKnxPrivate::fromKeyring(type, {}, keyring, password, validate);
+}
+
+/*!
+ Constructs a secure configurations for the given type \a type and the
+ given individual address \a ia from an ETS exported \a keyring (*.knxkeys)
+ file that was encrypted with the given password \a password.
+ Set the \a validate argument to \c true to verify that all data in the
+ keyring file is trustworthy, \c false to omit the check.
+
+ \note If an error occurred, no or invalid information for \a type or \a ia
+ was found in the keyring file;
+ the function returns a \l {default-constructed value} which can be invalid.
+*/
+QKnxNetIpSecureConfiguration QKnxNetIpSecureConfiguration::fromKeyring(QKnxNetIpSecureConfiguration::Type type,
+ const QKnxAddress &ia, const QString &keyring, const QByteArray &password, bool validate)
+{
+ return QKnxPrivate::fromKeyring(type, ia, keyring, password, validate).value(0, {});
+}
+
+/*!
+ Returns \c true if this is a default constructed secure configuration;
+ otherwise returns \c false. A secure configuration is considered \c null
+ if it contains no initialized values.
+*/
+bool QKnxNetIpSecureConfiguration::isNull() const
+{
+ return d->privateKey.isNull() && d->userId == QKnxNetIp::SecureUserId::Reserved
+ && d->userPassword.isNull() && d->deviceAuthenticationCode.isNull();
+}
+
+/*!
+ Returns \c true if the secure configuration contains initialized values and
+ is in itself valid, otherwise returns \c false.
+
+ A valid secure configuration consists of at least a valid user ID,
+ a valid \l {QKnxSecureKey} {secure key}, and sensible device authentication
+ code.
+*/
+bool QKnxNetIpSecureConfiguration::isValid() const
+{
+ if (isNull())
+ return false;
+
+ return d->privateKey.type() == QKnxSecureKey::Type::Private && d->privateKey.isValid()
+ && QKnxNetIp::isSecureUserId(d->userId) && (!d->deviceAuthenticationCode.isEmpty());
+}
+
+/*!
+ Returns the host address of the secure KNX device this secure configuration
+ targets. The host address can be empty and is not required to establish a
+ secure session.
+
+ The purpose of this field is more to help GUI applications to indicate
+ which KNX secure device can be used with this secure configuration.
+*/
+QKnxAddress QKnxNetIpSecureConfiguration::host() const
+{
+ return d->host;
+}
+
+/*!
+ Sets the secure configurations host address to \a hostAddress. The host
+ address can be empty and is not required to establish a secure session.
+*/
+void QKnxNetIpSecureConfiguration::setHost(const QKnxAddress &hostAddress)
+{
+ d->host = hostAddress;
+}
+
+/*!
+ Returns the public \l {QKnxSecureKey} {secure key} used to establish the
+ secure session. The public key is derived from the given private key.
+*/
+QKnxSecureKey QKnxNetIpSecureConfiguration::publicKey() const
+{
+ return d->publicKey;
+}
+
+/*!
+ Returns the private \l {QKnxSecureKey} {secure key} used to establish the
+ secure session.
+*/
+QKnxSecureKey QKnxNetIpSecureConfiguration::privateKey() const
+{
+ return d->privateKey;
+}
+
+/*!
+ Set the \l {QKnxSecureKey} {secure key} used to establish the secure
+ connection to \a key and returns \c true on success; \c false otherwise.
+*/
+bool QKnxNetIpSecureConfiguration::setPrivateKey(const QKnxSecureKey &key)
+{
+ auto valid = key.isValid() && key.type() == QKnxSecureKey::Type::Private;
+ if (valid) {
+ d->privateKey = key;
+ d->publicKey = QKnxSecureKey::publicKeyFromPrivate(key);
+ }
+ return valid;
+}
+
+/*!
+ Returns the user ID used in the KNXnet/IP session authentication frame.
+*/
+QKnxNetIp::SecureUserId QKnxNetIpSecureConfiguration::userId() const
+{
+ return d->userId;
+}
+
+/*!
+ Sets the user ID used in the KNXnet/IP session authentication frame
+ to \a userId and returns \c true on success; \c false otherwise.
+
+ \note A userId() with the value \c QKnxNetIp::SecureUserId::Reserved or
+ equal to or more than \c QKnxNetIp::SecureUserId::Invalid is considered
+ invalid according to the KNX application note AN159.
+*/
+bool QKnxNetIpSecureConfiguration::setUserId(QKnxNetIp::SecureUserId userId)
+{
+ auto valid = QKnxNetIp::isSecureUserId(userId);
+ if (valid)
+ d->userId = userId;
+ return valid;
+}
+
+/*!
+ Returns the user password used to authenticate the user while establishing
+ the secure session as an array of bytes.
+*/
+QByteArray QKnxNetIpSecureConfiguration::userPassword() const
+{
+ return d->userPassword;
+}
+
+/*!
+ Sets the user password to authenticate the user while establishing the
+ secure session to \a userPassword. Returns \c true on success; \c false
+ otherwise.
+*/
+void QKnxNetIpSecureConfiguration::setUserPassword(const QByteArray &userPassword)
+{
+ d->userPassword = userPassword;
+}
+
+/*!
+ Returns the requested individual address for the secure session.
+*/
+QKnxAddress QKnxNetIpSecureConfiguration::individualAddress() const
+{
+ return d->ia;
+}
+
+/*!
+ Sets the requested individual address of the secure session to \a address.
+ Returns \c true on success; \c false otherwise.
+
+ \note To request any of the freely available addresses for the secure
+ session, or to reset the requested one, pass an invalid \a address to
+ the function.
+*/
+bool QKnxNetIpSecureConfiguration::setIndividualAddress(const QKnxAddress &address)
+{
+ if ((address.type() == QKnxAddress::Type::Individual) || (!address.isValid()))
+ d->ia = address;
+ return d->ia == address;
+}
+
+/*!
+ Returns the device authentication code to establish the secure session
+ as an array of bytes.
+*/
+QByteArray QKnxNetIpSecureConfiguration::deviceAuthenticationCode() const
+{
+ return d->deviceAuthenticationCode;
+}
+
+/*!
+ Sets the device authentication code used to establish the secure session
+ to \a authenticationCode. Returns \c true on success; \c false otherwise.
+
+ \note The device authentication code cannot be empty.
+*/
+bool QKnxNetIpSecureConfiguration::setDeviceAuthenticationCode(const QByteArray &authenticationCode)
+{
+ auto valid = !authenticationCode.isEmpty();
+ if (valid)
+ d->deviceAuthenticationCode = authenticationCode;
+ return valid;
+}
+
+/*!
+ Returns \c true if the keep alive flag is set; \c false otherwise. By
+ default this is set to \c false.
+*/
+bool QKnxNetIpSecureConfiguration::isSecureSessionKeepAliveSet() const
+{
+ return d->keepAlive;
+}
+
+/*!
+ Determines whether the connection should be kept alive. Set \a keepAlive to
+ \c true to keep a secure session alive even if there is no traffic for more
+ than \l {QKnx::NetIp::SecureSessionTimeout} {60 seconds}.
+*/
+void QKnxNetIpSecureConfiguration::setKeepSecureSessionAlive(bool keepAlive)
+{
+ d->keepAlive = keepAlive;
+}
+
+/*!
+ Constructs a copy of \a other.
+*/
+QKnxNetIpSecureConfiguration::QKnxNetIpSecureConfiguration(const QKnxNetIpSecureConfiguration &other)
+ : d(other.d)
+{}
+
+/*!
+ Assigns the specified \a other to this secure configuration.
+*/
+QKnxNetIpSecureConfiguration &QKnxNetIpSecureConfiguration::operator=(const QKnxNetIpSecureConfiguration &other)
+{
+ d = other.d;
+ return *this;
+}
+
+/*!
+ Move-constructs a secure configuration, making it point to the same secure
+ configuration that \a other was pointing to.
+*/
+QKnxNetIpSecureConfiguration::QKnxNetIpSecureConfiguration(QKnxNetIpSecureConfiguration &&other) Q_DECL_NOTHROW
+ : d(other.d)
+{
+ other.d = nullptr;
+}
+
+/*!
+ Move-assigns \a other to this secure configuration.
+*/
+QKnxNetIpSecureConfiguration &
+ QKnxNetIpSecureConfiguration::operator=(QKnxNetIpSecureConfiguration &&other) Q_DECL_NOTHROW
+{
+ swap(other);
+ return *this;
+}
+
+/*!
+ Swaps \a other with this secure configuration. This operation is very fast
+ and never fails.
+*/
+void QKnxNetIpSecureConfiguration::swap(QKnxNetIpSecureConfiguration &other) Q_DECL_NOTHROW
+{
+ d.swap(other.d);
+}
+
+/*!
+ Returns \c true if this secure configuration and the given \a other are
+ equal; otherwise returns \c false.
+*/
+bool QKnxNetIpSecureConfiguration::operator==(const QKnxNetIpSecureConfiguration &other) const
+{
+ return d == other.d || (d->privateKey == other.d->privateKey
+ && d->publicKey == other.d->publicKey
+ && d->host == other.d->host
+ && d->userId == other.d->userId
+ && d->userPassword == other.d->userPassword
+ && d->ia == other.d->ia
+ && d->deviceAuthenticationCode == other.d->deviceAuthenticationCode
+ && d->keepAlive == other.d->keepAlive);
+}
+
+/*!
+ Returns \c true if this secure configuration and the given \a other are not
+ equal; otherwise returns \c false.
+*/
+bool QKnxNetIpSecureConfiguration::operator!=(const QKnxNetIpSecureConfiguration &other) const
+{
+ return !operator==(other);
+}
+
+QT_END_NAMESPACE