1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopen62541utils.h"
5#include <qopcuatype.h>
6
7#include <QtCore/qloggingcategory.h>
8#include <QtCore/qstringlist.h>
9#include <QtCore/quuid.h>
10
11#ifdef UA_ENABLE_ENCRYPTION
12#include <openssl/evp.h>
13#include <openssl/rsa.h>
14#endif
15
16#include <cstring>
17
18QT_BEGIN_NAMESPACE
19
20Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541)
21
22UA_NodeId Open62541Utils::nodeIdFromQString(const QString &name)
23{
24 quint16 namespaceIndex;
25 QString identifierString;
26 char identifierType;
27 bool success = QOpcUa::nodeIdStringSplit(nodeIdString: name, nsIndex: &namespaceIndex, identifier: &identifierString, identifierType: &identifierType);
28
29 if (!success) {
30 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to split node id string:" << name;
31 return UA_NODEID_NULL;
32 }
33
34 switch (identifierType) {
35 case 'i': {
36 bool isNumber;
37 uint identifier = identifierString.toUInt(ok: &isNumber);
38 if (isNumber && identifier <= ((std::numeric_limits<quint32>::max)()))
39 return UA_NODEID_NUMERIC(nsIndex: namespaceIndex, identifier: static_cast<UA_UInt32>(identifier));
40 else
41 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid numeric identifier";
42 break;
43 }
44 case 's': {
45 if (identifierString.size() > 0)
46 return UA_NODEID_STRING_ALLOC(nsIndex: namespaceIndex, chars: identifierString.toUtf8().constData());
47 else
48 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid string identifier";
49 break;
50 }
51 case 'g': {
52 QUuid uuid(identifierString);
53
54 if (uuid.isNull()) {
55 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid guid identifier";
56 break;
57 }
58
59 UA_Guid guid;
60 guid.data1 = uuid.data1;
61 guid.data2 = uuid.data2;
62 guid.data3 = uuid.data3;
63 std::memcpy(dest: guid.data4, src: uuid.data4, n: sizeof(uuid.data4));
64 return UA_NODEID_GUID(nsIndex: namespaceIndex, guid);
65 }
66 case 'b': {
67 const QByteArray temp = QByteArray::fromBase64(base64: identifierString.toLatin1());
68 if (temp.size() > 0) {
69 return UA_NODEID_BYTESTRING_ALLOC(nsIndex: namespaceIndex, chars: temp.constData());
70 }
71 else
72 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << name << "does not contain a valid byte string identifier";
73 break;
74 }
75 default:
76 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Could not parse node id:" << name;
77 }
78 return UA_NODEID_NULL;
79}
80
81QString Open62541Utils::nodeIdToQString(UA_NodeId id)
82{
83 QString result = QString::fromLatin1(ba: "ns=%1;").arg(a: id.namespaceIndex);
84
85 switch (id.identifierType) {
86 case UA_NODEIDTYPE_NUMERIC:
87 result.append(s: QString::fromLatin1(ba: "i=%1").arg(a: id.identifier.numeric));
88 break;
89 case UA_NODEIDTYPE_STRING:
90 result.append(s: QLatin1String("s="));
91 result.append(s: QString::fromUtf8(utf8: reinterpret_cast<char *>(id.identifier.string.data),
92 size: id.identifier.string.length));
93 break;
94 case UA_NODEIDTYPE_GUID: {
95 const UA_Guid &src = id.identifier.guid;
96 const QUuid uuid(src.data1, src.data2, src.data3, src.data4[0], src.data4[1], src.data4[2],
97 src.data4[3], src.data4[4], src.data4[5], src.data4[6], src.data4[7]);
98 result.append(QStringLiteral("g=")).append(v: QStringView(uuid.toString()).mid(pos: 1, n: 36)); // Remove enclosing {...}
99 break;
100 }
101 case UA_NODEIDTYPE_BYTESTRING: {
102 const QByteArray temp(reinterpret_cast<char *>(id.identifier.byteString.data), id.identifier.byteString.length);
103 result.append(QStringLiteral("b=")).append(s: temp.toBase64());
104 break;
105 }
106 default:
107 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Open62541 Utils: Could not convert UA_NodeId to QString";
108 result.clear();
109 }
110 return result;
111}
112
113#ifdef UA_ENABLE_ENCRYPTION
114bool Open62541Utils::checkSha1SignatureSupport()
115{
116 auto mdCtx = EVP_MD_CTX_create ();
117
118 if (!mdCtx)
119 return false;
120
121 auto pkCtx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
122
123 if (!pkCtx) {
124 EVP_MD_CTX_destroy(mdCtx);
125 return false;
126 }
127
128 auto ret = EVP_PKEY_keygen_init(ctx: pkCtx);
129
130 if (ret != 1) {
131 EVP_PKEY_CTX_free(ctx: pkCtx);
132 EVP_MD_CTX_destroy(mdCtx);
133 return false;
134 }
135
136 ret = EVP_PKEY_CTX_set_rsa_keygen_bits(ctx: pkCtx, bits: 2048);
137
138 if (ret != 1) {
139 EVP_PKEY_CTX_free(ctx: pkCtx);
140 EVP_MD_CTX_destroy(mdCtx);
141 return false;
142 }
143
144 EVP_PKEY *pKey = nullptr;
145 ret = EVP_PKEY_keygen(ctx: pkCtx, ppkey: &pKey);
146
147 if (ret != 1) {
148 EVP_PKEY_CTX_free(ctx: pkCtx);
149 EVP_MD_CTX_destroy(mdCtx);
150 EVP_PKEY_free(pkey: pKey);
151 return false;
152 }
153
154 bool hasSupport = false;
155
156 ret = EVP_DigestSignInit (ctx: mdCtx, NULL, type: EVP_sha1(), NULL, pkey: pKey);
157 if (ret == 1)
158 hasSupport = true;
159
160 EVP_PKEY_CTX_free(ctx: pkCtx);
161 EVP_MD_CTX_destroy(mdCtx);
162 EVP_PKEY_free(pkey: pKey);
163
164 return hasSupport;
165}
166#endif
167
168QT_END_NAMESPACE
169

source code of qtopcua/src/plugins/opcua/open62541/qopen62541utils.cpp