xref: /openssl/test/bio_pw_callback_test.c (revision d52e92f8)
1 /*
2  * Copyright 2024 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 
10 #include "testutil.h"
11 
12 #include <openssl/bio.h>
13 #include <openssl/pem.h>
14 
15 /* dummy data that needs to be passed to the callback */
16 typedef struct CallbackData {
17     char magic;
18     int result;
19 } CALLBACK_DATA;
20 
21 /* constants */
22 static const char weak_password[] = "weak_password";
23 static const char a0a_password[] = "aaaaaaaa\0aaaaaaaa";
24 static const char a0b_password[] = "aaaaaaaa\0bbbbbbbb";
25 static const char cb_magic = 'p';
26 
27 /* shared working data for all tests */
28 static char *key_file = NULL;
29 static EVP_PKEY *original_pkey = NULL;
30 
31 /* the test performed by the callback */
32 typedef enum CallbackTest {
33     CB_TEST_NEGATIVE = 0,
34     CB_TEST_ZERO_LENGTH,
35     CB_TEST_WEAK,
36     CB_TEST_16ZERO,
37     CB_TEST_A0A,
38     CB_TEST_A0B,
39     CB_TEST_MATCH_SIZE,
40     CB_TEST_EXCEED_SIZE
41 } CALLBACK_TEST;
42 static CALLBACK_TEST callback_test = CB_TEST_NEGATIVE;
43 
44 typedef enum KeyEncoding {
45     KE_PEM = 0,
46     KE_PKCS8
47 } KEY_ENCODING;
48 
49 typedef enum ExpectedResult {
50     ER_FAILURE = 0,
51     ER_SUCCESS
52 } EXPECTED_RESULT;
53 
54 typedef enum OPTION_choice {
55     OPT_ERR = -1,
56     OPT_EOF = 0,
57     OPT_KEY_FILE,
58     OPT_TEST_ENUM
59 } OPTION_CHOICE;
60 
test_get_options(void)61 const OPTIONS *test_get_options(void)
62 {
63     static const OPTIONS test_options[] = {
64         OPT_TEST_OPTIONS_DEFAULT_USAGE,
65         { "keyfile", OPT_KEY_FILE, '<',
66           "The PEM file with the encrypted key to load" },
67         { NULL }
68     };
69     return test_options;
70 }
71 
callback_copy_password(char * buf,int size)72 static int callback_copy_password(char *buf, int size)
73 {
74     int ret = -1;
75 
76     switch (callback_test) {
77     case CB_TEST_NEGATIVE:
78         break;
79     case CB_TEST_ZERO_LENGTH:
80         ret = 0;
81         break;
82     case CB_TEST_WEAK:
83         ret = sizeof(weak_password) - 1;
84         memcpy(buf, weak_password, ret);
85         break;
86     case CB_TEST_16ZERO:
87         memset(buf, 0, 16);
88         ret = 16;
89         break;
90     case CB_TEST_A0A:
91         ret = sizeof(a0a_password) - 1;
92         memcpy(buf, a0a_password, ret);
93         break;
94     case CB_TEST_A0B:
95         ret = sizeof(a0b_password) - 1;
96         memcpy(buf, a0b_password, ret);
97         break;
98     case CB_TEST_MATCH_SIZE:
99         memset(buf, 'e', size);
100         ret = size;
101         break;
102     case CB_TEST_EXCEED_SIZE:
103         memset(buf, 'e', size);
104         ret = 1000000;
105         break;
106     }
107     return ret;
108 }
109 
read_callback(char * buf,int size,int rwflag,void * u)110 static int read_callback(char *buf, int size, int rwflag, void *u)
111 {
112     CALLBACK_DATA *cb_data = (CALLBACK_DATA *)u;
113     int ret = -1;
114 
115     /* basic verification of the received data */
116     if (!TEST_ptr(cb_data))
117         goto err;
118     if (!TEST_char_eq(cb_data->magic, cb_magic))
119         goto err;
120     if (!TEST_ptr(buf))
121         goto err;
122     if (!TEST_int_gt(size, 0))
123         goto err;
124     if (!TEST_int_eq(rwflag, 0))
125         goto err;
126     ret = callback_copy_password(buf, size);
127     cb_data->result = 1;
128 err:
129     return ret;
130 }
131 
write_callback(char * buf,int size,int rwflag,void * u)132 static int write_callback(char *buf, int size, int rwflag, void *u)
133 {
134     CALLBACK_DATA *cb_data = (CALLBACK_DATA *)u;
135     int ret = -1;
136 
137     /* basic verification of the received data */
138     if (!TEST_ptr(cb_data))
139         goto err;
140     if (!TEST_char_eq(cb_data->magic, cb_magic))
141         goto err;
142     if (!TEST_ptr(buf))
143         goto err;
144     if (!TEST_int_gt(size, 0))
145         goto err;
146     if (!TEST_int_eq(rwflag, 1))
147         goto err;
148     ret = callback_copy_password(buf, size);
149     cb_data->result = 1;
150 err:
151     return ret;
152 }
153 
re_encrypt_key(char ** enc_data,int * enc_data_size,KEY_ENCODING key_encoding)154 static int re_encrypt_key(char **enc_data, int *enc_data_size,
155                           KEY_ENCODING key_encoding)
156 {
157     CALLBACK_DATA cb_data;
158     int w_ret = 0;
159     BUF_MEM *bptr = NULL;
160     BIO *bio = NULL;
161     int ret = 0;
162 
163     if (!TEST_ptr(enc_data))
164         goto err;
165     if (!TEST_ptr(enc_data_size))
166         goto err;
167     if (!TEST_ptr(bio = BIO_new(BIO_s_mem())))
168         goto err;
169     cb_data.magic = cb_magic;
170     cb_data.result = 0;
171     switch (key_encoding) {
172     case KE_PEM:
173         w_ret = PEM_write_bio_PrivateKey(bio, original_pkey, EVP_aes_256_cbc(),
174                                          NULL, 0, write_callback, &cb_data);
175         break;
176     case KE_PKCS8:
177         w_ret = i2d_PKCS8PrivateKey_bio(bio, original_pkey, EVP_aes_256_cbc(),
178                                         NULL, 0, write_callback, &cb_data);
179         break;
180     }
181     if (!TEST_int_ne(w_ret, 0))
182         goto err;
183     if (!TEST_char_eq(cb_data.magic, cb_magic))
184         goto err;
185     if (!TEST_int_eq(cb_data.result, 1))
186         goto err;
187     *enc_data_size = BIO_get_mem_data(bio, enc_data);
188     BIO_get_mem_ptr(bio, &bptr);
189     if (!BIO_set_close(bio, BIO_NOCLOSE))
190         goto err;
191     bptr->data = NULL;
192     ret = 1;
193 err:
194     BUF_MEM_free(bptr);
195     BIO_free(bio);
196     return ret;
197 }
198 
decrypt_key(char * enc_data,int enc_data_size,KEY_ENCODING key_encoding,EXPECTED_RESULT expected_result)199 static int decrypt_key(char *enc_data, int enc_data_size,
200                        KEY_ENCODING key_encoding,
201                        EXPECTED_RESULT expected_result)
202 {
203     CALLBACK_DATA cb_data;
204     EVP_PKEY *r_ret = NULL;
205     BIO *bio = NULL;
206     EVP_PKEY *pkey = NULL;
207     int ret = 0;
208 
209     if (!TEST_ptr(bio = BIO_new_mem_buf(enc_data, enc_data_size)))
210         goto err;
211     cb_data.magic = cb_magic;
212     cb_data.result = 0;
213     switch (key_encoding) {
214     case KE_PEM:
215         r_ret = PEM_read_bio_PrivateKey(bio, &pkey, read_callback, &cb_data);
216         break;
217     case KE_PKCS8:
218         r_ret = d2i_PKCS8PrivateKey_bio(bio, &pkey, read_callback, &cb_data);
219         break;
220     }
221     if (expected_result == ER_SUCCESS) {
222         if (!TEST_ptr(r_ret))
223             goto err;
224     } else {
225         if (!TEST_ptr_null(r_ret))
226             goto err;
227     }
228     if (!TEST_char_eq(cb_data.magic, cb_magic))
229         goto err;
230     if (!TEST_int_eq(cb_data.result, 1))
231         goto err;
232     ret = 1;
233 err:
234     EVP_PKEY_free(pkey);
235     BIO_free(bio);
236     return ret;
237 }
238 
full_cycle_test(KEY_ENCODING key_encoding,CALLBACK_TEST write_test,CALLBACK_TEST read_test,EXPECTED_RESULT expected_read_result)239 static int full_cycle_test(KEY_ENCODING key_encoding, CALLBACK_TEST write_test,
240                            CALLBACK_TEST read_test,
241                            EXPECTED_RESULT expected_read_result)
242 {
243     char *enc_data = NULL;
244     int enc_data_size = 0;
245     int ret = 0;
246 
247     callback_test = write_test;
248     if (!re_encrypt_key(&enc_data, &enc_data_size, key_encoding))
249         goto err;
250     callback_test = read_test;
251     if (!decrypt_key(enc_data, enc_data_size, key_encoding,
252                      expected_read_result))
253         goto err;
254     ret = 1;
255 err:
256     OPENSSL_free(enc_data);
257     return ret;
258 }
259 
test_pem_negative(void)260 static int test_pem_negative(void)
261 {
262     return full_cycle_test(KE_PEM, CB_TEST_WEAK, CB_TEST_NEGATIVE, ER_FAILURE);
263 }
264 
test_pem_zero_length(void)265 static int test_pem_zero_length(void)
266 {
267     return full_cycle_test(KE_PEM, CB_TEST_ZERO_LENGTH, CB_TEST_ZERO_LENGTH,
268                            ER_SUCCESS);
269 }
270 
test_pem_weak(void)271 static int test_pem_weak(void)
272 {
273     return full_cycle_test(KE_PEM, CB_TEST_WEAK, CB_TEST_WEAK, ER_SUCCESS);
274 }
275 
test_pem_16zero(void)276 static int test_pem_16zero(void)
277 {
278     return full_cycle_test(KE_PEM, CB_TEST_16ZERO, CB_TEST_16ZERO, ER_SUCCESS);
279 }
280 
test_pem_a0a(void)281 static int test_pem_a0a(void)
282 {
283     return full_cycle_test(KE_PEM, CB_TEST_A0A, CB_TEST_A0A, ER_SUCCESS);
284 }
285 
test_pem_a0a_a0b(void)286 static int test_pem_a0a_a0b(void)
287 {
288     return full_cycle_test(KE_PEM, CB_TEST_A0A, CB_TEST_A0B, ER_FAILURE);
289 }
290 
test_pem_match_size(void)291 static int test_pem_match_size(void)
292 {
293     return full_cycle_test(KE_PEM, CB_TEST_MATCH_SIZE, CB_TEST_MATCH_SIZE,
294                            ER_SUCCESS);
295 }
296 
test_pem_exceed_size(void)297 static int test_pem_exceed_size(void)
298 {
299     return full_cycle_test(KE_PEM, CB_TEST_MATCH_SIZE, CB_TEST_EXCEED_SIZE,
300                            ER_FAILURE);
301 }
302 
test_pkcs8_negative(void)303 static int test_pkcs8_negative(void)
304 {
305     return full_cycle_test(KE_PKCS8, CB_TEST_WEAK, CB_TEST_NEGATIVE, ER_FAILURE);
306 }
307 
test_pkcs8_zero_length(void)308 static int test_pkcs8_zero_length(void)
309 {
310     return full_cycle_test(KE_PKCS8, CB_TEST_ZERO_LENGTH, CB_TEST_ZERO_LENGTH,
311                            ER_SUCCESS);
312 }
313 
test_pkcs8_weak(void)314 static int test_pkcs8_weak(void)
315 {
316     return full_cycle_test(KE_PKCS8, CB_TEST_WEAK, CB_TEST_WEAK, ER_SUCCESS);
317 }
318 
test_pkcs8_16zero(void)319 static int test_pkcs8_16zero(void)
320 {
321     return full_cycle_test(KE_PKCS8, CB_TEST_16ZERO, CB_TEST_16ZERO,
322                            ER_SUCCESS);
323 }
324 
test_pkcs8_a0a(void)325 static int test_pkcs8_a0a(void)
326 {
327     return full_cycle_test(KE_PKCS8, CB_TEST_A0A, CB_TEST_A0A, ER_SUCCESS);
328 }
329 
test_pkcs8_a0a_a0b(void)330 static int test_pkcs8_a0a_a0b(void)
331 {
332     return full_cycle_test(KE_PKCS8, CB_TEST_A0A, CB_TEST_A0B, ER_FAILURE);
333 }
334 
test_pkcs8_match_size(void)335 static int test_pkcs8_match_size(void)
336 {
337     return full_cycle_test(KE_PKCS8, CB_TEST_MATCH_SIZE, CB_TEST_MATCH_SIZE,
338                            ER_SUCCESS);
339 }
340 
test_pkcs8_exceed_size(void)341 static int test_pkcs8_exceed_size(void)
342 {
343     return full_cycle_test(KE_PKCS8, CB_TEST_MATCH_SIZE, CB_TEST_EXCEED_SIZE,
344                            ER_FAILURE);
345 }
346 
callback_original_pw(char * buf,int size,int rwflag,void * u)347 static int callback_original_pw(char *buf, int size, int rwflag, void *u)
348 {
349     memcpy(buf, weak_password, sizeof(weak_password) - 1);
350     return sizeof(weak_password) - 1;
351 }
352 
setup_tests(void)353 int setup_tests(void)
354 {
355     OPTION_CHOICE o;
356     BIO *bio = NULL;
357 
358     while ((o = opt_next()) != OPT_EOF) {
359         switch (o) {
360         case OPT_KEY_FILE:
361             key_file = opt_arg();
362             break;
363         case OPT_TEST_CASES:
364             break;
365         default:
366         case OPT_ERR:
367             return 0;
368         }
369     }
370 
371     /* read the original key */
372     if (!TEST_ptr(bio = BIO_new_file(key_file, "r")))
373         return 0;
374     if (!TEST_ptr(PEM_read_bio_PrivateKey(bio, &original_pkey,
375                                           callback_original_pw, NULL)))
376         return 0;
377     BIO_free(bio);
378 
379     /* add all tests */
380     ADD_TEST(test_pem_negative);
381     ADD_TEST(test_pem_zero_length);
382     ADD_TEST(test_pem_weak);
383     ADD_TEST(test_pem_16zero);
384     ADD_TEST(test_pem_a0a);
385     ADD_TEST(test_pem_a0a_a0b);
386     ADD_TEST(test_pem_match_size);
387     ADD_TEST(test_pem_exceed_size);
388     ADD_TEST(test_pkcs8_negative);
389     ADD_TEST(test_pkcs8_zero_length);
390     ADD_TEST(test_pkcs8_weak);
391     ADD_TEST(test_pkcs8_16zero);
392     ADD_TEST(test_pkcs8_a0a);
393     ADD_TEST(test_pkcs8_a0a_a0b);
394     ADD_TEST(test_pkcs8_match_size);
395     ADD_TEST(test_pkcs8_exceed_size);
396     return 1;
397 }
398 
cleanup_tests(void)399 void cleanup_tests(void)
400 {
401     EVP_PKEY_free(original_pkey);
402 }
403