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

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