Revision control

++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qopen62541utils.h"
++ #include <qopcuatype.h>
++ #include "qopen62541valueconverter.h"
++
++ #include <QtCore/qloggingcategory.h>
++ #include <QtCore/qstringlist.h>
++ #include <QtCore/quuid.h>
++
++ #ifdef UA_ENABLE_ENCRYPTION
++ #include <openssl/evp.h>
++ #include <openssl/rsa.h>
++ #endif
++
++ #include <cstring>
++
++ QT_BEGIN_NAMESPACE
++
++ using namespace Qt::Literals::StringLiterals;
++
++ Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541)
++
++ UA_NodeId Open62541Utils::nodeIdFromQString(const QString &name)
++ {
++ quint16 namespaceIndex;
++ QString identifierString;
++ char identifierType;
++ bool success = QOpcUa::nodeIdStringSplit(name, &namespaceIndex, &identifierString, &identifierType);
++
++ if (!success) {
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to split node id string:" << name;
++ return UA_NODEID_NULL;
++ }
++
++ switch (identifierType) {
++ case 'i': {
++ bool isNumber;
++ uint identifier = identifierString.toUInt(&isNumber);
++ if (isNumber && identifier <= ((std::numeric_limits<quint32>::max)()))
++ return UA_NODEID_NUMERIC(namespaceIndex, static_cast<UA_UInt32>(identifier));
++ else
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid numeric identifier";
++ break;
++ }
++ case 's': {
++ if (identifierString.size() > 0)
++ return UA_NODEID_STRING_ALLOC(namespaceIndex, identifierString.toUtf8().constData());
++ else
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid string identifier";
++ break;
++ }
++ case 'g': {
++ QUuid uuid(identifierString);
++
++ if (uuid.isNull()) {
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid guid identifier";
++ break;
++ }
++
++ UA_Guid guid;
++ guid.data1 = uuid.data1;
++ guid.data2 = uuid.data2;
++ guid.data3 = uuid.data3;
++ std::memcpy(guid.data4, uuid.data4, sizeof(uuid.data4));
++ return UA_NODEID_GUID(namespaceIndex, guid);
++ }
++ case 'b': {
++ const QByteArray temp = QByteArray::fromBase64(identifierString.toLatin1());
++ if (temp.size() > 0) {
++ UA_NodeId byteStringId = UA_NODEID_NULL;
++ byteStringId.namespaceIndex = namespaceIndex;
++ byteStringId.identifierType = UA_NODEIDTYPE_BYTESTRING;
++ QOpen62541ValueConverter::scalarFromQt<UA_ByteString, QByteArray>(temp, &byteStringId.identifier.byteString);
++ return byteStringId;
++ }
++ else
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid byte string identifier";
++ break;
++ }
++ default:
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not parse node id:" << name;
++ }
++ return UA_NODEID_NULL;
++ }
++
++ QString Open62541Utils::nodeIdToQString(UA_NodeId id)
++ {
++ QString result = u"ns=%1;"_s.arg(id.namespaceIndex);
++
++ switch (id.identifierType) {
++ case UA_NODEIDTYPE_NUMERIC:
++ result.append(u"i=%1"_s.arg(id.identifier.numeric));
++ break;
++ case UA_NODEIDTYPE_STRING:
++ result.append("s="_L1);
++ result.append(QString::fromUtf8(reinterpret_cast<char *>(id.identifier.string.data),
++ id.identifier.string.length));
++ break;
++ case UA_NODEIDTYPE_GUID: {
++ const UA_Guid &src = id.identifier.guid;
++ const QUuid uuid(src.data1, src.data2, src.data3, src.data4[0], src.data4[1], src.data4[2],
++ src.data4[3], src.data4[4], src.data4[5], src.data4[6], src.data4[7]);
++ result.append("g="_L1).append(QStringView(uuid.toString()).mid(1, 36)); // Remove enclosing {...}
++ break;
++ }
++ case UA_NODEIDTYPE_BYTESTRING: {
++ const QByteArray temp(reinterpret_cast<char *>(id.identifier.byteString.data), id.identifier.byteString.length);
++ result.append("b="_L1).append(QString::fromUtf8(temp.toBase64()));
++ break;
++ }
++ default:
++ qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Open62541 Utils: Could not convert UA_NodeId to QString";
++ result.clear();
++ }
++ return result;
++ }
++
++ #ifdef UA_ENABLE_ENCRYPTION
++ bool Open62541Utils::checkSha1SignatureSupport()
++ {
++ auto mdCtx = EVP_MD_CTX_create ();
++
++ if (!mdCtx)
++ return false;
++
++ auto pkCtx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
++
++ if (!pkCtx) {
++ EVP_MD_CTX_destroy(mdCtx);
++ return false;
++ }
++
++ auto ret = EVP_PKEY_keygen_init(pkCtx);
++
++ if (ret != 1) {
++ EVP_PKEY_CTX_free(pkCtx);
++ EVP_MD_CTX_destroy(mdCtx);
++ return false;
++ }
++
++ ret = EVP_PKEY_CTX_set_rsa_keygen_bits(pkCtx, 2048);
++
++ if (ret != 1) {
++ EVP_PKEY_CTX_free(pkCtx);
++ EVP_MD_CTX_destroy(mdCtx);
++ return false;
++ }
++
++ EVP_PKEY *pKey = nullptr;
++ ret = EVP_PKEY_keygen(pkCtx, &pKey);
++
++ if (ret != 1) {
++ EVP_PKEY_CTX_free(pkCtx);
++ EVP_MD_CTX_destroy(mdCtx);
++ EVP_PKEY_free(pKey);
++ return false;
++ }
++
++ bool hasSupport = false;
++
++ ret = EVP_DigestSignInit (mdCtx, NULL, EVP_sha1(), NULL, pKey);
++ if (ret == 1)
++ hasSupport = true;
++
++ EVP_PKEY_CTX_free(pkCtx);
++ EVP_MD_CTX_destroy(mdCtx);
++ EVP_PKEY_free(pKey);
++
++ return hasSupport;
++ }
++ #endif
++
++ QT_END_NAMESPACE