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