xref: /freebsd/crypto/openssl/crypto/evp/bio_ok.c (revision e7be843b4a162e68651d3911f0357ed464915629)
1 /*
2  * Copyright 1995-2024 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 "crypto/evp.h"
81 
82 static int ok_write(BIO *h, const char *buf, int num);
83 static int ok_read(BIO *h, char *buf, int size);
84 static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2);
85 static int ok_new(BIO *h);
86 static int ok_free(BIO *data);
87 static long ok_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
88 
89 static __owur int sig_out(BIO *b);
90 static __owur int sig_in(BIO *b);
91 static __owur int block_out(BIO *b);
92 static __owur int block_in(BIO *b);
93 #define OK_BLOCK_SIZE   (1024*4)
94 #define OK_BLOCK_BLOCK  4
95 #define IOBS            (OK_BLOCK_SIZE+ OK_BLOCK_BLOCK+ 3*EVP_MAX_MD_SIZE)
96 #define WELLKNOWN "The quick brown fox jumped over the lazy dog's back."
97 
98 typedef struct ok_struct {
99     size_t buf_len;
100     size_t buf_off;
101     size_t buf_len_save;
102     size_t buf_off_save;
103     int cont;                   /* <= 0 when finished */
104     int finished;
105     EVP_MD_CTX *md;
106     int blockout;               /* output block is ready */
107     int sigio;                  /* must process signature */
108     unsigned char buf[IOBS];
109 } BIO_OK_CTX;
110 
111 static const BIO_METHOD methods_ok = {
112     BIO_TYPE_CIPHER,
113     "reliable",
114     bwrite_conv,
115     ok_write,
116     bread_conv,
117     ok_read,
118     NULL,                       /* ok_puts, */
119     NULL,                       /* ok_gets, */
120     ok_ctrl,
121     ok_new,
122     ok_free,
123     ok_callback_ctrl,
124 };
125 
BIO_f_reliable(void)126 const BIO_METHOD *BIO_f_reliable(void)
127 {
128     return &methods_ok;
129 }
130 
ok_new(BIO * bi)131 static int ok_new(BIO *bi)
132 {
133     BIO_OK_CTX *ctx;
134 
135     if ((ctx = OPENSSL_zalloc(sizeof(*ctx))) == NULL)
136         return 0;
137 
138     ctx->cont = 1;
139     ctx->sigio = 1;
140     ctx->md = EVP_MD_CTX_new();
141     if (ctx->md == NULL) {
142         OPENSSL_free(ctx);
143         return 0;
144     }
145     BIO_set_init(bi, 0);
146     BIO_set_data(bi, ctx);
147 
148     return 1;
149 }
150 
ok_free(BIO * a)151 static int ok_free(BIO *a)
152 {
153     BIO_OK_CTX *ctx;
154 
155     if (a == NULL)
156         return 0;
157 
158     ctx = BIO_get_data(a);
159 
160     EVP_MD_CTX_free(ctx->md);
161     OPENSSL_clear_free(ctx, sizeof(BIO_OK_CTX));
162     BIO_set_data(a, NULL);
163     BIO_set_init(a, 0);
164 
165     return 1;
166 }
167 
ok_read(BIO * b,char * out,int outl)168 static int ok_read(BIO *b, char *out, int outl)
169 {
170     int ret = 0, i, n;
171     BIO_OK_CTX *ctx;
172     BIO *next;
173 
174     if (out == NULL)
175         return 0;
176 
177     ctx = BIO_get_data(b);
178     next = BIO_next(b);
179 
180     if ((ctx == NULL) || (next == NULL) || (BIO_get_init(b) == 0))
181         return 0;
182 
183     while (outl > 0) {
184 
185         /* copy clean bytes to output buffer */
186         if (ctx->blockout) {
187             i = ctx->buf_len - ctx->buf_off;
188             if (i > outl)
189                 i = outl;
190             memcpy(out, &(ctx->buf[ctx->buf_off]), i);
191             ret += i;
192             out += i;
193             outl -= i;
194             ctx->buf_off += i;
195 
196             /* all clean bytes are out */
197             if (ctx->buf_len == ctx->buf_off) {
198                 ctx->buf_off = 0;
199 
200                 /*
201                  * copy start of the next block into proper place
202                  */
203                 if (ctx->buf_len_save > ctx->buf_off_save) {
204                     ctx->buf_len = ctx->buf_len_save - ctx->buf_off_save;
205                     memmove(ctx->buf, &(ctx->buf[ctx->buf_off_save]),
206                             ctx->buf_len);
207                 } else {
208                     ctx->buf_len = 0;
209                 }
210                 ctx->blockout = 0;
211             }
212         }
213 
214         /* output buffer full -- cancel */
215         if (outl == 0)
216             break;
217 
218         /* no clean bytes in buffer -- fill it */
219         n = IOBS - ctx->buf_len;
220         i = BIO_read(next, &(ctx->buf[ctx->buf_len]), n);
221 
222         if (i <= 0)
223             break;              /* nothing new */
224 
225         ctx->buf_len += i;
226 
227         /* no signature yet -- check if we got one */
228         if (ctx->sigio == 1) {
229             if (!sig_in(b)) {
230                 BIO_clear_retry_flags(b);
231                 return 0;
232             }
233         }
234 
235         /* signature ok -- check if we got block */
236         if (ctx->sigio == 0) {
237             if (!block_in(b)) {
238                 BIO_clear_retry_flags(b);
239                 return 0;
240             }
241         }
242 
243         /* invalid block -- cancel */
244         if (ctx->cont <= 0)
245             break;
246 
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) ?
299             (int)(OK_BLOCK_SIZE + OK_BLOCK_BLOCK - ctx->buf_len) : inl;
300 
301         memcpy(&ctx->buf[ctx->buf_len], in, n);
302         ctx->buf_len += n;
303         inl -= n;
304         in += n;
305 
306         if (ctx->buf_len >= OK_BLOCK_SIZE + OK_BLOCK_BLOCK) {
307             if (!block_out(b)) {
308                 BIO_clear_retry_flags(b);
309                 return 0;
310             }
311         }
312     } while (inl > 0);
313 
314     BIO_clear_retry_flags(b);
315     BIO_copy_next_retry(b);
316     return ret;
317 }
318 
ok_ctrl(BIO * b,int cmd,long num,void * ptr)319 static long ok_ctrl(BIO *b, int cmd, long num, void *ptr)
320 {
321     BIO_OK_CTX *ctx;
322     EVP_MD *md;
323     const EVP_MD **ppmd;
324     long ret = 1;
325     int i;
326     BIO *next;
327 
328     ctx = BIO_get_data(b);
329     next = BIO_next(b);
330 
331     switch (cmd) {
332     case BIO_CTRL_RESET:
333         ctx->buf_len = 0;
334         ctx->buf_off = 0;
335         ctx->buf_len_save = 0;
336         ctx->buf_off_save = 0;
337         ctx->cont = 1;
338         ctx->finished = 0;
339         ctx->blockout = 0;
340         ctx->sigio = 1;
341         ret = BIO_ctrl(next, cmd, num, ptr);
342         break;
343     case BIO_CTRL_EOF:         /* More to read */
344         if (ctx->cont <= 0)
345             ret = 1;
346         else
347             ret = BIO_ctrl(next, cmd, num, ptr);
348         break;
349     case BIO_CTRL_PENDING:     /* More to read in buffer */
350     case BIO_CTRL_WPENDING:    /* More to read in buffer */
351         ret = ctx->blockout ? ctx->buf_len - ctx->buf_off : 0;
352         if (ret <= 0)
353             ret = BIO_ctrl(next, cmd, num, ptr);
354         break;
355     case BIO_CTRL_FLUSH:
356         /* do a final write */
357         if (ctx->blockout == 0)
358             if (!block_out(b))
359                 return 0;
360 
361         while (ctx->blockout) {
362             i = ok_write(b, NULL, 0);
363             if (i < 0) {
364                 ret = i;
365                 break;
366             }
367         }
368 
369         ctx->finished = 1;
370         ctx->buf_off = ctx->buf_len = 0;
371         ctx->cont = (int)ret;
372 
373         /* Finally flush the underlying BIO */
374         ret = BIO_ctrl(next, cmd, num, ptr);
375         BIO_copy_next_retry(b);
376         break;
377     case BIO_C_DO_STATE_MACHINE:
378         BIO_clear_retry_flags(b);
379         ret = BIO_ctrl(next, cmd, num, ptr);
380         BIO_copy_next_retry(b);
381         break;
382     case BIO_CTRL_INFO:
383         ret = (long)ctx->cont;
384         break;
385     case BIO_C_SET_MD:
386         md = ptr;
387         if (!EVP_DigestInit_ex(ctx->md, md, NULL))
388             return 0;
389         BIO_set_init(b, 1);
390         break;
391     case BIO_C_GET_MD:
392         if (BIO_get_init(b)) {
393             ppmd = ptr;
394             *ppmd = EVP_MD_CTX_get0_md(ctx->md);
395         } else
396             ret = 0;
397         break;
398     default:
399         ret = BIO_ctrl(next, cmd, num, ptr);
400         break;
401     }
402     return ret;
403 }
404 
ok_callback_ctrl(BIO * b,int cmd,BIO_info_cb * fp)405 static long ok_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp)
406 {
407     BIO *next;
408 
409     next = BIO_next(b);
410 
411     if (next == NULL)
412         return 0;
413 
414     return BIO_callback_ctrl(next, cmd, fp);
415 }
416 
longswap(void * _ptr,size_t len)417 static void longswap(void *_ptr, size_t len)
418 {
419     DECLARE_IS_ENDIAN;
420 
421     if (IS_LITTLE_ENDIAN) {
422         size_t i;
423         unsigned char *p = _ptr, c;
424 
425         for (i = 0; i < len; i += 4) {
426             c = p[0], p[0] = p[3], p[3] = c;
427             c = p[1], p[1] = p[2], p[2] = c;
428         }
429     }
430 }
431 
sig_out(BIO * b)432 static int sig_out(BIO *b)
433 {
434     BIO_OK_CTX *ctx;
435     EVP_MD_CTX *md;
436     const EVP_MD *digest;
437     int md_size;
438     void *md_data;
439 
440     ctx = BIO_get_data(b);
441     md = ctx->md;
442     digest = EVP_MD_CTX_get0_md(md);
443     md_size = EVP_MD_get_size(digest);
444     md_data = EVP_MD_CTX_get0_md_data(md);
445 
446     if (md_size <= 0)
447         goto berr;
448     if (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
449         return 1;
450 
451     if (!EVP_DigestInit_ex(md, digest, NULL))
452         goto berr;
453     /*
454      * FIXME: there's absolutely no guarantee this makes any sense at all,
455      * particularly now EVP_MD_CTX has been restructured.
456      */
457     if (RAND_bytes(md_data, md_size) <= 0)
458         goto berr;
459     memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
460     longswap(&(ctx->buf[ctx->buf_len]), md_size);
461     ctx->buf_len += md_size;
462 
463     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
464         goto berr;
465     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
466         goto berr;
467     ctx->buf_len += md_size;
468     ctx->blockout = 1;
469     ctx->sigio = 0;
470     return 1;
471  berr:
472     BIO_clear_retry_flags(b);
473     return 0;
474 }
475 
sig_in(BIO * b)476 static int sig_in(BIO *b)
477 {
478     BIO_OK_CTX *ctx;
479     EVP_MD_CTX *md;
480     unsigned char tmp[EVP_MAX_MD_SIZE];
481     int ret = 0;
482     const EVP_MD *digest;
483     int md_size;
484     void *md_data;
485 
486     ctx = BIO_get_data(b);
487     if ((md = ctx->md) == NULL)
488         goto berr;
489     digest = EVP_MD_CTX_get0_md(md);
490     if ((md_size = EVP_MD_get_size(digest)) <= 0)
491         goto berr;
492     md_data = EVP_MD_CTX_get0_md_data(md);
493 
494     if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
495         return 1;
496 
497     if (!EVP_DigestInit_ex(md, digest, NULL))
498         goto berr;
499     memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
500     longswap(md_data, md_size);
501     ctx->buf_off += md_size;
502 
503     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
504         goto berr;
505     if (!EVP_DigestFinal_ex(md, tmp, NULL))
506         goto berr;
507     ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
508     ctx->buf_off += md_size;
509     if (ret == 1) {
510         ctx->sigio = 0;
511         if (ctx->buf_len != ctx->buf_off) {
512             memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
513                     ctx->buf_len - ctx->buf_off);
514         }
515         ctx->buf_len -= ctx->buf_off;
516         ctx->buf_off = 0;
517     } else {
518         ctx->cont = 0;
519     }
520     return 1;
521  berr:
522     BIO_clear_retry_flags(b);
523     return 0;
524 }
525 
block_out(BIO * b)526 static int block_out(BIO *b)
527 {
528     BIO_OK_CTX *ctx;
529     EVP_MD_CTX *md;
530     unsigned long tl;
531     const EVP_MD *digest;
532     int md_size;
533 
534     ctx = BIO_get_data(b);
535     md = ctx->md;
536     digest = EVP_MD_CTX_get0_md(md);
537     md_size = EVP_MD_get_size(digest);
538     if (md_size <= 0)
539         goto berr;
540 
541     tl = ctx->buf_len - OK_BLOCK_BLOCK;
542     ctx->buf[0] = (unsigned char)(tl >> 24);
543     ctx->buf[1] = (unsigned char)(tl >> 16);
544     ctx->buf[2] = (unsigned char)(tl >> 8);
545     ctx->buf[3] = (unsigned char)(tl);
546     if (!EVP_DigestUpdate(md,
547                           (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
548         goto berr;
549     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
550         goto berr;
551     ctx->buf_len += md_size;
552     ctx->blockout = 1;
553     return 1;
554  berr:
555     BIO_clear_retry_flags(b);
556     return 0;
557 }
558 
block_in(BIO * b)559 static int block_in(BIO *b)
560 {
561     BIO_OK_CTX *ctx;
562     EVP_MD_CTX *md;
563     unsigned long tl = 0;
564     unsigned char tmp[EVP_MAX_MD_SIZE];
565     int md_size;
566 
567     ctx = BIO_get_data(b);
568     md = ctx->md;
569     md_size = EVP_MD_get_size(EVP_MD_CTX_get0_md(md));
570     if (md_size <= 0)
571         goto berr;
572 
573     assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
574     tl = ctx->buf[0];
575     tl <<= 8;
576     tl |= ctx->buf[1];
577     tl <<= 8;
578     tl |= ctx->buf[2];
579     tl <<= 8;
580     tl |= ctx->buf[3];
581 
582     if (ctx->buf_len < tl + OK_BLOCK_BLOCK + md_size)
583         return 1;
584 
585     if (!EVP_DigestUpdate(md,
586                           (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
587         goto berr;
588     if (!EVP_DigestFinal_ex(md, tmp, NULL))
589         goto berr;
590     if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, md_size) == 0) {
591         /* there might be parts from next block lurking around ! */
592         ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
593         ctx->buf_len_save = ctx->buf_len;
594         ctx->buf_off = OK_BLOCK_BLOCK;
595         ctx->buf_len = tl + OK_BLOCK_BLOCK;
596         ctx->blockout = 1;
597     } else {
598         ctx->cont = 0;
599     }
600     return 1;
601  berr:
602     BIO_clear_retry_flags(b);
603     return 0;
604 }
605