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