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/qtimezone.h>
10#include <QtCore/qlist.h>
11#include <QDebug>
12#include <private/qtools_p.h>
13
14#include <limits>
15
16QT_BEGIN_NAMESPACE
17
18using namespace QtMiscUtils;
19
20typedef QMap<QByteArray, QByteArray> OidNameMap;
21static OidNameMap createOidMap()
22{
23 OidNameMap oids;
24 // used by unit tests
25 oids.insert(pos: oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
26 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
27 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
28 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
29 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
30 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
31 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
32 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
33 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
34 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
35 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
36 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
37 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
38 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description"));
39 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode"));
40 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN"));
41 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN"));
42 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name"));
43 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN"));
44 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials"));
45 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier"));
46 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber"));
47 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C"));
48 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L"));
49 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST"));
50 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street"));
51 return oids;
52}
53Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap()))
54
55QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value)
56 : mType(type)
57 , mValue(value)
58{
59}
60
61bool QAsn1Element::read(QDataStream &stream)
62{
63 // type
64 quint8 tmpType;
65 stream >> tmpType;
66 if (!tmpType)
67 return false;
68
69 // length
70 quint64 length = 0;
71 quint8 first;
72 stream >> first;
73 if (first & 0x80) {
74 // long form
75 const quint8 bytes = (first & 0x7f);
76 if (bytes > 7)
77 return false;
78
79 quint8 b;
80 for (int i = 0; i < bytes; i++) {
81 stream >> b;
82 length = (length << 8) | b;
83 }
84 } else {
85 // short form
86 length = (first & 0x7f);
87 }
88
89 if (length > quint64(std::numeric_limits<int>::max()))
90 return false;
91
92 // read value in blocks to avoid being fooled by incorrect length
93 const int BUFFERSIZE = 4 * 1024;
94 QByteArray tmpValue;
95 int remainingLength = length;
96 while (remainingLength) {
97 char readBuffer[BUFFERSIZE];
98 const int bytesToRead = qMin(a: remainingLength, b: BUFFERSIZE);
99 const int count = stream.readRawData(readBuffer, len: bytesToRead);
100 if (count != int(bytesToRead))
101 return false;
102 tmpValue.append(s: readBuffer, len: bytesToRead);
103 remainingLength -= bytesToRead;
104 }
105
106 mType = tmpType;
107 mValue.swap(other&: tmpValue);
108 return true;
109}
110
111bool QAsn1Element::read(const QByteArray &data)
112{
113 QDataStream stream(data);
114 return read(stream);
115}
116
117void QAsn1Element::write(QDataStream &stream) const
118{
119 // type
120 stream << mType;
121
122 // length
123 qint64 length = mValue.size();
124 if (length >= 128) {
125 // long form
126 quint8 encodedLength = 0x80;
127 QByteArray ba;
128 while (length) {
129 ba.prepend(c: quint8((length & 0xff)));
130 length >>= 8;
131 encodedLength += 1;
132 }
133 stream << encodedLength;
134 stream.writeRawData(ba.data(), len: ba.size());
135 } else {
136 // short form
137 stream << quint8(length);
138 }
139
140 // value
141 stream.writeRawData(mValue.data(), len: mValue.size());
142}
143
144QAsn1Element QAsn1Element::fromBool(bool val)
145{
146 return QAsn1Element(QAsn1Element::BooleanType,
147 QByteArray(1, val ? 0xff : 0x00));
148}
149
150QAsn1Element QAsn1Element::fromInteger(unsigned int val)
151{
152 QAsn1Element elem(QAsn1Element::IntegerType);
153 while (val > 127) {
154 elem.mValue.prepend(c: val & 0xff);
155 val >>= 8;
156 }
157 elem.mValue.prepend(c: val & 0x7f);
158 return elem;
159}
160
161QAsn1Element QAsn1Element::fromVector(const QList<QAsn1Element> &items)
162{
163 QAsn1Element seq;
164 seq.mType = SequenceType;
165 QDataStream stream(&seq.mValue, QDataStream::WriteOnly);
166 for (auto it = items.cbegin(), end = items.cend(); it != end; ++it)
167 it->write(stream);
168 return seq;
169}
170
171QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id)
172{
173 QAsn1Element elem;
174 elem.mType = ObjectIdentifierType;
175 const QList<QByteArray> bits = id.split(sep: '.');
176 Q_ASSERT(bits.size() > 2);
177 elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt()));
178 for (int i = 2; i < bits.size(); ++i) {
179 char buffer[std::numeric_limits<unsigned int>::digits / 7 + 2];
180 char *pBuffer = buffer + sizeof(buffer);
181 *--pBuffer = '\0';
182 unsigned int node = bits[i].toUInt();
183 *--pBuffer = quint8((node & 0x7f));
184 node >>= 7;
185 while (node) {
186 *--pBuffer = quint8(((node & 0x7f) | 0x80));
187 node >>= 7;
188 }
189 elem.mValue += pBuffer;
190 }
191 return elem;
192}
193
194bool QAsn1Element::toBool(bool *ok) const
195{
196 if (*this == fromBool(val: true)) {
197 if (ok)
198 *ok = true;
199 return true;
200 } else if (*this == fromBool(val: false)) {
201 if (ok)
202 *ok = true;
203 return false;
204 } else {
205 if (ok)
206 *ok = false;
207 return false;
208 }
209}
210
211QDateTime QAsn1Element::toDateTime() const
212{
213 QDateTime result;
214
215 if (mValue.size() != 13 && mValue.size() != 15)
216 return result;
217
218 // QDateTime::fromString is lenient and accepts +- signs in front
219 // of the year; but ASN.1 doesn't allow them.
220 if (!isAsciiDigit(c: mValue[0]))
221 return result;
222
223 // Timezone must be present, and UTC
224 if (mValue.back() != 'Z')
225 return result;
226
227 if (mType == UtcTimeType && mValue.size() == 13) {
228 // RFC 2459:
229 // Where YY is greater than or equal to 50, the year shall be
230 // interpreted as 19YY; and
231 //
232 // Where YY is less than 50, the year shall be interpreted as 20YY.
233 //
234 // so use 1950 as base year.
235 constexpr int rfc2459CenturyStart = 1950;
236 const QLatin1StringView inputView(mValue);
237 QDate date = QDate::fromString(string: inputView.first(n: 6), format: u"yyMMdd", baseYear: rfc2459CenturyStart);
238 if (!date.isValid())
239 return result;
240
241 Q_ASSERT(date.year() >= rfc2459CenturyStart);
242 Q_ASSERT(date.year() < 100 + rfc2459CenturyStart);
243
244 QTime time = QTime::fromString(string: inputView.sliced(pos: 6, n: 6), format: u"HHmmss");
245 if (!time.isValid())
246 return result;
247 result = QDateTime(date, time, QTimeZone::UTC);
248 } else if (mType == GeneralizedTimeType && mValue.size() == 15) {
249 result = QDateTime::fromString(string: QString::fromLatin1(ba: mValue), format: u"yyyyMMddHHmmsst");
250 }
251
252 return result;
253}
254
255QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const
256{
257 QMultiMap<QByteArray, QString> info;
258 QAsn1Element elem;
259 QDataStream issuerStream(mValue);
260 while (elem.read(stream&: issuerStream) && elem.mType == QAsn1Element::SetType) {
261 QAsn1Element issuerElem;
262 QDataStream setStream(elem.mValue);
263 if (issuerElem.read(stream&: setStream) && issuerElem.mType == QAsn1Element::SequenceType) {
264 const auto elems = issuerElem.toList();
265 if (elems.size() == 2) {
266 const QByteArray key = elems.front().toObjectName();
267 if (!key.isEmpty())
268 info.insert(key, value: elems.back().toString());
269 }
270 }
271 }
272 return info;
273}
274
275qint64 QAsn1Element::toInteger(bool *ok) const
276{
277 if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) {
278 if (ok)
279 *ok = false;
280 return 0;
281 }
282
283 // NOTE: - negative numbers are not handled
284 // - greater sizes would overflow
285 if (mValue.at(i: 0) & 0x80 || mValue.size() > 8) {
286 if (ok)
287 *ok = false;
288 return 0;
289 }
290
291 qint64 value = mValue.at(i: 0) & 0x7f;
292 for (int i = 1; i < mValue.size(); ++i)
293 value = (value << 8) | quint8(mValue.at(i));
294
295 if (ok)
296 *ok = true;
297 return value;
298}
299
300QList<QAsn1Element> QAsn1Element::toList() const
301{
302 QList<QAsn1Element> items;
303 if (mType == SequenceType) {
304 QAsn1Element elem;
305 QDataStream stream(mValue);
306 while (elem.read(stream))
307 items << elem;
308 }
309 return items;
310}
311
312QByteArray QAsn1Element::toObjectId() const
313{
314 QByteArray key;
315 if (mType == ObjectIdentifierType && !mValue.isEmpty()) {
316 quint8 b = mValue.at(i: 0);
317 key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40);
318 unsigned int val = 0;
319 for (int i = 1; i < mValue.size(); ++i) {
320 b = mValue.at(i);
321 val = (val << 7) | (b & 0x7f);
322 if (!(b & 0x80)) {
323 key += '.' + QByteArray::number(val);
324 val = 0;
325 }
326 }
327 }
328 return key;
329}
330
331QByteArray QAsn1Element::toObjectName() const
332{
333 QByteArray key = toObjectId();
334 return oidNameMap->value(key, defaultValue: key);
335}
336
337QString QAsn1Element::toString() const
338{
339 // Detect embedded NULs and reject
340 if (qstrlen(str: mValue) < uint(mValue.size()))
341 return QString();
342
343 if (mType == PrintableStringType || mType == TeletexStringType
344 || mType == Rfc822NameType || mType == DnsNameType
345 || mType == UniformResourceIdentifierType)
346 return QString::fromLatin1(str: mValue, size: mValue.size());
347 if (mType == Utf8StringType)
348 return QString::fromUtf8(utf8: mValue, size: mValue.size());
349
350 return QString();
351}
352
353QT_END_NAMESPACE
354

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/plugins/tls/shared/qasn1element.cpp