/*
* Copyright (C) 2017-2022 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
* This file is part of GnuTLS.
*
* The GnuTLS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see
*
*/
#include "compress.h"
#ifdef HAVE_LIBZ
#include
#endif
#ifdef HAVE_LIBBROTLI
#include
#include
#endif
#ifdef HAVE_LIBZSTD
#include
#endif
typedef struct {
gnutls_compression_method_t id;
const char *name;
} comp_entry;
static const comp_entry comp_algs[] = {
{ GNUTLS_COMP_NULL, "NULL" },
#ifdef HAVE_LIBZ
{ GNUTLS_COMP_ZLIB, "ZLIB" },
#endif
#ifdef HAVE_LIBBROTLI
{ GNUTLS_COMP_BROTLI, "BROTLI" },
#endif
#ifdef HAVE_LIBZSTD
{ GNUTLS_COMP_ZSTD, "ZSTD" },
#endif
{ GNUTLS_COMP_UNKNOWN, NULL }
};
static const gnutls_compression_method_t alg_list[] = {
GNUTLS_COMP_NULL,
#ifdef HAVE_LIBZ
GNUTLS_COMP_ZLIB,
#endif
#ifdef HAVE_LIBBROTLI
GNUTLS_COMP_BROTLI,
#endif
#ifdef HAVE_LIBZSTD
GNUTLS_COMP_ZSTD,
#endif
0
};
/**
* gnutls_compression_get_name:
* @algorithm: is a Compression algorithm
*
* Convert a #gnutls_compression_method_t value to a string.
*
* Returns: a pointer to a string that contains the name of the
* specified compression algorithm, or %NULL.
**/
const char *
gnutls_compression_get_name(gnutls_compression_method_t algorithm)
{
const comp_entry *p;
for (p = comp_algs; p->name; ++p)
if (p->id == algorithm)
return p->name;
return NULL;
}
/**
* gnutls_compression_get_id:
* @name: is a compression method name
*
* The names are compared in a case insensitive way.
*
* Returns: an id of the specified in a string compression method, or
* %GNUTLS_COMP_UNKNOWN on error.
**/
gnutls_compression_method_t
gnutls_compression_get_id(const char *name)
{
const comp_entry *p;
for (p = comp_algs; p->name; ++p)
if (!strcasecmp(p->name, name))
return p->id;
return GNUTLS_COMP_UNKNOWN;
}
/**
* gnutls_compression_list:
*
* Get a list of compression methods.
*
* Returns: a zero-terminated list of #gnutls_compression_method_t
* integers indicating the available compression methods.
**/
const gnutls_compression_method_t *
gnutls_compression_list(void)
{
return alg_list;
}
/*************************/
/* Compression functions */
/*************************/
size_t
_gnutls_compress_bound(gnutls_compression_method_t alg, size_t src_len)
{
switch (alg) {
#ifdef HAVE_LIBZ
case GNUTLS_COMP_ZLIB:
return compressBound(src_len);
#endif
#ifdef HAVE_LIBBROTLI
case GNUTLS_COMP_BROTLI:
return BrotliEncoderMaxCompressedSize(src_len);
#endif
#ifdef HAVE_LIBZSTD
case GNUTLS_COMP_ZSTD:
return ZSTD_compressBound(src_len);
#endif
default:
return 0;
}
return 0;
}
int
_gnutls_compress(gnutls_compression_method_t alg,
uint8_t * dst, size_t dst_len,
const uint8_t * src, size_t src_len)
{
int ret = GNUTLS_E_COMPRESSION_FAILED;
switch (alg) {
#ifdef HAVE_LIBZ
case GNUTLS_COMP_ZLIB:
{
int err;
uLongf comp_len = dst_len;
err = compress(dst, &comp_len, src, src_len);
if (err != Z_OK)
return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
ret = comp_len;
}
break;
#endif
#ifdef HAVE_LIBBROTLI
case GNUTLS_COMP_BROTLI:
{
BROTLI_BOOL err;
size_t comp_len = dst_len;
err = BrotliEncoderCompress(BROTLI_DEFAULT_QUALITY,
BROTLI_DEFAULT_WINDOW,
BROTLI_DEFAULT_MODE,
src_len, src, &comp_len, dst);
if (!err)
return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
ret = comp_len;
}
break;
#endif
#ifdef HAVE_LIBZSTD
case GNUTLS_COMP_ZSTD:
{
size_t comp_len;
comp_len = ZSTD_compress(dst, dst_len, src, src_len, ZSTD_CLEVEL_DEFAULT);
if (ZSTD_isError(comp_len))
return gnutls_assert_val(GNUTLS_E_COMPRESSION_FAILED);
ret = comp_len;
}
break;
#endif
default:
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
#ifdef COMPRESSION_DEBUG
_gnutls_debug_log("Compression ratio: %f\n", (float)((float)ret / (float)src_len));
#endif
return ret;
}
int
_gnutls_decompress(gnutls_compression_method_t alg,
uint8_t * dst, size_t dst_len,
const uint8_t * src, size_t src_len)
{
int ret = GNUTLS_E_DECOMPRESSION_FAILED;
switch (alg) {
#ifdef HAVE_LIBZ
case GNUTLS_COMP_ZLIB:
{
int err;
uLongf plain_len = dst_len;
err = uncompress(dst, &plain_len, src, src_len);
if (err != Z_OK)
return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
ret = plain_len;
}
break;
#endif
#ifdef HAVE_LIBBROTLI
case GNUTLS_COMP_BROTLI:
{
BrotliDecoderResult err;
size_t plain_len = dst_len;
err = BrotliDecoderDecompress(src_len, src, &plain_len, dst);
if (err != BROTLI_DECODER_RESULT_SUCCESS)
return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
ret = plain_len;
}
break;
#endif
#ifdef HAVE_LIBZSTD
case GNUTLS_COMP_ZSTD:
{
size_t plain_len;
plain_len = ZSTD_decompress(dst, dst_len, src, src_len);
if (ZSTD_isError(plain_len))
return gnutls_assert_val(GNUTLS_E_DECOMPRESSION_FAILED);
ret = plain_len;
}
break;
#endif
default:
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
}
return ret;
}