1 // Copyright 2018 The BoringSSL 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 <openssl/pem.h>
16
17 #include <functional>
18
19 #include <gtest/gtest.h>
20
21 #include <openssl/bio.h>
22 #include <openssl/cipher.h>
23 #include <openssl/err.h>
24 #include <openssl/rsa.h>
25
26 #include "../test/test_util.h"
27
28
29 namespace {
30
31 // Test that implausible ciphers, notably an IV-less RC4, aren't allowed in PEM.
32 // This is a regression test for https://github.com/openssl/openssl/issues/6347,
33 // though our fix differs from upstream.
TEST(PEMTest,NoRC4)34 TEST(PEMTest, NoRC4) {
35 static const char kPEM[] =
36 "-----BEGIN RSA PUBLIC KEY-----\n"
37 "Proc-Type: 4,ENCRYPTED\n"
38 "DEK-Info: RC4 -\n"
39 "extra-info\n"
40 "router-signature\n"
41 "\n"
42 "Z1w=\n"
43 "-----END RSA PUBLIC KEY-----\n";
44 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kPEM, sizeof(kPEM) - 1));
45 ASSERT_TRUE(bio);
46 bssl::UniquePtr<RSA> rsa(PEM_read_bio_RSAPublicKey(
47 bio.get(), nullptr, nullptr, const_cast<char *>("password")));
48 EXPECT_FALSE(rsa);
49 EXPECT_TRUE(
50 ErrorEquals(ERR_get_error(), ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION));
51 }
52
DecodePEMBytes(const char * pem)53 static std::vector<uint8_t> DecodePEMBytes(const char *pem) {
54 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
55 char *name, *header;
56 uint8_t *data;
57 long len;
58 if (bio == nullptr || //
59 !PEM_read_bio(bio.get(), &name, &header, &data, &len)) {
60 return {};
61 }
62 bssl::UniquePtr<char> free_name(name), free_header(header);
63 bssl::UniquePtr<uint8_t> free_data(data);
64 return std::vector<uint8_t>(data, data + len);
65 }
66
TEST(PEMTest,DecryptPassword)67 TEST(PEMTest, DecryptPassword) {
68 // A private key encrypted with the password "password", encrypted at the
69 // PKCS#8 level.
70 static const char kEncryptedPEM[] = R"(
71 -----BEGIN ENCRYPTED PRIVATE KEY-----
72 MIHeMEkGCSqGSIb3DQEFDTA8MBsGCSqGSIb3DQEFDDAOBAjnhMUlb9deeQICCAAw
73 HQYJYIZIAWUDBAECBBAO8j5GA5VK8wjvNrzp/iVhBIGQyQKFfFKlFhxiDkFfyhUc
74 nPLr0eboQOz8eIaTW1Rblo/qDkQwNtONyfYn909SoIP7iU8UehcBG1UQe41WvQpu
75 yRKYQteoWSzFl+yzktL2Y/25K7Uc+f2NScjdonYMZ+9/m1HGmEzKO+Hz28cAsJL7
76 rH2gQ0lkxr1GtW77m2rfMKKuGYhpkgjWUbzJwP9v3iq+
77 -----END ENCRYPTED PRIVATE KEY-----
78 )";
79 // The same key and password, but encrypted at the PEM level.
80 static const char kEncryptedPEM2[] = R"(
81 -----BEGIN EC PRIVATE KEY-----
82 Proc-Type: 4,ENCRYPTED
83 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123
84
85 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
86 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
87 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
88 -----END EC PRIVATE KEY-----
89 )";
90
91 for (const char *pem : {kEncryptedPEM, kEncryptedPEM2}) {
92 SCOPED_TRACE(pem);
93 // Decrypt with the correct password.
94 {
95 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
96 ASSERT_TRUE(bio);
97 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(
98 bio.get(), nullptr, nullptr, const_cast<char *>("password")));
99 EXPECT_TRUE(pkey);
100 }
101
102 // Decrypt with the wrong password.
103 {
104 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
105 ASSERT_TRUE(bio);
106 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(
107 bio.get(), nullptr, nullptr, const_cast<char *>("wrong")));
108 EXPECT_FALSE(pkey);
109 EXPECT_TRUE(
110 ErrorEquals(ERR_peek_error(), ERR_LIB_CIPHER, CIPHER_R_BAD_DECRYPT));
111 ERR_clear_error();
112 }
113
114 // If the caller did not pass in a password, we should not proceed to try to
115 // decrypt.
116 {
117 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
118 ASSERT_TRUE(bio);
119 bssl::UniquePtr<EVP_PKEY> pkey(
120 PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
121 EXPECT_FALSE(pkey);
122 EXPECT_TRUE(
123 ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ));
124 ERR_clear_error();
125 }
126
127 // If the password, with a NUL terminator, does not fit in the internal
128 // buffer used by the PEM library, the PEM library should notice.
129 {
130 std::string too_long(PEM_BUFSIZE, 'a');
131 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
132 ASSERT_TRUE(bio);
133 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(
134 bio.get(), nullptr, nullptr, const_cast<char *>(too_long.c_str())));
135 EXPECT_FALSE(pkey);
136 EXPECT_TRUE(
137 ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ));
138 ERR_clear_error();
139 }
140 }
141
142 // |d2i_PKCS8PrivateKey_bio| should also be able to manage the password
143 // callback correctly.
144 std::vector<uint8_t> bytes = DecodePEMBytes(kEncryptedPEM);
145 ASSERT_FALSE(bytes.empty());
146 {
147 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(bytes.data(), bytes.size()));
148 ASSERT_TRUE(bio);
149 bssl::UniquePtr<EVP_PKEY> pkey(d2i_PKCS8PrivateKey_bio(
150 bio.get(), nullptr, nullptr, const_cast<char *>("password")));
151 EXPECT_TRUE(pkey);
152 }
153
154 {
155 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(bytes.data(), bytes.size()));
156 ASSERT_TRUE(bio);
157 bssl::UniquePtr<EVP_PKEY> pkey(
158 d2i_PKCS8PrivateKey_bio(bio.get(), nullptr, nullptr, nullptr));
159 EXPECT_FALSE(pkey);
160 EXPECT_TRUE(
161 ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ));
162 ERR_clear_error();
163 }
164
165 {
166 std::string too_long(PEM_BUFSIZE, 'a');
167 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(bytes.data(), bytes.size()));
168 ASSERT_TRUE(bio);
169 bssl::UniquePtr<EVP_PKEY> pkey(d2i_PKCS8PrivateKey_bio(
170 bio.get(), nullptr, nullptr, const_cast<char *>(too_long.c_str())));
171 EXPECT_FALSE(pkey);
172 EXPECT_TRUE(
173 ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ));
174 ERR_clear_error();
175 }
176
177 // A private key encrypted with the empty password, encrypted at the PKCS#8
178 // level.
179 static const char kEncryptedPEMEmpty[] = R"(
180 -----BEGIN ENCRYPTED PRIVATE KEY-----
181 MIH0MF8GCSqGSIb3DQEFDTBSMDEGCSqGSIb3DQEFDDAkBBAXiHC8iDcjzF0I+D2g
182 zJOcAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBAgQQwupOMi8DtEWiuXt5
183 Odla9QSBkC37uJuG7HSCOyTVCEW76Kmf7GoH+Ou17bDAp6NGwm3KLxRfFoExki9g
184 hyLzdarBnhRbPqwMixhaQ2AtkpoSmjristGzZ9U7Y+TM3NnCA4+bu1TckdBn0g+Q
185 fvZI9eydS9buA0deGxCUytrMWrR3PxS1yoXBywMDJTom8u5hvvvkJ9WcNzUVRf0D
186 6z5NHHiXsQ==
187 -----END ENCRYPTED PRIVATE KEY-----
188 )";
189 // THe same key and password, but encrypted at the PEM level.
190 static const char kEncryptedPEMEmpty2[] = R"(
191 -----BEGIN EC PRIVATE KEY-----
192 Proc-Type: 4,ENCRYPTED
193 DEK-Info: AES-128-CBC,A9505A7DD5C3B51D8AACED18F5758256
194
195 yfJKjep7Koj8hU/PtGC+NNXSNbItQ2zyeXDMVoazffraoDGMg6g1hFPPjg9reC+J
196 iQQIf9uACF27zi9fpWwbszszimrxl0u6n0ddBXizcK6xzkTvk3PZ67Vz1KYmotwC
197 XjgdgSEeixwKhDOuHKFdlFGP/7sw5GHlK3jPSpqi2gI=
198 -----END EC PRIVATE KEY-----
199 )";
200
201 for (const char *pem : {kEncryptedPEMEmpty, kEncryptedPEMEmpty2}) {
202 SCOPED_TRACE(pem);
203
204 // The empty password should be correctly interpreted as a password.
205 {
206 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(pem, -1));
207 ASSERT_TRUE(bio);
208 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(
209 bio.get(), nullptr, nullptr, const_cast<char *>("")));
210 EXPECT_TRUE(pkey);
211 }
212 }
213
214 // |d2i_PKCS8PrivateKey_bio| should also be able to manage the password
215 // callback correctly.
216 bytes = DecodePEMBytes(kEncryptedPEMEmpty);
217 {
218 ASSERT_FALSE(bytes.empty());
219 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(bytes.data(), bytes.size()));
220 ASSERT_TRUE(bio);
221 bssl::UniquePtr<EVP_PKEY> pkey(d2i_PKCS8PrivateKey_bio(
222 bio.get(), nullptr, nullptr, const_cast<char *>("")));
223 EXPECT_TRUE(pkey);
224 }
225 }
226
TEST(PEMTest,EncryptPassword)227 TEST(PEMTest, EncryptPassword) {
228 static const char kKey[] = R"(
229 -----BEGIN PRIVATE KEY-----
230 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgBw8IcnrUoEqc3VnJ
231 TYlodwi1b8ldMHcO6NHJzgqLtGqhRANCAATmK2niv2Wfl74vHg2UikzVl2u3qR4N
232 Rvvdqakendy6WgHn1peoChj5w8SjHlbifINI2xYaHPUdfvGULUvPciLB
233 -----END PRIVATE KEY-----
234 )";
235 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(kKey, -1));
236 ASSERT_TRUE(bio);
237 bssl::UniquePtr<EVP_PKEY> pkey(
238 PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr));
239 EXPECT_TRUE(pkey);
240
241 // There are many ways to encrypt a PEM blob with a password.
242 struct PasswordMethod {
243 const char *name;
244 std::function<bool(BIO *, const char *)> func;
245 bool is_callback;
246 };
247 const PasswordMethod kPasswordMethods[] = {
248 {"PKCS#8 encryption, password from param",
249 [&](BIO *out, const char *pass) -> bool {
250 return PEM_write_bio_PrivateKey(
251 out, pkey.get(), EVP_aes_128_cbc(),
252 reinterpret_cast<const unsigned char *>(pass),
253 pass == nullptr ? 0 : strlen(pass), nullptr, nullptr);
254 },
255 /*is_callback=*/false},
256 {"PKCS#8 encryption, password from callback",
257 [&](BIO *out, const char *pass) -> bool {
258 return PEM_write_bio_PrivateKey(out, pkey.get(), EVP_aes_128_cbc(),
259 nullptr, 0, nullptr,
260 const_cast<char *>(pass));
261 },
262 /*is_callback=*/true},
263 {"PEM-level encryption, password from param",
264 [&](BIO *out, const char *pass) -> bool {
265 return PEM_write_bio_ECPrivateKey(
266 out, EVP_PKEY_get0_EC_KEY(pkey.get()), EVP_aes_128_cbc(), nullptr,
267 0, nullptr, const_cast<char *>(pass));
268 },
269 /*is_callback=*/false},
270 {"PKCS#8 encryption, password from callback",
271 [&](BIO *out, const char *pass) -> bool {
272 return PEM_write_bio_ECPrivateKey(
273 out, EVP_PKEY_get0_EC_KEY(pkey.get()), EVP_aes_128_cbc(), nullptr,
274 0, nullptr, const_cast<char *>(pass));
275 },
276 /*is_callback=*/true},
277 };
278 for (const auto &p : kPasswordMethods) {
279 SCOPED_TRACE(p.name);
280
281 // Encrypting the private key with a password should work.
282 bio.reset(BIO_new(BIO_s_mem()));
283 ASSERT_TRUE(bio);
284 ASSERT_TRUE(p.func(bio.get(), "password"));
285
286 // Check we can decrypt it.
287 bssl::UniquePtr<EVP_PKEY> pkey2(PEM_read_bio_PrivateKey(
288 bio.get(), nullptr, nullptr, const_cast<char *>("password")));
289 ASSERT_TRUE(pkey2);
290
291 // The empty string is a valid password.
292 bio.reset(BIO_new(BIO_s_mem()));
293 ASSERT_TRUE(bio);
294 ASSERT_TRUE(p.func(bio.get(), ""));
295
296 // Check we can decrypt it.
297 pkey2.reset(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr,
298 const_cast<char *>("")));
299 ASSERT_TRUE(pkey2);
300
301 // Check error-handling when the password is specified via the callback.
302 if (p.is_callback) {
303 bio.reset(BIO_new(BIO_s_mem()));
304 ASSERT_TRUE(bio);
305 EXPECT_FALSE(p.func(bio.get(), nullptr));
306 EXPECT_TRUE(ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_READ_KEY));
307 ERR_clear_error();
308
309 std::string too_long(PEM_BUFSIZE, 'a');
310 bio.reset(BIO_new(BIO_s_mem()));
311 ASSERT_TRUE(bio);
312 EXPECT_FALSE(p.func(bio.get(), too_long.c_str()));
313 EXPECT_TRUE(ErrorEquals(ERR_peek_error(), ERR_LIB_PEM, PEM_R_READ_KEY));
314 ERR_clear_error();
315 }
316 }
317 }
318
TEST(PEMTest,BadHeaders)319 TEST(PEMTest, BadHeaders) {
320 const struct {
321 const char *pem;
322 int err_lib, err_reason;
323 } kTests[] = {
324 // Proc-Type must be the first header.
325 {
326 R"(
327 -----BEGIN EC PRIVATE KEY-----
328 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123
329 Proc-Type: 4,ENCRYPTED
330
331 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
332 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
333 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
334 -----END EC PRIVATE KEY-----
335 )",
336 ERR_LIB_PEM, PEM_R_NOT_PROC_TYPE},
337 // Unsupported Proc-Type version.
338 {
339 R"(
340 -----BEGIN EC PRIVATE KEY-----
341 Proc-Type: 5,ENCRYPTED
342 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123
343
344 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
345 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
346 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
347 -----END EC PRIVATE KEY-----
348 )",
349 ERR_LIB_PEM, PEM_R_UNSUPPORTED_PROC_TYPE_VERSION},
350 // Unsupported Proc-Type version.
351 {
352 R"(
353 -----BEGIN EC PRIVATE KEY-----
354 Proc-Type: 42,ENCRYPTED
355 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123
356
357 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
358 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
359 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
360 -----END EC PRIVATE KEY-----
361 )",
362 ERR_LIB_PEM, PEM_R_UNSUPPORTED_PROC_TYPE_VERSION},
363 // Unsupported Proc-Type.
364 {
365 R"(
366 -----BEGIN EC PRIVATE KEY-----
367 Proc-Type: 4,MIC-ONLY
368 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C1123
369
370 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
371 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
372 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
373 -----END EC PRIVATE KEY-----
374 )",
375 ERR_LIB_PEM, PEM_R_NOT_ENCRYPTED},
376 // Missing DEK-Info.
377 {
378 R"(
379 -----BEGIN EC PRIVATE KEY-----
380 Proc-Type: 4,ENCRYPTED
381
382 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
383 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
384 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
385 -----END EC PRIVATE KEY-----
386 )",
387 ERR_LIB_PEM, PEM_R_NOT_DEK_INFO},
388 // Unsupported cipher.
389 {
390 R"(
391 -----BEGIN EC PRIVATE KEY-----
392 Proc-Type: 4,ENCRYPTED
393 DEK-Info: AES-127-CBC,B3B2988AECAE6EAB0D043105994C1123
394
395 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
396 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
397 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
398 -----END EC PRIVATE KEY-----
399 )",
400 ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION},
401 // IV is not hex.
402 {
403 R"(
404 -----BEGIN EC PRIVATE KEY-----
405 Proc-Type: 4,ENCRYPTED
406 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C112Z
407
408 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
409 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
410 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
411 -----END EC PRIVATE KEY-----
412 )",
413 ERR_LIB_PEM, PEM_R_BAD_IV_CHARS},
414 // Truncated IV.
415 {
416 R"(
417 -----BEGIN EC PRIVATE KEY-----
418 Proc-Type: 4,ENCRYPTED
419 DEK-Info: AES-128-CBC,B3B2988AECAE6EAB0D043105994C112
420
421 RK7DUIGDHWTFh2rpTX+dR88hUyC1PyDlIULiNCkuWFwHrJbc1gM6hMVOKmU196XC
422 iITrIKmilFm9CPD6Tpfk/NhI/QPxyJlk1geIkxpvUZ2FCeMuYI1To14oYOUKv14q
423 wr6JtaX2G+pOmwcSPymZC4u2TncAP7KHgS8UGcMw8CE=
424 -----END EC PRIVATE KEY-----
425 )",
426 ERR_LIB_PEM, PEM_R_BAD_IV_CHARS},
427 };
428 for (const auto &t : kTests) {
429 SCOPED_TRACE(t.pem);
430 bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(t.pem, -1));
431 ASSERT_TRUE(bio);
432 bssl::UniquePtr<EVP_PKEY> pkey(PEM_read_bio_PrivateKey(
433 bio.get(), nullptr, nullptr, const_cast<char *>("password")));
434 EXPECT_FALSE(pkey);
435 EXPECT_TRUE(ErrorEquals(ERR_get_error(), t.err_lib, t.err_reason));
436 ERR_clear_error();
437 }
438 }
439
440 } // namespace
441