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