Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qasn1element.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4
5#include "qasn1element_p.h"
6
7#include <QtCore/qdatastream.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qlist.h>
10#include <QDebug>
11#include <private/qtools_p.h>
12
13#include <limits>
14
16
17using namespace QtMiscUtils;
18
21{
22 OidNameMap oids;
23 // used by unit tests
24 oids.insert(oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
25 oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
26 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
27 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
28 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
29 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
30 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
31 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
32 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
33 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
34 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
35 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
36 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
37 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description"));
38 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode"));
39 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN"));
40 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN"));
41 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name"));
42 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN"));
43 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials"));
44 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier"));
45 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber"));
46 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C"));
47 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L"));
48 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST"));
49 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street"));
50 return oids;
51}
53
55 : mType(type)
56 , mValue(value)
57{
58}
59
61{
62 // type
63 quint8 tmpType;
64 stream >> tmpType;
65 if (!tmpType)
66 return false;
67
68 // length
69 quint64 length = 0;
71 stream >> first;
72 if (first & 0x80) {
73 // long form
74 const quint8 bytes = (first & 0x7f);
75 if (bytes > 7)
76 return false;
77
78 quint8 b;
79 for (int i = 0; i < bytes; i++) {
80 stream >> b;
81 length = (length << 8) | b;
82 }
83 } else {
84 // short form
85 length = (first & 0x7f);
86 }
87
88 if (length > quint64(std::numeric_limits<int>::max()))
89 return false;
90
91 // read value in blocks to avoid being fooled by incorrect length
92 const int BUFFERSIZE = 4 * 1024;
93 QByteArray tmpValue;
94 int remainingLength = length;
95 while (remainingLength) {
96 char readBuffer[BUFFERSIZE];
97 const int bytesToRead = qMin(remainingLength, BUFFERSIZE);
98 const int count = stream.readRawData(readBuffer, bytesToRead);
99 if (count != int(bytesToRead))
100 return false;
101 tmpValue.append(readBuffer, bytesToRead);
102 remainingLength -= bytesToRead;
103 }
104
105 mType = tmpType;
106 mValue.swap(tmpValue);
107 return true;
108}
109
111{
113 return read(stream);
114}
115
117{
118 // type
119 stream << mType;
120
121 // length
122 qint64 length = mValue.size();
123 if (length >= 128) {
124 // long form
125 quint8 encodedLength = 0x80;
127 while (length) {
128 ba.prepend(quint8((length & 0xff)));
129 length >>= 8;
130 encodedLength += 1;
131 }
132 stream << encodedLength;
133 stream.writeRawData(ba.data(), ba.size());
134 } else {
135 // short form
136 stream << quint8(length);
137 }
138
139 // value
140 stream.writeRawData(mValue.data(), mValue.size());
141}
142
144{
146 QByteArray(1, val ? 0xff : 0x00));
147}
148
150{
152 while (val > 127) {
153 elem.mValue.prepend(val & 0xff);
154 val >>= 8;
155 }
156 elem.mValue.prepend(val & 0x7f);
157 return elem;
158}
159
161{
162 QAsn1Element seq;
163 seq.mType = SequenceType;
165 for (auto it = items.cbegin(), end = items.cend(); it != end; ++it)
166 it->write(stream);
167 return seq;
168}
169
171{
172 QAsn1Element elem;
173 elem.mType = ObjectIdentifierType;
174 const QList<QByteArray> bits = id.split('.');
175 Q_ASSERT(bits.size() > 2);
176 elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt()));
177 for (int i = 2; i < bits.size(); ++i) {
178 char buffer[std::numeric_limits<unsigned int>::digits / 7 + 2];
179 char *pBuffer = buffer + sizeof(buffer);
180 *--pBuffer = '\0';
181 unsigned int node = bits[i].toUInt();
182 *--pBuffer = quint8((node & 0x7f));
183 node >>= 7;
184 while (node) {
185 *--pBuffer = quint8(((node & 0x7f) | 0x80));
186 node >>= 7;
187 }
188 elem.mValue += pBuffer;
189 }
190 return elem;
191}
192
193bool QAsn1Element::toBool(bool *ok) const
194{
195 if (*this == fromBool(true)) {
196 if (ok)
197 *ok = true;
198 return true;
199 } else if (*this == fromBool(false)) {
200 if (ok)
201 *ok = true;
202 return false;
203 } else {
204 if (ok)
205 *ok = false;
206 return false;
207 }
208}
209
211{
213
214 if (mValue.size() != 13 && mValue.size() != 15)
215 return result;
216
217 // QDateTime::fromString is lenient and accepts +- signs in front
218 // of the year; but ASN.1 doesn't allow them.
219 if (!isAsciiDigit(mValue[0]))
220 return result;
221
222 // Timezone must be present, and UTC
223 if (mValue.back() != 'Z')
224 return result;
225
226 if (mType == UtcTimeType && mValue.size() == 13) {
227 result = QDateTime::fromString(QString::fromLatin1(mValue),
228 QStringLiteral("yyMMddHHmmsst"));
229 if (!result.isValid())
230 return result;
231
232 Q_ASSERT(result.timeSpec() == Qt::UTC);
233
234 QDate date = result.date();
235
236 // RFC 2459:
237 // Where YY is greater than or equal to 50, the year shall be
238 // interpreted as 19YY; and
239 //
240 // Where YY is less than 50, the year shall be interpreted as 20YY.
241 //
242 // QDateTime interprets the 'yy' format as 19yy, so we may need to adjust
243 // the year (bring it in the [1950, 2049] range).
244 if (date.year() < 1950)
246
247 Q_ASSERT(result.date().year() >= 1950);
248 Q_ASSERT(result.date().year() <= 2049);
249 } else if (mType == GeneralizedTimeType && mValue.size() == 15) {
250 result = QDateTime::fromString(QString::fromLatin1(mValue),
251 QStringLiteral("yyyyMMddHHmmsst"));
252 }
253
254 return result;
255}
256
258{
260 QAsn1Element elem;
261 QDataStream issuerStream(mValue);
262 while (elem.read(issuerStream) && elem.mType == QAsn1Element::SetType) {
263 QAsn1Element issuerElem;
264 QDataStream setStream(elem.mValue);
265 if (issuerElem.read(setStream) && issuerElem.mType == QAsn1Element::SequenceType) {
266 const auto elems = issuerElem.toList();
267 if (elems.size() == 2) {
268 const QByteArray key = elems.front().toObjectName();
269 if (!key.isEmpty())
270 info.insert(key, elems.back().toString());
271 }
272 }
273 }
274 return info;
275}
276
278{
279 if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) {
280 if (ok)
281 *ok = false;
282 return 0;
283 }
284
285 // NOTE: - negative numbers are not handled
286 // - greater sizes would overflow
287 if (mValue.at(0) & 0x80 || mValue.size() > 8) {
288 if (ok)
289 *ok = false;
290 return 0;
291 }
292
293 qint64 value = mValue.at(0) & 0x7f;
294 for (int i = 1; i < mValue.size(); ++i)
295 value = (value << 8) | quint8(mValue.at(i));
296
297 if (ok)
298 *ok = true;
299 return value;
300}
301
303{
305 if (mType == SequenceType) {
306 QAsn1Element elem;
307 QDataStream stream(mValue);
308 while (elem.read(stream))
309 items << elem;
310 }
311 return items;
312}
313
315{
317 if (mType == ObjectIdentifierType && !mValue.isEmpty()) {
318 quint8 b = mValue.at(0);
319 key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40);
320 unsigned int val = 0;
321 for (int i = 1; i < mValue.size(); ++i) {
322 b = mValue.at(i);
323 val = (val << 7) | (b & 0x7f);
324 if (!(b & 0x80)) {
325 key += '.' + QByteArray::number(val);
326 val = 0;
327 }
328 }
329 }
330 return key;
331}
332
334{
336 return oidNameMap->value(key, key);
337}
338
340{
341 // Detect embedded NULs and reject
342 if (qstrlen(mValue) < uint(mValue.size()))
343 return QString();
344
345 if (mType == PrintableStringType || mType == TeletexStringType
346 || mType == Rfc822NameType || mType == DnsNameType
348 return QString::fromLatin1(mValue, mValue.size());
349 if (mType == Utf8StringType)
350 return QString::fromUtf8(mValue, mValue.size());
351
352 return QString();
353}
354
@ UniformResourceIdentifierType
QByteArray toObjectName() const
QMultiMap< QByteArray, QString > toInfo() const
QDateTime toDateTime() const
bool read(QDataStream &data)
qint64 toInteger(bool *ok=nullptr) const
static QAsn1Element fromVector(const QList< QAsn1Element > &items)
static QAsn1Element fromObjectId(const QByteArray &id)
void write(QDataStream &data) const
static QAsn1Element fromInteger(unsigned int val)
QString toString() const
QByteArray toObjectId() const
bool toBool(bool *ok=nullptr) const
static QAsn1Element fromBool(bool val)
QList< QAsn1Element > toList() const
QByteArray value() const
\inmodule QtCore
Definition qbytearray.h:57
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
Definition qbytearray.h:534
QByteArray & prepend(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:216
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
Definition qbytearray.h:474
char front() const
Definition qbytearray.h:132
char at(qsizetype i) const
Returns the byte at index position i in the byte array.
Definition qbytearray.h:523
void swap(QByteArray &other) noexcept
Definition qbytearray.h:103
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
char back() const
Definition qbytearray.h:134
\inmodule QtCore\reentrant
Definition qdatastream.h:30
\inmodule QtCore\reentrant
Definition qdatetime.h:257
\inmodule QtCore \reentrant
Definition qdatetime.h:27
QDate addYears(int years) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
int year() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
bool setDate(int year, int month, int day)
Definition qlist.h:74
const_iterator cend() const noexcept
Definition qlist.h:614
const_iterator cbegin() const noexcept
Definition qlist.h:613
Definition qmap.h:186
iterator insert(const Key &key, const T &value)
Definition qmap.h:687
const_iterator cend() const
Definition qmap.h:604
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5710
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
QDate date
[1]
QSet< QString >::iterator it
Combined button and popup list for selecting options.
constexpr bool isAsciiDigit(char32_t c) noexcept
Definition qtools_p.h:67
@ UTC
QMap< QByteArray, QByteArray > OidNameMap
static OidNameMap createOidMap()
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
size_t qstrlen(const char *str)
EGLStreamKHR stream
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_GLOBAL_STATIC_WITH_ARGS(TYPE, NAME, ARGS)
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
GLboolean GLboolean GLboolean b
GLuint64 key
GLuint GLuint end
GLenum GLuint GLenum GLsizei length
GLenum GLenum GLsizei count
GLenum GLuint buffer
GLenum type
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint first
GLuint GLfloat * val
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const void * bits
GLuint64EXT * result
[6]
GLbitfield GLuint readBuffer
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define QStringLiteral(str)
unsigned long long quint64
Definition qtypes.h:56
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
unsigned char quint8
Definition qtypes.h:41
QByteArray ba
[0]
QFileInfo info(fileName)
[8]
QList< QTreeWidgetItem * > items