]> git.ipfire.org Git - thirdparty/openssl.git/commitdiff
Add brotli compression support (RFC7924)
authorTodd Short <tshort@akamai.com>
Mon, 9 Aug 2021 20:56:29 +0000 (16:56 -0400)
committerTodd Short <todd.short@me.com>
Tue, 18 Oct 2022 13:30:18 +0000 (09:30 -0400)
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: Hugo Landau <hlandau@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/18186)

27 files changed:
Configurations/00-base-templates.conf
Configure
INSTALL.md
apps/enc.c
apps/list.c
apps/progs.pl
crypto/comp/build.info
crypto/comp/c_brotli.c [new file with mode: 0644]
crypto/comp/comp_err.c
crypto/err/openssl.txt
crypto/init.c
crypto/objects/obj_dat.h
crypto/objects/obj_mac.num
crypto/objects/objects.txt
doc/build.info
doc/man3/COMP_CTX_new.pod [new file with mode: 0644]
doc/man3/SSL_COMP_add_compression_method.pod
include/internal/comp.h
include/openssl/comp.h
include/openssl/comperr.h
include/openssl/obj_mac.h
test/bio_comp_test.c [new file with mode: 0644]
test/build.info
test/recipes/07-test_bio_comp.t [new file with mode: 0644]
util/libcrypto.num
util/missingcrypto.txt
util/perl/OpenSSL/Ordinals.pm

index 0e5d38d5a63273cf118819046ace4dd8665dae54..4bc19159062f202c593639c7eb15f06e44d8068d 100644 (file)
@@ -48,6 +48,8 @@ my %targets=(
        defines         =>
            sub {
                 my @defs = ( 'OPENSSL_BUILDING_OPENSSL' );
+                push @defs, "BROTLI" unless $disabled{brotli};
+                push @defs, "BROTLI_SHARED" unless $disabled{"brotli-dynamic"};
                 push @defs, "ZLIB" unless $disabled{zlib};
                 push @defs, "ZLIB_SHARED" unless $disabled{"zlib-dynamic"};
                 return [ @defs ];
@@ -55,6 +57,8 @@ my %targets=(
         includes        =>
             sub {
                 my @incs = ();
+                push @incs, $withargs{brotli_include}
+                    if !$disabled{brotli} && $withargs{brotli_include};
                 push @incs, $withargs{zlib_include}
                     if !$disabled{zlib} && $withargs{zlib_include};
                 return [ @incs ];
@@ -69,11 +73,24 @@ my %targets=(
         ARFLAGS         => "qc",
         CC              => "cc",
         lflags          =>
-            sub { $withargs{zlib_lib} ? "-L".$withargs{zlib_lib} : () },
+            sub {
+                my @libs = ();
+                push(@libs, "-L".$withargs{zlib_lib}) if $withargs{zlib_lib};
+                push(@libs, "-L".$withargs{brotli_lib}) if $withargs{brotli_lib};
+                return join(" ", @libs);
+            },
         ex_libs         =>
-            sub { !defined($disabled{zlib})
-                  && defined($disabled{"zlib-dynamic"})
-                  ? "-lz" : () },
+            sub {
+                my @libs = ();
+                push(@libs, "-lz") if !defined($disabled{zlib}) && defined($disabled{"zlib-dynamic"});
+                if (!defined($disabled{brotli}) && defined($disabled{"brotli-dynamic"})) {
+                    push(@libs, "-lbrotlienc");
+                    push(@libs, "-lbrotlidec");
+                    push(@libs, "-lbrotlicommon");
+                    push(@libs, "-lm");
+                }
+                return join(" ", @libs);
+            },
         HASHBANGPERL    => "/usr/bin/env perl", # Only Unix actually cares
         RANLIB          => sub { which("$config{cross_compile_prefix}ranlib")
                                      ? "ranlib" : "" },
@@ -100,12 +117,24 @@ my %targets=(
             },
         ex_libs         =>
             sub {
+                my @libs = ();
                 unless ($disabled{zlib}) {
                     if (defined($disabled{"zlib-dynamic"})) {
-                        return $withargs{zlib_lib} // "ZLIB1";
+                        push(@libs, $withargs{zlib_lib} // "ZLIB1");
+                    }
+                }
+                unless ($disabled{brotli}) {
+                    if (defined($disabled{"brotli-dynamic"})) {
+                        my $path = "";
+                        if (defined($withargs{brotli_lib})) {
+                            $path = $withargs{brotli_lib} . "\\";
+                        }
+                        push(@libs, $path . "brotlicommon.lib");
+                        push(@libs, $path . "brotlidec.lib");
+                        push(@libs, $path . "brotlienc.lib");
                     }
                 }
-                return ();
+                return join(" ", @libs);
             },
 
         MT              => "mt",
index fbafe0e867c9b2add7a80e9a2ab25af9d509c795..7f7c88922711d4f5b9f3abcbebbf0a08f5db6bdc 100755 (executable)
--- a/Configure
+++ b/Configure
@@ -27,7 +27,7 @@ use OpenSSL::config;
 my $orig_death_handler = $SIG{__DIE__};
 $SIG{__DIE__} = \&death_handler;
 
-my $usage="Usage: Configure [no-<cipher> ...] [enable-<cipher> ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]thread-pool] [[no-]default-thread-pool] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] os/compiler[:flags]\n";
+my $usage="Usage: Configure [no-<feature> ...] [enable-<feature> ...] [-Dxxx] [-lxxx] [-Lxxx] [-fxxx] [-Kxxx] [no-hw-xxx|no-hw] [[no-]threads] [[no-]thread-pool] [[no-]default-thread-pool] [[no-]shared] [[no-]zlib|zlib-dynamic] [no-asm] [no-egd] [sctp] [386] [--prefix=DIR] [--openssldir=OPENSSLDIR] [--with-xxx[=vvv]] [--config=FILE] os/compiler[:flags]\n";
 
 my $banner = <<"EOF";
 
@@ -92,7 +92,7 @@ EOF
 # no-egd        do not compile support for the entropy-gathering daemon APIs
 # [no-]zlib     [don't] compile support for zlib compression.
 # zlib-dynamic  Like "zlib", but the zlib library is expected to be a shared
-#               library and will be loaded in run-time by the OpenSSL library.
+#               library and will be loaded at run-time by the OpenSSL library.
 # sctp          include SCTP support
 # enable-quic   include QUIC support (currently just for developers as the
 #               implementation is by no means complete and usable)
@@ -416,6 +416,8 @@ my @disablables = (
     "autoload-config",
     "bf",
     "blake2",
+    "brotli",
+    "brotli-dynamic",
     "buildtest-c++",
     "bulk",
     "cached-fetch",
@@ -546,6 +548,8 @@ my %deprecated_disablables = (
 our %disabled = ( # "what"         => "comment"
                   "fips"                => "default",
                   "asan"                => "default",
+                  "brotli"              => "default",
+                  "brotli-dynamic"      => "default",
                   "buildtest-c++"       => "default",
                   "crypto-mdebug"       => "default",
                   "crypto-mdebug-backtrace" => "default",
@@ -597,6 +601,7 @@ my @disable_cascades = (
     "ssl"               => [ "ssl3" ],
     "ssl3-method"       => [ "ssl3" ],
     "zlib"              => [ "zlib-dynamic" ],
+    "brotli"            => [ "brotli-dynamic" ],
     "des"               => [ "mdc2" ],
     "ec"                => [ "ec2m", "ecdsa", "ecdh", "sm2", "gost" ],
     "dgram"             => [ "dtls", "quic", "sctp" ],
@@ -642,7 +647,7 @@ my @disable_cascades = (
     "stdio"             => [ "apps", "capieng", "egd" ],
     "apps"              => [ "tests" ],
     "tests"             => [ "external-tests" ],
-    "comp"              => [ "zlib" ],
+    "comp"              => [ "zlib", "brotli" ],
     "sm3"               => [ "sm2" ],
     sub { !$disabled{"unit-test"} } => [ "heartbeats" ],
 
@@ -903,6 +908,10 @@ while (@argvcopy)
                         {
                         delete $disabled{"zlib"};
                         }
+                elsif ($1 eq "brotli-dynamic")
+                        {
+                        delete $disabled{"brotli"};
+                        }
                 my $algo = $1;
                 delete $disabled{$algo};
 
@@ -979,6 +988,14 @@ while (@argvcopy)
                         {
                         $withargs{zlib_include}=$1;
                         }
+                elsif (/^--with-brotli-lib=(.*)$/)
+                        {
+                        $withargs{brotli_lib}=$1;
+                        }
+                elsif (/^--with-brotli-include=(.*)$/)
+                        {
+                        $withargs{brotli_include}=$1;
+                        }
                 elsif (/^--with-fuzzer-lib=(.*)$/)
                         {
                         $withargs{fuzzer_lib}=$1;
index f16ecf9c8993795bf9aa1ab49fc518630e5ae101..234f39c201d3a7ab501959981f243c5b7fe53992 100644 (file)
@@ -19,7 +19,7 @@ Table of Contents
    - [Build Type](#build-type)
    - [Directories](#directories)
    - [Compiler Warnings](#compiler-warnings)
-   - [ZLib Flags](#zlib-flags)
+   - [Compression Algorithm Flags](#compression-algorithm-flags)
    - [Seeding the Random Generator](#seeding-the-random-generator)
    - [Setting the FIPS HMAC key](#setting-the-FIPS-HMAC-key)
    - [Enable and Disable Features](#enable-and-disable-features)
@@ -382,8 +382,39 @@ for OpenSSL development.  It only works when using gcc or clang as the compiler.
 If you are developing a patch for OpenSSL then it is recommended that you use
 this option where possible.
 
-ZLib Flags
-----------
+Compression Algorithm Flags
+---------------------------
+
+### with-brotli-include
+
+    --with-brotli-include=DIR
+
+The directory for the location of the brotli include files (i.e. the location
+of the **brotli** include directory).  This option is only necessary if
+[enable-brotli](#enable-brotli) is used and the include files are not already
+on the system include path.
+
+### with-brotli-lib
+
+    --with-brotli-lib=LIB
+
+**On Unix**: this is the directory containing the brotli libraries.
+If not provided, the system library path will be used.
+
+The names of the libraries are:
+
+* libbrotlicommon.a or libbrotlicommon.so
+* libbrotlidec.a or libbrotlidec.so
+* libbrotlienc.a or libbrotlienc.so
+
+**On Windows:** this is the directory containing the brotli libraries.
+If not provided, the system library path will be used.
+
+The names of the libraries are:
+
+* brotlicommon.lib
+* brotlidec.lib
+* brotlienc.lib
 
 ### with-zlib-include
 
@@ -556,6 +587,17 @@ Typically OpenSSL will automatically load human readable error strings.  For a
 statically linked application this may be undesirable if small executable size
 is an objective.
 
+### enable-brotli
+
+Build with support for brotli compression/decompression.
+
+### enable-brotli-dynamic
+
+Like the enable-brotli option, but has OpenSSL load the brotli library dynamically
+when needed.
+
+This is only supported on systems where loading of shared libraries is supported.
+
 ### no-autoload-config
 
 Don't automatically load the default `openssl.cnf` file.
index 26ad3deb9afad11f6d2de92e5e09f4b67a355a9d..4da2342791650ecb6cf1504ff5814238302acd4c 100644 (file)
@@ -134,6 +134,8 @@ int enc_main(int argc, char **argv)
     int do_zlib = 0;
     BIO *bzl = NULL;
 #endif
+    int do_brotli = 0;
+    BIO *bbrot = NULL;
 
     /* first check the command name */
     if (strcmp(argv[0], "base64") == 0)
@@ -141,6 +143,10 @@ int enc_main(int argc, char **argv)
 #ifdef ZLIB
     else if (strcmp(argv[0], "zlib") == 0)
         do_zlib = 1;
+#endif
+#ifndef OPENSSL_NO_BROTLI
+    else if (strcmp(argv[0], "brotli") == 0)
+        do_brotli = 1;
 #endif
     else if (strcmp(argv[0], "enc") != 0)
         ciphername = argv[0];
@@ -321,14 +327,18 @@ int enc_main(int argc, char **argv)
         BIO_printf(bio_err, "bufsize=%d\n", bsize);
 
 #ifdef ZLIB
-    if (!do_zlib)
+    if (do_zlib)
+        base64 = 0;
 #endif
-        if (base64) {
-            if (enc)
-                outformat = FORMAT_BASE64;
-            else
-                informat = FORMAT_BASE64;
-        }
+    if (do_brotli)
+        base64 = 0;
+
+    if (base64) {
+        if (enc)
+            outformat = FORMAT_BASE64;
+        else
+            informat = FORMAT_BASE64;
+    }
 
     strbuf = app_malloc(SIZE, "strbuf");
     buff = app_malloc(EVP_ENCODE_LENGTH(bsize), "evp buffer");
@@ -398,7 +408,8 @@ int enc_main(int argc, char **argv)
     rbio = in;
     wbio = out;
 
-#ifdef ZLIB
+#ifndef OPENSSL_NO_COMP
+# ifdef ZLIB
     if (do_zlib) {
         if ((bzl = BIO_new(BIO_f_zlib())) == NULL)
             goto end;
@@ -411,6 +422,20 @@ int enc_main(int argc, char **argv)
         else
             rbio = BIO_push(bzl, rbio);
     }
+# endif
+
+    if (do_brotli) {
+        if ((bbrot = BIO_new(BIO_f_brotli())) == NULL)
+            goto end;
+        if (debug) {
+            BIO_set_callback_ex(bbrot, BIO_debug_callback_ex);
+            BIO_set_callback_arg(bbrot, (char *)bio_err);
+        }
+        if (enc)
+            wbio = BIO_push(bbrot, wbio);
+        else
+            rbio = BIO_push(bbrot, rbio);
+    }
 #endif
 
     if (base64) {
@@ -656,6 +681,7 @@ int enc_main(int argc, char **argv)
 #ifdef ZLIB
     BIO_free(bzl);
 #endif
+    BIO_free(bbrot);
     release_engine(e);
     OPENSSL_free(pass);
     return ret;
index adcfaa4260ef2033a5d881e7cabaa10c9ab4f2a3..f198c1cda75f6f66cb2b083046ce7b3508779e0f 100644 (file)
@@ -1424,6 +1424,9 @@ static void list_disabled(void)
 #ifndef ZLIB
     BIO_puts(bio_out, "ZLIB\n");
 #endif
+#ifdef OPENSSL_NO_BROTLI
+    BIO_puts(bio_out, "BROTLI\n");
+#endif
 }
 
 /* Unified enum for help and list commands. */
index 29f9be13ca088d56f1a3b1ed81a319c01faf813b..c4e7ae59cffebd233df1baff0c85d5c57a00837e 100644 (file)
@@ -188,7 +188,7 @@ EOF
         "camellia-128-cbc", "camellia-128-ecb",
         "camellia-192-cbc", "camellia-192-ecb",
         "camellia-256-cbc", "camellia-256-ecb",
-        "base64", "zlib",
+        "base64", "zlib", "brotli",
         "des", "des3", "desx", "idea", "seed", "rc4", "rc4-40",
         "rc2", "bf", "cast", "rc5",
         "des-ecb", "des-ede", "des-ede3",
index 65df46a175cd3be89a9a0e3b785800b5c1f77e9c..014628e45d1e72e2eed46c26400d3fb6a9b9dcfb 100644 (file)
@@ -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 (file)
index 0000000..ace6f22
--- /dev/null
@@ -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
index 70a6eea0f052715da49eebc4108530c72cd5087e..4f55f820da3b95eafbf418e85a14e70d6dbfe2aa 100644 (file)
 # 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),
index c2978074a6b773c5b1f1c49a8f3e8461fb8dc698..67179fa9aeaf8cd291c029a5eaf33ba3a53fd22d 100644 (file)
@@ -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
index a224542e03eaaa0764cbe727643e040b22abe305..fa8f0d694aba876b881464ceac8ff252d50f5094 100644 (file)
@@ -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) {
index b97118922cd7d11614f0732fc41512c8c8711e3c..115c707cd1875fc0e45047d1dc56604a477df3c7 100644 (file)
@@ -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" */
index 64dffcb7c1fe13096267e934bf643b27d1578424..5940f6911b2c3f5bf590b1d92056be2ac85d8a7d 100644 (file)
@@ -1285,3 +1285,4 @@ id_ct_signedTAL           1284
 brainpoolP256r1tls13           1285
 brainpoolP384r1tls13           1286
 brainpoolP512r1tls13           1287
+brotli         1288
index b627cfdfd1097b74889021ccd9735932a38ae843..a0991529b9082cf3bb2e055515f0def54d72b6ec 100644 (file)
@@ -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
index 45886a4f27db1eb34d4bafddfe790a089576131f..8884b0dd5f9142b6e0e10c83a147bdf98adbacbd 100644 (file)
@@ -859,6 +859,10 @@ DEPEND[html/man3/CMS_verify_receipt.html]=man3/CMS_verify_receipt.pod
 GENERATE[html/man3/CMS_verify_receipt.html]=man3/CMS_verify_receipt.pod
 DEPEND[man/man3/CMS_verify_receipt.3]=man3/CMS_verify_receipt.pod
 GENERATE[man/man3/CMS_verify_receipt.3]=man3/CMS_verify_receipt.pod
+DEPEND[html/man3/COMP_CTX_new.html]=man3/COMP_CTX_new.pod
+GENERATE[html/man3/COMP_CTX_new.html]=man3/COMP_CTX_new.pod
+DEPEND[man/man3/COMP_CTX_new.3]=man3/COMP_CTX_new.pod
+GENERATE[man/man3/COMP_CTX_new.3]=man3/COMP_CTX_new.pod
 DEPEND[html/man3/CONF_modules_free.html]=man3/CONF_modules_free.pod
 GENERATE[html/man3/CONF_modules_free.html]=man3/CONF_modules_free.pod
 DEPEND[man/man3/CONF_modules_free.3]=man3/CONF_modules_free.pod
@@ -2982,6 +2986,7 @@ html/man3/CMS_sign_receipt.html \
 html/man3/CMS_uncompress.html \
 html/man3/CMS_verify.html \
 html/man3/CMS_verify_receipt.html \
+html/man3/COMP_CTX_new.html \
 html/man3/CONF_modules_free.html \
 html/man3/CONF_modules_load_file.html \
 html/man3/CRYPTO_THREAD_run_once.html \
@@ -3586,6 +3591,7 @@ man/man3/CMS_sign_receipt.3 \
 man/man3/CMS_uncompress.3 \
 man/man3/CMS_verify.3 \
 man/man3/CMS_verify_receipt.3 \
+man/man3/COMP_CTX_new.3 \
 man/man3/CONF_modules_free.3 \
 man/man3/CONF_modules_load_file.3 \
 man/man3/CRYPTO_THREAD_run_once.3 \
diff --git a/doc/man3/COMP_CTX_new.pod b/doc/man3/COMP_CTX_new.pod
new file mode 100644 (file)
index 0000000..826fbb0
--- /dev/null
@@ -0,0 +1,156 @@
+=pod
+
+=head1 NAME
+
+COMP_CTX_new,
+COMP_CTX_get_method,
+COMP_CTX_get_type,
+COMP_get_type,
+COMP_get_name,
+COMP_CTX_free,
+COMP_compress_block,
+COMP_expand_block,
+COMP_zlib,
+COMP_brotli,
+COMP_brotli_oneshot,
+BIO_f_zlib,
+BIO_f_brotli
+- Compression support
+
+=head1 SYNOPSIS
+
+ #include <openssl/comp.h>
+
+ COMP_CTX *COMP_CTX_new(COMP_METHOD *meth);
+ void COMP_CTX_free(COMP_CTX *ctx);
+ const COMP_METHOD *COMP_CTX_get_method(const COMP_CTX *ctx);
+ int COMP_CTX_get_type(const COMP_CTX* comp);
+ int COMP_get_type(const COMP_METHOD *meth);
+ const char *COMP_get_name(const COMP_METHOD *meth);
+
+ int COMP_compress_block(COMP_CTX *ctx, unsigned char *out, int olen,
+                         unsigned char *in, int ilen);
+ int COMP_expand_block(COMP_CTX *ctx, unsigned char *out, int olen,
+                       unsigned char *in, int ilen);
+
+ COMP_METHOD *COMP_zlib(void);
+ COMP_METHOD *COMP_brotli(void);
+ COMP_METHOD *COMP_brotli_oneshot(void);
+
+ const BIO_METHOD *BIO_f_zlib(void);
+ const BIO_METHOD *BIO_f_brotli(void);
+
+=head1 DESCRIPTION
+
+These functions provide compression support for OpenSSL. Compression is used within
+the OpenSSL library to support TLS record and certificate compression.
+
+COMP_CTX_new() is used to create a new B<COMP_CTX> structure used to compress data.
+COMP_CTX_free() is used to free the returned B<COMP_CTX>.
+
+COMP_CTX_get_method() returns the B<COMP_METHOD> of the given I<ctx>.
+
+COMP_CTX_get_type() and COMP_get_type() return the NID for the B<COMP_CTX> and
+B<COMP_METHOD>, respectively. COMP_get_name() returns the name of the algorithm
+of the given B<COMP_METHOD>.
+
+COMP_compress_block() compresses b<ilen> bytes from the buffer I<in> into the
+buffer b<out> of size I<olen> using the algorithm specified by I<ctx>.
+
+COMP_expand_block() expands I<ilen> bytes from the buffer I<in> into the
+buffer I<out> of size I<olen> using the lgorithm specified by I<ctx>.
+
+Methods (B<COMP_METHOD>) may be specified by one of these functions. These functions
+will be available even if their corresponding compression algorithm is not configured
+into the OpenSSL library. In such a case, a non-operative method will be returned.
+Any compression operations using a non-operative method will fail.
+
+=over 4
+
+=item *
+
+COMP_zlib() returns a B<COMP_METHOD> for stream-based ZLIB compression.
+
+=item *
+
+COMP_brotli() returns a B<COMP_METHOD> for stream-based Brotli compression.
+
+=item *
+
+COMP_brotli_oneshot() returns a B<COMP_METHOD> for one-shot Brotli compression.
+
+=back
+
+BIO_f_zlib() and BIO_f_brotli() each return a B<BIO_METHOD> that may be used to
+create a B<BIO> via L<BIO_new(3)> to read and write compressed files or streams.
+The functions are only available if the corresponding algorithm is compiled into
+the OpenSSL library.
+
+=head1 NOTES
+
+While compressing non-compressible data, the output may be larger than the
+input. Care should be taken to size output buffers appropriate for both
+compression and expansion.
+
+Compression support and compression algorithms must be enabled and built into
+the library before use. Refer to the INSTALL.md file when configuring OpenSSL.
+
+ZLIB may be found at L<https://zlib.net>
+
+Brotli may be found at L<https://github.com/google/brotli>.
+
+Compression of SSL/TLS records is not recommended, as it has been
+shown to lead to the CRIME attack L<https://en.wikipedia.org/wiki/CRIME>.
+It is disabled by default, and may be enabled by clearing the
+SSL_OP_NO_COMPRESSION options of the L<SSL_CTX_set_options(3)> or
+L<SSL_set_options(3)> functions.
+
+Compression is also used to support certificate compression as described
+in RFC8879 L<https://datatracker.ietf.org/doc/html/rfc8879>.
+It may be disabled via the SSL_OP_NO_CERTIFICATE_COMPRESSION option of
+the L<SSL_CTX_set_options(3)> or L<SSL_set_options(3)> functions.
+
+COMP_zlib() and COMP_brotli() are both stream-based compression methods.
+Internal state (including compression dictionary) is maintained between calls. 
+If an error is returned, the stream is corrupted, and should be closed.
+
+COMP_brotli_oneshot() is not stream-based, it does not maintain state
+between calls. An error in one call does not affect future calls.
+
+=head1 RETURN VALUES
+
+COMP_CTX_new() returns a B<COMP_CTX> on success, or NULL on failure.
+
+COMP_CTX_get_method(), COMP_zlib(), COMP_brotli(), and COMP_brotli_oneshot()
+return a B<COMP_METHOD> on success, or NULL on failure.
+
+COMP_CTX_get_type() and COMP_get_type() return a NID value. On failure,
+NID_undef is returned.
+
+COMP_compress_block() and COMP_expand_block() return the number of
+bytes stored in the output buffer I<out>. This may be 0. On failure,
+-1 is returned.
+
+COMP_get_name() returns a B<const char *> that must not be freed
+on success, or NULL on failure.
+
+BIO_f_zlib() and BIO_f_brotli() return a B<BIO_METHOD>.
+
+=head1 SEE ALSO
+
+L<BIO_new(3)>, L<SSL_CTX_set_options(3)>, L<SSL_set_options(3)>
+
+=head1 HISTORY
+
+Brotli functions were added in OpenSSL 3.1.0.
+
+=head1 COPYRIGHT
+
+Copyright 2022 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
+L<https://www.openssl.org/source/license.html>.
+
+=cut
index 9e3dc560c4ba898ac1b10cc89964e85f1cf9e958..a5aa674cdb921c5cd34557b0c346a78ba7daf051 100644 (file)
@@ -60,7 +60,17 @@ when a matching identifier is found. There is no way to restrict the list
 of compression methods supported on a per connection basis.
 
 If enabled during compilation, the OpenSSL library will have the
-COMP_zlib() compression method available.
+following compression methods available:
+
+=over 4
+
+=item COMP_zlib()
+
+=item COMP_brotli()
+
+=item COMP_brotli_oneshot()
+
+=back
 
 =head1 RETURN VALUES
 
index 3ad86fc7b1f155ad239a62efbe747c6e69055602..45cab6c720f89d07688a9823052ec860b7f049e5 100644 (file)
@@ -10,3 +10,4 @@
 #include <openssl/comp.h>
 
 void ossl_comp_zlib_cleanup(void);
+void ossl_comp_brotli_cleanup(void);
index 06ff58100ff048e18e684b810bdc5f2650689489..28f674de4dbf982cf76b6d78e04c91f1a10b254a 100644 (file)
@@ -40,6 +40,8 @@ int COMP_expand_block(COMP_CTX *ctx, unsigned char *out, int olen,
                       unsigned char *in, int ilen);
 
 COMP_METHOD *COMP_zlib(void);
+COMP_METHOD *COMP_brotli(void);
+COMP_METHOD *COMP_brotli_oneshot(void);
 
 #ifndef OPENSSL_NO_DEPRECATED_1_1_0
 # define COMP_zlib_cleanup() while(0) continue
@@ -49,6 +51,7 @@ COMP_METHOD *COMP_zlib(void);
 #  ifdef ZLIB
 const BIO_METHOD *BIO_f_zlib(void);
 #  endif
+const BIO_METHOD *BIO_f_brotli(void);
 # endif
 
 
index 01dd3e6bc642fb66c73c86c67475ea88bd0e10ec..31dcda8957354fd6a4b68138c1fb088a390f2c45 100644 (file)
 /*
  * COMP reason codes.
  */
+#  define COMP_R_BROTLI_DECODE_ERROR                       102
+#  define COMP_R_BROTLI_DEFLATE_ERROR                      103
+#  define COMP_R_BROTLI_ENCODE_ERROR                       106
+#  define COMP_R_BROTLI_INFLATE_ERROR                      104
+#  define COMP_R_BROTLI_NOT_SUPPORTED                      105
 #  define COMP_R_ZLIB_DEFLATE_ERROR                        99
 #  define COMP_R_ZLIB_INFLATE_ERROR                        100
 #  define COMP_R_ZLIB_NOT_SUPPORTED                        101
index 8ad445259d57e0d8311453b3e7b3662d931b2cb4..daa523329411e42032c2e99b93bd26a3d7d588ad 100644 (file)
 #define NID_oracle_jdk_trustedkeyusage          1283
 #define OBJ_oracle_jdk_trustedkeyusage          OBJ_oracle,746875L,1L,1L
 
+#define SN_brotli               "brotli"
+#define LN_brotli               "Brotli compression"
+#define NID_brotli              1288
+
 #endif /* OPENSSL_OBJ_MAC_H */
 
 #ifndef OPENSSL_NO_DEPRECATED_3_0
diff --git a/test/bio_comp_test.c b/test/bio_comp_test.c
new file mode 100644 (file)
index 0000000..b148d02
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2022 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
+ */
+#include <stdio.h>
+#include <string.h>
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/rand.h>
+#include <openssl/comp.h>
+
+#include "testutil.h"
+#include "testutil/output.h"
+#include "testutil/tu_local.h"
+
+#define COMPRESS  1
+#define EXPAND    0
+
+#define BUFFER_SIZE    32 * 1024
+#define NUM_SIZES      4
+static int sizes[NUM_SIZES] = { 64, 512, 2048, 16 * 1024 };
+
+/* using global buffers */
+uint8_t *original = NULL;
+uint8_t *result = NULL;
+
+/*
+ * For compression:
+ *   the write operation compresses
+ *   the read operation decompresses
+ */
+
+static int do_bio_comp_test(const BIO_METHOD *meth, size_t size)
+{
+    BIO *bcomp = NULL;
+    BIO *bmem = NULL;
+    BIO *bexp = NULL;
+    int osize;
+    int rsize;
+    int ret = 0;
+
+    /* Compress */
+    if (!TEST_ptr(bcomp = BIO_new(meth)))
+        goto err;
+    if (!TEST_ptr(bmem = BIO_new(BIO_s_mem())))
+        goto err;
+    BIO_push(bcomp, bmem);
+    osize = BIO_write(bcomp, original, size);
+    if (!TEST_int_eq(osize, size)
+        || !TEST_true(BIO_flush(bcomp)))
+        goto err;
+    BIO_free(bcomp);
+    bcomp = NULL;
+
+    /* decompress */
+    if (!TEST_ptr(bexp = BIO_new(meth)))
+        goto err;
+    BIO_push(bexp, bmem);
+    rsize = BIO_read(bexp, result, size);
+
+    if (!TEST_int_eq(size, osize)
+        || !TEST_int_eq(size, rsize)
+        || !TEST_mem_eq(original, osize, result, rsize))
+        goto err;
+
+    ret = 1;
+ err:
+    BIO_free(bexp);
+    BIO_free(bcomp);
+    BIO_free(bmem);
+    return ret;
+}
+
+static int do_bio_comp(const BIO_METHOD *meth, int n)
+{
+    int i;
+    int success = 0;
+    int size = sizes[n % 4];
+    int type = n / 4;
+
+    if (!TEST_ptr(original = OPENSSL_malloc(BUFFER_SIZE))
+        || !TEST_ptr(result = OPENSSL_malloc(BUFFER_SIZE)))
+        goto err;
+
+    switch (type) {
+    case 0:
+        test_printf_stdout("# zeros of size %d\n", size);
+        memset(original, 0, BUFFER_SIZE);
+        break;
+    case 1:
+        test_printf_stdout("# ones of size %d\n", size);
+        memset(original, 0, BUFFER_SIZE);
+        break;
+    case 2:
+        test_printf_stdout("# sequential of size %d\n", size);
+        for (i = 0; i < BUFFER_SIZE; i++)
+            original[i] = i & 0xFF;
+        break;
+    case 3:
+        test_printf_stdout("# random of size %d\n", size);
+        if (!TEST_int_gt(RAND_bytes(original, BUFFER_SIZE), 0))
+            goto err;
+        break;
+    default:
+        goto err;
+    }
+
+    if (!TEST_true(do_bio_comp_test(meth, size)))
+        goto err;
+    success = 1;
+ err:
+    OPENSSL_free(original);
+    OPENSSL_free(result);
+    return success;
+}
+
+#ifndef OPENSSL_NO_BROTLI
+static int test_brotli(int n)
+{
+    return do_bio_comp(BIO_f_brotli(), n);
+}
+#endif
+#ifdef ZLIB
+static int test_zlib(int n)
+{
+    return do_bio_comp(BIO_f_zlib(), n);
+}
+#endif
+
+int setup_tests(void)
+{
+#ifdef ZLIB
+    ADD_ALL_TESTS(test_zlib, NUM_SIZES * 4);
+#endif
+#ifndef OPENSSL_NO_BROTLI
+    ADD_ALL_TESTS(test_brotli, NUM_SIZES * 4);
+#endif
+    return 1;
+}
index e2cfddb22264d93a5bd9715feaed6ec9256d58d6..1fffaa15eb404347de869558408f9d404a3ecf88 100644 (file)
@@ -899,6 +899,13 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[context_internal_test]=.. ../include ../apps/include
   DEPEND[context_internal_test]=../libcrypto.a libtestutil.a
 
+  IF[{- !$disabled{zlib} || !$disabled{brotli} -}]
+    PROGRAMS{noinst}=bio_comp_test
+    SOURCE[bio_comp_test]=bio_comp_test.c
+    INCLUDE[bio_comp_test]=../include ../apps/include
+    DEPEND[bio_comp_test]=../libcrypto.a libtestutil.a
+  ENDIF
+
   PROGRAMS{noinst}=provider_internal_test
   DEFINE[provider_internal_test]=PROVIDER_INIT_FUNCTION_NAME=p_test_init
   SOURCE[provider_internal_test]=provider_internal_test.c p_test.c
diff --git a/test/recipes/07-test_bio_comp.t b/test/recipes/07-test_bio_comp.t
new file mode 100644 (file)
index 0000000..e43fc49
--- /dev/null
@@ -0,0 +1,19 @@
+#! /usr/bin/env perl
+# Copyright 2022 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
+
+use strict;
+use OpenSSL::Test;
+use OpenSSL::Test::Simple;
+use OpenSSL::Test::Utils;
+
+setup("test_bio_comp");
+
+plan skip_all => "No compression algorithms"
+    if disabled("zlib") && disabled("brotli");
+
+simple_test("test_bio_comp", "bio_comp_test");
index f5951d59e58008dc05e86eb9f08af873ac21f715..245b02ff2e1a7f766260b4158f361d89adc6f94e 100644 (file)
@@ -5469,3 +5469,6 @@ OSSL_sleep                              ? 3_2_0   EXIST::FUNCTION:
 OSSL_get_thread_support_flags           ?      3_2_0   EXIST::FUNCTION:
 OSSL_set_max_threads                    ?      3_2_0   EXIST::FUNCTION:
 OSSL_get_max_threads                    ?      3_2_0   EXIST::FUNCTION:
+COMP_brotli                             ?      3_2_0   EXIST::FUNCTION:COMP
+COMP_brotli_oneshot                     ?      3_2_0   EXIST::FUNCTION:COMP
+BIO_f_brotli                            ?      3_2_0   EXIST::FUNCTION:COMP
index be292c1b516fce729c452e2ab3872057606527c4..56ee90c8a163c7a27a3e5d5506f48a6f40d9506b 100644 (file)
@@ -190,7 +190,6 @@ BIO_f_asn1(3)
 BIO_f_linebuffer(3)
 BIO_f_nbio_test(3)
 BIO_f_reliable(3)
-BIO_f_zlib(3)
 BIO_fd_non_fatal_error(3)
 BIO_fd_should_retry(3)
 BIO_get_accept_socket(3)
@@ -353,15 +352,6 @@ CMS_unsigned_get_attr(3)
 CMS_unsigned_get_attr_by_NID(3)
 CMS_unsigned_get_attr_by_OBJ(3)
 CMS_unsigned_get_attr_count(3)
-COMP_CTX_free(3)
-COMP_CTX_get_method(3)
-COMP_CTX_get_type(3)
-COMP_CTX_new(3)
-COMP_compress_block(3)
-COMP_expand_block(3)
-COMP_get_name(3)
-COMP_get_type(3)
-COMP_zlib(3)
 CONF_dump_bio(3)
 CONF_dump_fp(3)
 CONF_free(3)
index f6c63d14c471fa6f7b763cf303174ab641e178af..66914254e01a2ee529251a8bafdbd548b8be1fa5 100644 (file)
@@ -414,6 +414,7 @@ sub _parse_features {
         my $def = $';
 
         if ($def =~ m{^ZLIB$})                      { $features{$&} =  $op; }
+        if ($def =~ m{^BROTLI$})                    { $features{$&} =  $op; }
         if ($def =~ m{^OPENSSL_USE_})               { $features{$'} =  $op; }
         if ($def =~ m{^OPENSSL_NO_})                { $features{$'} = !$op; }
     }