summaryrefslogtreecommitdiffstats
path: root/src/knx/ssl/qknxsecurekey.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/knx/ssl/qknxsecurekey.cpp')
-rw-r--r--src/knx/ssl/qknxsecurekey.cpp389
1 files changed, 389 insertions, 0 deletions
diff --git a/src/knx/ssl/qknxsecurekey.cpp b/src/knx/ssl/qknxsecurekey.cpp
new file mode 100644
index 0000000..5aadbf9
--- /dev/null
+++ b/src/knx/ssl/qknxsecurekey.cpp
@@ -0,0 +1,389 @@
+/******************************************************************************
+**
+** Copyright (C) 2018 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 "qknxsecurekey.h"
+#include "qknxcryptographicengine.h"
+
+#include <QtNetwork/private/qtnetworkglobal_p.h>
+
+#if QT_CONFIG(opensslv11)
+# include "private/qsslsocket_openssl_symbols_p.h"
+# include "private/qsslsocket_openssl11_symbols_p.h"
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QKnxSecureKeyData : public QSharedData
+{
+public:
+ QKnxSecureKeyData() = default;
+ ~QKnxSecureKeyData()
+ {
+#if QT_CONFIG(opensslv11)
+ if (m_evpPKey)
+ q_EVP_PKEY_free(m_evpPKey);
+#endif
+ }
+
+ bool isTypeValid() const
+ {
+ return m_type >= QKnxSecureKey::Type::Private && m_type < QKnxSecureKey::Type::Invalid;
+ }
+
+#if QT_CONFIG(opensslv11)
+ EVP_PKEY *m_evpPKey{ nullptr };
+#endif
+ QKnxSecureKey::Type m_type{ QKnxSecureKey::Type::Invalid };
+};
+
+/*!
+ \since 5.13
+ \inmodule QtKnx
+
+ \class QKnxSecureKey
+ \ingroup qtknx-general-classes
+
+ \brief The QKnxSecureKey class represents the elliptic-curve secure key to
+ be used with the elliptic curve Diffie-Hellman (ECDH) key agreement scheme.
+
+ 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 QKnxSecureKey::Type
+
+ Describes the types of keys QKnxSecureKey supports.
+
+ \value Private A private key.
+ \value Public A public key.
+ \value Invalid An invalid key, please do not use.
+*/
+
+/*!
+ Constructs an empty invalid secure key.
+*/
+QKnxSecureKey::QKnxSecureKey()
+ : d_ptr(new QKnxSecureKeyData)
+{}
+
+/*!
+ Destroys the secure key and releases all allocated resources.
+*/
+QKnxSecureKey::~QKnxSecureKey() = default;
+
+/*!
+ Returns the type of the key: \l {QKnxSecureKey::Public}{Public} or
+ \l {QKnxSecureKey::Private}{Private}.
+*/
+QKnxSecureKey::Type QKnxSecureKey::type() const
+{
+ return d_ptr->m_type;
+}
+
+/*!
+ Returns \c true if this is a default constructed secure key; otherwise
+ returns \c false. A secure key is considered \c null if it contains no
+ initialized values.
+*/
+bool QKnxSecureKey::isNull() const
+{
+#if QT_CONFIG(opensslv11)
+ return d_ptr->m_evpPKey == nullptr;
+#else
+ return true;
+#endif
+}
+
+/*!
+ Returns \c true if OpenSSL is available and if the key contains initialized
+ values, otherwise returns \c false.
+*/
+bool QKnxSecureKey::isValid() const
+{
+#if QT_CONFIG(opensslv11)
+ return !isNull() && d_ptr->isTypeValid() && QKnxCryptographicEngine::supportsCryptography();
+#else
+ return false;
+#endif
+}
+
+/*!
+ Returns an array of bytes that represent the Curve25519 raw secure key.
+
+ \note The function will return an empty byte array for the private
+ key unless OpenSLL with version 1.1.1a is used as back-end.
+*/
+QKnxByteArray QKnxSecureKey::bytes() const
+{
+#if QT_CONFIG(opensslv11)
+ if (!isValid())
+ return {};
+
+ if (d_ptr->m_type == Type::Private) {
+ size_t len = 32;
+ QKnxByteArray ba(int(len), 0);
+ if (q_EVP_PKEY_get_raw_private_key(d_ptr->m_evpPKey, ba.data(), &len) <= 0)
+ return {}; // preferred, no other way possible
+ return ba;
+ }
+
+ size_t len = 32;
+ QKnxByteArray pub(32, Qt::Uninitialized);
+ if (q_EVP_PKEY_get_raw_public_key(d_ptr->m_evpPKey, pub.data(), &len) > 0)
+ return pub; // preferred way
+
+ pub.resize(q_i2d_PUBKEY(d_ptr->m_evpPKey, nullptr));
+ auto tmp = pub.data();
+ q_i2d_PUBKEY(d_ptr->m_evpPKey, &tmp);
+ return pub.right(32);
+#else
+ return {};
+#endif
+}
+
+/*!
+ Constructs the Curve25519 secure key from the byte array \a data starting
+ at position \a index inside the array and sets the key type to \a type if
+ OpenSSL is available and no error occurs;
+ otherwise returns a \e {default-constructed key} which can be invalid.
+*/
+QKnxSecureKey QKnxSecureKey::fromBytes(QKnxSecureKey::Type type, const QKnxByteArray &data,
+ quint16 index)
+{
+#if QT_CONFIG(opensslv11)
+ auto ba = data.mid(index, 32);
+ if (ba.size() < 32)
+ return {};
+
+ if (!QKnxCryptographicEngine::supportsCryptography())
+ return {};
+
+ QKnxSecureKey key;
+ key.d_ptr->m_type = type;
+
+ if (type == Type::Private) {
+ key.d_ptr->m_evpPKey = q_EVP_PKEY_new_raw_private_key(NID_X25519, nullptr, ba.constData(),
+ ba.size()); // preferred way
+ if (key.d_ptr->m_evpPKey)
+ return key;
+
+ static const auto pkcs8 = QKnxByteArray::fromHex("302e020100300506032b656e04220420");
+ auto tmp = pkcs8 + ba; // PKCS #8 is a standard syntax for storing private key information
+
+ BIO *bio = nullptr;
+ if ((bio = q_BIO_new_mem_buf(reinterpret_cast<void *> (tmp.data()), tmp.size())))
+ key.d_ptr->m_evpPKey = q_d2i_PrivateKey_bio(bio, nullptr);
+ q_BIO_free(bio);
+ return key;
+ }
+
+ if (type == Type::Public) {
+ key.d_ptr->m_evpPKey = q_EVP_PKEY_new_raw_public_key(NID_X25519, nullptr, ba.constData(),
+ ba.size()); // preferred way
+ if (key.d_ptr->m_evpPKey)
+ return key;
+
+ key.d_ptr->m_evpPKey = q_EVP_PKEY_new();
+ if (q_EVP_PKEY_set_type(key.d_ptr->m_evpPKey, NID_X25519) <= 0)
+ return {};
+
+ if (q_EVP_PKEY_set1_tls_encodedpoint(key.d_ptr->m_evpPKey, ba.constData(), ba.size()) <= 0)
+ return {};
+ return key;
+ }
+#else
+ Q_UNUSED(type)
+ Q_UNUSED(data)
+ Q_UNUSED(index)
+#endif
+ return {};
+}
+
+/*!
+ Returns a new valid private key if OpenSSL is available and no error occurs.
+*/
+QKnxSecureKey QKnxSecureKey::generatePrivateKey()
+{
+ QKnxSecureKey key;
+#if QT_CONFIG(opensslv11)
+ if (!QKnxCryptographicEngine::supportsCryptography())
+ return key;
+
+ if (auto *pctx = q_EVP_PKEY_CTX_new_id(NID_X25519, nullptr)) {
+ q_EVP_PKEY_keygen_init(pctx);
+ key.d_ptr->m_type = Type::Private;
+ q_EVP_PKEY_keygen(pctx, &key.d_ptr->m_evpPKey);
+ q_EVP_PKEY_CTX_free(pctx);
+ }
+#endif
+ return key;
+}
+
+/*!
+ Returns a new valid public key with the given private key \a privateKey if
+ OpenSSL is available and no error occurs.
+*/
+QKnxSecureKey QKnxSecureKey::publicKeyFromPrivate(const QKnxSecureKey &privateKey)
+{
+ QKnxSecureKey key;
+#if QT_CONFIG(opensslv11)
+ if (privateKey.type() == QKnxSecureKey::Type::Private && privateKey.isValid()) {
+ q_EVP_PKEY_up_ref(privateKey.d_ptr->m_evpPKey);
+ key.d_ptr->m_type = Type::Public;
+ key.d_ptr->m_evpPKey = privateKey.d_ptr->m_evpPKey;
+ }
+#else
+ Q_UNUSED(privateKey)
+#endif
+ return key;
+}
+
+/*!
+ \overload publicKeyFromPrivate()
+*/
+QKnxSecureKey QKnxSecureKey::publicKeyFromPrivate(const QKnxByteArray &privateKey)
+{
+ return QKnxSecureKey::publicKeyFromPrivate(QKnxSecureKey::fromBytes(QKnxSecureKey::Type::Private,
+ privateKey));
+}
+
+/*!
+ Sets \a privateKey to a new valid private key and \a publicKey to a new
+ valid public key derived from the freshly generated private key if OpenSSL
+ is available and no error occurs.
+*/
+void QKnxSecureKey::generateKeys(QKnxSecureKey *privateKey, QKnxSecureKey *publicKey)
+{
+ if (!privateKey || !publicKey)
+ return;
+
+ *privateKey = generatePrivateKey();
+ *publicKey = publicKeyFromPrivate(*privateKey);
+}
+
+/*!
+ Derives and returns the shared secret from the given private key
+ \a privateKey and the peer's public key \a peerPublicKey if OpenSSL
+ is available and no error occurs;
+ otherwise returns a \l {default-constructed value} which can be empty.
+*/
+QKnxByteArray QKnxSecureKey::sharedSecret(const QKnxSecureKey &privateKey,
+ const QKnxSecureKey &peerPublicKey)
+{
+#if QT_CONFIG(opensslv11)
+ if (privateKey.type() != QKnxSecureKey::Type::Private || !privateKey.isValid())
+ return {};
+
+ if (peerPublicKey.type() != QKnxSecureKey::Type::Public || !peerPublicKey.isValid())
+ return {};
+
+ auto evpPKeyCtx = q_EVP_PKEY_CTX_new(privateKey.d_ptr->m_evpPKey, nullptr);
+ if (!evpPKeyCtx)
+ return {};
+
+ struct ScopedFree final
+ {
+ ScopedFree(EVP_PKEY_CTX *key) : m_evpPKeyCtx(key) {}
+ ~ScopedFree() { q_EVP_PKEY_CTX_free(m_evpPKeyCtx); }
+ EVP_PKEY_CTX *m_evpPKeyCtx = nullptr;
+ } _{ evpPKeyCtx };
+
+ if (q_EVP_PKEY_derive_init(evpPKeyCtx) <= 0)
+ return {};
+
+ if (q_EVP_PKEY_derive_set_peer(evpPKeyCtx, peerPublicKey.d_ptr->m_evpPKey) <= 0)
+ return {};
+
+ size_t keylen = 0;
+ if (q_EVP_PKEY_derive(evpPKeyCtx, nullptr, &keylen) <= 0)
+ return {};
+
+ QKnxByteArray ba(int(keylen), 0);
+ if (q_EVP_PKEY_derive(evpPKeyCtx, ba.data(), &keylen) <= 0)
+ return {};
+ return ba;
+#else
+ Q_UNUSED(privateKey)
+ Q_UNUSED(peerPublicKey)
+ return {};
+#endif
+}
+
+/*!
+ \overload sharedSecret()
+*/
+QKnxByteArray QKnxSecureKey::sharedSecret(const QKnxByteArray &privateKey,
+ const QKnxByteArray &peerPublicKey)
+{
+ return sharedSecret(QKnxSecureKey::fromBytes(QKnxSecureKey::Type::Private, privateKey),
+ QKnxSecureKey::fromBytes(QKnxSecureKey::Type::Public, peerPublicKey));
+}
+
+
+/*!
+ Constructs a copy of \a other.
+*/
+QKnxSecureKey::QKnxSecureKey(const QKnxSecureKey &other)
+ : d_ptr(other.d_ptr)
+{}
+
+/*!
+ Assigns the specified \a other to this object.
+*/
+QKnxSecureKey &QKnxSecureKey::operator=(const QKnxSecureKey &other)
+{
+ d_ptr = other.d_ptr;
+ return *this;
+}
+
+/*!
+ Returns \c true if this key and the given \a other key are equal; otherwise
+ returns \c false.
+*/
+bool QKnxSecureKey::operator==(const QKnxSecureKey &other) const
+{
+ return d_ptr == other.d_ptr
+#if QT_CONFIG(opensslv11)
+ || (d_ptr->m_evpPKey == other.d_ptr->m_evpPKey && d_ptr->m_type == other.d_ptr->m_type)
+#endif
+ || (bytes() == other.bytes());
+}
+
+/*!
+ Returns \c true if this key and the given \a other key are not equal;
+ otherwise returns \c false.
+*/
+bool QKnxSecureKey::operator!=(const QKnxSecureKey &other) const
+{
+ return !operator==(other);
+}
+
+QT_END_NAMESPACE