Coverage Report

Created: 2026-02-16 07:47

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCrypto/ASN1/ASN1.cpp
Line
Count
Source
1
/*
2
 * Copyright (c) 2021, the SerenityOS developers.
3
 *
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 */
6
7
#include <AK/GenericLexer.h>
8
#include <LibCrypto/ASN1/ASN1.h>
9
10
namespace Crypto::ASN1 {
11
12
ByteString kind_name(Kind kind)
13
672
{
14
672
    switch (kind) {
15
171
    case Kind::Eol:
16
171
        return "EndOfList";
17
56
    case Kind::Boolean:
18
56
        return "Boolean";
19
25
    case Kind::Integer:
20
25
        return "Integer";
21
20
    case Kind::BitString:
22
20
        return "BitString";
23
6
    case Kind::OctetString:
24
6
        return "OctetString";
25
10
    case Kind::Null:
26
10
        return "Null";
27
24
    case Kind::ObjectIdentifier:
28
24
        return "ObjectIdentifier";
29
9
    case Kind::ObjectDescriptor:
30
9
        return "ObjectDescriptor";
31
12
    case Kind::External:
32
12
        return "External";
33
7
    case Kind::Real:
34
7
        return "Real";
35
9
    case Kind::Enumerated:
36
9
        return "Enumerated";
37
8
    case Kind::EmbeddedPdv:
38
8
        return "EmbeddedPdv";
39
6
    case Kind::Utf8String:
40
6
        return "Utf8String";
41
27
    case Kind::RelativeOid:
42
27
        return "RelativeOid";
43
15
    case Kind::Time:
44
15
        return "Time";
45
16
    case Kind::Reserved:
46
16
        return "Reserved";
47
11
    case Kind::Sequence:
48
11
        return "Sequence";
49
38
    case Kind::Set:
50
38
        return "Set";
51
13
    case Kind::NumericString:
52
13
        return "NumericString";
53
16
    case Kind::PrintableString:
54
16
        return "PrintableString";
55
12
    case Kind::T61String:
56
12
        return "T61String";
57
15
    case Kind::VideotexString:
58
15
        return "VideotexString";
59
12
    case Kind::IA5String:
60
12
        return "IA5String";
61
5
    case Kind::UTCTime:
62
5
        return "UTCTime";
63
7
    case Kind::GeneralizedTime:
64
7
        return "GeneralizedTime";
65
6
    case Kind::GraphicString:
66
6
        return "GraphicString";
67
12
    case Kind::VisibleString:
68
12
        return "VisibleString";
69
6
    case Kind::GeneralString:
70
6
        return "GeneralString";
71
5
    case Kind::UniversalString:
72
5
        return "UniversalString";
73
12
    case Kind::CharacterString:
74
12
        return "CharacterString";
75
10
    case Kind::BMPString:
76
10
        return "BMPString";
77
1
    case Kind::Date:
78
1
        return "Date";
79
1
    case Kind::TimeOfDay:
80
1
        return "TimeOfDay";
81
1
    case Kind::DateTime:
82
1
        return "DateTime";
83
1
    case Kind::Duration:
84
1
        return "Duration";
85
1
    case Kind::OidIri:
86
1
        return "OidIri";
87
1
    case Kind::RelativeOidIri:
88
1
        return "RelativeOidIri";
89
672
    }
90
91
65
    return "InvalidKind";
92
672
}
93
94
ByteString class_name(Class class_)
95
0
{
96
0
    switch (class_) {
97
0
    case Class::Application:
98
0
        return "Application";
99
0
    case Class::Context:
100
0
        return "Context";
101
0
    case Class::Private:
102
0
        return "Private";
103
0
    case Class::Universal:
104
0
        return "Universal";
105
0
    }
106
107
0
    return "InvalidClass";
108
0
}
109
110
ByteString type_name(Type type)
111
0
{
112
0
    switch (type) {
113
0
    case Type::Constructed:
114
0
        return "Constructed";
115
0
    case Type::Primitive:
116
0
        return "Primitive";
117
0
    }
118
119
0
    return "InvalidType";
120
0
}
121
122
Optional<UnixDateTime> parse_utc_time(StringView time)
123
2.20k
{
124
    // YYMMDDhhmm[ss]Z or YYMMDDhhmm[ss](+|-)hhmm
125
2.20k
    GenericLexer lexer(time);
126
2.20k
    auto year_in_century = lexer.consume(2).to_number<unsigned>();
127
2.20k
    auto month = lexer.consume(2).to_number<unsigned>();
128
2.20k
    auto day = lexer.consume(2).to_number<unsigned>();
129
2.20k
    auto hour = lexer.consume(2).to_number<unsigned>();
130
2.20k
    auto minute = lexer.consume(2).to_number<unsigned>();
131
2.20k
    Optional<unsigned> seconds, offset_hours, offset_minutes;
132
2.20k
    [[maybe_unused]] bool negative_offset = false;
133
134
2.20k
    if (lexer.next_is(is_any_of("0123456789"sv))) {
135
2.10k
        seconds = lexer.consume(2).to_number<unsigned>();
136
2.10k
        if (!seconds.has_value()) {
137
3
            return {};
138
3
        }
139
2.10k
    }
140
141
2.19k
    if (lexer.next_is('Z')) {
142
2.14k
        lexer.consume();
143
2.14k
    } else if (lexer.next_is(is_any_of("+-"sv))) {
144
10
        negative_offset = lexer.consume() == '-';
145
10
        offset_hours = lexer.consume(2).to_number<unsigned>();
146
10
        offset_minutes = lexer.consume(2).to_number<unsigned>();
147
10
        if (!offset_hours.has_value() || !offset_minutes.has_value()) {
148
5
            return {};
149
5
        }
150
46
    } else {
151
46
        return {};
152
46
    }
153
154
2.14k
    if (!year_in_century.has_value() || !month.has_value() || !day.has_value() || !hour.has_value() || !minute.has_value()) {
155
6
        return {};
156
6
    }
157
158
    // RFC5280 section 4.1.2.5.1.
159
2.14k
    auto full_year = year_in_century.value();
160
2.14k
    full_year += (full_year < 50) ? 2000 : 1900;
161
2.14k
    auto full_seconds = seconds.value_or(0);
162
163
    // FIXME: Handle offsets!
164
2.14k
    if (offset_hours.has_value() || offset_minutes.has_value())
165
4
        dbgln("FIXME: Implement UTCTime with offset!");
166
167
2.14k
    return UnixDateTime::from_unix_time_parts(full_year, month.value(), day.value(), hour.value(), minute.value(), full_seconds, 0);
168
2.14k
}
169
170
Optional<UnixDateTime> parse_generalized_time(StringView time)
171
505
{
172
    // YYYYMMDDhh[mm[ss[.fff]]] or YYYYMMDDhh[mm[ss[.fff]]]Z or YYYYMMDDhh[mm[ss[.fff]]](+|-)hhmm
173
505
    GenericLexer lexer(time);
174
505
    auto year = lexer.consume(4).to_number<unsigned>();
175
505
    auto month = lexer.consume(2).to_number<unsigned>();
176
505
    auto day = lexer.consume(2).to_number<unsigned>();
177
505
    auto hour = lexer.consume(2).to_number<unsigned>();
178
505
    Optional<unsigned> minute, seconds, milliseconds, offset_hours, offset_minutes;
179
505
    [[maybe_unused]] bool negative_offset = false;
180
181
505
    if (!lexer.is_eof()) {
182
165
        if (lexer.consume_specific('Z'))
183
26
            goto done_parsing;
184
185
139
        if (!lexer.next_is(is_any_of("+-"sv))) {
186
130
            minute = lexer.consume(2).to_number<unsigned>();
187
130
            if (!minute.has_value()) {
188
10
                return {};
189
10
            }
190
120
            if (lexer.is_eof() || lexer.consume_specific('Z'))
191
62
                goto done_parsing;
192
120
        }
193
194
67
        if (!lexer.next_is(is_any_of("+-"sv))) {
195
54
            seconds = lexer.consume(2).to_number<unsigned>();
196
54
            if (!seconds.has_value()) {
197
5
                return {};
198
5
            }
199
49
            if (lexer.is_eof() || lexer.consume_specific('Z'))
200
9
                goto done_parsing;
201
49
        }
202
203
53
        if (lexer.consume_specific('.')) {
204
21
            milliseconds = lexer.consume(3).to_number<unsigned>();
205
21
            if (!milliseconds.has_value()) {
206
1
                return {};
207
1
            }
208
20
            if (lexer.is_eof() || lexer.consume_specific('Z'))
209
7
                goto done_parsing;
210
20
        }
211
212
45
        if (lexer.next_is(is_any_of("+-"sv))) {
213
18
            negative_offset = lexer.consume() == '-';
214
18
            offset_hours = lexer.consume(2).to_number<unsigned>();
215
18
            offset_minutes = lexer.consume(2).to_number<unsigned>();
216
18
            if (!offset_hours.has_value() || !offset_minutes.has_value()) {
217
8
                return {};
218
8
            }
219
18
        }
220
221
        // Any character would be garbage.
222
37
        if (!lexer.is_eof()) {
223
28
            return {};
224
28
        }
225
37
    }
226
227
453
done_parsing:;
228
229
453
    if (!year.has_value() || !month.has_value() || !day.has_value() || !hour.has_value()) {
230
18
        return {};
231
18
    }
232
233
    // FIXME: Handle offsets!
234
435
    if (offset_hours.has_value() || offset_minutes.has_value())
235
8
        dbgln("FIXME: Implement GeneralizedTime with offset!");
236
237
435
    return UnixDateTime::from_unix_time_parts(year.value(), month.value(), day.value(), hour.value(), minute.value_or(0), seconds.value_or(0), milliseconds.value_or(0));
238
453
}
239
}