1 1.1 christos /* 2 1.1 christos * Copyright 2016-2025 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 #include <stdio.h> 10 1.1 christos #include <string.h> 11 1.1 christos #include <openssl/evp.h> 12 1.1 christos #include <openssl/bio.h> 13 1.1 christos #include <openssl/rand.h> 14 1.1 christos 15 1.1 christos #include "testutil.h" 16 1.1 christos 17 1.1.1.2 christos #define ENCRYPT 1 18 1.1.1.2 christos #define DECRYPT 0 19 1.1 christos 20 1.1.1.2 christos #define DATA_SIZE 1024 21 1.1.1.2 christos #define MAX_IV 32 22 1.1.1.2 christos #define BUF_SIZE (DATA_SIZE + MAX_IV) 23 1.1 christos 24 1.1 christos static const unsigned char KEY[] = { 25 1.1 christos 0x51, 0x50, 0xd1, 0x77, 0x2f, 0x50, 0x83, 0x4a, 26 1.1 christos 0x50, 0x3e, 0x06, 0x9a, 0x97, 0x3f, 0xbd, 0x7c, 27 1.1 christos 0xe6, 0x1c, 0x43, 0x2b, 0x72, 0x0b, 0x19, 0xd1, 28 1.1 christos 0x8e, 0xc8, 0xd8, 0x4b, 0xdc, 0x63, 0x15, 0x1b 29 1.1 christos }; 30 1.1 christos 31 1.1 christos static const unsigned char IV[] = { 32 1.1 christos 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 33 1.1 christos 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 34 1.1 christos 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 35 1.1 christos 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 36 1.1 christos }; 37 1.1 christos 38 1.1.1.2 christos static int do_bio_cipher(const EVP_CIPHER *cipher, const unsigned char *key, 39 1.1.1.2 christos const unsigned char *iv) 40 1.1 christos { 41 1.1 christos BIO *b, *mem; 42 1.1 christos static unsigned char inp[BUF_SIZE] = { 0 }; 43 1.1 christos unsigned char out[BUF_SIZE], ref[BUF_SIZE]; 44 1.1 christos int i, lref, len, tmplen; 45 1.1 christos 46 1.1 christos /* Fill buffer with non-zero data so that over steps can be detected */ 47 1.1 christos if (!TEST_int_gt(RAND_bytes(inp, DATA_SIZE), 0)) 48 1.1 christos return 0; 49 1.1 christos 50 1.1 christos /* Encrypt tests */ 51 1.1 christos 52 1.1 christos /* reference output for single-chunk operation */ 53 1.1 christos b = BIO_new(BIO_f_cipher()); 54 1.1 christos if (!TEST_ptr(b)) 55 1.1 christos return 0; 56 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, ENCRYPT))) 57 1.1 christos goto err; 58 1.1 christos mem = BIO_new_mem_buf(inp, DATA_SIZE); 59 1.1 christos if (!TEST_ptr(mem)) 60 1.1 christos goto err; 61 1.1 christos BIO_push(b, mem); 62 1.1 christos lref = BIO_read(b, ref, sizeof(ref)); 63 1.1 christos BIO_free_all(b); 64 1.1 christos 65 1.1 christos /* perform split operations and compare to reference */ 66 1.1 christos for (i = 1; i < lref; i++) { 67 1.1 christos b = BIO_new(BIO_f_cipher()); 68 1.1 christos if (!TEST_ptr(b)) 69 1.1 christos return 0; 70 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, ENCRYPT))) { 71 1.1 christos TEST_info("Split encrypt failed @ operation %d", i); 72 1.1 christos goto err; 73 1.1 christos } 74 1.1 christos mem = BIO_new_mem_buf(inp, DATA_SIZE); 75 1.1 christos if (!TEST_ptr(mem)) 76 1.1 christos goto err; 77 1.1 christos BIO_push(b, mem); 78 1.1 christos memset(out, 0, sizeof(out)); 79 1.1 christos out[i] = ~ref[i]; 80 1.1 christos tmplen = BIO_read(b, out, i); 81 1.1 christos if (tmplen < 0) 82 1.1 christos goto err; 83 1.1 christos len = tmplen; 84 1.1 christos /* check for overstep */ 85 1.1 christos if (!TEST_uchar_eq(out[i], (unsigned char)~ref[i])) { 86 1.1 christos TEST_info("Encrypt overstep check failed @ operation %d", i); 87 1.1 christos goto err; 88 1.1 christos } 89 1.1 christos tmplen = BIO_read(b, out + len, sizeof(out) - len); 90 1.1 christos if (tmplen < 0) 91 1.1 christos goto err; 92 1.1 christos len += tmplen; 93 1.1 christos 94 1.1 christos BIO_free_all(b); 95 1.1 christos 96 1.1 christos if (!TEST_mem_eq(out, len, ref, lref)) { 97 1.1 christos TEST_info("Encrypt compare failed @ operation %d", i); 98 1.1 christos return 0; 99 1.1 christos } 100 1.1 christos } 101 1.1 christos 102 1.1 christos /* perform small-chunk operations and compare to reference */ 103 1.1 christos for (i = 1; i < lref / 2; i++) { 104 1.1 christos int delta; 105 1.1 christos 106 1.1 christos b = BIO_new(BIO_f_cipher()); 107 1.1 christos if (!TEST_ptr(b)) 108 1.1 christos return 0; 109 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, ENCRYPT))) { 110 1.1 christos TEST_info("Small chunk encrypt failed @ operation %d", i); 111 1.1 christos goto err; 112 1.1 christos } 113 1.1 christos mem = BIO_new_mem_buf(inp, DATA_SIZE); 114 1.1 christos if (!TEST_ptr(mem)) 115 1.1 christos goto err; 116 1.1 christos BIO_push(b, mem); 117 1.1 christos memset(out, 0, sizeof(out)); 118 1.1.1.2 christos for (len = 0; (delta = BIO_read(b, out + len, i));) { 119 1.1 christos len += delta; 120 1.1 christos } 121 1.1 christos BIO_free_all(b); 122 1.1 christos 123 1.1 christos if (!TEST_mem_eq(out, len, ref, lref)) { 124 1.1 christos TEST_info("Small chunk encrypt compare failed @ operation %d", i); 125 1.1 christos return 0; 126 1.1 christos } 127 1.1 christos } 128 1.1 christos 129 1.1 christos /* Decrypt tests */ 130 1.1 christos 131 1.1 christos /* reference output for single-chunk operation */ 132 1.1 christos b = BIO_new(BIO_f_cipher()); 133 1.1 christos if (!TEST_ptr(b)) 134 1.1 christos return 0; 135 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, DECRYPT))) 136 1.1 christos goto err; 137 1.1 christos /* Use original reference output as input */ 138 1.1 christos mem = BIO_new_mem_buf(ref, lref); 139 1.1 christos if (!TEST_ptr(mem)) 140 1.1 christos goto err; 141 1.1 christos BIO_push(b, mem); 142 1.1 christos #if 0 143 1.1 christos /* 144 1.1 christos * This is wrong to do, it always fails, and incorrectly ends up 145 1.1 christos * calling `EVP_CipherFinal()` and setting ctx->finished = 1, ... 146 1.1 christos * all of which are unwanted. But just deleting this is less 147 1.1 christos * instructive to future readers of the code. Don't call BIO_flush 148 1.1 christos * until you're done either reading or writing and want to finalise 149 1.1 christos * the state. 150 1.1 christos */ 151 1.1 christos (void)BIO_flush(b); 152 1.1 christos #endif 153 1.1 christos memset(out, 0, sizeof(out)); 154 1.1 christos len = BIO_read(b, out, sizeof(out)); 155 1.1 christos BIO_free_all(b); 156 1.1 christos 157 1.1 christos if (!TEST_mem_eq(inp, DATA_SIZE, out, len)) 158 1.1 christos return 0; 159 1.1 christos 160 1.1 christos /* perform split operations and compare to reference */ 161 1.1 christos for (i = 1; i < lref; i++) { 162 1.1 christos b = BIO_new(BIO_f_cipher()); 163 1.1 christos if (!TEST_ptr(b)) 164 1.1 christos return 0; 165 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, DECRYPT))) { 166 1.1 christos TEST_info("Split decrypt failed @ operation %d", i); 167 1.1 christos goto err; 168 1.1 christos } 169 1.1 christos mem = BIO_new_mem_buf(ref, lref); 170 1.1 christos if (!TEST_ptr(mem)) 171 1.1 christos goto err; 172 1.1 christos BIO_push(b, mem); 173 1.1 christos memset(out, 0, sizeof(out)); 174 1.1 christos out[i] = ~ref[i]; 175 1.1 christos len = BIO_read(b, out, i); 176 1.1 christos /* check for overstep */ 177 1.1 christos if (!TEST_uchar_eq(out[i], (unsigned char)~ref[i])) { 178 1.1 christos TEST_info("Decrypt overstep check failed @ operation %d", i); 179 1.1 christos goto err; 180 1.1 christos } 181 1.1 christos len += BIO_read(b, out + len, sizeof(out) - len); 182 1.1 christos BIO_free_all(b); 183 1.1 christos 184 1.1 christos if (!TEST_mem_eq(inp, DATA_SIZE, out, len)) { 185 1.1 christos TEST_info("Decrypt compare failed @ operation %d", i); 186 1.1 christos return 0; 187 1.1 christos } 188 1.1 christos } 189 1.1 christos 190 1.1 christos /* perform small-chunk operations and compare to reference */ 191 1.1 christos for (i = 1; i < lref / 2; i++) { 192 1.1 christos int delta; 193 1.1 christos 194 1.1 christos b = BIO_new(BIO_f_cipher()); 195 1.1 christos if (!TEST_ptr(b)) 196 1.1 christos return 0; 197 1.1 christos if (!TEST_true(BIO_set_cipher(b, cipher, key, iv, DECRYPT))) { 198 1.1 christos TEST_info("Small chunk decrypt failed @ operation %d", i); 199 1.1 christos goto err; 200 1.1 christos } 201 1.1 christos mem = BIO_new_mem_buf(ref, lref); 202 1.1 christos if (!TEST_ptr(mem)) 203 1.1 christos goto err; 204 1.1 christos BIO_push(b, mem); 205 1.1 christos memset(out, 0, sizeof(out)); 206 1.1.1.2 christos for (len = 0; (delta = BIO_read(b, out + len, i));) { 207 1.1 christos len += delta; 208 1.1 christos } 209 1.1 christos BIO_free_all(b); 210 1.1 christos 211 1.1 christos if (!TEST_mem_eq(inp, DATA_SIZE, out, len)) { 212 1.1 christos TEST_info("Small chunk decrypt compare failed @ operation %d", i); 213 1.1 christos return 0; 214 1.1 christos } 215 1.1 christos } 216 1.1 christos 217 1.1 christos return 1; 218 1.1 christos 219 1.1 christos err: 220 1.1 christos BIO_free_all(b); 221 1.1 christos return 0; 222 1.1 christos } 223 1.1 christos 224 1.1.1.2 christos static int do_test_bio_cipher(const EVP_CIPHER *cipher, int idx) 225 1.1 christos { 226 1.1 christos switch (idx) { 227 1.1.1.2 christos case 0: 228 1.1.1.2 christos return do_bio_cipher(cipher, KEY, NULL); 229 1.1.1.2 christos case 1: 230 1.1.1.2 christos return do_bio_cipher(cipher, KEY, IV); 231 1.1 christos } 232 1.1 christos return 0; 233 1.1 christos } 234 1.1 christos 235 1.1 christos static int test_bio_enc_aes_128_cbc(int idx) 236 1.1 christos { 237 1.1 christos return do_test_bio_cipher(EVP_aes_128_cbc(), idx); 238 1.1 christos } 239 1.1 christos 240 1.1 christos static int test_bio_enc_aes_128_ctr(int idx) 241 1.1 christos { 242 1.1 christos return do_test_bio_cipher(EVP_aes_128_ctr(), idx); 243 1.1 christos } 244 1.1 christos 245 1.1 christos static int test_bio_enc_aes_256_cfb(int idx) 246 1.1 christos { 247 1.1 christos return do_test_bio_cipher(EVP_aes_256_cfb(), idx); 248 1.1 christos } 249 1.1 christos 250 1.1 christos static int test_bio_enc_aes_256_ofb(int idx) 251 1.1 christos { 252 1.1 christos return do_test_bio_cipher(EVP_aes_256_ofb(), idx); 253 1.1 christos } 254 1.1 christos 255 1.1.1.2 christos #ifndef OPENSSL_NO_CHACHA 256 1.1 christos static int test_bio_enc_chacha20(int idx) 257 1.1 christos { 258 1.1 christos return do_test_bio_cipher(EVP_chacha20(), idx); 259 1.1 christos } 260 1.1 christos 261 1.1.1.2 christos #ifndef OPENSSL_NO_POLY1305 262 1.1 christos static int test_bio_enc_chacha20_poly1305(int idx) 263 1.1 christos { 264 1.1 christos return do_test_bio_cipher(EVP_chacha20_poly1305(), idx); 265 1.1 christos } 266 1.1.1.2 christos #endif 267 1.1.1.2 christos #endif 268 1.1 christos 269 1.1 christos static int test_bio_enc_eof_read_flush(void) 270 1.1 christos { 271 1.1 christos /* Length chosen to ensure base64 encoding employs padding */ 272 1.1 christos const unsigned char pbuf[] = "Attack at dawn"; 273 1.1.1.2 christos unsigned char cbuf[16]; /* At least as long as pbuf */ 274 1.1 christos const EVP_CIPHER *cipher = EVP_aes_256_gcm(); 275 1.1 christos EVP_CIPHER_CTX *ctx = NULL; 276 1.1 christos BIO *mem = NULL, *b64 = NULL, *cbio = NULL; 277 1.1 christos unsigned char tag[16]; 278 1.1 christos size_t key_size, iv_size; 279 1.1 christos int n, ret = 0; 280 1.1 christos 281 1.1 christos memset(tag, 0, sizeof(tag)); 282 1.1 christos if (!TEST_ptr(cipher) 283 1.1 christos || !TEST_int_gt((key_size = EVP_CIPHER_key_length(cipher)), 0) 284 1.1 christos || !TEST_int_gt((iv_size = EVP_CIPHER_iv_length(cipher)), 0) 285 1.1 christos || !TEST_ptr(mem = BIO_new(BIO_s_mem())) 286 1.1 christos || !TEST_ptr(b64 = BIO_new(BIO_f_base64())) 287 1.1 christos || !TEST_ptr(cbio = BIO_new(BIO_f_cipher())) 288 1.1 christos || !TEST_ptr(BIO_push(b64, mem)) 289 1.1 christos || !TEST_ptr(BIO_push(cbio, b64)) 290 1.1 christos || !TEST_int_gt(BIO_get_cipher_ctx(cbio, &ctx), 0) 291 1.1 christos || !TEST_true(EVP_CipherInit_ex(ctx, cipher, NULL, KEY, IV, ENCRYPT)) 292 1.1 christos || !TEST_int_gt(BIO_write(cbio, pbuf, sizeof(pbuf) - 1), 0) 293 1.1 christos || !TEST_int_gt(BIO_flush(cbio), 0) 294 1.1 christos || !TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 295 1.1.1.2 christos sizeof(tag), tag), 296 1.1.1.2 christos 0)) 297 1.1 christos goto end; 298 1.1 christos BIO_free(cbio); 299 1.1 christos BIO_free(b64); 300 1.1 christos b64 = cbio = NULL; 301 1.1 christos 302 1.1 christos BIO_set_mem_eof_return(mem, 0); 303 1.1 christos BIO_set_flags(mem, BIO_FLAGS_NONCLEAR_RST); 304 1.1 christos if (!TEST_int_gt(BIO_reset(mem), 0) 305 1.1 christos || !TEST_ptr(b64 = BIO_new(BIO_f_base64())) 306 1.1 christos || !TEST_ptr(cbio = BIO_new(BIO_f_cipher())) 307 1.1 christos || !TEST_ptr(BIO_push(b64, mem)) 308 1.1 christos || !TEST_ptr(BIO_push(cbio, b64)) 309 1.1 christos || !TEST_int_gt(BIO_get_cipher_ctx(cbio, &ctx), 0) 310 1.1 christos || !TEST_true(EVP_CipherInit_ex(ctx, cipher, NULL, KEY, IV, DECRYPT)) 311 1.1 christos || !TEST_int_gt(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 312 1.1.1.2 christos sizeof(tag), tag), 313 1.1.1.2 christos 0) 314 1.1 christos || !TEST_int_gt((n = BIO_read(cbio, cbuf, sizeof(cbuf))), 0) 315 1.1 christos || !TEST_true(BIO_get_cipher_status(cbio)) 316 1.1 christos /* Evaluate both and report whether either or both failed */ 317 1.1.1.2 christos || (!TEST_int_gt(BIO_flush(cbio), 0) + !TEST_true(BIO_get_cipher_status(cbio))) 318 1.1 christos || !TEST_mem_eq(cbuf, n, pbuf, sizeof(pbuf) - 1)) 319 1.1 christos goto end; 320 1.1 christos 321 1.1 christos ret = 1; 322 1.1 christos 323 1.1.1.2 christos end: 324 1.1 christos BIO_free(cbio); 325 1.1 christos BIO_free(b64); 326 1.1 christos BIO_free(mem); 327 1.1 christos return ret; 328 1.1 christos } 329 1.1 christos 330 1.1 christos int setup_tests(void) 331 1.1 christos { 332 1.1 christos ADD_ALL_TESTS(test_bio_enc_aes_128_cbc, 2); 333 1.1 christos ADD_ALL_TESTS(test_bio_enc_aes_128_ctr, 2); 334 1.1 christos ADD_ALL_TESTS(test_bio_enc_aes_256_cfb, 2); 335 1.1 christos ADD_ALL_TESTS(test_bio_enc_aes_256_ofb, 2); 336 1.1.1.2 christos #ifndef OPENSSL_NO_CHACHA 337 1.1 christos ADD_ALL_TESTS(test_bio_enc_chacha20, 2); 338 1.1.1.2 christos #ifndef OPENSSL_NO_POLY1305 339 1.1 christos ADD_ALL_TESTS(test_bio_enc_chacha20_poly1305, 2); 340 1.1.1.2 christos #endif 341 1.1.1.2 christos #endif 342 1.1 christos ADD_TEST(test_bio_enc_eof_read_flush); 343 1.1 christos return 1; 344 1.1 christos } 345