VirtualBox

source: vbox/trunk/src/libs/openssl-3.4.1/crypto/evp/bio_ok.c@ 109302

Last change on this file since 109302 was 109052, checked in by vboxsync, 4 weeks ago

openssl-3.4.1: Applied our changes, regenerated files, added missing files and functions. This time with a three way merge. ​bugref:10890

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.9 KB
Line 
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
82static int ok_write(BIO *h, const char *buf, int num);
83static int ok_read(BIO *h, char *buf, int size);
84static long ok_ctrl(BIO *h, int cmd, long arg1, void *arg2);
85static int ok_new(BIO *h);
86static int ok_free(BIO *data);
87static long ok_callback_ctrl(BIO *h, int cmd, BIO_info_cb *fp);
88
89static __owur int sig_out(BIO *b);
90static __owur int sig_in(BIO *b);
91static __owur int block_out(BIO *b);
92static __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
98typedef 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
111static 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
126const BIO_METHOD *BIO_f_reliable(void)
127{
128 return &methods_ok;
129}
130
131static 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
151static 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
168static 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
254static 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
319static 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
405static 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
417static 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
432static 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
476static 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
526static 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
559static 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}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette