diff options
author | Todd Short <tshort@akamai.com> | 2021-08-09 16:56:29 -0400 |
---|---|---|
committer | Todd Short <todd.short@me.com> | 2022-10-18 09:30:18 -0400 |
commit | 12e96a23604a7aa1cd8f83486b02f1bcab6d468f (patch) | |
tree | 0b6be9589eaab31798122128c1237d40bff9bfe2 /crypto | |
parent | 846975f367f75f3503b44c12e49d980dca181647 (diff) | |
download | openssl-new-12e96a23604a7aa1cd8f83486b02f1bcab6d468f.tar.gz |
Add brotli compression support (RFC7924)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18186)
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/comp/build.info | 1 | ||||
-rw-r--r-- | crypto/comp/c_brotli.c | 770 | ||||
-rw-r--r-- | crypto/comp/comp_err.c | 10 | ||||
-rw-r--r-- | crypto/err/openssl.txt | 5 | ||||
-rw-r--r-- | crypto/init.c | 2 | ||||
-rw-r--r-- | crypto/objects/obj_dat.h | 9 | ||||
-rw-r--r-- | crypto/objects/obj_mac.num | 1 | ||||
-rw-r--r-- | crypto/objects/objects.txt | 3 |
8 files changed, 798 insertions, 3 deletions
diff --git a/crypto/comp/build.info b/crypto/comp/build.info index 65df46a175..014628e45d 100644 --- a/crypto/comp/build.info +++ b/crypto/comp/build.info @@ -1,4 +1,5 @@ LIBS=../../libcrypto SOURCE[../../libcrypto]= \ comp_lib.c comp_err.c \ + c_brotli.c \ c_zlib.c diff --git a/crypto/comp/c_brotli.c b/crypto/comp/c_brotli.c new file mode 100644 index 0000000000..ace6f221b8 --- /dev/null +++ b/crypto/comp/c_brotli.c @@ -0,0 +1,770 @@ +/* + * Copyright 1998-2021 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + * + * Uses brotli compression library from https://github.com/google/brotli + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <openssl/objects.h> +#include "internal/comp.h" +#include <openssl/err.h> +#include "crypto/cryptlib.h" +#include "internal/bio.h" +#include "internal/thread_once.h" +#include "comp_local.h" + +COMP_METHOD *COMP_brotli(void); + +static COMP_METHOD brotli_method_nobrotli = { + NID_undef, + "(undef)", + NULL, + NULL, + NULL, + NULL, +}; + +#ifdef OPENSSL_NO_BROTLI +# undef BROTLI_SHARED +#else + +# include <brotli/decode.h> +# include <brotli/encode.h> + +/* memory allocations functions for brotli initialisation */ +static void *brotli_alloc(void *opaque, size_t size) +{ + return OPENSSL_zalloc(size); +} + +static void brotli_free(void *opaque, void *address) +{ + OPENSSL_free(address); +} + +/* + * When OpenSSL is built on Windows, we do not want to require that + * the BROTLI.DLL be available in order for the OpenSSL DLLs to + * work. Therefore, all BROTLI routines are loaded at run time + * and we do not link to a .LIB file when BROTLI_SHARED is set. + */ +# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) +# include <windows.h> +# endif + +# ifdef BROTLI_SHARED +# include "internal/dso.h" + +/* Function pointers */ +typedef BrotliEncoderState *(*encode_init_ft)(brotli_alloc_func, brotli_free_func, void *); +typedef BROTLI_BOOL (*encode_stream_ft)(BrotliEncoderState *, BrotliEncoderOperation, size_t *, const uint8_t **, size_t *, uint8_t **, size_t *); +typedef BROTLI_BOOL (*encode_has_more_ft)(BrotliEncoderState *); +typedef void (*encode_end_ft)(BrotliEncoderState *); +typedef BROTLI_BOOL (*encode_oneshot_ft)(int, int, BrotliEncoderMode, size_t, const uint8_t in[], size_t *, uint8_t out[]); + +typedef BrotliDecoderState *(*decode_init_ft)(brotli_alloc_func, brotli_free_func, void *); +typedef BROTLI_BOOL (*decode_stream_ft)(BrotliDecoderState *, size_t *, const uint8_t **, size_t *, uint8_t **, size_t *); +typedef BROTLI_BOOL (*decode_has_more_ft)(BrotliDecoderState *); +typedef void (*decode_end_ft)(BrotliDecoderState *); +typedef BrotliDecoderErrorCode (*decode_error_ft)(BrotliDecoderState *); +typedef const char *(*decode_error_string_ft)(BrotliDecoderErrorCode); +typedef BROTLI_BOOL (*decode_is_finished_ft)(BrotliDecoderState *); +typedef BrotliDecoderResult (*decode_oneshot_ft)(size_t, const uint8_t in[], size_t *, uint8_t out[]); + +static encode_init_ft p_encode_init = NULL; +static encode_stream_ft p_encode_stream = NULL; +static encode_has_more_ft p_encode_has_more = NULL; +static encode_end_ft p_encode_end = NULL; +static encode_oneshot_ft p_encode_oneshot = NULL; + +static decode_init_ft p_decode_init = NULL; +static decode_stream_ft p_decode_stream = NULL; +static decode_has_more_ft p_decode_has_more = NULL; +static decode_end_ft p_decode_end = NULL; +static decode_error_ft p_decode_error = NULL; +static decode_error_string_ft p_decode_error_string = NULL; +static decode_is_finished_ft p_decode_is_finished = NULL; +static decode_oneshot_ft p_decode_oneshot = NULL; + +static DSO *brotli_encode_dso = NULL; +static DSO *brotli_decode_dso = NULL; + +# define BrotliEncoderCreateInstance p_encode_init +# define BrotliEncoderCompressStream p_encode_stream +# define BrotliEncoderHasMoreOutput p_encode_has_more +# define BrotliEncoderDestroyInstance p_encode_end +# define BrotliEncoderCompress p_encode_oneshot + +# define BrotliDecoderCreateInstance p_decode_init +# define BrotliDecoderDecompressStream p_decode_stream +# define BrotliDecoderHasMoreOutput p_decode_has_more +# define BrotliDecoderDestroyInstance p_decode_end +# define BrotliDecoderGetErrorCode p_decode_error +# define BrotliDecoderErrorString p_decode_error_string +# define BrotliDecoderIsFinished p_decode_is_finished +# define BrotliDecoderDecompress p_decode_oneshot + +# endif /* ifdef BROTLI_SHARED */ + + +struct brotli_state { + BrotliEncoderState *encoder; + BrotliDecoderState *decoder; +}; + +static int brotli_stateful_init(COMP_CTX *ctx) +{ + struct brotli_state *state = OPENSSL_zalloc(sizeof(*state)); + + if (state == NULL) + return 0; + + state->encoder = BrotliEncoderCreateInstance(brotli_alloc, brotli_free, NULL); + if (state->encoder == NULL) + goto err; + + state->decoder = BrotliDecoderCreateInstance(brotli_alloc, brotli_free, NULL); + if (state->decoder == NULL) + goto err; + + ctx->data = state; + return 1; + err: + BrotliDecoderDestroyInstance(state->decoder); + BrotliEncoderDestroyInstance(state->encoder); + OPENSSL_free(state); + return 0; +} + +static void brotli_stateful_finish(COMP_CTX *ctx) +{ + struct brotli_state *state = ctx->data; + + if (state != NULL) { + BrotliDecoderDestroyInstance(state->decoder); + BrotliEncoderDestroyInstance(state->encoder); + OPENSSL_free(state); + ctx->data = NULL; + } +} + +static int brotli_stateful_compress_block(COMP_CTX *ctx, unsigned char *out, + unsigned int olen, unsigned char *in, + unsigned int ilen) +{ + BROTLI_BOOL done; + struct brotli_state *state = ctx->data; + size_t in_avail = ilen; + size_t out_avail = olen; + + if (state == NULL) + return -1; + + if (ilen == 0) + return 0; + + /* + * The finish API does not provide a final output buffer, + * so each compress operation has to be flushed, if all + * the input data can't be accepted, or there is more output, + * this has to be considered an error, since there is no more + * output buffer space + */ + done = BrotliEncoderCompressStream(state->encoder, BROTLI_OPERATION_FLUSH, + &in_avail, (const uint8_t**)&in, &out_avail, &out, NULL); + if (done == BROTLI_FALSE || in_avail != 0 + || BrotliEncoderHasMoreOutput(state->encoder)) + return -1; + + return (int)(olen - out_avail); +} + +static int brotli_stateful_expand_block(COMP_CTX *ctx, unsigned char *out, + unsigned int olen, unsigned char *in, + unsigned int ilen) +{ + BrotliDecoderResult result; + struct brotli_state *state = ctx->data; + size_t in_avail = ilen; + size_t out_avail = olen; + + if (state == NULL) + return -1; + + if (ilen == 0) + return 0; + + result = BrotliDecoderDecompressStream(state->decoder, &in_avail, (const uint8_t**)&in, &out_avail, &out, NULL); + if (result == BROTLI_DECODER_RESULT_ERROR || in_avail != 0 + || BrotliDecoderHasMoreOutput(state->decoder)) + return -1; + + return (int)(olen - out_avail); +} + +static COMP_METHOD brotli_stateful_method = { + NID_brotli, + LN_brotli, + brotli_stateful_init, + brotli_stateful_finish, + brotli_stateful_compress_block, + brotli_stateful_expand_block +}; + +static int brotli_oneshot_init(COMP_CTX *ctx) +{ + return 1; +} + +static void brotli_oneshot_finish(COMP_CTX *ctx) +{ +} + +static int brotli_oneshot_compress_block(COMP_CTX *ctx, unsigned char *out, + unsigned int olen, unsigned char *in, + unsigned int ilen) +{ + size_t out_size = olen; + + if (ilen == 0) + return 0; + + if (BrotliEncoderCompress(BROTLI_DEFAULT_QUALITY, BROTLI_DEFAULT_WINDOW, + BROTLI_DEFAULT_MODE, ilen, in, + &out_size, out) == BROTLI_FALSE) + return -1; + + return (int)out_size; +} + +static int brotli_oneshot_expand_block(COMP_CTX *ctx, unsigned char *out, + unsigned int olen, unsigned char *in, + unsigned int ilen) +{ + size_t out_size = olen; + + if (ilen == 0) + return 0; + + if (BrotliDecoderDecompress(ilen, in, &out_size, out) != BROTLI_DECODER_RESULT_SUCCESS) + return -1; + + return (int)out_size; +} + +static COMP_METHOD brotli_oneshot_method = { + NID_brotli, + LN_brotli, + brotli_oneshot_init, + brotli_oneshot_finish, + brotli_oneshot_compress_block, + brotli_oneshot_expand_block +}; + +static CRYPTO_ONCE brotli_once = CRYPTO_ONCE_STATIC_INIT; +DEFINE_RUN_ONCE_STATIC(ossl_comp_brotli_init) +{ +# ifdef BROTLI_SHARED +# if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_WIN32) +# define LIBBROTLIENC "BROTLIENC" +# define LIBBROTLIDEC "BROTLIDEC" +# else +# define LIBBROTLIENC "brotlienc" +# define LIBBROTLIDEC "brotlidec" +# endif + + brotli_encode_dso = DSO_load(NULL, LIBBROTLIENC, NULL, 0); + if (brotli_encode_dso != NULL) { + p_encode_init = (encode_init_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCreateInstance"); + p_encode_stream = (encode_stream_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCompressStream"); + p_encode_has_more = (encode_has_more_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderHasMoreOutput"); + p_encode_end = (encode_end_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderDestroyInstance"); + p_encode_oneshot = (encode_oneshot_ft)DSO_bind_func(brotli_encode_dso, "BrotliEncoderCompress"); + } + + brotli_decode_dso = DSO_load(NULL, LIBBROTLIDEC, NULL, 0); + if (brotli_decode_dso != NULL) { + p_decode_init = (decode_init_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderCreateInstance"); + p_decode_stream = (decode_stream_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDecompressStream"); + p_decode_has_more = (decode_has_more_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderHasMoreOutput"); + p_decode_end = (decode_end_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDestroyInstance"); + p_decode_error = (decode_error_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderGetErrorCode"); + p_decode_error_string = (decode_error_string_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderErrorString"); + p_decode_is_finished = (decode_is_finished_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderIsFinished"); + p_decode_oneshot = (decode_oneshot_ft)DSO_bind_func(brotli_decode_dso, "BrotliDecoderDecompress"); + } + + if (p_encode_init == NULL || p_encode_stream == NULL || p_encode_has_more == NULL + || p_encode_end == NULL || p_encode_oneshot == NULL || p_decode_init == NULL + || p_decode_stream == NULL || p_decode_has_more == NULL || p_decode_end == NULL + || p_decode_error == NULL || p_decode_error_string == NULL || p_decode_is_finished == NULL + || p_decode_oneshot == NULL) { + ossl_comp_brotli_cleanup(); + return 0; + } +# endif + return 1; +} +#endif /* ifndef BROTLI / else */ + +COMP_METHOD *COMP_brotli(void) +{ + COMP_METHOD *meth = &brotli_method_nobrotli; + +#ifndef OPENSSL_NO_BROTLI + if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init)) + meth = &brotli_stateful_method; +#endif + return meth; +} + +COMP_METHOD *COMP_brotli_oneshot(void) +{ + COMP_METHOD *meth = &brotli_method_nobrotli; + +#ifndef OPENSSL_NO_BROTLI + if (RUN_ONCE(&brotli_once, ossl_comp_brotli_init)) + meth = &brotli_oneshot_method; +#endif + return meth; +} + +/* Also called from OPENSSL_cleanup() */ +void ossl_comp_brotli_cleanup(void) +{ +#ifdef BROTLI_SHARED + DSO_free(brotli_encode_dso); + brotli_encode_dso = NULL; + DSO_free(brotli_decode_dso); + brotli_decode_dso = NULL; + p_encode_init = NULL; + p_encode_stream = NULL; + p_encode_has_more = NULL; + p_encode_end = NULL; + p_encode_oneshot = NULL; + p_decode_init = NULL; + p_decode_stream = NULL; + p_decode_has_more = NULL; + p_decode_end = NULL; + p_decode_error = NULL; + p_decode_error_string = NULL; + p_decode_is_finished = NULL; + p_decode_oneshot = NULL; +#endif +} + +#ifndef OPENSSL_NO_BROTLI + +/* Brotli-based compression/decompression filter BIO */ + +typedef struct { + struct { /* input structure */ + size_t avail_in; + unsigned char *next_in; + size_t avail_out; + unsigned char *next_out; + unsigned char *buf; + size_t bufsize; + BrotliDecoderState *state; + } decode; + struct { /* output structure */ + size_t avail_in; + unsigned char *next_in; + size_t avail_out; + unsigned char *next_out; + unsigned char *buf; + size_t bufsize; + BrotliEncoderState *state; + int mode; /* Encoder mode to use */ + int done; + unsigned char *ptr; + size_t count; + } encode; +} BIO_BROTLI_CTX; + +# define BROTLI_DEFAULT_BUFSIZE 1024 + +static int bio_brotli_new(BIO *bi); +static int bio_brotli_free(BIO *bi); +static int bio_brotli_read(BIO *b, char *out, int outl); +static int bio_brotli_write(BIO *b, const char *in, int inl); +static long bio_brotli_ctrl(BIO *b, int cmd, long num, void *ptr); +static long bio_brotli_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp); + +static const BIO_METHOD bio_meth_brotli = { + BIO_TYPE_COMP, + "brotli", + /* TODO: Convert to new style write function */ + bwrite_conv, + bio_brotli_write, + /* TODO: Convert to new style read function */ + bread_conv, + bio_brotli_read, + NULL, /* bio_brotli_puts, */ + NULL, /* bio_brotli_gets, */ + bio_brotli_ctrl, + bio_brotli_new, + bio_brotli_free, + bio_brotli_callback_ctrl +}; +#endif + +const BIO_METHOD *BIO_f_brotli(void) +{ +#ifndef OPENSSL_NO_BROTLI + return &bio_meth_brotli; +#else + return NULL; +#endif +} + +#ifndef OPENSSL_NO_BROTLI + +static int bio_brotli_new(BIO *bi) +{ + BIO_BROTLI_CTX *ctx; + +# ifdef BROTLI_SHARED + if (!RUN_ONCE(&brotli_once, ossl_comp_brotli_init)) { + ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_NOT_SUPPORTED); + return 0; + } +# endif + ctx = OPENSSL_zalloc(sizeof(*ctx)); + if (ctx == NULL) { + ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); + return 0; + } + ctx->decode.bufsize = BROTLI_DEFAULT_BUFSIZE; + ctx->decode.state = BrotliDecoderCreateInstance(brotli_alloc, brotli_free, NULL); + if (ctx->decode.state == NULL) + goto err; + ctx->encode.bufsize = BROTLI_DEFAULT_BUFSIZE; + ctx->encode.state = BrotliEncoderCreateInstance(brotli_alloc, brotli_free, NULL); + if (ctx->encode.state == NULL) + goto err; + ctx->encode.mode = BROTLI_DEFAULT_MODE; + BIO_set_init(bi, 1); + BIO_set_data(bi, ctx); + + return 1; + + err: + ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); + BrotliDecoderDestroyInstance(ctx->decode.state); + BrotliEncoderDestroyInstance(ctx->encode.state); + OPENSSL_free(ctx); + return 0; +} + +static int bio_brotli_free(BIO *bi) +{ + BIO_BROTLI_CTX *ctx; + + if (bi == NULL) + return 0; + + ctx = BIO_get_data(bi); + if (ctx != NULL) { + BrotliDecoderDestroyInstance(ctx->decode.state); + OPENSSL_free(ctx->decode.buf); + BrotliEncoderDestroyInstance(ctx->encode.state); + OPENSSL_free(ctx->encode.buf); + OPENSSL_free(ctx); + } + BIO_set_data(bi, NULL); + BIO_set_init(bi, 0); + + return 1; +} + +static int bio_brotli_read(BIO *b, char *out, int outl) +{ + BIO_BROTLI_CTX *ctx; + BrotliDecoderResult bret; + int ret; + BIO *next = BIO_next(b); + + if (out == NULL || outl <= 0) + return 0; + + ctx = BIO_get_data(b); + BIO_clear_retry_flags(b); + if (ctx->decode.buf == NULL) { + ctx->decode.buf = OPENSSL_malloc(ctx->decode.bufsize); + if (ctx->decode.buf == NULL) { + ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); + return 0; + } + ctx->decode.next_in = ctx->decode.buf; + ctx->decode.avail_in = 0; + } + + /* Copy output data directly to supplied buffer */ + ctx->decode.next_out = (unsigned char *)out; + ctx->decode.avail_out = (size_t)outl; + for (;;) { + /* Decompress while data available */ + while (ctx->decode.avail_in > 0 || BrotliDecoderHasMoreOutput(ctx->decode.state)) { + bret = BrotliDecoderDecompressStream(ctx->decode.state, &ctx->decode.avail_in, (const uint8_t**)&ctx->decode.next_in, + &ctx->decode.avail_out, &ctx->decode.next_out, NULL); + if (bret == BROTLI_DECODER_RESULT_ERROR) { + ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR); + ERR_add_error_data(1, BrotliDecoderErrorString(BrotliDecoderGetErrorCode(ctx->decode.state))); + return 0; + } + /* If EOF or we've read everything then return */ + if (BrotliDecoderIsFinished(ctx->decode.state) || ctx->decode.avail_out == 0) + return (int)(outl - ctx->decode.avail_out); + } + + /* If EOF */ + if (BrotliDecoderIsFinished(ctx->decode.state)) + return 0; + + /* + * No data in input buffer try to read some in, if an error then + * return the total data read. + */ + ret = BIO_read(next, ctx->decode.buf, ctx->decode.bufsize); + if (ret <= 0) { + /* Total data read */ + int tot = outl - ctx->decode.avail_out; + + BIO_copy_next_retry(b); + if (ret < 0) + return (tot > 0) ? tot : ret; + return tot; + } + ctx->decode.avail_in = ret; + ctx->decode.next_in = ctx->decode.buf; + } +} + +static int bio_brotli_write(BIO *b, const char *in, int inl) +{ + BIO_BROTLI_CTX *ctx; + BROTLI_BOOL brret; + int ret; + BIO *next = BIO_next(b); + + if (in == NULL || inl <= 0) + return 0; + + ctx = BIO_get_data(b); + if (ctx->encode.done) + return 0; + + BIO_clear_retry_flags(b); + if (ctx->encode.buf == NULL) { + ctx->encode.buf = OPENSSL_malloc(ctx->encode.bufsize); + if (ctx->encode.buf == NULL) { + ERR_raise(ERR_LIB_COMP, ERR_R_MALLOC_FAILURE); + return 0; + } + ctx->encode.ptr = ctx->encode.buf; + ctx->encode.count = 0; + ctx->encode.next_out = ctx->encode.buf; + ctx->encode.avail_out = ctx->encode.bufsize; + } + /* Obtain input data directly from supplied buffer */ + ctx->encode.next_in = (unsigned char *)in; + ctx->encode.avail_in = inl; + for (;;) { + /* If data in output buffer write it first */ + while (ctx->encode.count > 0) { + ret = BIO_write(next, ctx->encode.ptr, ctx->encode.count); + if (ret <= 0) { + /* Total data written */ + int tot = inl - ctx->encode.avail_in; + + BIO_copy_next_retry(b); + if (ret < 0) + return (tot > 0) ? tot : ret; + return tot; + } + ctx->encode.ptr += ret; + ctx->encode.count -= ret; + } + + /* Have we consumed all supplied data? */ + if (ctx->encode.avail_in == 0 && !BrotliEncoderHasMoreOutput(ctx->encode.state)) + return inl; + + /* Compress some more */ + + /* Reset buffer */ + ctx->encode.ptr = ctx->encode.buf; + ctx->encode.next_out = ctx->encode.buf; + ctx->encode.avail_out = ctx->encode.bufsize; + /* Compress some more */ + brret = BrotliEncoderCompressStream(ctx->encode.state, BROTLI_OPERATION_FLUSH, &ctx->encode.avail_in, (const uint8_t**)&ctx->encode.next_in, + &ctx->encode.avail_out, &ctx->encode.next_out, NULL); + if (brret != BROTLI_TRUE) { + ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_ENCODE_ERROR); + ERR_add_error_data(1, "brotli encoder error"); + return 0; + } + ctx->encode.count = ctx->encode.bufsize - ctx->encode.avail_out; + } +} + +static int bio_brotli_flush(BIO *b) +{ + BIO_BROTLI_CTX *ctx; + BROTLI_BOOL brret; + int ret; + BIO *next = BIO_next(b); + + ctx = BIO_get_data(b); + + /* If no data written or already flush show success */ + if (ctx->encode.buf == NULL || (ctx->encode.done && ctx->encode.count == 0)) + return 1; + + BIO_clear_retry_flags(b); + /* No more input data */ + ctx->encode.next_in = NULL; + ctx->encode.avail_in = 0; + for (;;) { + /* If data in output buffer write it first */ + while (ctx->encode.count > 0) { + ret = BIO_write(next, ctx->encode.ptr, ctx->encode.count); + if (ret <= 0) { + BIO_copy_next_retry(b); + return ret; + } + ctx->encode.ptr += ret; + ctx->encode.count -= ret; + } + if (ctx->encode.done) + return 1; + + /* Compress some more */ + + /* Reset buffer */ + ctx->encode.ptr = ctx->encode.buf; + ctx->encode.next_out = ctx->encode.buf; + ctx->encode.avail_out = ctx->encode.bufsize; + /* Compress some more */ + brret = BrotliEncoderCompressStream(ctx->encode.state, BROTLI_OPERATION_FINISH, &ctx->encode.avail_in, + (const uint8_t**)&ctx->encode.next_in, &ctx->encode.avail_out, &ctx->encode.next_out, NULL); + if (brret != BROTLI_TRUE) { + ERR_raise(ERR_LIB_COMP, COMP_R_BROTLI_DECODE_ERROR); + ERR_add_error_data(1, "brotli encoder error"); + return 0; + } + if (!BrotliEncoderHasMoreOutput(ctx->encode.state) && ctx->encode.avail_in == 0) + ctx->encode.done = 1; + ctx->encode.count = ctx->encode.bufsize - ctx->encode.avail_out; + } +} + +static long bio_brotli_ctrl(BIO *b, int cmd, long num, void *ptr) +{ + BIO_BROTLI_CTX *ctx; + unsigned char *tmp; + int ret = 0, *ip; + size_t ibs, obs; + BIO *next = BIO_next(b); + + if (next == NULL) + return 0; + ctx = BIO_get_data(b); + switch (cmd) { + + case BIO_CTRL_RESET: + ctx->encode.count = 0; + ctx->encode.done = 0; + ret = 1; + break; + + case BIO_CTRL_FLUSH: + ret = bio_brotli_flush(b); + if (ret > 0) + ret = BIO_flush(next); + break; + + case BIO_C_SET_BUFF_SIZE: + ibs = ctx->decode.bufsize; + obs = ctx->encode.bufsize; + if (ptr != NULL) { + ip = ptr; + if (*ip == 0) + ibs = (size_t)num; + else + obs = (size_t)num; + } else { + ibs = (size_t)num; + obs = ibs; + } + + if (ibs > 0 && ibs != ctx->decode.bufsize) { + /* Do not free/alloc, only reallocate */ + if (ctx->decode.buf != NULL) { + tmp = OPENSSL_realloc(ctx->decode.buf, ibs); + if (tmp == NULL) + return 0; + ctx->decode.buf = tmp; + } + ctx->decode.bufsize = ibs; + } + + if (obs > 0 && obs != ctx->encode.bufsize) { + /* Do not free/alloc, only reallocate */ + if (ctx->encode.buf != NULL) { + tmp = OPENSSL_realloc(ctx->encode.buf, obs); + if (tmp == NULL) + return 0; + ctx->encode.buf = tmp; + } + ctx->encode.bufsize = obs; + } + ret = 1; + break; + + case BIO_C_DO_STATE_MACHINE: + BIO_clear_retry_flags(b); + ret = BIO_ctrl(next, cmd, num, ptr); + BIO_copy_next_retry(b); + break; + + case BIO_CTRL_WPENDING: + if (BrotliEncoderHasMoreOutput(ctx->encode.state)) + ret = 1; + else + ret = BIO_ctrl(next, cmd, num, ptr); + break; + + case BIO_CTRL_PENDING: + if (!BrotliDecoderIsFinished(ctx->decode.state)) + ret = 1; + else + ret = BIO_ctrl(next, cmd, num, ptr); + break; + + default: + ret = BIO_ctrl(next, cmd, num, ptr); + break; + + } + + return ret; +} + +static long bio_brotli_callback_ctrl(BIO *b, int cmd, BIO_info_cb *fp) +{ + BIO *next = BIO_next(b); + if (next == NULL) + return 0; + return BIO_callback_ctrl(next, cmd, fp); +} + +#endif diff --git a/crypto/comp/comp_err.c b/crypto/comp/comp_err.c index 70a6eea0f0..4f55f820da 100644 --- a/crypto/comp/comp_err.c +++ b/crypto/comp/comp_err.c @@ -17,6 +17,16 @@ # ifndef OPENSSL_NO_ERR static const ERR_STRING_DATA COMP_str_reasons[] = { + {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_DECODE_ERROR), + "brotli decode error"}, + {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_DEFLATE_ERROR), + "brotli deflate error"}, + {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_ENCODE_ERROR), + "brotli encode error"}, + {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_INFLATE_ERROR), + "brotli inflate error"}, + {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_BROTLI_NOT_SUPPORTED), + "brotli not supported"}, {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_ZLIB_DEFLATE_ERROR), "zlib deflate error"}, {ERR_PACK(ERR_LIB_COMP, 0, COMP_R_ZLIB_INFLATE_ERROR), diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index c2978074a6..67179fa9ae 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -382,6 +382,11 @@ CMS_R_UNWRAP_ERROR:157:unwrap error CMS_R_UNWRAP_FAILURE:180:unwrap failure CMS_R_VERIFICATION_FAILURE:158:verification failure CMS_R_WRAP_ERROR:159:wrap error +COMP_R_BROTLI_DECODE_ERROR:102:brotli decode error +COMP_R_BROTLI_DEFLATE_ERROR:103:brotli deflate error +COMP_R_BROTLI_ENCODE_ERROR:106:brotli encode error +COMP_R_BROTLI_INFLATE_ERROR:104:brotli inflate error +COMP_R_BROTLI_NOT_SUPPORTED:105:brotli not supported COMP_R_ZLIB_DEFLATE_ERROR:99:zlib deflate error COMP_R_ZLIB_INFLATE_ERROR:100:zlib inflate error COMP_R_ZLIB_NOT_SUPPORTED:101:zlib not supported diff --git a/crypto/init.c b/crypto/init.c index a224542e03..fa8f0d694a 100644 --- a/crypto/init.c +++ b/crypto/init.c @@ -389,6 +389,8 @@ void OPENSSL_cleanup(void) #ifndef OPENSSL_NO_COMP OSSL_TRACE(INIT, "OPENSSL_cleanup: ossl_comp_zlib_cleanup()\n"); ossl_comp_zlib_cleanup(); + OSSL_TRACE(INIT, "OPENSSL_cleanup: ossl_comp_brotli_cleanup()\n"); + ossl_comp_brotli_cleanup(); #endif if (async_inited) { diff --git a/crypto/objects/obj_dat.h b/crypto/objects/obj_dat.h index b97118922c..115c707cd1 100644 --- a/crypto/objects/obj_dat.h +++ b/crypto/objects/obj_dat.h @@ -1154,7 +1154,7 @@ static const unsigned char so[8356] = { 0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x09,0x10,0x01,0x32, /* [ 8344] OBJ_id_ct_signedTAL */ }; -#define NUM_NID 1288 +#define NUM_NID 1289 static const ASN1_OBJECT nid_objs[NUM_NID] = { {"UNDEF", "undefined", NID_undef}, {"rsadsi", "RSA Data Security, Inc.", NID_rsadsi, 6, &so[0]}, @@ -2444,9 +2444,10 @@ static const ASN1_OBJECT nid_objs[NUM_NID] = { {"brainpoolP256r1tls13", "brainpoolP256r1tls13", NID_brainpoolP256r1tls13}, {"brainpoolP384r1tls13", "brainpoolP384r1tls13", NID_brainpoolP384r1tls13}, {"brainpoolP512r1tls13", "brainpoolP512r1tls13", NID_brainpoolP512r1tls13}, + {"brotli", "Brotli compression", NID_brotli}, }; -#define NUM_SN 1279 +#define NUM_SN 1280 static const unsigned int sn_objs[NUM_SN] = { 364, /* "AD_DVCS" */ 419, /* "AES-128-CBC" */ @@ -2794,6 +2795,7 @@ static const unsigned int sn_objs[NUM_SN] = { 933, /* "brainpoolP512r1" */ 1287, /* "brainpoolP512r1tls13" */ 934, /* "brainpoolP512t1" */ + 1288, /* "brotli" */ 494, /* "buildingName" */ 860, /* "businessCategory" */ 691, /* "c2onb191v4" */ @@ -3729,7 +3731,7 @@ static const unsigned int sn_objs[NUM_SN] = { 1093, /* "x509ExtAdmission" */ }; -#define NUM_LN 1279 +#define NUM_LN 1280 static const unsigned int ln_objs[NUM_LN] = { 363, /* "AD Time Stamping" */ 405, /* "ANSI X9.62" */ @@ -3741,6 +3743,7 @@ static const unsigned int ln_objs[NUM_LN] = { 365, /* "Basic OCSP Response" */ 285, /* "Biometric Info" */ 1221, /* "Brand Indicator for Message Identification" */ + 1288, /* "Brotli compression" */ 179, /* "CA Issuers" */ 785, /* "CA Repository" */ 1219, /* "CMC Archive Server" */ diff --git a/crypto/objects/obj_mac.num b/crypto/objects/obj_mac.num index 64dffcb7c1..5940f6911b 100644 --- a/crypto/objects/obj_mac.num +++ b/crypto/objects/obj_mac.num @@ -1285,3 +1285,4 @@ id_ct_signedTAL 1284 brainpoolP256r1tls13 1285 brainpoolP384r1tls13 1286 brainpoolP512r1tls13 1287 +brotli 1288 diff --git a/crypto/objects/objects.txt b/crypto/objects/objects.txt index b627cfdfd1..a0991529b9 100644 --- a/crypto/objects/objects.txt +++ b/crypto/objects/objects.txt @@ -1802,3 +1802,6 @@ dstu4145le 2 9 : uacurve9 : DSTU curve 9 joint-iso-itu-t 16 840 1 113894 : oracle-organization : Oracle organization # Jdk trustedKeyUsage attribute oracle 746875 1 1 : oracle-jdk-trustedkeyusage : Trusted key usage (Oracle) + +# NID for brotli + : brotli : Brotli compression |