1/****************************************************************************
2**
3** Copyright (C) 2014 Jeremy Lainé <jeremy.laine@m4x.org>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40
41#include "qasn1element_p.h"
42
43#include <QtCore/qdatastream.h>
44#include <QtCore/qdatetime.h>
45#include <QtCore/qvector.h>
46#include <QDebug>
47
48#include <limits>
49#include <locale>
50
51QT_BEGIN_NAMESPACE
52
53typedef QMap<QByteArray, QByteArray> OidNameMap;
54static OidNameMap createOidMap()
55{
56 OidNameMap oids;
57 // used by unit tests
58 oids.insert(pos: oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
59 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
60 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
61 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
62 oids.insert(pos: oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
63 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
64 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
65 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
66 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
67 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
68 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
69 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
70 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
71 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description"));
72 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode"));
73 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN"));
74 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN"));
75 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name"));
76 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN"));
77 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials"));
78 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier"));
79 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber"));
80 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C"));
81 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L"));
82 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST"));
83 oids.insert(pos: oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street"));
84 return oids;
85}
86Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap()))
87
88static bool stringToNonNegativeInt(const QByteArray &asnString, int *val)
89{
90 // Helper function for toDateTime(), which handles chunking of the original
91 // string into smaller sub-components, so we expect the whole 'asnString' to
92 // be a valid non-negative number.
93 Q_ASSERT(val);
94
95 // We want the C locale, as used by QByteArray; however, no leading sign is
96 // allowed (which QByteArray would accept), so we have to check the data:
97 const std::locale localeC;
98 for (char v : asnString) {
99 if (!std::isdigit(c: v, loc: localeC))
100 return false;
101 }
102
103 bool ok = false;
104 *val = asnString.toInt(ok: &ok);
105 Q_ASSERT(ok && *val >= 0);
106 return true;
107}
108
109QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value)
110 : mType(type)
111 , mValue(value)
112{
113}
114
115bool QAsn1Element::read(QDataStream &stream)
116{
117 // type
118 quint8 tmpType;
119 stream >> tmpType;
120 if (!tmpType)
121 return false;
122
123 // length
124 quint64 length = 0;
125 quint8 first;
126 stream >> first;
127 if (first & 0x80) {
128 // long form
129 const quint8 bytes = (first & 0x7f);
130 if (bytes > 7)
131 return false;
132
133 quint8 b;
134 for (int i = 0; i < bytes; i++) {
135 stream >> b;
136 length = (length << 8) | b;
137 }
138 } else {
139 // short form
140 length = (first & 0x7f);
141 }
142
143 if (length > quint64(std::numeric_limits<int>::max()))
144 return false;
145
146 // read value in blocks to avoid being fooled by incorrect length
147 const int BUFFERSIZE = 4 * 1024;
148 QByteArray tmpValue;
149 int remainingLength = length;
150 while (remainingLength) {
151 char readBuffer[BUFFERSIZE];
152 const int bytesToRead = qMin(a: remainingLength, b: BUFFERSIZE);
153 const int count = stream.readRawData(readBuffer, len: bytesToRead);
154 if (count != int(bytesToRead))
155 return false;
156 tmpValue.append(s: readBuffer, len: bytesToRead);
157 remainingLength -= bytesToRead;
158 }
159
160 mType = tmpType;
161 mValue.swap(other&: tmpValue);
162 return true;
163}
164
165bool QAsn1Element::read(const QByteArray &data)
166{
167 QDataStream stream(data);
168 return read(stream);
169}
170
171void QAsn1Element::write(QDataStream &stream) const
172{
173 // type
174 stream << mType;
175
176 // length
177 qint64 length = mValue.size();
178 if (length >= 128) {
179 // long form
180 quint8 encodedLength = 0x80;
181 QByteArray ba;
182 while (length) {
183 ba.prepend(c: quint8((length & 0xff)));
184 length >>= 8;
185 encodedLength += 1;
186 }
187 stream << encodedLength;
188 stream.writeRawData(ba.data(), len: ba.size());
189 } else {
190 // short form
191 stream << quint8(length);
192 }
193
194 // value
195 stream.writeRawData(mValue.data(), len: mValue.size());
196}
197
198QAsn1Element QAsn1Element::fromBool(bool val)
199{
200 return QAsn1Element(QAsn1Element::BooleanType,
201 QByteArray(1, val ? 0xff : 0x00));
202}
203
204QAsn1Element QAsn1Element::fromInteger(unsigned int val)
205{
206 QAsn1Element elem(QAsn1Element::IntegerType);
207 while (val > 127) {
208 elem.mValue.prepend(c: val & 0xff);
209 val >>= 8;
210 }
211 elem.mValue.prepend(c: val & 0x7f);
212 return elem;
213}
214
215QAsn1Element QAsn1Element::fromVector(const QVector<QAsn1Element> &items)
216{
217 QAsn1Element seq;
218 seq.mType = SequenceType;
219 QDataStream stream(&seq.mValue, QIODevice::WriteOnly);
220 for (QVector<QAsn1Element>::const_iterator it = items.cbegin(), end = items.cend(); it != end; ++it)
221 it->write(stream);
222 return seq;
223}
224
225QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id)
226{
227 QAsn1Element elem;
228 elem.mType = ObjectIdentifierType;
229 const QList<QByteArray> bits = id.split(sep: '.');
230 Q_ASSERT(bits.size() > 2);
231 elem.mValue += quint8((bits[0].toUInt() * 40 + bits[1].toUInt()));
232 for (int i = 2; i < bits.size(); ++i) {
233 char buffer[std::numeric_limits<unsigned int>::digits / 7 + 2];
234 char *pBuffer = buffer + sizeof(buffer);
235 *--pBuffer = '\0';
236 unsigned int node = bits[i].toUInt();
237 *--pBuffer = quint8((node & 0x7f));
238 node >>= 7;
239 while (node) {
240 *--pBuffer = quint8(((node & 0x7f) | 0x80));
241 node >>= 7;
242 }
243 elem.mValue += pBuffer;
244 }
245 return elem;
246}
247
248bool QAsn1Element::toBool(bool *ok) const
249{
250 if (*this == fromBool(val: true)) {
251 if (ok)
252 *ok = true;
253 return true;
254 } else if (*this == fromBool(val: false)) {
255 if (ok)
256 *ok = true;
257 return false;
258 } else {
259 if (ok)
260 *ok = false;
261 return false;
262 }
263}
264
265QDateTime QAsn1Element::toDateTime() const
266{
267 if (mValue.endsWith(c: 'Z')) {
268 if (mType == UtcTimeType && mValue.size() == 13) {
269 int year = 0;
270 if (!stringToNonNegativeInt(asnString: mValue.mid(index: 0, len: 2), val: &year))
271 return QDateTime();
272 // RFC 2459: YY represents a year in the range [1950, 2049]
273 return QDateTime(QDate(year < 50 ? 2000 + year : 1900 + year,
274 mValue.mid(index: 2, len: 2).toInt(),
275 mValue.mid(index: 4, len: 2).toInt()),
276 QTime(mValue.mid(index: 6, len: 2).toInt(),
277 mValue.mid(index: 8, len: 2).toInt(),
278 mValue.mid(index: 10, len: 2).toInt()),
279 Qt::UTC);
280 } else if (mType == GeneralizedTimeType && mValue.size() == 15) {
281 return QDateTime(QDate(mValue.mid(index: 0, len: 4).toInt(),
282 mValue.mid(index: 4, len: 2).toInt(),
283 mValue.mid(index: 6, len: 2).toInt()),
284 QTime(mValue.mid(index: 8, len: 2).toInt(),
285 mValue.mid(index: 10, len: 2).toInt(),
286 mValue.mid(index: 12, len: 2).toInt()),
287 Qt::UTC);
288 }
289 }
290 return QDateTime();
291}
292
293QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const
294{
295 QMultiMap<QByteArray, QString> info;
296 QAsn1Element elem;
297 QDataStream issuerStream(mValue);
298 while (elem.read(stream&: issuerStream) && elem.mType == QAsn1Element::SetType) {
299 QAsn1Element issuerElem;
300 QDataStream setStream(elem.mValue);
301 if (issuerElem.read(stream&: setStream) && issuerElem.mType == QAsn1Element::SequenceType) {
302 QVector<QAsn1Element> elems = issuerElem.toVector();
303 if (elems.size() == 2) {
304 const QByteArray key = elems.front().toObjectName();
305 if (!key.isEmpty())
306 info.insert(akey: key, avalue: elems.back().toString());
307 }
308 }
309 }
310 return info;
311}
312
313qint64 QAsn1Element::toInteger(bool *ok) const
314{
315 if (mType != QAsn1Element::IntegerType || mValue.isEmpty()) {
316 if (ok)
317 *ok = false;
318 return 0;
319 }
320
321 // NOTE: - negative numbers are not handled
322 // - greater sizes would overflow
323 if (mValue.at(i: 0) & 0x80 || mValue.size() > 8) {
324 if (ok)
325 *ok = false;
326 return 0;
327 }
328
329 qint64 value = mValue.at(i: 0) & 0x7f;
330 for (int i = 1; i < mValue.size(); ++i)
331 value = (value << 8) | quint8(mValue.at(i));
332
333 if (ok)
334 *ok = true;
335 return value;
336}
337
338QVector<QAsn1Element> QAsn1Element::toVector() const
339{
340 QVector<QAsn1Element> items;
341 if (mType == SequenceType) {
342 QAsn1Element elem;
343 QDataStream stream(mValue);
344 while (elem.read(stream))
345 items << elem;
346 }
347 return items;
348}
349
350QByteArray QAsn1Element::toObjectId() const
351{
352 QByteArray key;
353 if (mType == ObjectIdentifierType && !mValue.isEmpty()) {
354 quint8 b = mValue.at(i: 0);
355 key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40);
356 unsigned int val = 0;
357 for (int i = 1; i < mValue.size(); ++i) {
358 b = mValue.at(i);
359 val = (val << 7) | (b & 0x7f);
360 if (!(b & 0x80)) {
361 key += '.' + QByteArray::number(val);
362 val = 0;
363 }
364 }
365 }
366 return key;
367}
368
369QByteArray QAsn1Element::toObjectName() const
370{
371 QByteArray key = toObjectId();
372 return oidNameMap->value(akey: key, adefaultValue: key);
373}
374
375QString QAsn1Element::toString() const
376{
377 // Detect embedded NULs and reject
378 if (qstrlen(str: mValue) < uint(mValue.size()))
379 return QString();
380
381 if (mType == PrintableStringType || mType == TeletexStringType
382 || mType == Rfc822NameType || mType == DnsNameType
383 || mType == UniformResourceIdentifierType)
384 return QString::fromLatin1(str: mValue, size: mValue.size());
385 if (mType == Utf8StringType)
386 return QString::fromUtf8(str: mValue, size: mValue.size());
387
388 return QString();
389}
390
391QT_END_NAMESPACE
392

source code of qtbase/src/network/ssl/qasn1element.cpp