xref: /openbmc/linux/crypto/deflate.c (revision 565d76cb)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * Cryptographic API.
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Deflate algorithm (RFC 1951), implemented here primarily for use
51da177e4SLinus Torvalds  * by IPCOMP (RFC 3173 & RFC 2394).
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
81da177e4SLinus Torvalds  *
91da177e4SLinus Torvalds  * This program is free software; you can redistribute it and/or modify it
101da177e4SLinus Torvalds  * under the terms of the GNU General Public License as published by the Free
111da177e4SLinus Torvalds  * Software Foundation; either version 2 of the License, or (at your option)
121da177e4SLinus Torvalds  * any later version.
131da177e4SLinus Torvalds  *
141da177e4SLinus Torvalds  * FIXME: deflate transforms will require up to a total of about 436k of kernel
151da177e4SLinus Torvalds  * memory on i386 (390k for compression, the rest for decompression), as the
161da177e4SLinus Torvalds  * current zlib kernel code uses a worst case pre-allocation system by default.
171da177e4SLinus Torvalds  * This needs to be fixed so that the amount of memory required is properly
181da177e4SLinus Torvalds  * related to the  winbits and memlevel parameters.
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  * The default winbits of 11 should suit most packets, and it may be something
211da177e4SLinus Torvalds  * to configure on a per-tfm basis in the future.
221da177e4SLinus Torvalds  *
231da177e4SLinus Torvalds  * Currently, compression history is not maintained between tfm calls, as
241da177e4SLinus Torvalds  * it is not needed for IPCOMP and keeps the code simpler.  It can be
251da177e4SLinus Torvalds  * implemented if someone wants it.
261da177e4SLinus Torvalds  */
271da177e4SLinus Torvalds #include <linux/init.h>
281da177e4SLinus Torvalds #include <linux/module.h>
291da177e4SLinus Torvalds #include <linux/crypto.h>
301da177e4SLinus Torvalds #include <linux/zlib.h>
311da177e4SLinus Torvalds #include <linux/vmalloc.h>
321da177e4SLinus Torvalds #include <linux/interrupt.h>
331da177e4SLinus Torvalds #include <linux/mm.h>
341da177e4SLinus Torvalds #include <linux/net.h>
351da177e4SLinus Torvalds #include <linux/slab.h>
361da177e4SLinus Torvalds 
371da177e4SLinus Torvalds #define DEFLATE_DEF_LEVEL		Z_DEFAULT_COMPRESSION
381da177e4SLinus Torvalds #define DEFLATE_DEF_WINBITS		11
391da177e4SLinus Torvalds #define DEFLATE_DEF_MEMLEVEL		MAX_MEM_LEVEL
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds struct deflate_ctx {
421da177e4SLinus Torvalds 	struct z_stream_s comp_stream;
431da177e4SLinus Torvalds 	struct z_stream_s decomp_stream;
441da177e4SLinus Torvalds };
451da177e4SLinus Torvalds 
461da177e4SLinus Torvalds static int deflate_comp_init(struct deflate_ctx *ctx)
471da177e4SLinus Torvalds {
481da177e4SLinus Torvalds 	int ret = 0;
491da177e4SLinus Torvalds 	struct z_stream_s *stream = &ctx->comp_stream;
501da177e4SLinus Torvalds 
51565d76cbSJim Keniston 	stream->workspace = vzalloc(zlib_deflate_workspacesize(
52565d76cbSJim Keniston 				-DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL));
531da177e4SLinus Torvalds 	if (!stream->workspace) {
541da177e4SLinus Torvalds 		ret = -ENOMEM;
551da177e4SLinus Torvalds 		goto out;
561da177e4SLinus Torvalds 	}
571da177e4SLinus Torvalds 	ret = zlib_deflateInit2(stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
581da177e4SLinus Torvalds 	                        -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
591da177e4SLinus Torvalds 	                        Z_DEFAULT_STRATEGY);
601da177e4SLinus Torvalds 	if (ret != Z_OK) {
611da177e4SLinus Torvalds 		ret = -EINVAL;
621da177e4SLinus Torvalds 		goto out_free;
631da177e4SLinus Torvalds 	}
641da177e4SLinus Torvalds out:
651da177e4SLinus Torvalds 	return ret;
661da177e4SLinus Torvalds out_free:
671da177e4SLinus Torvalds 	vfree(stream->workspace);
681da177e4SLinus Torvalds 	goto out;
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
711da177e4SLinus Torvalds static int deflate_decomp_init(struct deflate_ctx *ctx)
721da177e4SLinus Torvalds {
731da177e4SLinus Torvalds 	int ret = 0;
741da177e4SLinus Torvalds 	struct z_stream_s *stream = &ctx->decomp_stream;
751da177e4SLinus Torvalds 
76bbeb563fSEric Sesterhenn 	stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL);
771da177e4SLinus Torvalds 	if (!stream->workspace) {
781da177e4SLinus Torvalds 		ret = -ENOMEM;
791da177e4SLinus Torvalds 		goto out;
801da177e4SLinus Torvalds 	}
811da177e4SLinus Torvalds 	ret = zlib_inflateInit2(stream, -DEFLATE_DEF_WINBITS);
821da177e4SLinus Torvalds 	if (ret != Z_OK) {
831da177e4SLinus Torvalds 		ret = -EINVAL;
841da177e4SLinus Torvalds 		goto out_free;
851da177e4SLinus Torvalds 	}
861da177e4SLinus Torvalds out:
871da177e4SLinus Torvalds 	return ret;
881da177e4SLinus Torvalds out_free:
891da177e4SLinus Torvalds 	kfree(stream->workspace);
901da177e4SLinus Torvalds 	goto out;
911da177e4SLinus Torvalds }
921da177e4SLinus Torvalds 
931da177e4SLinus Torvalds static void deflate_comp_exit(struct deflate_ctx *ctx)
941da177e4SLinus Torvalds {
959ffb7146SArtem B. Bityuckiy 	zlib_deflateEnd(&ctx->comp_stream);
961da177e4SLinus Torvalds 	vfree(ctx->comp_stream.workspace);
971da177e4SLinus Torvalds }
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds static void deflate_decomp_exit(struct deflate_ctx *ctx)
1001da177e4SLinus Torvalds {
1019ffb7146SArtem B. Bityuckiy 	zlib_inflateEnd(&ctx->decomp_stream);
1021da177e4SLinus Torvalds 	kfree(ctx->decomp_stream.workspace);
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
1056c2bb98bSHerbert Xu static int deflate_init(struct crypto_tfm *tfm)
1061da177e4SLinus Torvalds {
1076c2bb98bSHerbert Xu 	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
1081da177e4SLinus Torvalds 	int ret;
1091da177e4SLinus Torvalds 
1101da177e4SLinus Torvalds 	ret = deflate_comp_init(ctx);
1111da177e4SLinus Torvalds 	if (ret)
1121da177e4SLinus Torvalds 		goto out;
1131da177e4SLinus Torvalds 	ret = deflate_decomp_init(ctx);
1141da177e4SLinus Torvalds 	if (ret)
1151da177e4SLinus Torvalds 		deflate_comp_exit(ctx);
1161da177e4SLinus Torvalds out:
1171da177e4SLinus Torvalds 	return ret;
1181da177e4SLinus Torvalds }
1191da177e4SLinus Torvalds 
1206c2bb98bSHerbert Xu static void deflate_exit(struct crypto_tfm *tfm)
1211da177e4SLinus Torvalds {
1226c2bb98bSHerbert Xu 	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
1236c2bb98bSHerbert Xu 
1241da177e4SLinus Torvalds 	deflate_comp_exit(ctx);
1251da177e4SLinus Torvalds 	deflate_decomp_exit(ctx);
1261da177e4SLinus Torvalds }
1271da177e4SLinus Torvalds 
1286c2bb98bSHerbert Xu static int deflate_compress(struct crypto_tfm *tfm, const u8 *src,
1296c2bb98bSHerbert Xu 			    unsigned int slen, u8 *dst, unsigned int *dlen)
1301da177e4SLinus Torvalds {
1311da177e4SLinus Torvalds 	int ret = 0;
1326c2bb98bSHerbert Xu 	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
1331da177e4SLinus Torvalds 	struct z_stream_s *stream = &dctx->comp_stream;
1341da177e4SLinus Torvalds 
1351da177e4SLinus Torvalds 	ret = zlib_deflateReset(stream);
1361da177e4SLinus Torvalds 	if (ret != Z_OK) {
1371da177e4SLinus Torvalds 		ret = -EINVAL;
1381da177e4SLinus Torvalds 		goto out;
1391da177e4SLinus Torvalds 	}
1401da177e4SLinus Torvalds 
1411da177e4SLinus Torvalds 	stream->next_in = (u8 *)src;
1421da177e4SLinus Torvalds 	stream->avail_in = slen;
1431da177e4SLinus Torvalds 	stream->next_out = (u8 *)dst;
1441da177e4SLinus Torvalds 	stream->avail_out = *dlen;
1451da177e4SLinus Torvalds 
1461da177e4SLinus Torvalds 	ret = zlib_deflate(stream, Z_FINISH);
1471da177e4SLinus Torvalds 	if (ret != Z_STREAM_END) {
1481da177e4SLinus Torvalds 		ret = -EINVAL;
1491da177e4SLinus Torvalds 		goto out;
1501da177e4SLinus Torvalds 	}
1511da177e4SLinus Torvalds 	ret = 0;
1521da177e4SLinus Torvalds 	*dlen = stream->total_out;
1531da177e4SLinus Torvalds out:
1541da177e4SLinus Torvalds 	return ret;
1551da177e4SLinus Torvalds }
1561da177e4SLinus Torvalds 
1576c2bb98bSHerbert Xu static int deflate_decompress(struct crypto_tfm *tfm, const u8 *src,
1586c2bb98bSHerbert Xu 			      unsigned int slen, u8 *dst, unsigned int *dlen)
1591da177e4SLinus Torvalds {
1601da177e4SLinus Torvalds 
1611da177e4SLinus Torvalds 	int ret = 0;
1626c2bb98bSHerbert Xu 	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
1631da177e4SLinus Torvalds 	struct z_stream_s *stream = &dctx->decomp_stream;
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds 	ret = zlib_inflateReset(stream);
1661da177e4SLinus Torvalds 	if (ret != Z_OK) {
1671da177e4SLinus Torvalds 		ret = -EINVAL;
1681da177e4SLinus Torvalds 		goto out;
1691da177e4SLinus Torvalds 	}
1701da177e4SLinus Torvalds 
1711da177e4SLinus Torvalds 	stream->next_in = (u8 *)src;
1721da177e4SLinus Torvalds 	stream->avail_in = slen;
1731da177e4SLinus Torvalds 	stream->next_out = (u8 *)dst;
1741da177e4SLinus Torvalds 	stream->avail_out = *dlen;
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 	ret = zlib_inflate(stream, Z_SYNC_FLUSH);
1771da177e4SLinus Torvalds 	/*
1781da177e4SLinus Torvalds 	 * Work around a bug in zlib, which sometimes wants to taste an extra
1791da177e4SLinus Torvalds 	 * byte when being used in the (undocumented) raw deflate mode.
1801da177e4SLinus Torvalds 	 * (From USAGI).
1811da177e4SLinus Torvalds 	 */
1821da177e4SLinus Torvalds 	if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
1831da177e4SLinus Torvalds 		u8 zerostuff = 0;
1841da177e4SLinus Torvalds 		stream->next_in = &zerostuff;
1851da177e4SLinus Torvalds 		stream->avail_in = 1;
1861da177e4SLinus Torvalds 		ret = zlib_inflate(stream, Z_FINISH);
1871da177e4SLinus Torvalds 	}
1881da177e4SLinus Torvalds 	if (ret != Z_STREAM_END) {
1891da177e4SLinus Torvalds 		ret = -EINVAL;
1901da177e4SLinus Torvalds 		goto out;
1911da177e4SLinus Torvalds 	}
1921da177e4SLinus Torvalds 	ret = 0;
1931da177e4SLinus Torvalds 	*dlen = stream->total_out;
1941da177e4SLinus Torvalds out:
1951da177e4SLinus Torvalds 	return ret;
1961da177e4SLinus Torvalds }
1971da177e4SLinus Torvalds 
1981da177e4SLinus Torvalds static struct crypto_alg alg = {
1991da177e4SLinus Torvalds 	.cra_name		= "deflate",
2001da177e4SLinus Torvalds 	.cra_flags		= CRYPTO_ALG_TYPE_COMPRESS,
2011da177e4SLinus Torvalds 	.cra_ctxsize		= sizeof(struct deflate_ctx),
2021da177e4SLinus Torvalds 	.cra_module		= THIS_MODULE,
2031da177e4SLinus Torvalds 	.cra_list		= LIST_HEAD_INIT(alg.cra_list),
204c7fc0599SHerbert Xu 	.cra_init		= deflate_init,
205c7fc0599SHerbert Xu 	.cra_exit		= deflate_exit,
2061da177e4SLinus Torvalds 	.cra_u			= { .compress = {
2071da177e4SLinus Torvalds 	.coa_compress 		= deflate_compress,
2081da177e4SLinus Torvalds 	.coa_decompress  	= deflate_decompress } }
2091da177e4SLinus Torvalds };
2101da177e4SLinus Torvalds 
2113af5b90bSKamalesh Babulal static int __init deflate_mod_init(void)
2121da177e4SLinus Torvalds {
2131da177e4SLinus Torvalds 	return crypto_register_alg(&alg);
2141da177e4SLinus Torvalds }
2151da177e4SLinus Torvalds 
2163af5b90bSKamalesh Babulal static void __exit deflate_mod_fini(void)
2171da177e4SLinus Torvalds {
2181da177e4SLinus Torvalds 	crypto_unregister_alg(&alg);
2191da177e4SLinus Torvalds }
2201da177e4SLinus Torvalds 
2213af5b90bSKamalesh Babulal module_init(deflate_mod_init);
2223af5b90bSKamalesh Babulal module_exit(deflate_mod_fini);
2231da177e4SLinus Torvalds 
2241da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2251da177e4SLinus Torvalds MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
2261da177e4SLinus Torvalds MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
2271da177e4SLinus Torvalds 
228