Commit bf68e65ec9ea61e32ab71bef59aa5d24d255241f
Committed by
Herbert Xu
1 parent
8064efb874
Exists in
master
and in
20 other branches
crypto: zlib - New zlib crypto module, using pcomp
Signed-off-by: Geert Uytterhoeven <Geert.Uytterhoeven@sonycom.com> Cc: James Morris <jmorris@namei.org> Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Showing 4 changed files with 408 additions and 0 deletions Side-by-side Diff
crypto/Kconfig
... | ... | @@ -748,6 +748,15 @@ |
748 | 748 | |
749 | 749 | You will most probably want this if using IPSec. |
750 | 750 | |
751 | +config CRYPTO_ZLIB | |
752 | + tristate "Zlib compression algorithm" | |
753 | + select CRYPTO_PCOMP | |
754 | + select ZLIB_INFLATE | |
755 | + select ZLIB_DEFLATE | |
756 | + select NLATTR | |
757 | + help | |
758 | + This is the zlib algorithm. | |
759 | + | |
751 | 760 | config CRYPTO_LZO |
752 | 761 | tristate "LZO compression algorithm" |
753 | 762 | select CRYPTO_ALGAPI |
crypto/Makefile
... | ... | @@ -74,6 +74,7 @@ |
74 | 74 | obj-$(CONFIG_CRYPTO_SEED) += seed.o |
75 | 75 | obj-$(CONFIG_CRYPTO_SALSA20) += salsa20_generic.o |
76 | 76 | obj-$(CONFIG_CRYPTO_DEFLATE) += deflate.o |
77 | +obj-$(CONFIG_CRYPTO_ZLIB) += zlib.o | |
77 | 78 | obj-$(CONFIG_CRYPTO_MICHAEL_MIC) += michael_mic.o |
78 | 79 | obj-$(CONFIG_CRYPTO_CRC32C) += crc32c.o |
79 | 80 | obj-$(CONFIG_CRYPTO_AUTHENC) += authenc.o |
crypto/zlib.c
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 | +#include <linux/slab.h> | |
33 | + | |
34 | +#include <crypto/internal/compress.h> | |
35 | + | |
36 | +#include <net/netlink.h> | |
37 | + | |
38 | + | |
39 | +struct zlib_ctx { | |
40 | + struct z_stream_s comp_stream; | |
41 | + struct z_stream_s decomp_stream; | |
42 | + int decomp_windowBits; | |
43 | +}; | |
44 | + | |
45 | + | |
46 | +static void zlib_comp_exit(struct zlib_ctx *ctx) | |
47 | +{ | |
48 | + struct z_stream_s *stream = &ctx->comp_stream; | |
49 | + | |
50 | + if (stream->workspace) { | |
51 | + zlib_deflateEnd(stream); | |
52 | + vfree(stream->workspace); | |
53 | + stream->workspace = NULL; | |
54 | + } | |
55 | +} | |
56 | + | |
57 | +static void zlib_decomp_exit(struct zlib_ctx *ctx) | |
58 | +{ | |
59 | + struct z_stream_s *stream = &ctx->decomp_stream; | |
60 | + | |
61 | + if (stream->workspace) { | |
62 | + zlib_inflateEnd(stream); | |
63 | + kfree(stream->workspace); | |
64 | + stream->workspace = NULL; | |
65 | + } | |
66 | +} | |
67 | + | |
68 | +static int zlib_init(struct crypto_tfm *tfm) | |
69 | +{ | |
70 | + return 0; | |
71 | +} | |
72 | + | |
73 | +static void zlib_exit(struct crypto_tfm *tfm) | |
74 | +{ | |
75 | + struct zlib_ctx *ctx = crypto_tfm_ctx(tfm); | |
76 | + | |
77 | + zlib_comp_exit(ctx); | |
78 | + zlib_decomp_exit(ctx); | |
79 | +} | |
80 | + | |
81 | + | |
82 | +static int zlib_compress_setup(struct crypto_pcomp *tfm, void *params, | |
83 | + unsigned int len) | |
84 | +{ | |
85 | + struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
86 | + struct z_stream_s *stream = &ctx->comp_stream; | |
87 | + struct nlattr *tb[ZLIB_COMP_MAX + 1]; | |
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 | + workspacesize = zlib_deflate_workspacesize(); | |
98 | + stream->workspace = vmalloc(workspacesize); | |
99 | + if (!stream->workspace) | |
100 | + return -ENOMEM; | |
101 | + | |
102 | + memset(stream->workspace, 0, workspacesize); | |
103 | + ret = zlib_deflateInit2(stream, | |
104 | + tb[ZLIB_COMP_LEVEL] | |
105 | + ? nla_get_u32(tb[ZLIB_COMP_LEVEL]) | |
106 | + : Z_DEFAULT_COMPRESSION, | |
107 | + tb[ZLIB_COMP_METHOD] | |
108 | + ? nla_get_u32(tb[ZLIB_COMP_METHOD]) | |
109 | + : Z_DEFLATED, | |
110 | + tb[ZLIB_COMP_WINDOWBITS] | |
111 | + ? nla_get_u32(tb[ZLIB_COMP_WINDOWBITS]) | |
112 | + : MAX_WBITS, | |
113 | + tb[ZLIB_COMP_MEMLEVEL] | |
114 | + ? nla_get_u32(tb[ZLIB_COMP_MEMLEVEL]) | |
115 | + : DEF_MEM_LEVEL, | |
116 | + tb[ZLIB_COMP_STRATEGY] | |
117 | + ? nla_get_u32(tb[ZLIB_COMP_STRATEGY]) | |
118 | + : Z_DEFAULT_STRATEGY); | |
119 | + if (ret != Z_OK) { | |
120 | + vfree(stream->workspace); | |
121 | + stream->workspace = NULL; | |
122 | + return -EINVAL; | |
123 | + } | |
124 | + | |
125 | + return 0; | |
126 | +} | |
127 | + | |
128 | +static int zlib_compress_init(struct crypto_pcomp *tfm) | |
129 | +{ | |
130 | + int ret; | |
131 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
132 | + struct z_stream_s *stream = &dctx->comp_stream; | |
133 | + | |
134 | + ret = zlib_deflateReset(stream); | |
135 | + if (ret != Z_OK) | |
136 | + return -EINVAL; | |
137 | + | |
138 | + return 0; | |
139 | +} | |
140 | + | |
141 | +static int zlib_compress_update(struct crypto_pcomp *tfm, | |
142 | + struct comp_request *req) | |
143 | +{ | |
144 | + int ret; | |
145 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
146 | + struct z_stream_s *stream = &dctx->comp_stream; | |
147 | + | |
148 | + pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out); | |
149 | + stream->next_in = req->next_in; | |
150 | + stream->avail_in = req->avail_in; | |
151 | + stream->next_out = req->next_out; | |
152 | + stream->avail_out = req->avail_out; | |
153 | + | |
154 | + ret = zlib_deflate(stream, Z_NO_FLUSH); | |
155 | + switch (ret) { | |
156 | + case Z_OK: | |
157 | + break; | |
158 | + | |
159 | + case Z_BUF_ERROR: | |
160 | + pr_debug("zlib_deflate could not make progress\n"); | |
161 | + return -EAGAIN; | |
162 | + | |
163 | + default: | |
164 | + pr_debug("zlib_deflate failed %d\n", ret); | |
165 | + return -EINVAL; | |
166 | + } | |
167 | + | |
168 | + pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", | |
169 | + stream->avail_in, stream->avail_out, | |
170 | + req->avail_in - stream->avail_in, | |
171 | + req->avail_out - stream->avail_out); | |
172 | + req->next_in = stream->next_in; | |
173 | + req->avail_in = stream->avail_in; | |
174 | + req->next_out = stream->next_out; | |
175 | + req->avail_out = stream->avail_out; | |
176 | + return 0; | |
177 | +} | |
178 | + | |
179 | +static int zlib_compress_final(struct crypto_pcomp *tfm, | |
180 | + struct comp_request *req) | |
181 | +{ | |
182 | + int ret; | |
183 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
184 | + struct z_stream_s *stream = &dctx->comp_stream; | |
185 | + | |
186 | + pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out); | |
187 | + stream->next_in = req->next_in; | |
188 | + stream->avail_in = req->avail_in; | |
189 | + stream->next_out = req->next_out; | |
190 | + stream->avail_out = req->avail_out; | |
191 | + | |
192 | + ret = zlib_deflate(stream, Z_FINISH); | |
193 | + if (ret != Z_STREAM_END) { | |
194 | + pr_debug("zlib_deflate failed %d\n", ret); | |
195 | + return -EINVAL; | |
196 | + } | |
197 | + | |
198 | + pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", | |
199 | + stream->avail_in, stream->avail_out, | |
200 | + req->avail_in - stream->avail_in, | |
201 | + req->avail_out - stream->avail_out); | |
202 | + req->next_in = stream->next_in; | |
203 | + req->avail_in = stream->avail_in; | |
204 | + req->next_out = stream->next_out; | |
205 | + req->avail_out = stream->avail_out; | |
206 | + return 0; | |
207 | +} | |
208 | + | |
209 | + | |
210 | +static int zlib_decompress_setup(struct crypto_pcomp *tfm, void *params, | |
211 | + unsigned int len) | |
212 | +{ | |
213 | + struct zlib_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
214 | + struct z_stream_s *stream = &ctx->decomp_stream; | |
215 | + struct nlattr *tb[ZLIB_DECOMP_MAX + 1]; | |
216 | + int ret = 0; | |
217 | + | |
218 | + ret = nla_parse(tb, ZLIB_DECOMP_MAX, params, len, NULL); | |
219 | + if (ret) | |
220 | + return ret; | |
221 | + | |
222 | + zlib_decomp_exit(ctx); | |
223 | + | |
224 | + ctx->decomp_windowBits = tb[ZLIB_DECOMP_WINDOWBITS] | |
225 | + ? nla_get_u32(tb[ZLIB_DECOMP_WINDOWBITS]) | |
226 | + : DEF_WBITS; | |
227 | + | |
228 | + stream->workspace = kzalloc(zlib_inflate_workspacesize(), GFP_KERNEL); | |
229 | + if (!stream->workspace) | |
230 | + return -ENOMEM; | |
231 | + | |
232 | + ret = zlib_inflateInit2(stream, ctx->decomp_windowBits); | |
233 | + if (ret != Z_OK) { | |
234 | + kfree(stream->workspace); | |
235 | + stream->workspace = NULL; | |
236 | + return -EINVAL; | |
237 | + } | |
238 | + | |
239 | + return 0; | |
240 | +} | |
241 | + | |
242 | +static int zlib_decompress_init(struct crypto_pcomp *tfm) | |
243 | +{ | |
244 | + int ret; | |
245 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
246 | + struct z_stream_s *stream = &dctx->decomp_stream; | |
247 | + | |
248 | + ret = zlib_inflateReset(stream); | |
249 | + if (ret != Z_OK) | |
250 | + return -EINVAL; | |
251 | + | |
252 | + return 0; | |
253 | +} | |
254 | + | |
255 | +static int zlib_decompress_update(struct crypto_pcomp *tfm, | |
256 | + struct comp_request *req) | |
257 | +{ | |
258 | + int ret; | |
259 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
260 | + struct z_stream_s *stream = &dctx->decomp_stream; | |
261 | + | |
262 | + pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out); | |
263 | + stream->next_in = req->next_in; | |
264 | + stream->avail_in = req->avail_in; | |
265 | + stream->next_out = req->next_out; | |
266 | + stream->avail_out = req->avail_out; | |
267 | + | |
268 | + ret = zlib_inflate(stream, Z_SYNC_FLUSH); | |
269 | + switch (ret) { | |
270 | + case Z_OK: | |
271 | + case Z_STREAM_END: | |
272 | + break; | |
273 | + | |
274 | + case Z_BUF_ERROR: | |
275 | + pr_debug("zlib_inflate could not make progress\n"); | |
276 | + return -EAGAIN; | |
277 | + | |
278 | + default: | |
279 | + pr_debug("zlib_inflate failed %d\n", ret); | |
280 | + return -EINVAL; | |
281 | + } | |
282 | + | |
283 | + pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", | |
284 | + stream->avail_in, stream->avail_out, | |
285 | + req->avail_in - stream->avail_in, | |
286 | + req->avail_out - stream->avail_out); | |
287 | + req->next_in = stream->next_in; | |
288 | + req->avail_in = stream->avail_in; | |
289 | + req->next_out = stream->next_out; | |
290 | + req->avail_out = stream->avail_out; | |
291 | + return 0; | |
292 | +} | |
293 | + | |
294 | +static int zlib_decompress_final(struct crypto_pcomp *tfm, | |
295 | + struct comp_request *req) | |
296 | +{ | |
297 | + int ret; | |
298 | + struct zlib_ctx *dctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm)); | |
299 | + struct z_stream_s *stream = &dctx->decomp_stream; | |
300 | + | |
301 | + pr_debug("avail_in %u, avail_out %u\n", req->avail_in, req->avail_out); | |
302 | + stream->next_in = req->next_in; | |
303 | + stream->avail_in = req->avail_in; | |
304 | + stream->next_out = req->next_out; | |
305 | + stream->avail_out = req->avail_out; | |
306 | + | |
307 | + if (dctx->decomp_windowBits < 0) { | |
308 | + ret = zlib_inflate(stream, Z_SYNC_FLUSH); | |
309 | + /* | |
310 | + * Work around a bug in zlib, which sometimes wants to taste an | |
311 | + * extra byte when being used in the (undocumented) raw deflate | |
312 | + * mode. (From USAGI). | |
313 | + */ | |
314 | + if (ret == Z_OK && !stream->avail_in && stream->avail_out) { | |
315 | + const void *saved_next_in = stream->next_in; | |
316 | + u8 zerostuff = 0; | |
317 | + | |
318 | + stream->next_in = &zerostuff; | |
319 | + stream->avail_in = 1; | |
320 | + ret = zlib_inflate(stream, Z_FINISH); | |
321 | + stream->next_in = saved_next_in; | |
322 | + stream->avail_in = 0; | |
323 | + } | |
324 | + } else | |
325 | + ret = zlib_inflate(stream, Z_FINISH); | |
326 | + if (ret != Z_STREAM_END) { | |
327 | + pr_debug("zlib_inflate failed %d\n", ret); | |
328 | + return -EINVAL; | |
329 | + } | |
330 | + | |
331 | + pr_debug("avail_in %u, avail_out %u (consumed %u, produced %u)\n", | |
332 | + stream->avail_in, stream->avail_out, | |
333 | + req->avail_in - stream->avail_in, | |
334 | + req->avail_out - stream->avail_out); | |
335 | + req->next_in = stream->next_in; | |
336 | + req->avail_in = stream->avail_in; | |
337 | + req->next_out = stream->next_out; | |
338 | + req->avail_out = stream->avail_out; | |
339 | + return 0; | |
340 | +} | |
341 | + | |
342 | + | |
343 | +static struct pcomp_alg zlib_alg = { | |
344 | + .compress_setup = zlib_compress_setup, | |
345 | + .compress_init = zlib_compress_init, | |
346 | + .compress_update = zlib_compress_update, | |
347 | + .compress_final = zlib_compress_final, | |
348 | + .decompress_setup = zlib_decompress_setup, | |
349 | + .decompress_init = zlib_decompress_init, | |
350 | + .decompress_update = zlib_decompress_update, | |
351 | + .decompress_final = zlib_decompress_final, | |
352 | + | |
353 | + .base = { | |
354 | + .cra_name = "zlib", | |
355 | + .cra_flags = CRYPTO_ALG_TYPE_PCOMPRESS, | |
356 | + .cra_ctxsize = sizeof(struct zlib_ctx), | |
357 | + .cra_module = THIS_MODULE, | |
358 | + .cra_init = zlib_init, | |
359 | + .cra_exit = zlib_exit, | |
360 | + } | |
361 | +}; | |
362 | + | |
363 | +static int __init zlib_mod_init(void) | |
364 | +{ | |
365 | + return crypto_register_pcomp(&zlib_alg); | |
366 | +} | |
367 | + | |
368 | +static void __exit zlib_mod_fini(void) | |
369 | +{ | |
370 | + crypto_unregister_pcomp(&zlib_alg); | |
371 | +} | |
372 | + | |
373 | +module_init(zlib_mod_init); | |
374 | +module_exit(zlib_mod_fini); | |
375 | + | |
376 | +MODULE_LICENSE("GPL"); | |
377 | +MODULE_DESCRIPTION("Zlib Compression Algorithm"); | |
378 | +MODULE_AUTHOR("Sony Corporation"); |
include/crypto/compress.h
... | ... | @@ -30,6 +30,26 @@ |
30 | 30 | unsigned int avail_out; /* bytes available at next_out */ |
31 | 31 | }; |
32 | 32 | |
33 | +enum zlib_comp_params { | |
34 | + ZLIB_COMP_LEVEL = 1, /* e.g. Z_DEFAULT_COMPRESSION */ | |
35 | + ZLIB_COMP_METHOD, /* e.g. Z_DEFLATED */ | |
36 | + ZLIB_COMP_WINDOWBITS, /* e.g. MAX_WBITS */ | |
37 | + ZLIB_COMP_MEMLEVEL, /* e.g. DEF_MEM_LEVEL */ | |
38 | + ZLIB_COMP_STRATEGY, /* e.g. Z_DEFAULT_STRATEGY */ | |
39 | + __ZLIB_COMP_MAX, | |
40 | +}; | |
41 | + | |
42 | +#define ZLIB_COMP_MAX (__ZLIB_COMP_MAX - 1) | |
43 | + | |
44 | + | |
45 | +enum zlib_decomp_params { | |
46 | + ZLIB_DECOMP_WINDOWBITS = 1, /* e.g. DEF_WBITS */ | |
47 | + __ZLIB_DECOMP_MAX, | |
48 | +}; | |
49 | + | |
50 | +#define ZLIB_DECOMP_MAX (__ZLIB_DECOMP_MAX - 1) | |
51 | + | |
52 | + | |
33 | 53 | struct crypto_pcomp { |
34 | 54 | struct crypto_tfm base; |
35 | 55 | }; |