]>
Commit | Line | Data |
---|---|---|
5a23d78c RL |
1 | /* |
2 | * Copyright 2020 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * | |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
3ecbea6a | 10 | #include <string.h> |
5a23d78c RL |
11 | #include <openssl/evp.h> |
12 | #include <openssl/pem.h> | |
13 | #include <openssl/rsa.h> | |
14 | #include <openssl/x509.h> | |
15 | #include <openssl/serializer.h> | |
16 | #include <openssl/deserializer.h> | |
17 | ||
e2ac846e RL |
18 | #include "internal/cryptlib.h" /* ossl_assert */ |
19 | ||
5a23d78c RL |
20 | #include "testutil.h" |
21 | ||
e2ac846e RL |
22 | /* |
23 | * TODO(3.0) Modify PEM_write_bio_PrivateKey_traditional() to handle | |
24 | * provider side EVP_PKEYs (which don't necessarily have an ameth) | |
25 | * | |
26 | * In the mean time, we use separate "downgraded" EVP_PKEYs to test | |
27 | * serializing/deserializing with "traditional" keys. | |
28 | */ | |
29 | ||
5a23d78c | 30 | static EVP_PKEY *key_RSA = NULL; |
e2ac846e | 31 | static EVP_PKEY *legacy_key_RSA = NULL; |
846f96f8 RL |
32 | static EVP_PKEY *key_RSA_PSS = NULL; |
33 | static EVP_PKEY *legacy_key_RSA_PSS = NULL; | |
5a23d78c | 34 | |
e2ac846e | 35 | static EVP_PKEY *make_RSA(const char *rsa_type, int make_legacy) |
5a23d78c RL |
36 | { |
37 | EVP_PKEY *pkey = NULL; | |
38 | EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new_from_name(NULL, rsa_type, NULL); | |
39 | ||
40 | /* | |
41 | * No real need to check the errors other than for the cascade | |
42 | * effect. |pkey| will imply remain NULL if something goes wrong. | |
43 | */ | |
44 | (void)(ctx != NULL | |
45 | && EVP_PKEY_keygen_init(ctx) > 0 | |
46 | && EVP_PKEY_keygen(ctx, &pkey) > 0); | |
47 | EVP_PKEY_CTX_free(ctx); | |
e2ac846e RL |
48 | if (make_legacy && EVP_PKEY_get0(pkey) == NULL) { |
49 | EVP_PKEY_free(pkey); | |
50 | pkey = NULL; | |
51 | } | |
5a23d78c RL |
52 | |
53 | return pkey; | |
54 | } | |
55 | ||
56 | /* Main test driver */ | |
57 | ||
4701f0a9 RL |
58 | /* |
59 | * TODO(3.0) For better error output, changed the callbacks to take __FILE__ | |
60 | * and __LINE__ as first two arguments, and have them use the lower case | |
61 | * functions, such as test_strn_eq(), rather than the uppercase macros | |
62 | * (TEST_strn2_eq(), for example). | |
63 | */ | |
64 | ||
5a23d78c | 65 | typedef int (serializer)(void **serialized, long *serialized_len, |
4701f0a9 | 66 | void *object, const char *pass, const char *pcipher, |
3ecbea6a | 67 | const char *ser_propq); |
5a23d78c | 68 | typedef int (deserializer)(void **object, |
3ecbea6a | 69 | void *serialized, long serialized_len, |
4701f0a9 | 70 | const char *pass); |
846f96f8 | 71 | typedef int (checker)(const char *type, const void *data, size_t data_len); |
5a23d78c RL |
72 | typedef void (dumper)(const char *label, const void *data, size_t data_len); |
73 | ||
846f96f8 | 74 | static int test_serialize_deserialize(const char *type, EVP_PKEY *pkey, |
3ecbea6a | 75 | const char *pass, const char *pcipher, |
5a23d78c RL |
76 | serializer *serialize_cb, |
77 | deserializer *deserialize_cb, | |
78 | checker *check_cb, dumper *dump_cb, | |
e2ac846e | 79 | const char *ser_propq, int make_legacy) |
5a23d78c RL |
80 | { |
81 | void *serialized = NULL; | |
82 | long serialized_len = 0; | |
83 | EVP_PKEY *pkey2 = NULL; | |
84 | void *serialized2 = NULL; | |
85 | long serialized2_len = 0; | |
86 | int ok = 0; | |
87 | ||
3ecbea6a RL |
88 | if (!serialize_cb(&serialized, &serialized_len, pkey, |
89 | pass, pcipher, ser_propq) | |
846f96f8 | 90 | || !check_cb(type, serialized, serialized_len) |
3ecbea6a | 91 | || !deserialize_cb((void **)&pkey2, serialized, serialized_len, |
4701f0a9 | 92 | pass) |
5a23d78c RL |
93 | || !TEST_int_eq(EVP_PKEY_eq(pkey, pkey2), 1)) |
94 | goto end; | |
95 | ||
e2ac846e RL |
96 | /* |
97 | * TODO(3.0) Remove this when PEM_write_bio_PrivateKey_traditional() | |
98 | * handles provider side keys. | |
99 | */ | |
100 | if (make_legacy | |
101 | && !TEST_ptr(EVP_PKEY_get0(pkey2))) | |
102 | goto end; | |
103 | ||
5a23d78c | 104 | /* |
3ecbea6a RL |
105 | * Double check the serialization, but only for unprotected keys, |
106 | * as protected keys have a random component, which makes the output | |
107 | * differ. | |
5a23d78c | 108 | */ |
3ecbea6a RL |
109 | if ((pass == NULL && pcipher == NULL) |
110 | && (!serialize_cb(&serialized2, &serialized2_len, pkey2, | |
111 | pass, pcipher, ser_propq) | |
112 | || !TEST_mem_eq(serialized, serialized_len, | |
113 | serialized2, serialized2_len))) | |
5a23d78c RL |
114 | goto end; |
115 | ||
116 | ok = 1; | |
117 | end: | |
118 | if (!ok) | |
119 | dump_cb("serialized result", serialized, serialized_len); | |
120 | ||
121 | OPENSSL_free(serialized); | |
122 | OPENSSL_free(serialized2); | |
123 | EVP_PKEY_free(pkey2); | |
124 | return ok; | |
125 | } | |
126 | ||
127 | /* Serializing and desserializing methods */ | |
128 | ||
e2ac846e RL |
129 | static int serialize_EVP_PKEY_prov(void **serialized, long *serialized_len, |
130 | void *object, | |
131 | const char *pass, const char *pcipher, | |
132 | const char *ser_propq) | |
5a23d78c RL |
133 | { |
134 | EVP_PKEY *pkey = object; | |
135 | OSSL_SERIALIZER_CTX *sctx = NULL; | |
136 | BIO *mem_ser = NULL; | |
137 | BUF_MEM *mem_buf = NULL; | |
3ecbea6a | 138 | const unsigned char *upass = (const unsigned char *)pass; |
5a23d78c RL |
139 | int ok = 0; |
140 | ||
141 | if (!TEST_ptr(sctx = OSSL_SERIALIZER_CTX_new_by_EVP_PKEY(pkey, ser_propq)) | |
3ecbea6a | 142 | || (pass != NULL |
e2ac846e RL |
143 | && !TEST_true(OSSL_SERIALIZER_CTX_set_passphrase(sctx, upass, |
144 | strlen(pass)))) | |
3ecbea6a | 145 | || (pcipher != NULL |
e2ac846e | 146 | && !TEST_true(OSSL_SERIALIZER_CTX_set_cipher(sctx, pcipher, NULL))) |
5a23d78c RL |
147 | || !TEST_ptr(mem_ser = BIO_new(BIO_s_mem())) |
148 | || !TEST_true(OSSL_SERIALIZER_to_bio(sctx, mem_ser)) | |
149 | || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0) | |
150 | || !TEST_ptr(*serialized = mem_buf->data) | |
151 | || !TEST_long_gt(*serialized_len = mem_buf->length, 0)) | |
152 | goto end; | |
153 | ||
154 | /* Detach the serialized output */ | |
155 | mem_buf->data = NULL; | |
156 | mem_buf->length = 0; | |
157 | ok = 1; | |
158 | end: | |
159 | BIO_free(mem_ser); | |
160 | OSSL_SERIALIZER_CTX_free(sctx); | |
161 | return ok; | |
162 | } | |
163 | ||
e2ac846e RL |
164 | static int deserialize_EVP_PKEY_prov(void **object, |
165 | void *serialized, long serialized_len, | |
4701f0a9 | 166 | const char *pass) |
5a23d78c RL |
167 | { |
168 | EVP_PKEY *pkey = NULL; | |
169 | OSSL_DESERIALIZER_CTX *dctx = NULL; | |
170 | BIO *mem_deser = NULL; | |
3ecbea6a | 171 | const unsigned char *upass = (const unsigned char *)pass; |
5a23d78c RL |
172 | int ok = 0; |
173 | ||
174 | if (!TEST_ptr(dctx = OSSL_DESERIALIZER_CTX_new_by_EVP_PKEY(&pkey, NULL, | |
175 | NULL, NULL)) | |
3ecbea6a RL |
176 | || (pass != NULL |
177 | && !OSSL_DESERIALIZER_CTX_set_passphrase(dctx, upass, | |
178 | strlen(pass))) | |
5a23d78c RL |
179 | || !TEST_ptr(mem_deser = BIO_new_mem_buf(serialized, serialized_len)) |
180 | || !TEST_true(OSSL_DESERIALIZER_from_bio(dctx, mem_deser))) | |
181 | goto end; | |
182 | ok = 1; | |
183 | *object = pkey; | |
184 | end: | |
185 | BIO_free(mem_deser); | |
186 | OSSL_DESERIALIZER_CTX_free(dctx); | |
187 | return ok; | |
188 | } | |
189 | ||
e2ac846e RL |
190 | static int serialize_EVP_PKEY_legacy_PEM(void **serialized, |
191 | long *serialized_len, | |
192 | void *object, | |
193 | const char *pass, const char *pcipher, | |
194 | ossl_unused const char *ser_propq) | |
195 | { | |
196 | EVP_PKEY *pkey = object; | |
197 | EVP_CIPHER *cipher = NULL; | |
198 | BIO *mem_ser = NULL; | |
199 | BUF_MEM *mem_buf = NULL; | |
200 | const unsigned char *upass = (const unsigned char *)pass; | |
201 | size_t passlen = 0; | |
202 | int ok = 0; | |
203 | ||
204 | if (pcipher != NULL && pass != NULL) { | |
205 | passlen = strlen(pass); | |
206 | if (!TEST_ptr(cipher = EVP_CIPHER_fetch(NULL, pcipher, NULL))) | |
207 | goto end; | |
208 | } | |
209 | if (!TEST_ptr(mem_ser = BIO_new(BIO_s_mem())) | |
210 | || !TEST_true(PEM_write_bio_PrivateKey_traditional(mem_ser, pkey, | |
211 | cipher, | |
212 | upass, passlen, | |
213 | NULL, NULL)) | |
214 | || !TEST_true(BIO_get_mem_ptr(mem_ser, &mem_buf) > 0) | |
215 | || !TEST_ptr(*serialized = mem_buf->data) | |
216 | || !TEST_long_gt(*serialized_len = mem_buf->length, 0)) | |
217 | goto end; | |
218 | ||
219 | /* Detach the serialized output */ | |
220 | mem_buf->data = NULL; | |
221 | mem_buf->length = 0; | |
222 | ok = 1; | |
223 | end: | |
224 | BIO_free(mem_ser); | |
225 | EVP_CIPHER_free(cipher); | |
226 | return ok; | |
227 | } | |
228 | ||
5a23d78c RL |
229 | /* Test cases and their dumpers / checkers */ |
230 | ||
231 | static void dump_der(const char *label, const void *data, size_t data_len) | |
232 | { | |
233 | test_output_memory(label, data, data_len); | |
234 | } | |
235 | ||
236 | static void dump_pem(const char *label, const void *data, size_t data_len) | |
237 | { | |
238 | test_output_string(label, data, data_len - 1); | |
239 | } | |
240 | ||
846f96f8 | 241 | static int check_unprotected_PKCS8_DER(const char *type, |
3ecbea6a | 242 | const void *data, size_t data_len) |
5a23d78c RL |
243 | { |
244 | const unsigned char *datap = data; | |
245 | PKCS8_PRIV_KEY_INFO *p8inf = | |
246 | d2i_PKCS8_PRIV_KEY_INFO(NULL, &datap, data_len); | |
247 | int ok = 0; | |
248 | ||
249 | if (TEST_ptr(p8inf)) { | |
250 | EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf); | |
251 | ||
846f96f8 | 252 | ok = (TEST_ptr(pkey) && TEST_true(EVP_PKEY_is_a(pkey, type))); |
5a23d78c RL |
253 | EVP_PKEY_free(pkey); |
254 | } | |
255 | PKCS8_PRIV_KEY_INFO_free(p8inf); | |
256 | return ok; | |
257 | } | |
258 | ||
3ecbea6a | 259 | static int test_unprotected_RSA_via_DER(void) |
5a23d78c | 260 | { |
846f96f8 | 261 | return test_serialize_deserialize("RSA", key_RSA, NULL, NULL, |
e2ac846e RL |
262 | serialize_EVP_PKEY_prov, |
263 | deserialize_EVP_PKEY_prov, | |
3ecbea6a | 264 | check_unprotected_PKCS8_DER, dump_der, |
e2ac846e RL |
265 | OSSL_SERIALIZER_PrivateKey_TO_DER_PQ, |
266 | 0); | |
5a23d78c RL |
267 | } |
268 | ||
846f96f8 RL |
269 | static int test_unprotected_RSA_PSS_via_DER(void) |
270 | { | |
271 | return test_serialize_deserialize("RSA-PSS", key_RSA_PSS, NULL, NULL, | |
272 | serialize_EVP_PKEY_prov, | |
273 | deserialize_EVP_PKEY_prov, | |
274 | check_unprotected_PKCS8_DER, dump_der, | |
275 | OSSL_SERIALIZER_PrivateKey_TO_DER_PQ, | |
276 | 0); | |
277 | } | |
278 | ||
279 | static int check_unprotected_PKCS8_PEM(const char *type, | |
3ecbea6a | 280 | const void *data, size_t data_len) |
5a23d78c RL |
281 | { |
282 | static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8INF "-----"; | |
283 | ||
284 | return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1); | |
285 | } | |
286 | ||
3ecbea6a | 287 | static int test_unprotected_RSA_via_PEM(void) |
5a23d78c | 288 | { |
846f96f8 RL |
289 | return test_serialize_deserialize("RSA", key_RSA, NULL, NULL, |
290 | serialize_EVP_PKEY_prov, | |
291 | deserialize_EVP_PKEY_prov, | |
292 | check_unprotected_PKCS8_PEM, dump_pem, | |
293 | OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ, | |
294 | 0); | |
295 | } | |
296 | ||
297 | static int test_unprotected_RSA_PSS_via_PEM(void) | |
298 | { | |
299 | return test_serialize_deserialize("RSA-PSS", key_RSA_PSS, NULL, NULL, | |
e2ac846e RL |
300 | serialize_EVP_PKEY_prov, |
301 | deserialize_EVP_PKEY_prov, | |
3ecbea6a | 302 | check_unprotected_PKCS8_PEM, dump_pem, |
e2ac846e RL |
303 | OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ, |
304 | 0); | |
305 | } | |
306 | ||
846f96f8 | 307 | static int check_unprotected_legacy_PEM(const char *type, |
e2ac846e RL |
308 | const void *data, size_t data_len) |
309 | { | |
846f96f8 | 310 | static char pem_header[80]; |
e2ac846e | 311 | |
846f96f8 RL |
312 | return |
313 | TEST_int_gt(BIO_snprintf(pem_header, sizeof(pem_header), | |
314 | "-----BEGIN %s PRIVATE KEY-----", type), 0) | |
315 | && TEST_strn_eq(data, pem_header, strlen(pem_header)); | |
e2ac846e RL |
316 | } |
317 | ||
318 | static int test_unprotected_RSA_via_legacy_PEM(void) | |
319 | { | |
846f96f8 RL |
320 | return test_serialize_deserialize("RSA", legacy_key_RSA, NULL, NULL, |
321 | serialize_EVP_PKEY_legacy_PEM, | |
322 | deserialize_EVP_PKEY_prov, | |
323 | check_unprotected_legacy_PEM, dump_pem, | |
324 | NULL, 1); | |
325 | } | |
326 | ||
327 | static int test_unprotected_RSA_PSS_via_legacy_PEM(void) | |
328 | { | |
329 | return test_serialize_deserialize("RSA-PSS", legacy_key_RSA_PSS, NULL, NULL, | |
e2ac846e RL |
330 | serialize_EVP_PKEY_legacy_PEM, |
331 | deserialize_EVP_PKEY_prov, | |
332 | check_unprotected_legacy_PEM, dump_pem, | |
333 | NULL, 1); | |
3ecbea6a RL |
334 | } |
335 | ||
336 | static const char *pass_cipher = "AES-256-CBC"; | |
337 | static const char *pass = "the holy handgrenade of antioch"; | |
338 | ||
846f96f8 | 339 | static int check_protected_PKCS8_DER(const char *type, |
3ecbea6a RL |
340 | const void *data, size_t data_len) |
341 | { | |
342 | const unsigned char *datap = data; | |
343 | X509_SIG *p8 = d2i_X509_SIG(NULL, &datap, data_len); | |
344 | int ok = TEST_ptr(p8); | |
345 | ||
346 | X509_SIG_free(p8); | |
347 | return ok; | |
348 | } | |
349 | ||
350 | static int test_protected_RSA_via_DER(void) | |
351 | { | |
846f96f8 RL |
352 | return test_serialize_deserialize("RSA", key_RSA, pass, pass_cipher, |
353 | serialize_EVP_PKEY_prov, | |
354 | deserialize_EVP_PKEY_prov, | |
355 | check_protected_PKCS8_DER, dump_der, | |
356 | OSSL_SERIALIZER_PrivateKey_TO_DER_PQ, | |
357 | 0); | |
358 | } | |
359 | ||
360 | static int test_protected_RSA_PSS_via_DER(void) | |
361 | { | |
362 | return test_serialize_deserialize("RSA", key_RSA, pass, pass_cipher, | |
e2ac846e RL |
363 | serialize_EVP_PKEY_prov, |
364 | deserialize_EVP_PKEY_prov, | |
3ecbea6a | 365 | check_protected_PKCS8_DER, dump_der, |
e2ac846e RL |
366 | OSSL_SERIALIZER_PrivateKey_TO_DER_PQ, |
367 | 0); | |
3ecbea6a RL |
368 | } |
369 | ||
846f96f8 | 370 | static int check_protected_PKCS8_PEM(const char *type, |
3ecbea6a RL |
371 | const void *data, size_t data_len) |
372 | { | |
373 | static const char pem_header[] = "-----BEGIN " PEM_STRING_PKCS8 "-----"; | |
374 | ||
375 | return TEST_strn_eq(data, pem_header, sizeof(pem_header) - 1); | |
376 | } | |
377 | ||
378 | static int test_protected_RSA_via_PEM(void) | |
379 | { | |
846f96f8 RL |
380 | return test_serialize_deserialize("RSA", key_RSA, pass, pass_cipher, |
381 | serialize_EVP_PKEY_prov, | |
382 | deserialize_EVP_PKEY_prov, | |
383 | check_protected_PKCS8_PEM, dump_pem, | |
384 | OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ, | |
385 | 0); | |
386 | } | |
387 | ||
388 | static int test_protected_RSA_PSS_via_PEM(void) | |
389 | { | |
390 | return test_serialize_deserialize("RSA-PSS", key_RSA_PSS, pass, pass_cipher, | |
e2ac846e RL |
391 | serialize_EVP_PKEY_prov, |
392 | deserialize_EVP_PKEY_prov, | |
3ecbea6a | 393 | check_protected_PKCS8_PEM, dump_pem, |
e2ac846e RL |
394 | OSSL_SERIALIZER_PrivateKey_TO_PEM_PQ, |
395 | 0); | |
396 | } | |
397 | ||
846f96f8 | 398 | static int check_protected_legacy_PEM(const char *type, |
e2ac846e RL |
399 | const void *data, size_t data_len) |
400 | { | |
846f96f8 | 401 | static char pem_header[80]; |
e2ac846e RL |
402 | |
403 | return | |
846f96f8 RL |
404 | TEST_int_gt(BIO_snprintf(pem_header, sizeof(pem_header), |
405 | "-----BEGIN %s PRIVATE KEY-----", type), 0) | |
406 | && TEST_strn_eq(data, pem_header, strlen(pem_header)) | |
e2ac846e RL |
407 | && TEST_ptr(strstr(data, "\nDEK-Info: ")); |
408 | } | |
409 | ||
410 | static int test_protected_RSA_via_legacy_PEM(void) | |
411 | { | |
846f96f8 RL |
412 | return test_serialize_deserialize("RSA", legacy_key_RSA, pass, pass_cipher, |
413 | serialize_EVP_PKEY_legacy_PEM, | |
414 | deserialize_EVP_PKEY_prov, | |
415 | check_protected_legacy_PEM, dump_pem, | |
416 | NULL, 1); | |
417 | } | |
418 | ||
419 | static int test_protected_RSA_PSS_via_legacy_PEM(void) | |
420 | { | |
421 | return test_serialize_deserialize("RSA-PSS", legacy_key_RSA_PSS, | |
422 | pass, pass_cipher, | |
e2ac846e RL |
423 | serialize_EVP_PKEY_legacy_PEM, |
424 | deserialize_EVP_PKEY_prov, | |
425 | check_protected_legacy_PEM, dump_pem, | |
426 | NULL, 1); | |
5a23d78c RL |
427 | } |
428 | ||
429 | int setup_tests(void) | |
430 | { | |
e2ac846e RL |
431 | TEST_info("Generating keys..."); |
432 | if (!TEST_ptr(key_RSA = make_RSA("RSA", 0)) | |
846f96f8 RL |
433 | || !TEST_ptr(legacy_key_RSA = make_RSA("RSA", 1)) |
434 | || !TEST_ptr(key_RSA_PSS = make_RSA("RSA-PSS", 0)) | |
435 | || !TEST_ptr(legacy_key_RSA_PSS = make_RSA("RSA-PSS", 1))) { | |
e2ac846e RL |
436 | EVP_PKEY_free(key_RSA); |
437 | EVP_PKEY_free(legacy_key_RSA); | |
846f96f8 RL |
438 | EVP_PKEY_free(key_RSA_PSS); |
439 | EVP_PKEY_free(legacy_key_RSA_PSS); | |
5a23d78c | 440 | return 0; |
e2ac846e | 441 | } |
5a23d78c RL |
442 | TEST_info("Generating key... done"); |
443 | ||
3ecbea6a RL |
444 | ADD_TEST(test_unprotected_RSA_via_DER); |
445 | ADD_TEST(test_unprotected_RSA_via_PEM); | |
e2ac846e | 446 | ADD_TEST(test_unprotected_RSA_via_legacy_PEM); |
3ecbea6a RL |
447 | ADD_TEST(test_protected_RSA_via_DER); |
448 | ADD_TEST(test_protected_RSA_via_PEM); | |
e2ac846e | 449 | ADD_TEST(test_protected_RSA_via_legacy_PEM); |
846f96f8 RL |
450 | ADD_TEST(test_unprotected_RSA_PSS_via_DER); |
451 | ADD_TEST(test_unprotected_RSA_PSS_via_PEM); | |
452 | ADD_TEST(test_unprotected_RSA_PSS_via_legacy_PEM); | |
453 | ADD_TEST(test_protected_RSA_PSS_via_DER); | |
454 | ADD_TEST(test_protected_RSA_PSS_via_PEM); | |
455 | ADD_TEST(test_protected_RSA_PSS_via_legacy_PEM); | |
5a23d78c RL |
456 | |
457 | return 1; | |
458 | } |