• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Cryptographic API.
3  *
4  * Zlib algorithm
5  *
6  * Copyright 2008 Sony Corporation
7  *
8  * Based on deflate.c, which is
9  * Copyright (c) 2003 James Morris <jmorris@intercode.com.au>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU General Public License as published by the Free
13  * Software Foundation; either version 2 of the License, or (at your option)
14  * any later version.
15  *
16  * FIXME: deflate transforms will require up to a total of about 436k of kernel
17  * memory on i386 (390k for compression, the rest for decompression), as the
18  * current zlib kernel code uses a worst case pre-allocation system by default.
19  * This needs to be fixed so that the amount of memory required is properly
20  * related to the winbits and memlevel parameters.
21  */
22 
23 #define pr_fmt(fmt)	"%s: " fmt, __func__
24 
25 #include <linux/init.h>
26 #include <linux/module.h>
27 #include <linux/zlib.h>
28 #include <linux/vmalloc.h>
29 #include <linux/interrupt.h>
30 #include <linux/mm.h>
31 #include <linux/net.h>
32 
33 #include <crypto/internal/compress.h>
34 
35 #include <net/netlink.h>
36 
37 
38 struct zlib_ctx {
39 	struct z_stream_s comp_stream;
40 	struct z_stream_s decomp_stream;
41 	int decomp_windowBits;
42 };
43 
44 
zlib_comp_exit(struct zlib_ctx * ctx)45 static void zlib_comp_exit(struct zlib_ctx *ctx)
46 {
47 	struct z_stream_s *stream = &ctx->comp_stream;
48 
49 	if (stream->workspace) {
50 		zlib_deflateEnd(stream);
51 		vfree(stream->workspace);
52 		stream->workspace = NULL;
53 	}
54 }
55 
zlib_decomp_exit(struct zlib_ctx * ctx)56 static void zlib_decomp_exit(struct zlib_ctx *ctx)
57 {
58 	struct z_stream_s *stream = &ctx->decomp_stream;
59 
60 	if (stream->workspace) {
61 		zlib_inflateEnd(stream);
62 		vfree(stream->workspace);
63 		stream->workspace = NULL;
64 	}
65 }
66 
zlib_init(struct crypto_tfm * tfm)67 static int zlib_init(struct crypto_tfm *tfm)
68 {
69 	return 0;
70 }
71 
zlib_exit(struct crypto_tfm * tfm)72 static void zlib_exit(struct crypto_tfm *tfm)
73 {
74 	struct zlib_ctx *ctx = crypto_tfm_ctx(tfm);
75 
76 	zlib_comp_exit(ctx);
77 	zlib_decomp_exit(ctx);
78 }
79 
80 
zlib_compress_setup(struct crypto_pcomp * tfm,void * params,unsigned int len)81 static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params,
82 			       unsigned int len)
83 {
84 	struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
85 	struct z_stream_s *stream = &ctx->comp_stream;
86 	struct nlattr *tb[ZLIB_COMP_MAX + 1];
87 	int window_bits, mem_level;
88 	size_t workspacesize;
89 	int ret;
90 
91 	ret = nla_parse(tb, ZLIB_COMP_MAX, params, len, NULL);
92 	if (ret)
93 		return ret;
94 
95 	zlib_comp_exit(ctx);
96 
97 	window_bits = tb[ZLIB_COMP_WINDOWBITS]
98 					? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS])
99 					: MAX_WBITS;
100 	mem_level = tb[ZLIB_COMP_MEMLEVEL]
101 					? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL])
102 					: DEF_MEM_LEVEL;
103 
104 	workspacesize = zlib_deflate_workspacesize(window_bits, mem_level);
105 	stream->workspace = vzalloc(workspacesize);
106 	if (!stream->workspace)
107 		return -ENOMEM;
108 
109 	ret = zlib_deflateInit2(stream,
110 				tb[ZLIB_COMP_LEVEL]
111 					? nla_get_u32(tb[ZLIB_COMP_LEVEL])
112 					: Z_DEFAULT_COMPRESSION,
113 				tb[ZLIB_COMP_METHOD]
114 					? nla_get_u32(tb[ZLIB_COMP_METHOD])
115 					: Z_DEFLATED,
116 				window_bits,
117 				mem_level,
118 				tb[ZLIB_COMP_STRATEGY]
119 					? nla_get_u32(tb[ZLIB_COMP_STRATEGY])
120 					: Z_DEFAULT_STRATEGY);
121 	if (ret != Z_OK) {
122 		vfree(stream->workspace);
123 		stream->workspace = NULL;
124 		return -EINVAL;
125 	}
126 
127 	return 0;
128 }
129 
zlib_compress_init(struct crypto_pcomp * tfm)130 static int zlib_compress_init(struct crypto_pcomp *tfm)
131 {
132 	int ret;
133 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
134 	struct z_stream_s *stream = &dctx->comp_stream;
135 
136 	ret = zlib_deflateReset(stream);
137 	if (ret != Z_OK)
138 		return -EINVAL;
139 
140 	return 0;
141 }
142 
zlib_compress_update(struct crypto_pcomp * tfm,struct comp_request * req)143 static int zlib_compress_update(struct crypto_pcomp *tfm,
144 				struct comp_request *req)
145 {
146 	int ret;
147 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
148 	struct z_stream_s *stream = &dctx->comp_stream;
149 
150 	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
151 	stream->next_in = req->next_in;
152 	stream->avail_in = req->avail_in;
153 	stream->next_out = req->next_out;
154 	stream->avail_out = req->avail_out;
155 
156 	ret = zlib_deflate(stream, Z_NO_FLUSH);
157 	switch (ret) {
158 	case Z_OK:
159 		break;
160 
161 	case Z_BUF_ERROR:
162 		pr_debug("zlib_deflate could not make progress\n");
163 		return -EAGAIN;
164 
165 	default:
166 		pr_debug("zlib_deflate failed %d\n", ret);
167 		return -EINVAL;
168 	}
169 
170 	ret = req->avail_out - stream->avail_out;
171 	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
172 		 stream->avail_in, stream->avail_out,
173 		 req->avail_in - stream->avail_in, ret);
174 	req->next_in = stream->next_in;
175 	req->avail_in = stream->avail_in;
176 	req->next_out = stream->next_out;
177 	req->avail_out = stream->avail_out;
178 	return ret;
179 }
180 
zlib_compress_final(struct crypto_pcomp * tfm,struct comp_request * req)181 static int zlib_compress_final(struct crypto_pcomp *tfm,
182 			       struct comp_request *req)
183 {
184 	int ret;
185 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
186 	struct z_stream_s *stream = &dctx->comp_stream;
187 
188 	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
189 	stream->next_in = req->next_in;
190 	stream->avail_in = req->avail_in;
191 	stream->next_out = req->next_out;
192 	stream->avail_out = req->avail_out;
193 
194 	ret = zlib_deflate(stream, Z_FINISH);
195 	if (ret != Z_STREAM_END) {
196 		pr_debug("zlib_deflate failed %d\n", ret);
197 		return -EINVAL;
198 	}
199 
200 	ret = req->avail_out - stream->avail_out;
201 	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
202 		 stream->avail_in, stream->avail_out,
203 		 req->avail_in - stream->avail_in, ret);
204 	req->next_in = stream->next_in;
205 	req->avail_in = stream->avail_in;
206 	req->next_out = stream->next_out;
207 	req->avail_out = stream->avail_out;
208 	return ret;
209 }
210 
211 
zlib_decompress_setup(struct crypto_pcomp * tfm,void * params,unsigned int len)212 static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params,
213 				 unsigned int len)
214 {
215 	struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
216 	struct z_stream_s *stream = &ctx->decomp_stream;
217 	struct nlattr *tb[ZLIB_DECOMP_MAX + 1];
218 	int ret = 0;
219 
220 	ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL);
221 	if (ret)
222 		return ret;
223 
224 	zlib_decomp_exit(ctx);
225 
226 	ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS]
227 				 ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS])
228 				 : DEF_WBITS;
229 
230 	stream->workspace = vzalloc(zlib_inflate_workspacesize());
231 	if (!stream->workspace)
232 		return -ENOMEM;
233 
234 	ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
235 	if (ret != Z_OK) {
236 		vfree(stream->workspace);
237 		stream->workspace = NULL;
238 		return -EINVAL;
239 	}
240 
241 	return 0;
242 }
243 
zlib_decompress_init(struct crypto_pcomp * tfm)244 static int zlib_decompress_init(struct crypto_pcomp *tfm)
245 {
246 	int ret;
247 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
248 	struct z_stream_s *stream = &dctx->decomp_stream;
249 
250 	ret = zlib_inflateReset(stream);
251 	if (ret != Z_OK)
252 		return -EINVAL;
253 
254 	return 0;
255 }
256 
zlib_decompress_update(struct crypto_pcomp * tfm,struct comp_request * req)257 static int zlib_decompress_update(struct crypto_pcomp *tfm,
258 				  struct comp_request *req)
259 {
260 	int ret;
261 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
262 	struct z_stream_s *stream = &dctx->decomp_stream;
263 
264 	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
265 	stream->next_in = req->next_in;
266 	stream->avail_in = req->avail_in;
267 	stream->next_out = req->next_out;
268 	stream->avail_out = req->avail_out;
269 
270 	ret = zlib_inflate(stream, Z_SYNC_FLUSH);
271 	switch (ret) {
272 	case Z_OK:
273 	case Z_STREAM_END:
274 		break;
275 
276 	case Z_BUF_ERROR:
277 		pr_debug("zlib_inflate could not make progress\n");
278 		return -EAGAIN;
279 
280 	default:
281 		pr_debug("zlib_inflate failed %d\n", ret);
282 		return -EINVAL;
283 	}
284 
285 	ret = req->avail_out - stream->avail_out;
286 	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
287 		 stream->avail_in, stream->avail_out,
288 		 req->avail_in - stream->avail_in, ret);
289 	req->next_in = stream->next_in;
290 	req->avail_in = stream->avail_in;
291 	req->next_out = stream->next_out;
292 	req->avail_out = stream->avail_out;
293 	return ret;
294 }
295 
zlib_decompress_final(struct crypto_pcomp * tfm,struct comp_request * req)296 static int zlib_decompress_final(struct crypto_pcomp *tfm,
297 				 struct comp_request *req)
298 {
299 	int ret;
300 	struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
301 	struct z_stream_s *stream = &dctx->decomp_stream;
302 
303 	pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out);
304 	stream->next_in = req->next_in;
305 	stream->avail_in = req->avail_in;
306 	stream->next_out = req->next_out;
307 	stream->avail_out = req->avail_out;
308 
309 	if (dctx->decomp_windowBits < 0) {
310 		ret = zlib_inflate(stream, Z_SYNC_FLUSH);
311 		/*
312 		 * Work around a bug in zlib, which sometimes wants to taste an
313 		 * extra byte when being used in the (undocumented) raw deflate
314 		 * mode. (From USAGI).
315 		 */
316 		if (ret == Z_OK && !stream->avail_in && stream->avail_out) {
317 			const void *saved_next_in = stream->next_in;
318 			u8 zerostuff = 0;
319 
320 			stream->next_in = &zerostuff;
321 			stream->avail_in = 1;
322 			ret = zlib_inflate(stream, Z_FINISH);
323 			stream->next_in = saved_next_in;
324 			stream->avail_in = 0;
325 		}
326 	} else
327 		ret = zlib_inflate(stream, Z_FINISH);
328 	if (ret != Z_STREAM_END) {
329 		pr_debug("zlib_inflate failed %d\n", ret);
330 		return -EINVAL;
331 	}
332 
333 	ret = req->avail_out - stream->avail_out;
334 	pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
335 		 stream->avail_in, stream->avail_out,
336 		 req->avail_in - stream->avail_in, ret);
337 	req->next_in = stream->next_in;
338 	req->avail_in = stream->avail_in;
339 	req->next_out = stream->next_out;
340 	req->avail_out = stream->avail_out;
341 	return ret;
342 }
343 
344 
345 static struct pcomp_alg zlib_alg = {
346 	.compress_setup		= zlib_compress_setup,
347 	.compress_init		= zlib_compress_init,
348 	.compress_update	= zlib_compress_update,
349 	.compress_final		= zlib_compress_final,
350 	.decompress_setup	= zlib_decompress_setup,
351 	.decompress_init	= zlib_decompress_init,
352 	.decompress_update	= zlib_decompress_update,
353 	.decompress_final	= zlib_decompress_final,
354 
355 	.base			= {
356 		.cra_name	= "zlib",
357 		.cra_flags	= CRYPTO_ALG_TYPE_PCOMPRESS,
358 		.cra_ctxsize	= sizeof(struct zlib_ctx),
359 		.cra_module	= THIS_MODULE,
360 		.cra_init	= zlib_init,
361 		.cra_exit	= zlib_exit,
362 	}
363 };
364 
zlib_mod_init(void)365 static int __init zlib_mod_init(void)
366 {
367 	return crypto_register_pcomp(&zlib_alg);
368 }
369 
zlib_mod_fini(void)370 static void __exit zlib_mod_fini(void)
371 {
372 	crypto_unregister_pcomp(&zlib_alg);
373 }
374 
375 module_init(zlib_mod_init);
376 module_exit(zlib_mod_fini);
377 
378 MODULE_LICENSE("GPL");
379 MODULE_DESCRIPTION("Zlib Compression Algorithm");
380 MODULE_AUTHOR("Sony Corporation");
381