xref: /openbmc/linux/crypto/deflate.c (revision 5d26a105)
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 
361da177e4SLinus Torvalds #define DEFLATE_DEF_LEVEL		Z_DEFAULT_COMPRESSION
371da177e4SLinus Torvalds #define DEFLATE_DEF_WINBITS		11
381da177e4SLinus Torvalds #define DEFLATE_DEF_MEMLEVEL		MAX_MEM_LEVEL
391da177e4SLinus Torvalds 
401da177e4SLinus Torvalds struct deflate_ctx {
411da177e4SLinus Torvalds 	struct z_stream_s comp_stream;
421da177e4SLinus Torvalds 	struct z_stream_s decomp_stream;
431da177e4SLinus Torvalds };
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static int deflate_comp_init(struct deflate_ctx *ctx)
461da177e4SLinus Torvalds {
471da177e4SLinus Torvalds 	int ret = 0;
481da177e4SLinus Torvalds 	struct z_stream_s *stream = &ctx->comp_stream;
491da177e4SLinus Torvalds 
50565d76cbSJim Keniston 	stream->workspace = vzalloc(zlib_deflate_workspacesize(
51565d76cbSJim Keniston 				-DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL));
521da177e4SLinus Torvalds 	if (!stream->workspace) {
531da177e4SLinus Torvalds 		ret = -ENOMEM;
541da177e4SLinus Torvalds 		goto out;
551da177e4SLinus Torvalds 	}
561da177e4SLinus Torvalds 	ret = zlib_deflateInit2(stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,
571da177e4SLinus Torvalds 	                        -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,
581da177e4SLinus Torvalds 	                        Z_DEFAULT_STRATEGY);
591da177e4SLinus Torvalds 	if (ret != Z_OK) {
601da177e4SLinus Torvalds 		ret = -EINVAL;
611da177e4SLinus Torvalds 		goto out_free;
621da177e4SLinus Torvalds 	}
631da177e4SLinus Torvalds out:
641da177e4SLinus Torvalds 	return ret;
651da177e4SLinus Torvalds out_free:
661da177e4SLinus Torvalds 	vfree(stream->workspace);
671da177e4SLinus Torvalds 	goto out;
681da177e4SLinus Torvalds }
691da177e4SLinus Torvalds 
701da177e4SLinus Torvalds static int deflate_decomp_init(struct deflate_ctx *ctx)
711da177e4SLinus Torvalds {
721da177e4SLinus Torvalds 	int ret = 0;
731da177e4SLinus Torvalds 	struct z_stream_s *stream = &ctx->decomp_stream;
741da177e4SLinus Torvalds 
757ab24bfdSDavid S. Miller 	stream->workspace = vzalloc(zlib_inflate_workspacesize());
761da177e4SLinus Torvalds 	if (!stream->workspace) {
771da177e4SLinus Torvalds 		ret = -ENOMEM;
781da177e4SLinus Torvalds 		goto out;
791da177e4SLinus Torvalds 	}
801da177e4SLinus Torvalds 	ret = zlib_inflateInit2(stream, -DEFLATE_DEF_WINBITS);
811da177e4SLinus Torvalds 	if (ret != Z_OK) {
821da177e4SLinus Torvalds 		ret = -EINVAL;
831da177e4SLinus Torvalds 		goto out_free;
841da177e4SLinus Torvalds 	}
851da177e4SLinus Torvalds out:
861da177e4SLinus Torvalds 	return ret;
871da177e4SLinus Torvalds out_free:
887ab24bfdSDavid S. Miller 	vfree(stream->workspace);
891da177e4SLinus Torvalds 	goto out;
901da177e4SLinus Torvalds }
911da177e4SLinus Torvalds 
921da177e4SLinus Torvalds static void deflate_comp_exit(struct deflate_ctx *ctx)
931da177e4SLinus Torvalds {
949ffb7146SArtem B. Bityuckiy 	zlib_deflateEnd(&ctx->comp_stream);
951da177e4SLinus Torvalds 	vfree(ctx->comp_stream.workspace);
961da177e4SLinus Torvalds }
971da177e4SLinus Torvalds 
981da177e4SLinus Torvalds static void deflate_decomp_exit(struct deflate_ctx *ctx)
991da177e4SLinus Torvalds {
1009ffb7146SArtem B. Bityuckiy 	zlib_inflateEnd(&ctx->decomp_stream);
1017ab24bfdSDavid S. Miller 	vfree(ctx->decomp_stream.workspace);
1021da177e4SLinus Torvalds }
1031da177e4SLinus Torvalds 
1046c2bb98bSHerbert Xu static int deflate_init(struct crypto_tfm *tfm)
1051da177e4SLinus Torvalds {
1066c2bb98bSHerbert Xu 	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
1071da177e4SLinus Torvalds 	int ret;
1081da177e4SLinus Torvalds 
1091da177e4SLinus Torvalds 	ret = deflate_comp_init(ctx);
1101da177e4SLinus Torvalds 	if (ret)
1111da177e4SLinus Torvalds 		goto out;
1121da177e4SLinus Torvalds 	ret = deflate_decomp_init(ctx);
1131da177e4SLinus Torvalds 	if (ret)
1141da177e4SLinus Torvalds 		deflate_comp_exit(ctx);
1151da177e4SLinus Torvalds out:
1161da177e4SLinus Torvalds 	return ret;
1171da177e4SLinus Torvalds }
1181da177e4SLinus Torvalds 
1196c2bb98bSHerbert Xu static void deflate_exit(struct crypto_tfm *tfm)
1201da177e4SLinus Torvalds {
1216c2bb98bSHerbert Xu 	struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);
1226c2bb98bSHerbert Xu 
1231da177e4SLinus Torvalds 	deflate_comp_exit(ctx);
1241da177e4SLinus Torvalds 	deflate_decomp_exit(ctx);
1251da177e4SLinus Torvalds }
1261da177e4SLinus Torvalds 
1276c2bb98bSHerbert Xu static int deflate_compress(struct crypto_tfm *tfm, const u8 *src,
1286c2bb98bSHerbert Xu 			    unsigned int slen, u8 *dst, unsigned int *dlen)
1291da177e4SLinus Torvalds {
1301da177e4SLinus Torvalds 	int ret = 0;
1316c2bb98bSHerbert Xu 	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
1321da177e4SLinus Torvalds 	struct z_stream_s *stream = &dctx->comp_stream;
1331da177e4SLinus Torvalds 
1341da177e4SLinus Torvalds 	ret = zlib_deflateReset(stream);
1351da177e4SLinus Torvalds 	if (ret != Z_OK) {
1361da177e4SLinus Torvalds 		ret = -EINVAL;
1371da177e4SLinus Torvalds 		goto out;
1381da177e4SLinus Torvalds 	}
1391da177e4SLinus Torvalds 
1401da177e4SLinus Torvalds 	stream->next_in = (u8 *)src;
1411da177e4SLinus Torvalds 	stream->avail_in = slen;
1421da177e4SLinus Torvalds 	stream->next_out = (u8 *)dst;
1431da177e4SLinus Torvalds 	stream->avail_out = *dlen;
1441da177e4SLinus Torvalds 
1451da177e4SLinus Torvalds 	ret = zlib_deflate(stream, Z_FINISH);
1461da177e4SLinus Torvalds 	if (ret != Z_STREAM_END) {
1471da177e4SLinus Torvalds 		ret = -EINVAL;
1481da177e4SLinus Torvalds 		goto out;
1491da177e4SLinus Torvalds 	}
1501da177e4SLinus Torvalds 	ret = 0;
1511da177e4SLinus Torvalds 	*dlen = stream->total_out;
1521da177e4SLinus Torvalds out:
1531da177e4SLinus Torvalds 	return ret;
1541da177e4SLinus Torvalds }
1551da177e4SLinus Torvalds 
1566c2bb98bSHerbert Xu static int deflate_decompress(struct crypto_tfm *tfm, const u8 *src,
1576c2bb98bSHerbert Xu 			      unsigned int slen, u8 *dst, unsigned int *dlen)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	int ret = 0;
1616c2bb98bSHerbert Xu 	struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);
1621da177e4SLinus Torvalds 	struct z_stream_s *stream = &dctx->decomp_stream;
1631da177e4SLinus Torvalds 
1641da177e4SLinus Torvalds 	ret = zlib_inflateReset(stream);
1651da177e4SLinus Torvalds 	if (ret != Z_OK) {
1661da177e4SLinus Torvalds 		ret = -EINVAL;
1671da177e4SLinus Torvalds 		goto out;
1681da177e4SLinus Torvalds 	}
1691da177e4SLinus Torvalds 
1701da177e4SLinus Torvalds 	stream->next_in = (u8 *)src;
1711da177e4SLinus Torvalds 	stream->avail_in = slen;
1721da177e4SLinus Torvalds 	stream->next_out = (u8 *)dst;
1731da177e4SLinus Torvalds 	stream->avail_out = *dlen;
1741da177e4SLinus Torvalds 
1751da177e4SLinus Torvalds 	ret = zlib_inflate(stream, Z_SYNC_FLUSH);
1761da177e4SLinus Torvalds 	/*
1771da177e4SLinus Torvalds 	 * Work around a bug in zlib, which sometimes wants to taste an extra
1781da177e4SLinus Torvalds 	 * byte when being used in the (undocumented) raw deflate mode.
1791da177e4SLinus Torvalds 	 * (From USAGI).
1801da177e4SLinus Torvalds 	 */
1811da177e4SLinus Torvalds 	if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
1821da177e4SLinus Torvalds 		u8 zerostuff = 0;
1831da177e4SLinus Torvalds 		stream->next_in = &zerostuff;
1841da177e4SLinus Torvalds 		stream->avail_in = 1;
1851da177e4SLinus Torvalds 		ret = zlib_inflate(stream, Z_FINISH);
1861da177e4SLinus Torvalds 	}
1871da177e4SLinus Torvalds 	if (ret != Z_STREAM_END) {
1881da177e4SLinus Torvalds 		ret = -EINVAL;
1891da177e4SLinus Torvalds 		goto out;
1901da177e4SLinus Torvalds 	}
1911da177e4SLinus Torvalds 	ret = 0;
1921da177e4SLinus Torvalds 	*dlen = stream->total_out;
1931da177e4SLinus Torvalds out:
1941da177e4SLinus Torvalds 	return ret;
1951da177e4SLinus Torvalds }
1961da177e4SLinus Torvalds 
1971da177e4SLinus Torvalds static struct crypto_alg alg = {
1981da177e4SLinus Torvalds 	.cra_name		= "deflate",
1991da177e4SLinus Torvalds 	.cra_flags		= CRYPTO_ALG_TYPE_COMPRESS,
2001da177e4SLinus Torvalds 	.cra_ctxsize		= sizeof(struct deflate_ctx),
2011da177e4SLinus Torvalds 	.cra_module		= THIS_MODULE,
202c7fc0599SHerbert Xu 	.cra_init		= deflate_init,
203c7fc0599SHerbert Xu 	.cra_exit		= deflate_exit,
2041da177e4SLinus Torvalds 	.cra_u			= { .compress = {
2051da177e4SLinus Torvalds 	.coa_compress 		= deflate_compress,
2061da177e4SLinus Torvalds 	.coa_decompress  	= deflate_decompress } }
2071da177e4SLinus Torvalds };
2081da177e4SLinus Torvalds 
2093af5b90bSKamalesh Babulal static int __init deflate_mod_init(void)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	return crypto_register_alg(&alg);
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds 
2143af5b90bSKamalesh Babulal static void __exit deflate_mod_fini(void)
2151da177e4SLinus Torvalds {
2161da177e4SLinus Torvalds 	crypto_unregister_alg(&alg);
2171da177e4SLinus Torvalds }
2181da177e4SLinus Torvalds 
2193af5b90bSKamalesh Babulal module_init(deflate_mod_init);
2203af5b90bSKamalesh Babulal module_exit(deflate_mod_fini);
2211da177e4SLinus Torvalds 
2221da177e4SLinus Torvalds MODULE_LICENSE("GPL");
2231da177e4SLinus Torvalds MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");
2241da177e4SLinus Torvalds MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");
2255d26a105SKees Cook MODULE_ALIAS_CRYPTO("deflate");
226