315 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Cryptographic API.4 *5 * Deflate algorithm (RFC 1951), implemented here primarily for use6 * by IPCOMP (RFC 3173 & RFC 2394).7 *8 * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>9 *10 * FIXME: deflate transforms will require up to a total of about 436k of kernel11 * memory on i386 (390k for compression, the rest for decompression), as the12 * current zlib kernel code uses a worst case pre-allocation system by default.13 * This needs to be fixed so that the amount of memory required is properly14 * related to the winbits and memlevel parameters.15 *16 * The default winbits of 11 should suit most packets, and it may be something17 * to configure on a per-tfm basis in the future.18 *19 * Currently, compression history is not maintained between tfm calls, as20 * it is not needed for IPCOMP and keeps the code simpler. It can be21 * implemented if someone wants it.22 */23#include <linux/init.h>24#include <linux/module.h>25#include <linux/crypto.h>26#include <linux/zlib.h>27#include <linux/vmalloc.h>28#include <linux/interrupt.h>29#include <linux/mm.h>30#include <linux/net.h>31#include <crypto/internal/scompress.h>32 33#define DEFLATE_DEF_LEVEL Z_DEFAULT_COMPRESSION34#define DEFLATE_DEF_WINBITS 1135#define DEFLATE_DEF_MEMLEVEL MAX_MEM_LEVEL36 37struct deflate_ctx {38 struct z_stream_s comp_stream;39 struct z_stream_s decomp_stream;40};41 42static int deflate_comp_init(struct deflate_ctx *ctx)43{44 int ret = 0;45 struct z_stream_s *stream = &ctx->comp_stream;46 47 stream->workspace = vzalloc(zlib_deflate_workspacesize(48 -DEFLATE_DEF_WINBITS, MAX_MEM_LEVEL));49 if (!stream->workspace) {50 ret = -ENOMEM;51 goto out;52 }53 ret = zlib_deflateInit2(stream, DEFLATE_DEF_LEVEL, Z_DEFLATED,54 -DEFLATE_DEF_WINBITS, DEFLATE_DEF_MEMLEVEL,55 Z_DEFAULT_STRATEGY);56 if (ret != Z_OK) {57 ret = -EINVAL;58 goto out_free;59 }60out:61 return ret;62out_free:63 vfree(stream->workspace);64 goto out;65}66 67static int deflate_decomp_init(struct deflate_ctx *ctx)68{69 int ret = 0;70 struct z_stream_s *stream = &ctx->decomp_stream;71 72 stream->workspace = vzalloc(zlib_inflate_workspacesize());73 if (!stream->workspace) {74 ret = -ENOMEM;75 goto out;76 }77 ret = zlib_inflateInit2(stream, -DEFLATE_DEF_WINBITS);78 if (ret != Z_OK) {79 ret = -EINVAL;80 goto out_free;81 }82out:83 return ret;84out_free:85 vfree(stream->workspace);86 goto out;87}88 89static void deflate_comp_exit(struct deflate_ctx *ctx)90{91 zlib_deflateEnd(&ctx->comp_stream);92 vfree(ctx->comp_stream.workspace);93}94 95static void deflate_decomp_exit(struct deflate_ctx *ctx)96{97 zlib_inflateEnd(&ctx->decomp_stream);98 vfree(ctx->decomp_stream.workspace);99}100 101static int __deflate_init(void *ctx)102{103 int ret;104 105 ret = deflate_comp_init(ctx);106 if (ret)107 goto out;108 ret = deflate_decomp_init(ctx);109 if (ret)110 deflate_comp_exit(ctx);111out:112 return ret;113}114 115static void *deflate_alloc_ctx(struct crypto_scomp *tfm)116{117 struct deflate_ctx *ctx;118 int ret;119 120 ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);121 if (!ctx)122 return ERR_PTR(-ENOMEM);123 124 ret = __deflate_init(ctx);125 if (ret) {126 kfree(ctx);127 return ERR_PTR(ret);128 }129 130 return ctx;131}132 133static int deflate_init(struct crypto_tfm *tfm)134{135 struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);136 137 return __deflate_init(ctx);138}139 140static void __deflate_exit(void *ctx)141{142 deflate_comp_exit(ctx);143 deflate_decomp_exit(ctx);144}145 146static void deflate_free_ctx(struct crypto_scomp *tfm, void *ctx)147{148 __deflate_exit(ctx);149 kfree_sensitive(ctx);150}151 152static void deflate_exit(struct crypto_tfm *tfm)153{154 struct deflate_ctx *ctx = crypto_tfm_ctx(tfm);155 156 __deflate_exit(ctx);157}158 159static int __deflate_compress(const u8 *src, unsigned int slen,160 u8 *dst, unsigned int *dlen, void *ctx)161{162 int ret = 0;163 struct deflate_ctx *dctx = ctx;164 struct z_stream_s *stream = &dctx->comp_stream;165 166 ret = zlib_deflateReset(stream);167 if (ret != Z_OK) {168 ret = -EINVAL;169 goto out;170 }171 172 stream->next_in = (u8 *)src;173 stream->avail_in = slen;174 stream->next_out = (u8 *)dst;175 stream->avail_out = *dlen;176 177 ret = zlib_deflate(stream, Z_FINISH);178 if (ret != Z_STREAM_END) {179 ret = -EINVAL;180 goto out;181 }182 ret = 0;183 *dlen = stream->total_out;184out:185 return ret;186}187 188static int deflate_compress(struct crypto_tfm *tfm, const u8 *src,189 unsigned int slen, u8 *dst, unsigned int *dlen)190{191 struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);192 193 return __deflate_compress(src, slen, dst, dlen, dctx);194}195 196static int deflate_scompress(struct crypto_scomp *tfm, const u8 *src,197 unsigned int slen, u8 *dst, unsigned int *dlen,198 void *ctx)199{200 return __deflate_compress(src, slen, dst, dlen, ctx);201}202 203static int __deflate_decompress(const u8 *src, unsigned int slen,204 u8 *dst, unsigned int *dlen, void *ctx)205{206 207 int ret = 0;208 struct deflate_ctx *dctx = ctx;209 struct z_stream_s *stream = &dctx->decomp_stream;210 211 ret = zlib_inflateReset(stream);212 if (ret != Z_OK) {213 ret = -EINVAL;214 goto out;215 }216 217 stream->next_in = (u8 *)src;218 stream->avail_in = slen;219 stream->next_out = (u8 *)dst;220 stream->avail_out = *dlen;221 222 ret = zlib_inflate(stream, Z_SYNC_FLUSH);223 /*224 * Work around a bug in zlib, which sometimes wants to taste an extra225 * byte when being used in the (undocumented) raw deflate mode.226 * (From USAGI).227 */228 if (ret == Z_OK && !stream->avail_in && stream->avail_out) {229 u8 zerostuff = 0;230 stream->next_in = &zerostuff;231 stream->avail_in = 1;232 ret = zlib_inflate(stream, Z_FINISH);233 }234 if (ret != Z_STREAM_END) {235 ret = -EINVAL;236 goto out;237 }238 ret = 0;239 *dlen = stream->total_out;240out:241 return ret;242}243 244static int deflate_decompress(struct crypto_tfm *tfm, const u8 *src,245 unsigned int slen, u8 *dst, unsigned int *dlen)246{247 struct deflate_ctx *dctx = crypto_tfm_ctx(tfm);248 249 return __deflate_decompress(src, slen, dst, dlen, dctx);250}251 252static int deflate_sdecompress(struct crypto_scomp *tfm, const u8 *src,253 unsigned int slen, u8 *dst, unsigned int *dlen,254 void *ctx)255{256 return __deflate_decompress(src, slen, dst, dlen, ctx);257}258 259static struct crypto_alg alg = {260 .cra_name = "deflate",261 .cra_driver_name = "deflate-generic",262 .cra_flags = CRYPTO_ALG_TYPE_COMPRESS,263 .cra_ctxsize = sizeof(struct deflate_ctx),264 .cra_module = THIS_MODULE,265 .cra_init = deflate_init,266 .cra_exit = deflate_exit,267 .cra_u = { .compress = {268 .coa_compress = deflate_compress,269 .coa_decompress = deflate_decompress } }270};271 272static struct scomp_alg scomp = {273 .alloc_ctx = deflate_alloc_ctx,274 .free_ctx = deflate_free_ctx,275 .compress = deflate_scompress,276 .decompress = deflate_sdecompress,277 .base = {278 .cra_name = "deflate",279 .cra_driver_name = "deflate-scomp",280 .cra_module = THIS_MODULE,281 }282};283 284static int __init deflate_mod_init(void)285{286 int ret;287 288 ret = crypto_register_alg(&alg);289 if (ret)290 return ret;291 292 ret = crypto_register_scomp(&scomp);293 if (ret) {294 crypto_unregister_alg(&alg);295 return ret;296 }297 298 return ret;299}300 301static void __exit deflate_mod_fini(void)302{303 crypto_unregister_alg(&alg);304 crypto_unregister_scomp(&scomp);305}306 307subsys_initcall(deflate_mod_init);308module_exit(deflate_mod_fini);309 310MODULE_LICENSE("GPL");311MODULE_DESCRIPTION("Deflate Compression Algorithm for IPCOMP");312MODULE_AUTHOR("James Morris <jmorris@intercode.com.au>");313MODULE_ALIAS_CRYPTO("deflate");314MODULE_ALIAS_CRYPTO("deflate-generic");315