/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 | | } |