1 // Copyright 2010 The Chromium Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "pem.h"
16
17 #include <array>
18 #include <string>
19 #include <string_view>
20 #include <vector>
21
22 #include <gtest/gtest.h>
23
24 BSSL_NAMESPACE_BEGIN
25
TEST(PEMTokenizerTest,BasicParsing)26 TEST(PEMTokenizerTest, BasicParsing) {
27 const char data[] =
28 "-----BEGIN EXPECTED-BLOCK-----\n"
29 "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\n"
30 "-----END EXPECTED-BLOCK-----\n";
31 std::string_view string_piece(data);
32 std::vector<std::string> accepted_types;
33 accepted_types.push_back("EXPECTED-BLOCK");
34
35 PEMTokenizer tokenizer(string_piece, accepted_types);
36 EXPECT_TRUE(tokenizer.GetNext());
37
38 EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type());
39 EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data());
40
41 EXPECT_FALSE(tokenizer.GetNext());
42 }
43
TEST(PEMTokenizerTest,CarriageReturnLineFeeds)44 TEST(PEMTokenizerTest, CarriageReturnLineFeeds) {
45 const char data[] =
46 "-----BEGIN EXPECTED-BLOCK-----\r\n"
47 "TWF0Y2hlc0FjY2VwdGVkQmxvY2tUeXBl\r\n"
48 "-----END EXPECTED-BLOCK-----\r\n";
49 std::string_view string_piece(data);
50 std::vector<std::string> accepted_types;
51 accepted_types.push_back("EXPECTED-BLOCK");
52
53 PEMTokenizer tokenizer(string_piece, accepted_types);
54 EXPECT_TRUE(tokenizer.GetNext());
55
56 EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type());
57 EXPECT_EQ("MatchesAcceptedBlockType", tokenizer.data());
58
59 EXPECT_FALSE(tokenizer.GetNext());
60 }
61
TEST(PEMTokenizerTest,NoAcceptedBlockTypes)62 TEST(PEMTokenizerTest, NoAcceptedBlockTypes) {
63 const char data[] =
64 "-----BEGIN UNEXPECTED-BLOCK-----\n"
65 "SWdub3Jlc1JlamVjdGVkQmxvY2tUeXBl\n"
66 "-----END UNEXPECTED-BLOCK-----\n";
67 std::string_view string_piece(data);
68 std::vector<std::string> accepted_types;
69 accepted_types.push_back("EXPECTED-BLOCK");
70
71 PEMTokenizer tokenizer(string_piece, accepted_types);
72 EXPECT_FALSE(tokenizer.GetNext());
73 }
74
TEST(PEMTokenizerTest,MultipleAcceptedBlockTypes)75 TEST(PEMTokenizerTest, MultipleAcceptedBlockTypes) {
76 const char data[] =
77 "-----BEGIN BLOCK-ONE-----\n"
78 "RW5jb2RlZERhdGFPbmU=\n"
79 "-----END BLOCK-ONE-----\n"
80 "-----BEGIN BLOCK-TWO-----\n"
81 "RW5jb2RlZERhdGFUd28=\n"
82 "-----END BLOCK-TWO-----\n";
83 std::string_view string_piece(data);
84 std::vector<std::string> accepted_types;
85 accepted_types.push_back("BLOCK-ONE");
86 accepted_types.push_back("BLOCK-TWO");
87
88 PEMTokenizer tokenizer(string_piece, accepted_types);
89 EXPECT_TRUE(tokenizer.GetNext());
90
91 EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
92 EXPECT_EQ("EncodedDataOne", tokenizer.data());
93
94 EXPECT_TRUE(tokenizer.GetNext());
95
96 EXPECT_EQ("BLOCK-TWO", tokenizer.block_type());
97 EXPECT_EQ("EncodedDataTwo", tokenizer.data());
98
99 EXPECT_FALSE(tokenizer.GetNext());
100 }
101
TEST(PEMTokenizerTest,MissingFooter)102 TEST(PEMTokenizerTest, MissingFooter) {
103 const char data[] =
104 "-----BEGIN MISSING-FOOTER-----\n"
105 "RW5jb2RlZERhdGFPbmU=\n"
106 "-----END MISSING-FOOTER-----\n"
107 "-----BEGIN MISSING-FOOTER-----\n"
108 "RW5jb2RlZERhdGFUd28=\n";
109 std::string_view string_piece(data);
110 std::vector<std::string> accepted_types;
111 accepted_types.push_back("MISSING-FOOTER");
112
113 PEMTokenizer tokenizer(string_piece, accepted_types);
114 EXPECT_TRUE(tokenizer.GetNext());
115
116 EXPECT_EQ("MISSING-FOOTER", tokenizer.block_type());
117 EXPECT_EQ("EncodedDataOne", tokenizer.data());
118
119 EXPECT_FALSE(tokenizer.GetNext());
120 }
121
TEST(PEMTokenizerTest,NestedEncoding)122 TEST(PEMTokenizerTest, NestedEncoding) {
123 const char data[] =
124 "-----BEGIN BLOCK-ONE-----\n"
125 "RW5jb2RlZERhdGFPbmU=\n"
126 "-----BEGIN BLOCK-TWO-----\n"
127 "RW5jb2RlZERhdGFUd28=\n"
128 "-----END BLOCK-TWO-----\n"
129 "-----END BLOCK-ONE-----\n"
130 "-----BEGIN BLOCK-ONE-----\n"
131 "RW5jb2RlZERhdGFUaHJlZQ==\n"
132 "-----END BLOCK-ONE-----\n";
133 std::string_view string_piece(data);
134 std::vector<std::string> accepted_types;
135 accepted_types.push_back("BLOCK-ONE");
136
137 PEMTokenizer tokenizer(string_piece, accepted_types);
138 EXPECT_TRUE(tokenizer.GetNext());
139
140 EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
141 EXPECT_EQ("EncodedDataThree", tokenizer.data());
142
143 EXPECT_FALSE(tokenizer.GetNext());
144 }
145
TEST(PEMTokenizerTest,EmptyAcceptedTypes)146 TEST(PEMTokenizerTest, EmptyAcceptedTypes) {
147 const char data[] =
148 "-----BEGIN BLOCK-ONE-----\n"
149 "RW5jb2RlZERhdGFPbmU=\n"
150 "-----END BLOCK-ONE-----\n";
151 std::string_view string_piece(data);
152 std::vector<std::string> accepted_types;
153
154 PEMTokenizer tokenizer(string_piece, accepted_types);
155 EXPECT_FALSE(tokenizer.GetNext());
156 }
157
TEST(PEMTokenizerTest,BlockWithHeader)158 TEST(PEMTokenizerTest, BlockWithHeader) {
159 const char data[] =
160 "-----BEGIN BLOCK-ONE-----\n"
161 "Header-One: Data data data\n"
162 "Header-Two: \n"
163 " continuation\n"
164 "Header-Three: Mix-And,Match\n"
165 "\n"
166 "RW5jb2RlZERhdGFPbmU=\n"
167 "-----END BLOCK-ONE-----\n"
168 "-----BEGIN BLOCK-ONE-----\n"
169 "RW5jb2RlZERhdGFUd28=\n"
170 "-----END BLOCK-ONE-----\n";
171 std::string_view string_piece(data);
172 std::vector<std::string> accepted_types;
173 accepted_types.push_back("BLOCK-ONE");
174
175 PEMTokenizer tokenizer(string_piece, accepted_types);
176 EXPECT_TRUE(tokenizer.GetNext());
177
178 EXPECT_EQ("BLOCK-ONE", tokenizer.block_type());
179 EXPECT_EQ("EncodedDataTwo", tokenizer.data());
180
181 EXPECT_FALSE(tokenizer.GetNext());
182 }
183
TEST(PEMTokenizerTest,SpanConstructor)184 TEST(PEMTokenizerTest, SpanConstructor) {
185 const std::string_view data =
186 "-----BEGIN EXPECTED-BLOCK-----\n"
187 "U3BhbkNvbnN0cnVjdG9y\n"
188 "-----END EXPECTED-BLOCK-----\n";
189 const std::array<std::string_view, 1> accepted_types = { "EXPECTED-BLOCK" };
190 PEMTokenizer tokenizer(data, bssl::Span(accepted_types));
191 EXPECT_TRUE(tokenizer.GetNext());
192 EXPECT_EQ("EXPECTED-BLOCK", tokenizer.block_type());
193 EXPECT_EQ("SpanConstructor", tokenizer.data());
194
195 EXPECT_FALSE(tokenizer.GetNext());
196 }
197
TEST(PEMDecodeTest,BasicSingle)198 TEST(PEMDecodeTest, BasicSingle) {
199 const std::string_view data =
200 "-----BEGIN SINGLE-----\n"
201 "YmxvY2sgYm9keQ=="
202 "-----END SINGLE-----\n"
203 "-----BEGIN WRONG-----\n"
204 "d3JvbmcgYmxvY2sgYm9keQ=="
205 "-----END WRONG-----\n";
206 std::optional<std::string> result = PEMDecodeSingle(data, "SINGLE");
207 ASSERT_TRUE(result);
208 EXPECT_EQ(*result, "block body");
209 }
210
TEST(PEMDecodeTest,BasicMulti)211 TEST(PEMDecodeTest, BasicMulti) {
212 const std::string_view data =
213 "-----BEGIN MULTI-1-----\n"
214 "YmxvY2sgYm9keSAx"
215 "-----END MULTI-1-----\n"
216 "-----BEGIN WRONG-----\n"
217 "d3JvbmcgYmxvY2sgYm9keQ=="
218 "-----END WRONG-----\n"
219 "-----BEGIN MULTI-2-----\n"
220 "YmxvY2sgYm9keSAy"
221 "-----END MULTI-2-----\n";
222 const std::array<std::string_view, 2> accepted_types = {"MULTI-1", "MULTI-2"};
223 std::vector<PEMToken> result = PEMDecode(data, accepted_types);
224 EXPECT_EQ(result.size(), 2u);
225 EXPECT_EQ(result[0].type, "MULTI-1");
226 EXPECT_EQ(result[0].data, "block body 1");
227 EXPECT_EQ(result[1].type, "MULTI-2");
228 EXPECT_EQ(result[1].data, "block body 2");
229 }
230
TEST(PEMDecodeTest,TypeMismatchSingle)231 TEST(PEMDecodeTest, TypeMismatchSingle) {
232 const std::string_view data =
233 "-----BEGIN WRONG-----\n"
234 "d3JvbmcgYmxvY2sgYm9keQ=="
235 "-----END WRONG-----\n";
236 std::optional<std::string> result = PEMDecodeSingle(data, "SINGLE");
237 EXPECT_FALSE(result);
238 }
239
TEST(PEMDecodeTest,TooManySingle)240 TEST(PEMDecodeTest, TooManySingle) {
241 const std::string_view data =
242 "-----BEGIN SINGLE-----\n"
243 "YmV0dGVyIG5vdCBzZWUgdGhpcw=="
244 "-----END SINGLE-----\n"
245 "-----BEGIN SINGLE-----\n"
246 "b3IgdGhpcw=="
247 "-----END SINGLE-----\n";
248 std::optional<std::string> result = PEMDecodeSingle(data, "SINGLE");
249 EXPECT_FALSE(result);
250 }
251
TEST(PEMEncodeTest,Basic)252 TEST(PEMEncodeTest, Basic) {
253 EXPECT_EQ(
254 "-----BEGIN BLOCK-ONE-----\n"
255 "RW5jb2RlZERhdGFPbmU=\n"
256 "-----END BLOCK-ONE-----\n",
257 PEMEncode("EncodedDataOne", "BLOCK-ONE"));
258 EXPECT_EQ(
259 "-----BEGIN BLOCK-TWO-----\n"
260 "RW5jb2RlZERhdGFUd28=\n"
261 "-----END BLOCK-TWO-----\n",
262 PEMEncode("EncodedDataTwo", "BLOCK-TWO"));
263 }
264
TEST(PEMEncodeTest,Empty)265 TEST(PEMEncodeTest, Empty) {
266 EXPECT_EQ(
267 "-----BEGIN EMPTY-----\n"
268 "-----END EMPTY-----\n",
269 PEMEncode("", "EMPTY"));
270 }
271
TEST(PEMEncodeTest,Wrapping)272 TEST(PEMEncodeTest, Wrapping) {
273 EXPECT_EQ(
274 "-----BEGIN SINGLE LINE-----\n"
275 "MTIzNDU2Nzg5MGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktM\n"
276 "-----END SINGLE LINE-----\n",
277 PEMEncode("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKL",
278 "SINGLE LINE"));
279
280 EXPECT_EQ(
281 "-----BEGIN WRAPPED LINE-----\n"
282 "MTIzNDU2Nzg5MGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6QUJDREVGR0hJSktM\nTQ==\n"
283 "-----END WRAPPED LINE-----\n",
284 PEMEncode("1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLM",
285 "WRAPPED LINE"));
286 }
287
288 BSSL_NAMESPACE_END
289