xref: /freebsd/crypto/openssl/crypto/evp/bio_ok.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 1995-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 
10 /*-
11         From: Arne Ansper
12 
13         Why BIO_f_reliable?
14 
15         I wrote function which took BIO* as argument, read data from it
16         and processed it. Then I wanted to store the input file in
17         encrypted form. OK I pushed BIO_f_cipher to the BIO stack
18         and everything was OK. BUT if user types wrong password
19         BIO_f_cipher outputs only garbage and my function crashes. Yes
20         I can and I should fix my function, but BIO_f_cipher is
21         easy way to add encryption support to many existing applications
22         and it's hard to debug and fix them all.
23 
24         So I wanted another BIO which would catch the incorrect passwords and
25         file damages which cause garbage on BIO_f_cipher's output.
26 
27         The easy way is to push the BIO_f_md and save the checksum at
28         the end of the file. However there are several problems with this
29         approach:
30 
31         1) you must somehow separate checksum from actual data.
32         2) you need lot's of memory when reading the file, because you
33         must read to the end of the file and verify the checksum before
34         letting the application to read the data.
35 
36         BIO_f_reliable tries to solve both problems, so that you can
37         read and write arbitrary long streams using only fixed amount
38         of memory.
39 
40         BIO_f_reliable splits data stream into blocks. Each block is prefixed
41         with its length and suffixed with its digest. So you need only
42         several Kbytes of memory to buffer single block before verifying
43         its digest.
44 
45         BIO_f_reliable goes further and adds several important capabilities:
46 
47         1) the digest of the block is computed over the whole stream
48         -- so nobody can rearrange the blocks or remove or replace them.
49 
50         2) to detect invalid passwords right at the start BIO_f_reliable
51         adds special prefix to the stream. In order to avoid known plain-text
52         attacks this prefix is generated as follows:
53 
54                 *) digest is initialized with random seed instead of
55                 standardized one.
56                 *) same seed is written to output
57                 *) well-known text is then hashed and the output
58                 of the digest is also written to output.
59 
60         reader can now read the seed from stream, hash the same string
61         and then compare the digest output.
62 
63         Bad things: BIO_f_reliable knows what's going on in EVP_Digest. I
64         initially wrote and tested this code on x86 machine and wrote the
65         digests out in machine-dependent order :( There are people using
66         this code and I cannot change this easily without making existing
67         data files unreadable.
68 
69 */
70 
71 #include <stdio.h>
72 #include <errno.h>
73 #include <assert.h>
74 #include "internal/cryptlib.h"
75 #include <openssl/buffer.h>
76 #include "internal/bio.h"
77 #include <openssl/evp.h>
78 #include <openssl/rand.h>
79 #include "internal/endian.h"
80 #include "internal/numbers.h" /* includes SIZE_MAX */
81 #include "crypto/evp.h"
82 
83 static int ok_write(BIO *h, const char *buf, int num);
84 static int ok_read(BIO *h, char *buf, int size);
85 static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2);
86 static int ok_new(BIO *h);
87 static int ok_free(BIO *data);
88 static long ok_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
89 
90 static __owur int sig_out(BIO *b);
91 static __owur int sig_in(BIO *b);
92 static __owur int block_out(BIO *b);
93 static __owur int block_in(BIO *b);
94 #define OK_BLOCK_SIZE (1024 * 4)
95 #define OK_BLOCK_BLOCK 4
96 #define IOBS (OK_BLOCK_SIZE + OK_BLOCK_BLOCK + 3 * EVP_MAX_MD_SIZE)
97 #define WELLKNOWN "The quick brown fox jumped over the lazy dog's back."
98 
99 typedef struct ok_struct {
100     size_t buf_len;
101     size_t buf_off;
102     size_t buf_len_save;
103     size_t buf_off_save;
104     int cont; /* <= 0 when finished */
105     int finished;
106     EVP_MD_CTX *md;
107     int blockout; /* output block is ready */
108     int sigio; /* must process signature */
109     unsigned char buf[IOBS];
110 } BIO_OK_CTX;
111 
112 static const BIO_METHOD methods_ok = {
113     BIO_TYPE_CIPHER,
114     "reliable",
115     bwrite_conv,
116     ok_write,
117     bread_conv,
118     ok_read,
119     NULL, /* ok_puts, */
120     NULL, /* ok_gets, */
121     ok_ctrl,
122     ok_new,
123     ok_free,
124     ok_callback_ctrl,
125 };
126 
BIO_f_reliable(void)127 const BIO_METHOD *BIO_f_reliable(void)
128 {
129     return &methods_ok;
130 }
131 
ok_new(BIO * bi)132 static int ok_new(BIO *bi)
133 {
134     BIO_OK_CTX *ctx;
135 
136     if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
137         return 0;
138 
139     ctx->cont = 1;
140     ctx->sigio = 1;
141     ctx->md = EVP_MD_CTX_new();
142     if (ctx->md == NULL) {
143         OPENSSL_free(ctx);
144         return 0;
145     }
146     BIO_set_init(bi, 0);
147     BIO_set_data(bi, ctx);
148 
149     return 1;
150 }
151 
ok_free(BIO * a)152 static int ok_free(BIO *a)
153 {
154     BIO_OK_CTX *ctx;
155 
156     if (a == NULL)
157         return 0;
158 
159     ctx = BIO_get_data(a);
160 
161     EVP_MD_CTX_free(ctx->md);
162     OPENSSL_clear_free(ctx, sizeof(BIO_OK_CTX));
163     BIO_set_data(a, NULL);
164     BIO_set_init(a, 0);
165 
166     return 1;
167 }
168 
ok_read(BIO * b,char * out,int outl)169 static int ok_read(BIO *b, char *out, int outl)
170 {
171     int ret = 0, i, n;
172     BIO_OK_CTX *ctx;
173     BIO *next;
174 
175     if (out == NULL)
176         return 0;
177 
178     ctx = BIO_get_data(b);
179     next = BIO_next(b);
180 
181     if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
182         return 0;
183 
184     while (outl > 0) {
185 
186         /* copy clean bytes to output buffer */
187         if (ctx->blockout) {
188             i = ctx->buf_len - ctx->buf_off;
189             if (i > outl)
190                 i = outl;
191             memcpy(out, &(ctx->buf[ctx->buf_off]), i);
192             ret += i;
193             out += i;
194             outl -= i;
195             ctx->buf_off += i;
196 
197             /* all clean bytes are out */
198             if (ctx->buf_len == ctx->buf_off) {
199                 ctx->buf_off = 0;
200 
201                 /*
202                  * copy start of the next block into proper place
203                  */
204                 if (ctx->buf_len_save > ctx->buf_off_save) {
205                     ctx->buf_len = ctx->buf_len_save - ctx->buf_off_save;
206                     memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]),
207                         ctx->buf_len);
208                 } else {
209                     ctx->buf_len = 0;
210                 }
211                 ctx->blockout = 0;
212             }
213         }
214 
215         /* output buffer full -- cancel */
216         if (outl == 0)
217             break;
218 
219         /* no clean bytes in buffer -- fill it */
220         n = IOBS - ctx->buf_len;
221         i = BIO_read(next, &(ctx->buf[ctx->buf_len]), n);
222 
223         if (i <= 0)
224             break; /* nothing new */
225 
226         ctx->buf_len += i;
227 
228         /* no signature yet -- check if we got one */
229         if (ctx->sigio == 1) {
230             if (!sig_in(b)) {
231                 BIO_clear_retry_flags(b);
232                 return 0;
233             }
234         }
235 
236         /* signature ok -- check if we got block */
237         if (ctx->sigio == 0) {
238             if (!block_in(b)) {
239                 BIO_clear_retry_flags(b);
240                 return 0;
241             }
242         }
243 
244         /* invalid block -- cancel */
245         if (ctx->cont <= 0)
246             break;
247     }
248 
249     BIO_clear_retry_flags(b);
250     BIO_copy_next_retry(b);
251     return ret;
252 }
253 
ok_write(BIO * b,const char * in,int inl)254 static int ok_write(BIO *b, const char *in, int inl)
255 {
256     int ret = 0, n, i;
257     BIO_OK_CTX *ctx;
258     BIO *next;
259 
260     if (inl <= 0)
261         return inl;
262 
263     ctx = BIO_get_data(b);
264     next = BIO_next(b);
265     ret = inl;
266 
267     if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
268         return 0;
269 
270     if (ctx->sigio && !sig_out(b))
271         return 0;
272 
273     do {
274         BIO_clear_retry_flags(b);
275         n = ctx->buf_len - ctx->buf_off;
276         while (ctx->blockout && n > 0) {
277             i = BIO_write(next, &(ctx->buf[ctx->buf_off]), n);
278             if (i <= 0) {
279                 BIO_copy_next_retry(b);
280                 if (!BIO_should_retry(b))
281                     ctx->cont = 0;
282                 return i;
283             }
284             ctx->buf_off += i;
285             n -= i;
286         }
287 
288         /* at this point all pending data has been written */
289         ctx->blockout = 0;
290         if (ctx->buf_len == ctx->buf_off) {
291             ctx->buf_len = OK_BLOCK_BLOCK;
292             ctx->buf_off = 0;
293         }
294 
295         if ((in == NULL) || (inl <= 0))
296             return 0;
297 
298         n = (inl + ctx->buf_len > OK_BLOCK_SIZE + OK_BLOCK_BLOCK) ? (int)(OK_BLOCK_SIZE + OK_BLOCK_BLOCK - ctx->buf_len) : inl;
299 
300         memcpy(&ctx->buf[ctx->buf_len], in, n);
301         ctx->buf_len += n;
302         inl -= n;
303         in += n;
304 
305         if (ctx->buf_len >= OK_BLOCK_SIZE + OK_BLOCK_BLOCK) {
306             if (!block_out(b)) {
307                 BIO_clear_retry_flags(b);
308                 return 0;
309             }
310         }
311     } while (inl > 0);
312 
313     BIO_clear_retry_flags(b);
314     BIO_copy_next_retry(b);
315     return ret;
316 }
317 
ok_ctrl(BIO * b,int cmd,long num,void * ptr)318 static long ok_ctrl(BIO *b, int cmd, long num, void *ptr)
319 {
320     BIO_OK_CTX *ctx;
321     EVP_MD *md;
322     const EVP_MD **ppmd;
323     long ret = 1;
324     int i;
325     BIO *next;
326 
327     ctx = BIO_get_data(b);
328     next = BIO_next(b);
329 
330     switch (cmd) {
331     case BIO_CTRL_RESET:
332         ctx->buf_len = 0;
333         ctx->buf_off = 0;
334         ctx->buf_len_save = 0;
335         ctx->buf_off_save = 0;
336         ctx->cont = 1;
337         ctx->finished = 0;
338         ctx->blockout = 0;
339         ctx->sigio = 1;
340         ret = BIO_ctrl(next, cmd, num, ptr);
341         break;
342     case BIO_CTRL_EOF: /* More to read */
343         if (ctx->cont <= 0)
344             ret = 1;
345         else
346             ret = BIO_ctrl(next, cmd, num, ptr);
347         break;
348     case BIO_CTRL_PENDING: /* More to read in buffer */
349     case BIO_CTRL_WPENDING: /* More to read in buffer */
350         ret = ctx->blockout ? ctx->buf_len - ctx->buf_off : 0;
351         if (ret <= 0)
352             ret = BIO_ctrl(next, cmd, num, ptr);
353         break;
354     case BIO_CTRL_FLUSH:
355         /* do a final write */
356         if (ctx->blockout == 0)
357             if (!block_out(b))
358                 return 0;
359 
360         while (ctx->blockout) {
361             i = ok_write(b, NULL, 0);
362             if (i < 0) {
363                 ret = i;
364                 break;
365             }
366         }
367 
368         ctx->finished = 1;
369         ctx->buf_off = ctx->buf_len = 0;
370         ctx->cont = (int)ret;
371 
372         /* Finally flush the underlying BIO */
373         ret = BIO_ctrl(next, cmd, num, ptr);
374         BIO_copy_next_retry(b);
375         break;
376     case BIO_C_DO_STATE_MACHINE:
377         BIO_clear_retry_flags(b);
378         ret = BIO_ctrl(next, cmd, num, ptr);
379         BIO_copy_next_retry(b);
380         break;
381     case BIO_CTRL_INFO:
382         ret = (long)ctx->cont;
383         break;
384     case BIO_C_SET_MD:
385         md = ptr;
386         if (!EVP_DigestInit_ex(ctx->md, md, NULL))
387             return 0;
388         BIO_set_init(b, 1);
389         break;
390     case BIO_C_GET_MD:
391         if (BIO_get_init(b)) {
392             ppmd = ptr;
393             *ppmd = EVP_MD_CTX_get0_md(ctx->md);
394         } else
395             ret = 0;
396         break;
397     default:
398         ret = BIO_ctrl(next, cmd, num, ptr);
399         break;
400     }
401     return ret;
402 }
403 
ok_callback_ctrl(BIO * b,int cmd,BIO_info_cb * fp)404 static long ok_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
405 {
406     BIO *next;
407 
408     next = BIO_next(b);
409 
410     if (next == NULL)
411         return 0;
412 
413     return BIO_callback_ctrl(next, cmd, fp);
414 }
415 
longswap(void * _ptr,size_t len)416 static void longswap(void *_ptr, size_t len)
417 {
418     DECLARE_IS_ENDIAN;
419 
420     if (IS_LITTLE_ENDIAN) {
421         size_t i;
422         unsigned char *p = _ptr, c;
423 
424         for (i = 0; i < len; i += 4) {
425             c = p[0], p[0] = p[3], p[3] = c;
426             c = p[1], p[1] = p[2], p[2] = c;
427         }
428     }
429 }
430 
sig_out(BIO * b)431 static int sig_out(BIO *b)
432 {
433     BIO_OK_CTX *ctx;
434     EVP_MD_CTX *md;
435     const EVP_MD *digest;
436     int md_size;
437     void *md_data;
438 
439     ctx = BIO_get_data(b);
440     md = ctx->md;
441     digest = EVP_MD_CTX_get0_md(md);
442     md_size = EVP_MD_get_size(digest);
443     md_data = EVP_MD_CTX_get0_md_data(md);
444 
445     if (md_size <= 0)
446         goto berr;
447     if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
448         return 1;
449 
450     if (!EVP_DigestInit_ex(md, digest, NULL))
451         goto berr;
452     /*
453      * FIXME: there's absolutely no guarantee this makes any sense at all,
454      * particularly now EVP_MD_CTX has been restructured.
455      */
456     if (RAND_bytes(md_data, md_size) <= 0)
457         goto berr;
458     memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
459     longswap(&(ctx->buf[ctx->buf_len]), md_size);
460     ctx->buf_len += md_size;
461 
462     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
463         goto berr;
464     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
465         goto berr;
466     ctx->buf_len += md_size;
467     ctx->blockout = 1;
468     ctx->sigio = 0;
469     return 1;
470 berr:
471     BIO_clear_retry_flags(b);
472     return 0;
473 }
474 
sig_in(BIO * b)475 static int sig_in(BIO *b)
476 {
477     BIO_OK_CTX *ctx;
478     EVP_MD_CTX *md;
479     unsigned char tmp[EVP_MAX_MD_SIZE];
480     int ret = 0;
481     const EVP_MD *digest;
482     int md_size;
483     void *md_data;
484 
485     ctx = BIO_get_data(b);
486     if ((md = ctx->md) == NULL)
487         goto berr;
488     digest = EVP_MD_CTX_get0_md(md);
489     if ((md_size = EVP_MD_get_size(digest)) <= 0)
490         goto berr;
491     md_data = EVP_MD_CTX_get0_md_data(md);
492 
493     if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
494         return 1;
495 
496     if (!EVP_DigestInit_ex(md, digest, NULL))
497         goto berr;
498     memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
499     longswap(md_data, md_size);
500     ctx->buf_off += md_size;
501 
502     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
503         goto berr;
504     if (!EVP_DigestFinal_ex(md, tmp, NULL))
505         goto berr;
506     ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
507     ctx->buf_off += md_size;
508     if (ret == 1) {
509         ctx->sigio = 0;
510         if (ctx->buf_len != ctx->buf_off) {
511             memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
512                 ctx->buf_len - ctx->buf_off);
513         }
514         ctx->buf_len -= ctx->buf_off;
515         ctx->buf_off = 0;
516     } else {
517         ctx->cont = 0;
518     }
519     return 1;
520 berr:
521     BIO_clear_retry_flags(b);
522     return 0;
523 }
524 
block_out(BIO * b)525 static int block_out(BIO *b)
526 {
527     BIO_OK_CTX *ctx;
528     EVP_MD_CTX *md;
529     unsigned long tl;
530     const EVP_MD *digest;
531     int md_size;
532 
533     ctx = BIO_get_data(b);
534     md = ctx->md;
535     digest = EVP_MD_CTX_get0_md(md);
536     md_size = EVP_MD_get_size(digest);
537     if (md_size <= 0)
538         goto berr;
539 
540     tl = ctx->buf_len - OK_BLOCK_BLOCK;
541     ctx->buf[0] = (unsigned char)(tl >> 24);
542     ctx->buf[1] = (unsigned char)(tl >> 16);
543     ctx->buf[2] = (unsigned char)(tl >> 8);
544     ctx->buf[3] = (unsigned char)(tl);
545     if (!EVP_DigestUpdate(md,
546             (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
547         goto berr;
548     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
549         goto berr;
550     ctx->buf_len += md_size;
551     ctx->blockout = 1;
552     return 1;
553 berr:
554     BIO_clear_retry_flags(b);
555     return 0;
556 }
557 
block_in(BIO * b)558 static int block_in(BIO *b)
559 {
560     BIO_OK_CTX *ctx;
561     EVP_MD_CTX *md;
562     size_t tl = 0;
563     unsigned char tmp[EVP_MAX_MD_SIZE];
564     int md_size;
565 
566     ctx = BIO_get_data(b);
567     md = ctx->md;
568     md_size = EVP_MD_get_size(EVP_MD_CTX_get0_md(md));
569     if (md_size <= 0)
570         goto berr;
571 
572     assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
573     tl = ((size_t)ctx->buf[0] << 24)
574         | ((size_t)ctx->buf[1] << 16)
575         | ((size_t)ctx->buf[2] << 8)
576         | ((size_t)ctx->buf[3]);
577 
578     if (tl > OK_BLOCK_SIZE)
579         goto berr;
580 
581     if (tl > SIZE_MAX - OK_BLOCK_BLOCK - (size_t)md_size)
582         goto berr;
583 
584     if (ctx->buf_len < tl + OK_BLOCK_BLOCK + (size_t)md_size)
585         return 1;
586 
587     if (!EVP_DigestUpdate(md,
588             (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
589         goto berr;
590     if (!EVP_DigestFinal_ex(md, tmp, NULL))
591         goto berr;
592     if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, (size_t)md_size) == 0) {
593         /* there might be parts from next block lurking around ! */
594         ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
595         ctx->buf_len_save = ctx->buf_len;
596         ctx->buf_off = OK_BLOCK_BLOCK;
597         ctx->buf_len = tl + OK_BLOCK_BLOCK;
598         ctx->blockout = 1;
599     } else {
600         ctx->cont = 0;
601     }
602     return 1;
603 berr:
604     BIO_clear_retry_flags(b);
605     return 0;
606 }
607