Coverage Report

Created: 2026-11-13 08:07

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