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