Coverage Report

Created: 2026-07-25 07:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/serenity/Userland/Libraries/LibCrypto/ASN1/PEM.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/Base64.h>
8
#include <AK/GenericLexer.h>
9
#include <LibCrypto/ASN1/PEM.h>
10
11
namespace Crypto {
12
13
ByteBuffer decode_pem(ReadonlyBytes data)
14
1.17k
{
15
1.17k
    GenericLexer lexer { data };
16
1.17k
    ByteBuffer decoded;
17
18
    // FIXME: Parse multiple.
19
1.17k
    enum {
20
1.17k
        PreStartData,
21
1.17k
        Started,
22
1.17k
        Ended,
23
1.17k
    } state { PreStartData };
24
1.15M
    while (!lexer.is_eof()) {
25
1.15M
        switch (state) {
26
1.14M
        case PreStartData:
27
1.14M
            if (lexer.consume_specific("-----BEGIN"sv))
28
768
                state = Started;
29
1.14M
            lexer.consume_line();
30
1.14M
            break;
31
6.09k
        case Started: {
32
6.09k
            if (lexer.consume_specific("-----END"sv)) {
33
28
                state = Ended;
34
28
                lexer.consume_line();
35
28
                break;
36
28
            }
37
6.06k
            auto b64decoded = decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right));
38
6.06k
            if (b64decoded.is_error()) {
39
436
                dbgln("Failed to decode PEM: {}", b64decoded.error().string_literal());
40
436
                return {};
41
436
            }
42
5.63k
            if (decoded.try_append(b64decoded.value().data(), b64decoded.value().size()).is_error()) {
43
0
                dbgln("Failed to decode PEM, likely OOM condition");
44
0
                return {};
45
0
            }
46
5.63k
            break;
47
5.63k
        }
48
5.63k
        case Ended:
49
20
            lexer.consume_all();
50
20
            break;
51
0
        default:
52
0
            VERIFY_NOT_REACHED();
53
1.15M
        }
54
1.15M
    }
55
56
737
    return decoded;
57
1.17k
}
58
59
ErrorOr<Vector<ByteBuffer>> decode_pems(ReadonlyBytes data)
60
0
{
61
0
    GenericLexer lexer { data };
62
0
    ByteBuffer decoded;
63
0
    Vector<ByteBuffer> pems;
64
65
0
    enum {
66
0
        Junk,
67
0
        Parsing,
68
0
    } state { Junk };
69
0
    while (!lexer.is_eof()) {
70
0
        switch (state) {
71
0
        case Junk:
72
0
            if (lexer.consume_specific("-----BEGIN"sv))
73
0
                state = Parsing;
74
0
            lexer.consume_line();
75
0
            break;
76
0
        case Parsing: {
77
0
            if (lexer.consume_specific("-----END"sv)) {
78
0
                state = Junk;
79
0
                lexer.consume_line();
80
0
                TRY(pems.try_append(decoded));
81
0
                decoded.clear();
82
0
                break;
83
0
            }
84
0
            auto b64decoded = TRY(decode_base64(lexer.consume_line().trim_whitespace(TrimMode::Right)));
85
0
            TRY(decoded.try_append(b64decoded.data(), b64decoded.size()));
86
0
            break;
87
0
        }
88
0
        default:
89
0
            VERIFY_NOT_REACHED();
90
0
        }
91
0
    }
92
93
0
    return pems;
94
0
}
95
96
ErrorOr<ByteBuffer> encode_pem(ReadonlyBytes data, PEMType type)
97
0
{
98
0
    ByteBuffer encoded;
99
0
    StringView block_start;
100
0
    StringView block_end;
101
102
0
    switch (type) {
103
0
    case Certificate:
104
0
        block_start = "-----BEGIN CERTIFICATE-----\n"sv;
105
0
        block_end = "-----END CERTIFICATE-----\n"sv;
106
0
        break;
107
0
    case PrivateKey:
108
0
        block_start = "-----BEGIN PRIVATE KEY-----\n"sv;
109
0
        block_end = "-----END PRIVATE KEY-----\n"sv;
110
0
        break;
111
0
    default:
112
0
        VERIFY_NOT_REACHED();
113
0
    }
114
115
0
    auto b64encoded = TRY(encode_base64(data));
116
117
0
    TRY(encoded.try_append(block_start.bytes()));
118
119
0
    size_t to_read = 64;
120
0
    for (size_t i = 0; i < b64encoded.bytes().size(); i += to_read) {
121
0
        if (i + to_read > b64encoded.bytes().size())
122
0
            to_read = b64encoded.bytes().size() - i;
123
0
        TRY(encoded.try_append(b64encoded.bytes().slice(i, to_read)));
124
0
        TRY(encoded.try_append("\n"sv.bytes()));
125
0
    }
126
127
0
    TRY(encoded.try_append(block_end.bytes()));
128
129
0
    return encoded;
130
0
}
131
132
}