xref: /freebsd/crypto/openssl/test/bio_enc_test.c (revision e7be843b4a162e68651d3911f0357ed464915629)
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