xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/compress.c (revision b1c86f5f087524e68db12794ee9c3e3da1ab17a0)
1 /*-
2  * Copyright (c) 2009 The NetBSD Foundation, Inc.
3  * All rights reserved.
4  *
5  * This code is derived from software contributed to The NetBSD Foundation
6  * by Alistair Crooks (agc@NetBSD.org)
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  * POSSIBILITY OF SUCH DAMAGE.
28  */
29 /*
30  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
31  * All rights reserved.
32  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
33  * their moral rights under the UK Copyright Design and Patents Act 1988 to
34  * be recorded as the authors of this copyright work.
35  *
36  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
37  * use this file except in compliance with the License.
38  *
39  * You may obtain a copy of the License at
40  *     http://www.apache.org/licenses/LICENSE-2.0
41  *
42  * Unless required by applicable law or agreed to in writing, software
43  * distributed under the License is distributed on an "AS IS" BASIS,
44  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
45  *
46  * See the License for the specific language governing permissions and
47  * limitations under the License.
48  */
49 
50 /** \file
51  */
52 #include "config.h"
53 
54 #ifdef HAVE_SYS_CDEFS_H
55 #include <sys/cdefs.h>
56 #endif
57 
58 #if defined(__NetBSD__)
59 __COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
60 __RCSID("$NetBSD: compress.c,v 1.17 2010/08/15 16:10:56 agc Exp $");
61 #endif
62 
63 #ifdef HAVE_ZLIB_H
64 #include <zlib.h>
65 #endif
66 
67 #ifdef HAVE_BZLIB_H
68 #include <bzlib.h>
69 #endif
70 
71 #include <string.h>
72 
73 #include "packet-parse.h"
74 #include "errors.h"
75 #include "netpgpdefs.h"
76 #include "crypto.h"
77 #include "memory.h"
78 #include "writer.h"
79 
80 #define DECOMPRESS_BUFFER	1024
81 
82 typedef struct {
83 	__ops_compression_type_t type;
84 	__ops_region_t   *region;
85 	uint8_t   	in[DECOMPRESS_BUFFER];
86 	uint8_t   	out[DECOMPRESS_BUFFER];
87 	z_stream        zstream;/* ZIP and ZLIB */
88 	size_t          offset;
89 	int             inflate_ret;
90 } z_decompress_t;
91 
92 typedef struct {
93 	__ops_compression_type_t type;
94 	__ops_region_t   *region;
95 	char            in[DECOMPRESS_BUFFER];
96 	char            out[DECOMPRESS_BUFFER];
97 	bz_stream       bzstream;	/* BZIP2 */
98 	size_t          offset;
99 	int             inflate_ret;
100 } bz_decompress_t;
101 
102 typedef struct {
103 	z_stream        stream;
104 	uint8_t  	*src;
105 	uint8_t  	*dst;
106 } compress_t;
107 
108 /*
109  * \todo remove code duplication between this and
110  * bzip2_compressed_data_reader
111  */
112 static int
113 zlib_compressed_data_reader(void *dest, size_t length,
114 			    __ops_error_t **errors,
115 			    __ops_reader_t *readinfo,
116 			    __ops_cbdata_t *cbinfo)
117 {
118 	z_decompress_t *z = __ops_reader_get_arg(readinfo);
119 	size_t           len;
120 	size_t		 cc;
121 	char		*cdest = dest;
122 
123 	if (z->type != OPS_C_ZIP && z->type != OPS_C_ZLIB) {
124 		(void) fprintf(stderr,
125 			"zlib_compressed_data_reader: weird type %d\n",
126 			z->type);
127 		return 0;
128 	}
129 
130 	if (z->inflate_ret == Z_STREAM_END &&
131 	    z->zstream.next_out == &z->out[z->offset]) {
132 		return 0;
133 	}
134 
135 	if (__ops_get_debug_level(__FILE__)) {
136 		(void) fprintf(stderr,
137 			"zlib_compressed_data_reader: length %" PRIsize "d\n",
138 			length);
139 	}
140 
141 	if (z->region->readc == z->region->length) {
142 		if (z->inflate_ret != Z_STREAM_END) {
143 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
144 			"Compressed data didn't end when region ended.");
145 		}
146 	}
147 	for (cc = 0 ; cc < length ; cc += len) {
148 		if (&z->out[z->offset] == z->zstream.next_out) {
149 			int             ret;
150 
151 			z->zstream.next_out = z->out;
152 			z->zstream.avail_out = sizeof(z->out);
153 			z->offset = 0;
154 			if (z->zstream.avail_in == 0) {
155 				unsigned        n = z->region->length;
156 
157 				if (!z->region->indeterminate) {
158 					n -= z->region->readc;
159 					if (n > sizeof(z->in)) {
160 						n = sizeof(z->in);
161 					}
162 				} else {
163 					n = sizeof(z->in);
164 				}
165 				if (!__ops_stacked_limited_read(z->in, n,
166 						z->region,
167 						errors, readinfo, cbinfo)) {
168 					return -1;
169 				}
170 
171 				z->zstream.next_in = z->in;
172 				z->zstream.avail_in = (z->region->indeterminate) ?
173 					z->region->last_read : n;
174 			}
175 			ret = inflate(&z->zstream, Z_SYNC_FLUSH);
176 			if (ret == Z_STREAM_END) {
177 				if (!z->region->indeterminate &&
178 				    z->region->readc != z->region->length) {
179 					OPS_ERROR(cbinfo->errors,
180 						OPS_E_P_DECOMPRESSION_ERROR,
181 						"Compressed stream ended before packet end.");
182 				}
183 			} else if (ret != Z_OK) {
184 				(void) fprintf(stderr, "ret=%d\n", ret);
185 				OPS_ERROR(cbinfo->errors,
186 				OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg);
187 			}
188 			z->inflate_ret = ret;
189 		}
190 		if (z->zstream.next_out <= &z->out[z->offset]) {
191 			(void) fprintf(stderr, "Out of memory in buffer\n");
192 			return 0;
193 		}
194 		len = (size_t)(z->zstream.next_out - &z->out[z->offset]);
195 		if (len > length) {
196 			len = length;
197 		}
198 		(void) memcpy(&cdest[cc], &z->out[z->offset], len);
199 		z->offset += len;
200 	}
201 
202 	return (int)length;
203 }
204 
205 /* \todo remove code duplication between this and zlib_compressed_data_reader */
206 static int
207 bzip2_compressed_data_reader(void *dest, size_t length,
208 			     __ops_error_t **errors,
209 			     __ops_reader_t *readinfo,
210 			     __ops_cbdata_t *cbinfo)
211 {
212 	bz_decompress_t *bz = __ops_reader_get_arg(readinfo);
213 	size_t		len;
214 	size_t		 cc;
215 	char		*cdest = dest;
216 
217 	if (bz->type != OPS_C_BZIP2) {
218 		(void) fprintf(stderr, "Weird type %d\n", bz->type);
219 		return 0;
220 	}
221 
222 	if (bz->inflate_ret == BZ_STREAM_END &&
223 	    bz->bzstream.next_out == &bz->out[bz->offset]) {
224 		return 0;
225 	}
226 	if (bz->region->readc == bz->region->length) {
227 		if (bz->inflate_ret != BZ_STREAM_END) {
228 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
229 			"Compressed data didn't end when region ended.");
230 		}
231 	}
232 	for (cc = 0 ; cc < length ; cc += len) {
233 		if (&bz->out[bz->offset] == bz->bzstream.next_out) {
234 			int             ret;
235 
236 			bz->bzstream.next_out = (char *) bz->out;
237 			bz->bzstream.avail_out = sizeof(bz->out);
238 			bz->offset = 0;
239 			if (bz->bzstream.avail_in == 0) {
240 				unsigned        n = bz->region->length;
241 
242 				if (!bz->region->indeterminate) {
243 					n -= bz->region->readc;
244 					if (n > sizeof(bz->in))
245 						n = sizeof(bz->in);
246 				} else
247 					n = sizeof(bz->in);
248 
249 				if (!__ops_stacked_limited_read(
250 						(uint8_t *) bz->in,
251 						n, bz->region,
252 						errors, readinfo, cbinfo))
253 					return -1;
254 
255 				bz->bzstream.next_in = bz->in;
256 				bz->bzstream.avail_in =
257 					(bz->region->indeterminate) ?
258 					 bz->region->last_read : n;
259 			}
260 			ret = BZ2_bzDecompress(&bz->bzstream);
261 			if (ret == BZ_STREAM_END) {
262 				if (!bz->region->indeterminate &&
263 				    bz->region->readc != bz->region->length)
264 					OPS_ERROR(cbinfo->errors,
265 						OPS_E_P_DECOMPRESSION_ERROR,
266 						"Compressed stream ended before packet end.");
267 			} else if (ret != BZ_OK) {
268 				OPS_ERROR_1(cbinfo->errors,
269 					OPS_E_P_DECOMPRESSION_ERROR,
270 					"Invalid return %d from BZ2_bzDecompress", ret);
271 			}
272 			bz->inflate_ret = ret;
273 		}
274 		if (bz->bzstream.next_out <= &bz->out[bz->offset]) {
275 			(void) fprintf(stderr, "Out of bz memroy\n");
276 			return 0;
277 		}
278 		len = (size_t)(bz->bzstream.next_out - &bz->out[bz->offset]);
279 		if (len > length) {
280 			len = length;
281 		}
282 		(void) memcpy(&cdest[cc], &bz->out[bz->offset], len);
283 		bz->offset += len;
284 	}
285 
286 	return (int)length;
287 }
288 
289 /**
290  * \ingroup Core_Compress
291  *
292  * \param *region 	Pointer to a region
293  * \param *stream 	How to parse
294  * \param type Which compression type to expect
295 */
296 
297 int
298 __ops_decompress(__ops_region_t *region, __ops_stream_t *stream,
299 	       __ops_compression_type_t type)
300 {
301 	z_decompress_t z;
302 	bz_decompress_t bz;
303 	const int	printerrors = 1;
304 	int             ret;
305 
306 	switch (type) {
307 	case OPS_C_ZIP:
308 	case OPS_C_ZLIB:
309 		(void) memset(&z, 0x0, sizeof(z));
310 
311 		z.region = region;
312 		z.offset = 0;
313 		z.type = type;
314 
315 		z.zstream.next_in = Z_NULL;
316 		z.zstream.avail_in = 0;
317 		z.zstream.next_out = z.out;
318 		z.zstream.zalloc = Z_NULL;
319 		z.zstream.zfree = Z_NULL;
320 		z.zstream.opaque = Z_NULL;
321 
322 		break;
323 
324 	case OPS_C_BZIP2:
325 		(void) memset(&bz, 0x0, sizeof(bz));
326 
327 		bz.region = region;
328 		bz.offset = 0;
329 		bz.type = type;
330 
331 		bz.bzstream.next_in = NULL;
332 		bz.bzstream.avail_in = 0;
333 		bz.bzstream.next_out = bz.out;
334 		bz.bzstream.bzalloc = NULL;
335 		bz.bzstream.bzfree = NULL;
336 		bz.bzstream.opaque = NULL;
337 
338 		break;
339 
340 	default:
341 		OPS_ERROR_1(&stream->errors,
342 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
343 			"Compression algorithm %d is not yet supported", type);
344 		return 0;
345 	}
346 
347 	switch (type) {
348 	case OPS_C_ZIP:
349 		/* LINTED */ /* this is a lint problem in zlib.h header */
350 		ret = (int)inflateInit2(&z.zstream, -15);
351 		break;
352 
353 	case OPS_C_ZLIB:
354 		/* LINTED */ /* this is a lint problem in zlib.h header */
355 		ret = (int)inflateInit(&z.zstream);
356 		break;
357 
358 	case OPS_C_BZIP2:
359 		ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0);
360 		break;
361 
362 	default:
363 		OPS_ERROR_1(&stream->errors,
364 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
365 			"Compression algorithm %d is not yet supported", type);
366 		return 0;
367 	}
368 
369 	switch (type) {
370 	case OPS_C_ZIP:
371 	case OPS_C_ZLIB:
372 		if (ret != Z_OK) {
373 			OPS_ERROR_1(&stream->errors,
374 				OPS_E_P_DECOMPRESSION_ERROR,
375 "Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret);
376 			return 0;
377 		}
378 		__ops_reader_push(stream, zlib_compressed_data_reader,
379 					NULL, &z);
380 		break;
381 
382 	case OPS_C_BZIP2:
383 		if (ret != BZ_OK) {
384 			OPS_ERROR_1(&stream->errors,
385 				OPS_E_P_DECOMPRESSION_ERROR,
386 "Cannot initialise BZIP2 stream for decompression: error=%d", ret);
387 			return 0;
388 		}
389 		__ops_reader_push(stream, bzip2_compressed_data_reader,
390 					NULL, &bz);
391 		break;
392 
393 	default:
394 		OPS_ERROR_1(&stream->errors,
395 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
396 			"Compression algorithm %d is not yet supported", type);
397 		return 0;
398 	}
399 
400 	ret = __ops_parse(stream, !printerrors);
401 
402 	__ops_reader_pop(stream);
403 
404 	return ret;
405 }
406 
407 /**
408 \ingroup Core_WritePackets
409 \brief Writes Compressed packet
410 \param data Data to write out
411 \param len Length of data
412 \param output Write settings
413 \return 1 if OK; else 0
414 */
415 
416 unsigned
417 __ops_writez(__ops_output_t *out, const uint8_t *data, const unsigned len)
418 {
419 	compress_t	*zip;
420 	size_t		 sz_in;
421 	size_t		 sz_out;
422 	int              ret;
423 	int              r = 0;
424 
425 	/* compress the data */
426 	const int       level = Z_DEFAULT_COMPRESSION;	/* \todo allow varying
427 							 * levels */
428 
429 	if ((zip = calloc(1, sizeof(*zip))) == NULL) {
430 		(void) fprintf(stderr, "__ops_writez: bad alloc\n");
431 		return 0;
432 	}
433 	zip->stream.zalloc = Z_NULL;
434 	zip->stream.zfree = Z_NULL;
435 	zip->stream.opaque = NULL;
436 
437 	/* all other fields set to zero by use of calloc */
438 
439 	/* LINTED */ /* this is a lint problem in zlib.h header */
440 	if ((int)deflateInit(&zip->stream, level) != Z_OK) {
441 		(void) fprintf(stderr, "__ops_writez: can't initialise\n");
442 		return 0;
443 	}
444 	/* do necessary transformation */
445 	/* copy input to maintain const'ness of src */
446 	if (zip->src != NULL || zip->dst != NULL) {
447 		(void) fprintf(stderr, "__ops_writez: non-null streams\n");
448 		return 0;
449 	}
450 
451 	sz_in = len * sizeof(uint8_t);
452 	sz_out = ((101 * sz_in) / 100) + 12;	/* from zlib webpage */
453 	if ((zip->src = calloc(1, sz_in)) == NULL) {
454 		free(zip);
455 		(void) fprintf(stderr, "__ops_writez: bad alloc2\n");
456 		return 0;
457 	}
458 	if ((zip->dst = calloc(1, sz_out)) == NULL) {
459 		free(zip->src);
460 		free(zip);
461 		(void) fprintf(stderr, "__ops_writez: bad alloc3\n");
462 		return 0;
463 	}
464 	(void) memcpy(zip->src, data, len);
465 
466 	/* setup stream */
467 	zip->stream.next_in = zip->src;
468 	zip->stream.avail_in = (unsigned)sz_in;
469 	zip->stream.total_in = 0;
470 
471 	zip->stream.next_out = zip->dst;
472 	zip->stream.avail_out = (unsigned)sz_out;
473 	zip->stream.total_out = 0;
474 
475 	do {
476 		r = deflate(&zip->stream, Z_FINISH);
477 	} while (r != Z_STREAM_END);
478 
479 	/* write it out */
480 	ret = __ops_write_ptag(out, OPS_PTAG_CT_COMPRESSED) &&
481 		__ops_write_length(out, (unsigned)(zip->stream.total_out + 1))&&
482 		__ops_write_scalar(out, OPS_C_ZLIB, 1) &&
483 		__ops_write(out, zip->dst, (unsigned)zip->stream.total_out);
484 
485 	free(zip->src);
486 	free(zip->dst);
487 	free(zip);
488 	return ret;
489 }
490