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