/src/botan/src/lib/pubkey/pem/pem.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * PEM Encoding/Decoding |
3 | | * (C) 1999-2007 Jack Lloyd |
4 | | * |
5 | | * Botan is released under the Simplified BSD License (see license.txt) |
6 | | */ |
7 | | |
8 | | #include <botan/pem.h> |
9 | | |
10 | | #include <botan/base64.h> |
11 | | #include <botan/data_src.h> |
12 | | #include <botan/exceptn.h> |
13 | | #include <botan/internal/fmt.h> |
14 | | |
15 | | namespace Botan::PEM_Code { |
16 | | |
17 | | namespace { |
18 | | |
19 | 0 | std::string linewrap(size_t width, std::string_view in) { |
20 | 0 | std::string out; |
21 | 0 | for(size_t i = 0; i != in.size(); ++i) { |
22 | 0 | if(i > 0 && i % width == 0) { |
23 | 0 | out.push_back('\n'); |
24 | 0 | } |
25 | 0 | out.push_back(in[i]); |
26 | 0 | } |
27 | 0 | if(!out.empty() && out[out.size() - 1] != '\n') { |
28 | 0 | out.push_back('\n'); |
29 | 0 | } |
30 | |
|
31 | 0 | return out; |
32 | 0 | } |
33 | | |
34 | | } // namespace |
35 | | |
36 | | /* |
37 | | * PEM encode BER/DER-encoded objects |
38 | | */ |
39 | 0 | std::string encode(const uint8_t der[], size_t length, std::string_view label, size_t width) { |
40 | 0 | const std::string PEM_HEADER = fmt("-----BEGIN {}-----\n", label); |
41 | 0 | const std::string PEM_TRAILER = fmt("-----END {}-----\n", label); |
42 | |
|
43 | 0 | return (PEM_HEADER + linewrap(width, base64_encode(der, length)) + PEM_TRAILER); |
44 | 0 | } |
45 | | |
46 | | /* |
47 | | * Decode PEM down to raw BER/DER |
48 | | */ |
49 | 0 | secure_vector<uint8_t> decode_check_label(DataSource& source, std::string_view label_want) { |
50 | 0 | std::string label_got; |
51 | 0 | secure_vector<uint8_t> ber = decode(source, label_got); |
52 | 0 | if(label_got != label_want) { |
53 | 0 | throw Decoding_Error(fmt("PEM: Label mismatch, wanted '{}' got '{}'", label_want, label_got)); |
54 | 0 | } |
55 | | |
56 | 0 | return ber; |
57 | 0 | } |
58 | | |
59 | | /* |
60 | | * Decode PEM down to raw BER/DER |
61 | | */ |
62 | 0 | secure_vector<uint8_t> decode(DataSource& source, std::string& label) { |
63 | 0 | const size_t RANDOM_CHAR_LIMIT = 8; |
64 | |
|
65 | 0 | label.clear(); |
66 | |
|
67 | 0 | const std::string PEM_HEADER1 = "-----BEGIN "; |
68 | 0 | const std::string PEM_HEADER2 = "-----"; |
69 | 0 | size_t position = 0; |
70 | |
|
71 | 0 | while(position != PEM_HEADER1.length()) { |
72 | 0 | uint8_t b; |
73 | 0 | if(!source.read_byte(b)) { |
74 | 0 | throw Decoding_Error("PEM: No PEM header found"); |
75 | 0 | } |
76 | 0 | if(static_cast<char>(b) == PEM_HEADER1[position]) { |
77 | 0 | ++position; |
78 | 0 | } else if(position >= RANDOM_CHAR_LIMIT) { |
79 | 0 | throw Decoding_Error("PEM: Malformed PEM header"); |
80 | 0 | } else { |
81 | 0 | position = 0; |
82 | 0 | } |
83 | 0 | } |
84 | 0 | position = 0; |
85 | 0 | while(position != PEM_HEADER2.length()) { |
86 | 0 | uint8_t b; |
87 | 0 | if(!source.read_byte(b)) { |
88 | 0 | throw Decoding_Error("PEM: No PEM header found"); |
89 | 0 | } |
90 | 0 | if(static_cast<char>(b) == PEM_HEADER2[position]) { |
91 | 0 | ++position; |
92 | 0 | } else if(position) { |
93 | 0 | throw Decoding_Error("PEM: Malformed PEM header"); |
94 | 0 | } |
95 | | |
96 | 0 | if(position == 0) { |
97 | 0 | label += static_cast<char>(b); |
98 | 0 | } |
99 | 0 | } |
100 | | |
101 | 0 | std::vector<char> b64; |
102 | |
|
103 | 0 | const std::string PEM_TRAILER = fmt("-----END {}-----", label); |
104 | 0 | position = 0; |
105 | 0 | while(position != PEM_TRAILER.length()) { |
106 | 0 | uint8_t b; |
107 | 0 | if(!source.read_byte(b)) { |
108 | 0 | throw Decoding_Error("PEM: No PEM trailer found"); |
109 | 0 | } |
110 | 0 | if(static_cast<char>(b) == PEM_TRAILER[position]) { |
111 | 0 | ++position; |
112 | 0 | } else if(position) { |
113 | 0 | throw Decoding_Error("PEM: Malformed PEM trailer"); |
114 | 0 | } |
115 | | |
116 | 0 | if(position == 0) { |
117 | 0 | b64.push_back(b); |
118 | 0 | } |
119 | 0 | } |
120 | | |
121 | 0 | return base64_decode(b64.data(), b64.size()); |
122 | 0 | } |
123 | | |
124 | 0 | secure_vector<uint8_t> decode_check_label(std::string_view pem, std::string_view label_want) { |
125 | 0 | DataSource_Memory src(pem); |
126 | 0 | return decode_check_label(src, label_want); |
127 | 0 | } |
128 | | |
129 | 0 | secure_vector<uint8_t> decode(std::string_view pem, std::string& label) { |
130 | 0 | DataSource_Memory src(pem); |
131 | 0 | return decode(src, label); |
132 | 0 | } |
133 | | |
134 | | /* |
135 | | * Search for a PEM signature |
136 | | */ |
137 | 0 | bool matches(DataSource& source, std::string_view extra, size_t search_range) { |
138 | 0 | const std::string PEM_HEADER = fmt("-----BEGIN {}", extra); |
139 | |
|
140 | 0 | secure_vector<uint8_t> search_buf(search_range); |
141 | 0 | const size_t got = source.peek(search_buf.data(), search_buf.size(), 0); |
142 | |
|
143 | 0 | if(got < PEM_HEADER.length()) { |
144 | 0 | return false; |
145 | 0 | } |
146 | | |
147 | 0 | size_t index = 0; |
148 | |
|
149 | 0 | for(size_t j = 0; j != got; ++j) { |
150 | 0 | if(static_cast<char>(search_buf[j]) == PEM_HEADER[index]) { |
151 | 0 | ++index; |
152 | 0 | } else { |
153 | 0 | index = 0; |
154 | 0 | } |
155 | |
|
156 | 0 | if(index == PEM_HEADER.size()) { |
157 | 0 | return true; |
158 | 0 | } |
159 | 0 | } |
160 | | |
161 | 0 | return false; |
162 | 0 | } |
163 | | |
164 | | } // namespace Botan::PEM_Code |