xref: /openssl/crypto/evp/bio_ok.c (revision da1c088f)
1 /*
2  * Copyright 1995-2023 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 (ctx->buf_len + 2 * md_size > OK_BLOCK_SIZE)
447         return 1;
448 
449     if (!EVP_DigestInit_ex(md, digest, NULL))
450         goto berr;
451     /*
452      * FIXME: there's absolutely no guarantee this makes any sense at all,
453      * particularly now EVP_MD_CTX has been restructured.
454      */
455     if (RAND_bytes(md_data, md_size) <= 0)
456         goto berr;
457     memcpy(&(ctx->buf[ctx->buf_len]), md_data, md_size);
458     longswap(&(ctx->buf[ctx->buf_len]), md_size);
459     ctx->buf_len += md_size;
460 
461     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
462         goto berr;
463     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
464         goto berr;
465     ctx->buf_len += md_size;
466     ctx->blockout = 1;
467     ctx->sigio = 0;
468     return 1;
469  berr:
470     BIO_clear_retry_flags(b);
471     return 0;
472 }
473 
sig_in(BIO * b)474 static int sig_in(BIO *b)
475 {
476     BIO_OK_CTX *ctx;
477     EVP_MD_CTX *md;
478     unsigned char tmp[EVP_MAX_MD_SIZE];
479     int ret = 0;
480     const EVP_MD *digest;
481     int md_size;
482     void *md_data;
483 
484     ctx = BIO_get_data(b);
485     if ((md = ctx->md) == NULL)
486         goto berr;
487     digest = EVP_MD_CTX_get0_md(md);
488     if ((md_size = EVP_MD_get_size(digest)) < 0)
489         goto berr;
490     md_data = EVP_MD_CTX_get0_md_data(md);
491 
492     if ((int)(ctx->buf_len - ctx->buf_off) < 2 * md_size)
493         return 1;
494 
495     if (!EVP_DigestInit_ex(md, digest, NULL))
496         goto berr;
497     memcpy(md_data, &(ctx->buf[ctx->buf_off]), md_size);
498     longswap(md_data, md_size);
499     ctx->buf_off += md_size;
500 
501     if (!EVP_DigestUpdate(md, WELLKNOWN, strlen(WELLKNOWN)))
502         goto berr;
503     if (!EVP_DigestFinal_ex(md, tmp, NULL))
504         goto berr;
505     ret = memcmp(&(ctx->buf[ctx->buf_off]), tmp, md_size) == 0;
506     ctx->buf_off += md_size;
507     if (ret == 1) {
508         ctx->sigio = 0;
509         if (ctx->buf_len != ctx->buf_off) {
510             memmove(ctx->buf, &(ctx->buf[ctx->buf_off]),
511                     ctx->buf_len - ctx->buf_off);
512         }
513         ctx->buf_len -= ctx->buf_off;
514         ctx->buf_off = 0;
515     } else {
516         ctx->cont = 0;
517     }
518     return 1;
519  berr:
520     BIO_clear_retry_flags(b);
521     return 0;
522 }
523 
block_out(BIO * b)524 static int block_out(BIO *b)
525 {
526     BIO_OK_CTX *ctx;
527     EVP_MD_CTX *md;
528     unsigned long tl;
529     const EVP_MD *digest;
530     int md_size;
531 
532     ctx = BIO_get_data(b);
533     md = ctx->md;
534     digest = EVP_MD_CTX_get0_md(md);
535     md_size = EVP_MD_get_size(digest);
536 
537     tl = ctx->buf_len - OK_BLOCK_BLOCK;
538     ctx->buf[0] = (unsigned char)(tl >> 24);
539     ctx->buf[1] = (unsigned char)(tl >> 16);
540     ctx->buf[2] = (unsigned char)(tl >> 8);
541     ctx->buf[3] = (unsigned char)(tl);
542     if (!EVP_DigestUpdate(md,
543                           (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
544         goto berr;
545     if (!EVP_DigestFinal_ex(md, &(ctx->buf[ctx->buf_len]), NULL))
546         goto berr;
547     ctx->buf_len += md_size;
548     ctx->blockout = 1;
549     return 1;
550  berr:
551     BIO_clear_retry_flags(b);
552     return 0;
553 }
554 
block_in(BIO * b)555 static int block_in(BIO *b)
556 {
557     BIO_OK_CTX *ctx;
558     EVP_MD_CTX *md;
559     unsigned long tl = 0;
560     unsigned char tmp[EVP_MAX_MD_SIZE];
561     int md_size;
562 
563     ctx = BIO_get_data(b);
564     md = ctx->md;
565     md_size = EVP_MD_get_size(EVP_MD_CTX_get0_md(md));
566     if (md_size < 0)
567         goto berr;
568 
569     assert(sizeof(tl) >= OK_BLOCK_BLOCK); /* always true */
570     tl = ctx->buf[0];
571     tl <<= 8;
572     tl |= ctx->buf[1];
573     tl <<= 8;
574     tl |= ctx->buf[2];
575     tl <<= 8;
576     tl |= ctx->buf[3];
577 
578     if (ctx->buf_len < tl + OK_BLOCK_BLOCK + md_size)
579         return 1;
580 
581     if (!EVP_DigestUpdate(md,
582                           (unsigned char *)&(ctx->buf[OK_BLOCK_BLOCK]), tl))
583         goto berr;
584     if (!EVP_DigestFinal_ex(md, tmp, NULL))
585         goto berr;
586     if (memcmp(&(ctx->buf[tl + OK_BLOCK_BLOCK]), tmp, md_size) == 0) {
587         /* there might be parts from next block lurking around ! */
588         ctx->buf_off_save = tl + OK_BLOCK_BLOCK + md_size;
589         ctx->buf_len_save = ctx->buf_len;
590         ctx->buf_off = OK_BLOCK_BLOCK;
591         ctx->buf_len = tl + OK_BLOCK_BLOCK;
592         ctx->blockout = 1;
593     } else {
594         ctx->cont = 0;
595     }
596     return 1;
597  berr:
598     BIO_clear_retry_flags(b);
599     return 0;
600 }
601