Add a "zlib" crypto module, which differs from the existing "deflate" crypto module in the following aspects: - Support for the new partial (de)compression API has been added, - The (de)compression parameters have been changed to accomodate SquashFS ("zlib" format with 15 window bits instead of "raw deflate" format with 11 windows bits). Todo: - Make the (de)compression parameters configurable, so it can be merged back into the "deflate" crypto module Caveats: - Compression hasn't been tested yet Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@xxxxxxxxxxx> --- Kconfig | 8 + Makefile | 1 zlib.c | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 398 insertions(+) diff --git a/crypto/Kconfig b/crypto/Kconfig index 39dbd8e..51bf6fa 100644 --- a/crypto/Kconfig +++ b/crypto/Kconfig @@ -695,6 +695,14 @@ config CRYPTO_LZO help This is the LZO algorithm. +config CRYPTO_ZLIB + tristate "Zlib compression algorithm" + select CRYPTO_ALGAPI + select ZLIB_INFLATE + select ZLIB_DEFLATE + help + This is the Zlib algorithm. + comment "Random Number Generation" config CRYPTO_ANSI_CPRNG diff --git a/crypto/Makefile b/crypto/Makefile index 5862b80..8d4cdb7 100644 --- a/crypto/Makefile +++ b/crypto/Makefile @@ -76,6 +76,7 @@ obj-$(CONFIG_CRYPTO_LZO) += lzo.o obj-$(CONFIG_CRYPTO_RNG) += rng.o obj-$(CONFIG_CRYPTO_RNG) += krng.o obj-$(CONFIG_CRYPTO_ANSI_CPRNG) += ansi_cprng.o +obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o obj-$(CONFIG_CRYPTO_TEST) += tcrypt.o # diff --git a/crypto/zlib.c b/crypto/zlib.c new file mode 100644 index 0000000..a95340d --- /dev/null +++ b/crypto/zlib.c @@ -0,0 +1,389 @@ +#define DEBUG +/* + * Cryptographic API. + * + * "zlib" crypto module, based on the "deflate" crypto module + * + * Copyright (c) 2003 James Morris <jmorris@xxxxxxxxxxxxxxxx> + * Copyright 2008 Sony Corp. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * FIXME: deflate transforms will require up to a total of about 436k of kernel + * memory on i386 (390k for compression, the rest for decompression), as the + * current zlib kernel code uses a worst case pre-allocation system by default. + * This needs to be fixed so that the amount of memory required is properly + * related to the winbits and memlevel parameters. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/crypto.h> +#include <linux/zlib.h> +#include <linux/vmalloc.h> +#include <linux/interrupt.h> +#include <linux/mm.h> +#include <linux/net.h> +#include <linux/slab.h> + +#define ZLIB_DEF_LEVEL Z_DEFAULT_COMPRESSION + +struct zlib_ctx { + struct z_stream_s comp_stream; + struct z_stream_s decomp_stream; +}; + +static int zlib_comp_init(struct zlib_ctx *ctx) +{ + int ret = 0; + struct z_stream_s *stream = &ctx->comp_stream; + + stream->workspace = vmalloc(zlib_deflate_workspacesize()); + if (!stream->workspace) { + ret = -ENOMEM; + goto out; + } + memset(stream->workspace, 0, zlib_deflate_workspacesize()); + ret = zlib_deflateInit(stream, ZLIB_DEF_LEVEL); + if (ret != Z_OK) { + ret = -EINVAL; + goto out_free; + } +out: + return ret; +out_free: + vfree(stream->workspace); + goto out; +} + +static int zlib_decomp_init(struct zlib_ctx *ctx) +{ + int ret = 0; + struct z_stream_s *stream = &ctx->decomp_stream; + + stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); + if (!stream->workspace) { + ret = -ENOMEM; + goto out; + } + ret = zlib_inflateInit(stream); + if (ret != Z_OK) { + ret = -EINVAL; + goto out_free; + } +out: + return ret; +out_free: + kfree(stream->workspace); + goto out; +} + +static void zlib_comp_exit(struct zlib_ctx *ctx) +{ + zlib_deflateEnd(&ctx->comp_stream); + vfree(ctx->comp_stream.workspace); +} + +static void zlib_decomp_exit(struct zlib_ctx *ctx) +{ + zlib_inflateEnd(&ctx->decomp_stream); + kfree(ctx->decomp_stream.workspace); +} + +static int zlib_init(struct crypto_tfm *tfm) +{ + struct zlib_ctx *ctx = crypto_tfm_ctx(tfm); + int ret; + + ret = zlib_comp_init(ctx); + if (ret) + goto out; + ret = zlib_decomp_init(ctx); + if (ret) + zlib_comp_exit(ctx); +out: + return ret; +} + +static void zlib_exit(struct crypto_tfm *tfm) +{ + struct zlib_ctx *ctx = crypto_tfm_ctx(tfm); + + zlib_comp_exit(ctx); + zlib_decomp_exit(ctx); +} + +static int zlib_compress(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->comp_stream; + + pr_debug("%s: slen %u, dlen %u\n", __func__, slen, *dlen); + ret = zlib_deflateReset(stream); + if (ret != Z_OK) { + pr_err("%s: zlib_deflateReset failed %d\n", __func__, ret); + return -EINVAL; + } + + stream->next_in = (u8 *)src; + stream->avail_in = slen; + stream->next_out = (u8 *)dst; + stream->avail_out = *dlen; + + ret = zlib_deflate(stream, Z_FINISH); + if (ret != Z_STREAM_END) { + pr_err("%s: zlib_deflate failed %d\n", __func__, ret); + return -EINVAL; + } + + *dlen = stream->total_out; + pr_debug("%s: dlen_out %u\n", __func__, *dlen); + return 0; +} + +static int zlib_decompress(struct crypto_tfm *tfm, const u8 *src, + unsigned int slen, u8 *dst, unsigned int *dlen) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->decomp_stream; + + pr_debug("%s: slen %u, dlen %u\n", __func__, slen, *dlen); + ret = zlib_inflateReset(stream); + if (ret != Z_OK) { + pr_err("%s: zlib_inflateReset failed %d\n", __func__, ret); + return -EINVAL; + } + + stream->next_in = (u8 *)src; + stream->avail_in = slen; + stream->next_out = (u8 *)dst; + stream->avail_out = *dlen; + + ret = zlib_inflate(stream, Z_FINISH); + if (ret != Z_STREAM_END) { + pr_err("%s: zlib_inflate failed %d\n", __func__, ret); + return -EINVAL; + } + + *dlen = stream->total_out; + pr_debug("%s: dlen_out %u\n", __func__, *dlen); + return 0; +} + +static int zlib_compress_update(struct crypto_tfm *tfm, + struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->comp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + stream->next_in = req->next_in; + stream->avail_in = req->avail_in; + stream->next_out = req->next_out; + stream->avail_out = req->avail_out; + + ret = zlib_deflate(stream, Z_NO_FLUSH); + switch (ret) { + case Z_OK: + break; + + case Z_BUF_ERROR: + pr_err("%s: zlib_deflate could not make progress\n", __func__); + return -EAGAIN; + + default: + pr_err("%s: zlib_deflate failed %d\n", __func__, ret); + return -EINVAL; + } + + pr_debug("%s: avail_in %u, avail_out %u (consumed %u, produced %u)\n", + __func__, stream->avail_in, stream->avail_out, + req->avail_in - stream->avail_in, + req->avail_out - stream->avail_out); + req->next_in = stream->next_in; + req->avail_in = stream->avail_in; + req->next_out = stream->next_out; + req->avail_out = stream->avail_out; + return 0; +} + +static int zlib_compress_init(struct crypto_tfm *tfm, struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->comp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + ret = zlib_deflateReset(stream); + if (ret != Z_OK) + return -EINVAL; + + return zlib_compress_update(tfm, req); +} + +static int zlib_compress_final(struct crypto_tfm *tfm, + struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->comp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + stream->next_in = req->next_in; + stream->avail_in = req->avail_in; + stream->next_out = req->next_out; + stream->avail_out = req->avail_out; + + ret = zlib_deflate(stream, Z_FINISH); + if (ret != Z_OK) { + pr_err("%s: zlib_deflate failed %d\n", __func__, ret); + return -EINVAL; + } + + pr_debug("%s: avail_in %u, avail_out %u (consumed %u, produced %u)\n", + __func__, stream->avail_in, stream->avail_out, + req->avail_in - stream->avail_in, + req->avail_out - stream->avail_out); + req->next_in = stream->next_in; + req->avail_in = stream->avail_in; + req->next_out = stream->next_out; + req->avail_out = stream->avail_out; + return 0; +} + +static int zlib_decompress_update(struct crypto_tfm *tfm, + struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->decomp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + stream->next_in = req->next_in; + stream->avail_in = req->avail_in; + stream->next_out = req->next_out; + stream->avail_out = req->avail_out; + + ret = zlib_inflate(stream, Z_SYNC_FLUSH); + switch (ret) { + case Z_OK: + case Z_STREAM_END: + break; + + case Z_BUF_ERROR: + pr_err("%s: zlib_inflate could not make progress\n", __func__); + return -EAGAIN; + + default: + pr_err("%s: zlib_inflate failed %d\n", __func__, ret); + return -EINVAL; + } + + pr_debug("%s: avail_in %u, avail_out %u (consumed %u, produced %u)\n", + __func__, stream->avail_in, stream->avail_out, + req->avail_in - stream->avail_in, + req->avail_out - stream->avail_out); + req->next_in = stream->next_in; + req->avail_in = stream->avail_in; + req->next_out = stream->next_out; + req->avail_out = stream->avail_out; + return 0; +} + +static int zlib_decompress_init(struct crypto_tfm *tfm, + struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->decomp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + ret = zlib_inflateReset(stream); + if (ret != Z_OK) + return -EINVAL; + + return zlib_decompress_update(tfm, req); +} + +static int zlib_decompress_final(struct crypto_tfm *tfm, + struct comp_request *req) +{ + int ret; + struct zlib_ctx *dctx = crypto_tfm_ctx(tfm); + struct z_stream_s *stream = &dctx->decomp_stream; + + pr_debug("%s: avail_in %u, avail_out %u\n", __func__, req->avail_in, + req->avail_out); + stream->next_in = req->next_in; + stream->avail_in = req->avail_in; + stream->next_out = req->next_out; + stream->avail_out = req->avail_out; + + ret = zlib_inflate(stream, Z_FINISH); + if (ret != Z_STREAM_END) { + pr_err("%s: zlib_inflate failed %d\n", __func__, ret); + return -EINVAL; + } + + pr_debug("%s: avail_in %u, avail_out %u (consumed %u, produced %u)\n", + __func__, stream->avail_in, stream->avail_out, + req->avail_in - stream->avail_in, + req->avail_out - stream->avail_out); + req->next_in = stream->next_in; + req->avail_in = stream->avail_in; + req->next_out = stream->next_out; + req->avail_out = stream->avail_out; + return 0; +} + +static struct crypto_alg alg = { + .cra_name = "zlib", + .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, + .cra_ctxsize = sizeof(struct zlib_ctx), + .cra_module = THIS_MODULE, + .cra_list = LIST_HEAD_INIT(alg.cra_list), + .cra_init = zlib_init, + .cra_exit = zlib_exit, + .cra_u = { + .compress = { + /* one shot */ + .coa_compress = zlib_compress, + .coa_decompress = zlib_decompress, + /* partial */ + .coa_compress_init = zlib_compress_init, + .coa_compress_update = zlib_compress_update, + .coa_compress_final = zlib_compress_final, + .coa_decompress_init = zlib_decompress_init, + .coa_decompress_update = zlib_decompress_update, + .coa_decompress_final = zlib_decompress_final + } + } +}; + +static int __init zlib_mod_init(void) +{ + return crypto_register_alg(&alg); +} + +static void __exit zlib_mod_fini(void) +{ + crypto_unregister_alg(&alg); +} + +module_init(zlib_mod_init); +module_exit(zlib_mod_fini); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Zlib Compression Algorithm"); + With kind regards, Geert Uytterhoeven Software Architect Sony Techsoft Centre Europe The Corporate Village · Da Vincilaan 7-D1 · B-1935 Zaventem · Belgium Phone: +32 (0)2 700 8453 Fax: +32 (0)2 700 8622 E-mail: Geert.Uytterhoeven@xxxxxxxxxxx Internet: http://www.sony-europe.com/ A division of Sony Europe (Belgium) N.V. VAT BE 0413.825.160 · RPR Brussels Fortis · BIC GEBABEBB · IBAN BE41293037680010 -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html