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
51 QT_BEGIN_NAMESPACE
52
53 typedef QMap<QByteArray, QByteArray> OidNameMap;
createOidMap()54 static OidNameMap createOidMap()
55 {
56 OidNameMap oids;
57 // used by unit tests
58 oids.insert(oids.cend(), QByteArrayLiteral("0.9.2342.19200300.100.1.5"), QByteArrayLiteral("favouriteDrink"));
59 oids.insert(oids.cend(), QByteArrayLiteral("1.2.840.113549.1.9.1"), QByteArrayLiteral("emailAddress"));
60 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.1.1"), QByteArrayLiteral("authorityInfoAccess"));
61 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.1"), QByteArrayLiteral("OCSP"));
62 oids.insert(oids.cend(), QByteArrayLiteral("1.3.6.1.5.5.7.48.2"), QByteArrayLiteral("caIssuers"));
63 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.14"), QByteArrayLiteral("subjectKeyIdentifier"));
64 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.15"), QByteArrayLiteral("keyUsage"));
65 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.17"), QByteArrayLiteral("subjectAltName"));
66 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.19"), QByteArrayLiteral("basicConstraints"));
67 oids.insert(oids.cend(), QByteArrayLiteral("2.5.29.35"), QByteArrayLiteral("authorityKeyIdentifier"));
68 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.10"), QByteArrayLiteral("O"));
69 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.11"), QByteArrayLiteral("OU"));
70 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.12"), QByteArrayLiteral("title"));
71 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.13"), QByteArrayLiteral("description"));
72 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.17"), QByteArrayLiteral("postalCode"));
73 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.3"), QByteArrayLiteral("CN"));
74 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.4"), QByteArrayLiteral("SN"));
75 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.41"), QByteArrayLiteral("name"));
76 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.42"), QByteArrayLiteral("GN"));
77 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.43"), QByteArrayLiteral("initials"));
78 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.46"), QByteArrayLiteral("dnQualifier"));
79 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.5"), QByteArrayLiteral("serialNumber"));
80 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.6"), QByteArrayLiteral("C"));
81 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.7"), QByteArrayLiteral("L"));
82 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.8"), QByteArrayLiteral("ST"));
83 oids.insert(oids.cend(), QByteArrayLiteral("2.5.4.9"), QByteArrayLiteral("street"));
84 return oids;
85 }
86 Q_GLOBAL_STATIC_WITH_ARGS(OidNameMap, oidNameMap, (createOidMap()))
87
stringToNonNegativeInt(const QByteArray & asnString,int * val)88 static 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(v, localeC))
100 return false;
101 }
102
103 bool ok = false;
104 *val = asnString.toInt(&ok);
105 Q_ASSERT(ok && *val >= 0);
106 return true;
107 }
108
QAsn1Element(quint8 type,const QByteArray & value)109 QAsn1Element::QAsn1Element(quint8 type, const QByteArray &value)
110 : mType(type)
111 , mValue(value)
112 {
113 }
114
read(QDataStream & stream)115 bool 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(remainingLength, BUFFERSIZE);
153 const int count = stream.readRawData(readBuffer, bytesToRead);
154 if (count != int(bytesToRead))
155 return false;
156 tmpValue.append(readBuffer, bytesToRead);
157 remainingLength -= bytesToRead;
158 }
159
160 mType = tmpType;
161 mValue.swap(tmpValue);
162 return true;
163 }
164
read(const QByteArray & data)165 bool QAsn1Element::read(const QByteArray &data)
166 {
167 QDataStream stream(data);
168 return read(stream);
169 }
170
write(QDataStream & stream) const171 void 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(quint8((length & 0xff)));
184 length >>= 8;
185 encodedLength += 1;
186 }
187 stream << encodedLength;
188 stream.writeRawData(ba.data(), ba.size());
189 } else {
190 // short form
191 stream << quint8(length);
192 }
193
194 // value
195 stream.writeRawData(mValue.data(), mValue.size());
196 }
197
fromBool(bool val)198 QAsn1Element QAsn1Element::fromBool(bool val)
199 {
200 return QAsn1Element(QAsn1Element::BooleanType,
201 QByteArray(1, val ? 0xff : 0x00));
202 }
203
fromInteger(unsigned int val)204 QAsn1Element QAsn1Element::fromInteger(unsigned int val)
205 {
206 QAsn1Element elem(QAsn1Element::IntegerType);
207 while (val > 127) {
208 elem.mValue.prepend(val & 0xff);
209 val >>= 8;
210 }
211 elem.mValue.prepend(val & 0x7f);
212 return elem;
213 }
214
fromVector(const QVector<QAsn1Element> & items)215 QAsn1Element 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
fromObjectId(const QByteArray & id)225 QAsn1Element QAsn1Element::fromObjectId(const QByteArray &id)
226 {
227 QAsn1Element elem;
228 elem.mType = ObjectIdentifierType;
229 const QList<QByteArray> bits = id.split('.');
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
toBool(bool * ok) const248 bool QAsn1Element::toBool(bool *ok) const
249 {
250 if (*this == fromBool(true)) {
251 if (ok)
252 *ok = true;
253 return true;
254 } else if (*this == fromBool(false)) {
255 if (ok)
256 *ok = true;
257 return false;
258 } else {
259 if (ok)
260 *ok = false;
261 return false;
262 }
263 }
264
toDateTime() const265 QDateTime QAsn1Element::toDateTime() const
266 {
267 if (mValue.endsWith('Z')) {
268 if (mType == UtcTimeType && mValue.size() == 13) {
269 int year = 0;
270 if (!stringToNonNegativeInt(mValue.mid(0, 2), &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(2, 2).toInt(),
275 mValue.mid(4, 2).toInt()),
276 QTime(mValue.mid(6, 2).toInt(),
277 mValue.mid(8, 2).toInt(),
278 mValue.mid(10, 2).toInt()),
279 Qt::UTC);
280 } else if (mType == GeneralizedTimeType && mValue.size() == 15) {
281 return QDateTime(QDate(mValue.mid(0, 4).toInt(),
282 mValue.mid(4, 2).toInt(),
283 mValue.mid(6, 2).toInt()),
284 QTime(mValue.mid(8, 2).toInt(),
285 mValue.mid(10, 2).toInt(),
286 mValue.mid(12, 2).toInt()),
287 Qt::UTC);
288 }
289 }
290 return QDateTime();
291 }
292
toInfo() const293 QMultiMap<QByteArray, QString> QAsn1Element::toInfo() const
294 {
295 QMultiMap<QByteArray, QString> info;
296 QAsn1Element elem;
297 QDataStream issuerStream(mValue);
298 while (elem.read(issuerStream) && elem.mType == QAsn1Element::SetType) {
299 QAsn1Element issuerElem;
300 QDataStream setStream(elem.mValue);
301 if (issuerElem.read(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(key, elems.back().toString());
307 }
308 }
309 }
310 return info;
311 }
312
toInteger(bool * ok) const313 qint64 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 if (mValue.at(0) & 0x80) {
323 if (ok)
324 *ok = false;
325 return 0;
326 }
327
328 qint64 value = mValue.at(0) & 0x7f;
329 for (int i = 1; i < mValue.size(); ++i)
330 value = (value << 8) | quint8(mValue.at(i));
331
332 if (ok)
333 *ok = true;
334 return value;
335 }
336
toVector() const337 QVector<QAsn1Element> QAsn1Element::toVector() const
338 {
339 QVector<QAsn1Element> items;
340 if (mType == SequenceType) {
341 QAsn1Element elem;
342 QDataStream stream(mValue);
343 while (elem.read(stream))
344 items << elem;
345 }
346 return items;
347 }
348
toObjectId() const349 QByteArray QAsn1Element::toObjectId() const
350 {
351 QByteArray key;
352 if (mType == ObjectIdentifierType && !mValue.isEmpty()) {
353 quint8 b = mValue.at(0);
354 key += QByteArray::number(b / 40) + '.' + QByteArray::number (b % 40);
355 unsigned int val = 0;
356 for (int i = 1; i < mValue.size(); ++i) {
357 b = mValue.at(i);
358 val = (val << 7) | (b & 0x7f);
359 if (!(b & 0x80)) {
360 key += '.' + QByteArray::number(val);
361 val = 0;
362 }
363 }
364 }
365 return key;
366 }
367
toObjectName() const368 QByteArray QAsn1Element::toObjectName() const
369 {
370 QByteArray key = toObjectId();
371 return oidNameMap->value(key, key);
372 }
373
toString() const374 QString QAsn1Element::toString() const
375 {
376 // Detect embedded NULs and reject
377 if (qstrlen(mValue) < uint(mValue.size()))
378 return QString();
379
380 if (mType == PrintableStringType || mType == TeletexStringType
381 || mType == Rfc822NameType || mType == DnsNameType
382 || mType == UniformResourceIdentifierType)
383 return QString::fromLatin1(mValue, mValue.size());
384 if (mType == Utf8StringType)
385 return QString::fromUtf8(mValue, mValue.size());
386
387 return QString();
388 }
389
390 QT_END_NAMESPACE
391