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 511da177e4SLinus Torvalds stream->workspace = vmalloc(zlib_deflate_workspacesize()); 521da177e4SLinus Torvalds if (!stream->workspace ) { 531da177e4SLinus Torvalds ret = -ENOMEM; 541da177e4SLinus Torvalds goto out; 551da177e4SLinus Torvalds } 561da177e4SLinus Torvalds memset(stream->workspace, 0, zlib_deflate_workspacesize()); 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 1051da177e4SLinus Torvalds static int deflate_init(void *ctx) 1061da177e4SLinus Torvalds { 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 1191da177e4SLinus Torvalds static void deflate_exit(void *ctx) 1201da177e4SLinus Torvalds { 1211da177e4SLinus Torvalds deflate_comp_exit(ctx); 1221da177e4SLinus Torvalds deflate_decomp_exit(ctx); 1231da177e4SLinus Torvalds } 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds static int deflate_compress(void *ctx, const u8 *src, unsigned int slen, 1261da177e4SLinus Torvalds u8 *dst, unsigned int *dlen) 1271da177e4SLinus Torvalds { 1281da177e4SLinus Torvalds int ret = 0; 1291da177e4SLinus Torvalds struct deflate_ctx *dctx = ctx; 1301da177e4SLinus Torvalds struct z_stream_s *stream = &dctx->comp_stream; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds ret = zlib_deflateReset(stream); 1331da177e4SLinus Torvalds if (ret != Z_OK) { 1341da177e4SLinus Torvalds ret = -EINVAL; 1351da177e4SLinus Torvalds goto out; 1361da177e4SLinus Torvalds } 1371da177e4SLinus Torvalds 1381da177e4SLinus Torvalds stream->next_in = (u8 *)src; 1391da177e4SLinus Torvalds stream->avail_in = slen; 1401da177e4SLinus Torvalds stream->next_out = (u8 *)dst; 1411da177e4SLinus Torvalds stream->avail_out = *dlen; 1421da177e4SLinus Torvalds 1431da177e4SLinus Torvalds ret = zlib_deflate(stream, Z_FINISH); 1441da177e4SLinus Torvalds if (ret != Z_STREAM_END) { 1451da177e4SLinus Torvalds ret = -EINVAL; 1461da177e4SLinus Torvalds goto out; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds ret = 0; 1491da177e4SLinus Torvalds *dlen = stream->total_out; 1501da177e4SLinus Torvalds out: 1511da177e4SLinus Torvalds return ret; 1521da177e4SLinus Torvalds } 1531da177e4SLinus Torvalds 1541da177e4SLinus Torvalds static int deflate_decompress(void *ctx, const u8 *src, unsigned int slen, 1551da177e4SLinus Torvalds u8 *dst, unsigned int *dlen) 1561da177e4SLinus Torvalds { 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds int ret = 0; 1591da177e4SLinus Torvalds struct deflate_ctx *dctx = ctx; 1601da177e4SLinus Torvalds struct z_stream_s *stream = &dctx->decomp_stream; 1611da177e4SLinus Torvalds 1621da177e4SLinus Torvalds ret = zlib_inflateReset(stream); 1631da177e4SLinus Torvalds if (ret != Z_OK) { 1641da177e4SLinus Torvalds ret = -EINVAL; 1651da177e4SLinus Torvalds goto out; 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds stream->next_in = (u8 *)src; 1691da177e4SLinus Torvalds stream->avail_in = slen; 1701da177e4SLinus Torvalds stream->next_out = (u8 *)dst; 1711da177e4SLinus Torvalds stream->avail_out = *dlen; 1721da177e4SLinus Torvalds 1731da177e4SLinus Torvalds ret = zlib_inflate(stream, Z_SYNC_FLUSH); 1741da177e4SLinus Torvalds /* 1751da177e4SLinus Torvalds * Work around a bug in zlib, which sometimes wants to taste an extra 1761da177e4SLinus Torvalds * byte when being used in the (undocumented) raw deflate mode. 1771da177e4SLinus Torvalds * (From USAGI). 1781da177e4SLinus Torvalds */ 1791da177e4SLinus Torvalds if (ret == Z_OK && !stream->avail_in && stream->avail_out) { 1801da177e4SLinus Torvalds u8 zerostuff = 0; 1811da177e4SLinus Torvalds stream->next_in = &zerostuff; 1821da177e4SLinus Torvalds stream->avail_in = 1; 1831da177e4SLinus Torvalds ret = zlib_inflate(stream, Z_FINISH); 1841da177e4SLinus Torvalds } 1851da177e4SLinus Torvalds if (ret != Z_STREAM_END) { 1861da177e4SLinus Torvalds ret = -EINVAL; 1871da177e4SLinus Torvalds goto out; 1881da177e4SLinus Torvalds } 1891da177e4SLinus Torvalds ret = 0; 1901da177e4SLinus Torvalds *dlen = stream->total_out; 1911da177e4SLinus Torvalds out: 1921da177e4SLinus Torvalds return ret; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 1951da177e4SLinus Torvalds static struct crypto_alg alg = { 1961da177e4SLinus Torvalds .cra_name = "deflate", 1971da177e4SLinus Torvalds .cra_flags = CRYPTO_ALG_TYPE_COMPRESS, 1981da177e4SLinus Torvalds .cra_ctxsize = sizeof(struct deflate_ctx), 1991da177e4SLinus Torvalds .cra_module = THIS_MODULE, 2001da177e4SLinus Torvalds .cra_list = LIST_HEAD_INIT(alg.cra_list), 2011da177e4SLinus Torvalds .cra_u = { .compress = { 2021da177e4SLinus Torvalds .coa_init = deflate_init, 2031da177e4SLinus Torvalds .coa_exit = deflate_exit, 2041da177e4SLinus Torvalds .coa_compress = deflate_compress, 2051da177e4SLinus Torvalds .coa_decompress = deflate_decompress } } 2061da177e4SLinus Torvalds }; 2071da177e4SLinus Torvalds 2081da177e4SLinus Torvalds static int __init init(void) 2091da177e4SLinus Torvalds { 2101da177e4SLinus Torvalds return crypto_register_alg(&alg); 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds 2131da177e4SLinus Torvalds static void __exit fini(void) 2141da177e4SLinus Torvalds { 2151da177e4SLinus Torvalds crypto_unregister_alg(&alg); 2161da177e4SLinus Torvalds } 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds module_init(init); 2191da177e4SLinus Torvalds module_exit(fini); 2201da177e4SLinus Torvalds 2211da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 2221da177e4SLinus Torvalds MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP"); 2231da177e4SLinus Torvalds MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>"); 2241da177e4SLinus Torvalds 225