Coverage Report

Created: 2026-08-17 07:50

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