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