Coverage Report

Created: 2026-09-14 07:15

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
751
{
14
751
    switch (kind) {
15
177
    case Kind::Eol:
16
177
        return "EndOfList";
17
49
    case Kind::Boolean:
18
49
        return "Boolean";
19
20
    case Kind::Integer:
20
20
        return "Integer";
21
15
    case Kind::BitString:
22
15
        return "BitString";
23
15
    case Kind::OctetString:
24
15
        return "OctetString";
25
15
    case Kind::Null:
26
15
        return "Null";
27
24
    case Kind::ObjectIdentifier:
28
24
        return "ObjectIdentifier";
29
8
    case Kind::ObjectDescriptor:
30
8
        return "ObjectDescriptor";
31
23
    case Kind::External:
32
23
        return "External";
33
6
    case Kind::Real:
34
6
        return "Real";
35
18
    case Kind::Enumerated:
36
18
        return "Enumerated";
37
21
    case Kind::EmbeddedPdv:
38
21
        return "EmbeddedPdv";
39
16
    case Kind::Utf8String:
40
16
        return "Utf8String";
41
24
    case Kind::RelativeOid:
42
24
        return "RelativeOid";
43
18
    case Kind::Time:
44
18
        return "Time";
45
17
    case Kind::Reserved:
46
17
        return "Reserved";
47
15
    case Kind::Sequence:
48
15
        return "Sequence";
49
34
    case Kind::Set:
50
34
        return "Set";
51
12
    case Kind::NumericString:
52
12
        return "NumericString";
53
12
    case Kind::PrintableString:
54
12
        return "PrintableString";
55
14
    case Kind::T61String:
56
14
        return "T61String";
57
12
    case Kind::VideotexString:
58
12
        return "VideotexString";
59
8
    case Kind::IA5String:
60
8
        return "IA5String";
61
23
    case Kind::UTCTime:
62
23
        return "UTCTime";
63
9
    case Kind::GeneralizedTime:
64
9
        return "GeneralizedTime";
65
5
    case Kind::GraphicString:
66
5
        return "GraphicString";
67
3
    case Kind::VisibleString:
68
3
        return "VisibleString";
69
6
    case Kind::GeneralString:
70
6
        return "GeneralString";
71
26
    case Kind::UniversalString:
72
26
        return "UniversalString";
73
10
    case Kind::CharacterString:
74
10
        return "CharacterString";
75
15
    case Kind::BMPString:
76
15
        return "BMPString";
77
2
    case Kind::Date:
78
2
        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
2
    case Kind::RelativeOidIri:
88
2
        return "RelativeOidIri";
89
751
    }
90
91
73
    return "InvalidKind";
92
751
}
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.05k
{
124
    // YYMMDDhhmm[ss]Z or YYMMDDhhmm[ss](+|-)hhmm
125
2.05k
    GenericLexer lexer(time);
126
2.05k
    auto year_in_century = lexer.consume(2).to_number<unsigned>();
127
2.05k
    auto month = lexer.consume(2).to_number<unsigned>();
128
2.05k
    auto day = lexer.consume(2).to_number<unsigned>();
129
2.05k
    auto hour = lexer.consume(2).to_number<unsigned>();
130
2.05k
    auto minute = lexer.consume(2).to_number<unsigned>();
131
2.05k
    Optional<unsigned> seconds, offset_hours, offset_minutes;
132
2.05k
    [[maybe_unused]] bool negative_offset = false;
133
134
2.05k
    if (lexer.next_is(is_any_of("0123456789"sv))) {
135
1.77k
        seconds = lexer.consume(2).to_number<unsigned>();
136
1.77k
        if (!seconds.has_value()) {
137
2
            return {};
138
2
        }
139
1.77k
    }
140
141
2.05k
    if (lexer.next_is('Z')) {
142
1.99k
        lexer.consume();
143
1.99k
    } 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
49
    } else {
151
49
        return {};
152
49
    }
153
154
2.00k
    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
1.99k
    auto full_year = year_in_century.value();
160
1.99k
    full_year += (full_year < 50) ? 2000 : 1900;
161
1.99k
    auto full_seconds = seconds.value_or(0);
162
163
    // FIXME: Handle offsets!
164
1.99k
    if (offset_hours.has_value() || offset_minutes.has_value())
165
4
        dbgln("FIXME: Implement UTCTime with offset!");
166
167
1.99k
    return UnixDateTime::from_unix_time_parts(full_year, month.value(), day.value(), hour.value(), minute.value(), full_seconds, 0);
168
2.00k
}
169
170
Optional<UnixDateTime> parse_generalized_time(StringView time)
171
493
{
172
    // YYYYMMDDhh[mm[ss[.fff]]] or YYYYMMDDhh[mm[ss[.fff]]]Z or YYYYMMDDhh[mm[ss[.fff]]](+|-)hhmm
173
493
    GenericLexer lexer(time);
174
493
    auto year = lexer.consume(4).to_number<unsigned>();
175
493
    auto month = lexer.consume(2).to_number<unsigned>();
176
493
    auto day = lexer.consume(2).to_number<unsigned>();
177
493
    auto hour = lexer.consume(2).to_number<unsigned>();
178
493
    Optional<unsigned> minute, seconds, milliseconds, offset_hours, offset_minutes;
179
493
    [[maybe_unused]] bool negative_offset = false;
180
181
493
    if (!lexer.is_eof()) {
182
125
        if (lexer.consume_specific('Z'))
183
17
            goto done_parsing;
184
185
108
        if (!lexer.next_is(is_any_of("+-"sv))) {
186
100
            minute = lexer.consume(2).to_number<unsigned>();
187
100
            if (!minute.has_value()) {
188
11
                return {};
189
11
            }
190
89
            if (lexer.is_eof() || lexer.consume_specific('Z'))
191
33
                goto done_parsing;
192
89
        }
193
194
64
        if (!lexer.next_is(is_any_of("+-"sv))) {
195
52
            seconds = lexer.consume(2).to_number<unsigned>();
196
52
            if (!seconds.has_value()) {
197
8
                return {};
198
8
            }
199
44
            if (lexer.is_eof() || lexer.consume_specific('Z'))
200
10
                goto done_parsing;
201
44
        }
202
203
46
        if (lexer.consume_specific('.')) {
204
20
            milliseconds = lexer.consume(3).to_number<unsigned>();
205
20
            if (!milliseconds.has_value()) {
206
1
                return {};
207
1
            }
208
19
            if (lexer.is_eof() || lexer.consume_specific('Z'))
209
7
                goto done_parsing;
210
19
        }
211
212
38
        if (lexer.next_is(is_any_of("+-"sv))) {
213
15
            negative_offset = lexer.consume() == '-';
214
15
            offset_hours = lexer.consume(2).to_number<unsigned>();
215
15
            offset_minutes = lexer.consume(2).to_number<unsigned>();
216
15
            if (!offset_hours.has_value() || !offset_minutes.has_value()) {
217
10
                return {};
218
10
            }
219
15
        }
220
221
        // Any character would be garbage.
222
28
        if (!lexer.is_eof()) {
223
23
            return {};
224
23
        }
225
28
    }
226
227
440
done_parsing:;
228
229
440
    if (!year.has_value() || !month.has_value() || !day.has_value() || !hour.has_value()) {
230
28
        return {};
231
28
    }
232
233
    // FIXME: Handle offsets!
234
412
    if (offset_hours.has_value() || offset_minutes.has_value())
235
4
        dbgln("FIXME: Implement GeneralizedTime with offset!");
236
237
412
    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
440
}
239
}