blob: 06b62e5cdcc72a93281051a7c07b5090be9abeaa [file] [log] [blame]
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +08001/*
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>
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +080032
33#include <crypto/internal/compress.h>
34
35#include <net/netlink.h>
36
37
38struct zlib_ctx {
39 struct z_stream_s comp_stream;
40 struct z_stream_s decomp_stream;
41 int decomp_windowBits;
42};
43
44
45static 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
56static 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);
David S. Miller7ab24bf2011-06-29 05:48:41 -070062 vfree(stream->workspace);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +080063 stream->workspace = NULL;
64 }
65}
66
67static int zlib_init(struct crypto_tfm *tfm)
68{
69 return 0;
70}
71
72static 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
81static 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];
Jim Keniston565d76c2011-03-22 16:35:12 -070087 int window_bits, mem_level;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +080088 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
Jim Keniston565d76c2011-03-22 16:35:12 -070097 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);
Joe Perchesc8484592010-11-27 16:30:39 +0800105 stream->workspace = vzalloc(workspacesize);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800106 if (!stream->workspace)
107 return -ENOMEM;
108
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800109 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,
Jim Keniston565d76c2011-03-22 16:35:12 -0700116 window_bits,
117 mem_level,
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800118 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
130static 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
143static 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
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000170 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800171 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
172 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000173 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800174 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;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000178 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800179}
180
181static 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
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000200 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800201 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
202 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000203 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800204 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;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000208 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800209}
210
211
212static 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
David S. Miller7ab24bf2011-06-29 05:48:41 -0700230 stream->workspace = vzalloc(zlib_inflate_workspacesize());
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800231 if (!stream->workspace)
232 return -ENOMEM;
233
234 ret = zlib_inflateInit2(stream, ctx->decomp_windowBits);
235 if (ret != Z_OK) {
David S. Miller7ab24bf2011-06-29 05:48:41 -0700236 vfree(stream->workspace);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800237 stream->workspace = NULL;
238 return -EINVAL;
239 }
240
241 return 0;
242}
243
244static 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
257static 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
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000285 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800286 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
287 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000288 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800289 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;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000293 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800294}
295
296static 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
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000333 ret = req->avail_out - stream->avail_out;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800334 pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n",
335 stream->avail_in, stream->avail_out,
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000336 req->avail_in - stream->avail_in, ret);
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800337 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;
Geert Uytterhoeven3ce858c2009-05-27 15:05:02 +1000341 return ret;
Geert Uytterhoevenbf68e652009-03-04 15:15:49 +0800342}
343
344
345static 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
365static int __init zlib_mod_init(void)
366{
367 return crypto_register_pcomp(&zlib_alg);
368}
369
370static void __exit zlib_mod_fini(void)
371{
372 crypto_unregister_pcomp(&zlib_alg);
373}
374
375module_init(zlib_mod_init);
376module_exit(zlib_mod_fini);
377
378MODULE_LICENSE("GPL");
379MODULE_DESCRIPTION("Zlib Compression Algorithm");
380MODULE_AUTHOR("Sony Corporation");