xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/compress.c (revision c8da0e5fefd3800856b306200a18b2315c7fbb9f)
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_ZLIB_H
55 #include <zlib.h>
56 #endif
57 
58 #ifdef HAVE_BZLIB_H
59 #include <bzlib.h>
60 #endif
61 
62 #include <string.h>
63 
64 #include "packet-parse.h"
65 #include "errors.h"
66 #include "netpgpdefs.h"
67 #include "parse_local.h"
68 #include "memory.h"
69 #include "writer.h"
70 
71 #define DECOMPRESS_BUFFER	1024
72 
73 typedef struct {
74 	__ops_compression_type_t type;
75 	__ops_region_t   *region;
76 	unsigned char   in[DECOMPRESS_BUFFER];
77 	unsigned char   out[DECOMPRESS_BUFFER];
78 	z_stream        zstream;/* ZIP and ZLIB */
79 	size_t          offset;
80 	int             inflate_ret;
81 }               z_decompress_t;
82 
83 typedef struct {
84 	__ops_compression_type_t type;
85 	__ops_region_t   *region;
86 	char            in[DECOMPRESS_BUFFER];
87 	char            out[DECOMPRESS_BUFFER];
88 	bz_stream       bzstream;	/* BZIP2 */
89 	size_t          offset;
90 	int             inflate_ret;
91 }               bz_decompress_t;
92 
93 typedef struct {
94 	z_stream        stream;
95 	unsigned char  *src;
96 	unsigned char  *dst;
97 }               compress_t;
98 
99 /*
100  * \todo remove code duplication between this and
101  * bzip2_compressed_data_reader
102  */
103 static int
104 zlib_compressed_data_reader(void *dest, size_t length,
105 			    __ops_error_t **errors,
106 			    __ops_reader_t *readinfo,
107 			    __ops_callback_data_t *cbinfo)
108 {
109 	z_decompress_t *z = __ops_reader_get_arg(readinfo);
110 	size_t           len;
111 	size_t		 cc;
112 	char		*cdest = dest;
113 
114 	if (z->type != OPS_C_ZIP && z->type != OPS_C_ZLIB) {
115 		(void) fprintf(stderr,
116 			"zlib_compressed_data_reader: weird type %d\n",
117 			z->type);
118 		return 0;
119 	}
120 
121 	if (z->inflate_ret == Z_STREAM_END &&
122 	    z->zstream.next_out == &z->out[z->offset]) {
123 		return 0;
124 	}
125 
126 	if (__ops_get_debug_level(__FILE__)) {
127 		(void) fprintf(stderr, "zlib_compressed_data_reader: length %" PRIsize "d\n", length);
128 	}
129 
130 	if (z->region->length_read == z->region->length) {
131 		if (z->inflate_ret != Z_STREAM_END) {
132 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
133 				"Compressed data didn't end when region ended.");
134 		}
135 	}
136 	for (cc = 0 ; cc < length ; cc += len) {
137 		if (&z->out[z->offset] == z->zstream.next_out) {
138 			int             ret;
139 
140 			z->zstream.next_out = z->out;
141 			z->zstream.avail_out = sizeof(z->out);
142 			z->offset = 0;
143 			if (z->zstream.avail_in == 0) {
144 				unsigned        n = z->region->length;
145 
146 				if (!z->region->indeterminate) {
147 					n -= z->region->length_read;
148 					if (n > sizeof(z->in)) {
149 						n = sizeof(z->in);
150 					}
151 				} else {
152 					n = sizeof(z->in);
153 				}
154 
155 				if (!__ops_stacked_limited_read(z->in, n, z->region,
156 						     errors, readinfo, cbinfo)) {
157 					return -1;
158 				}
159 
160 				z->zstream.next_in = z->in;
161 				z->zstream.avail_in = (z->region->indeterminate) ?
162 					z->region->last_read : n;
163 			}
164 			ret = inflate(&z->zstream, Z_SYNC_FLUSH);
165 			if (ret == Z_STREAM_END) {
166 				if (!z->region->indeterminate &&
167 				    z->region->length_read != z->region->length) {
168 					OPS_ERROR(cbinfo->errors,
169 						OPS_E_P_DECOMPRESSION_ERROR,
170 						"Compressed stream ended before packet end.");
171 				}
172 			} else if (ret != Z_OK) {
173 				(void) fprintf(stderr, "ret=%d\n", ret);
174 				OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg);
175 			}
176 			z->inflate_ret = ret;
177 		}
178 		if (z->zstream.next_out <= &z->out[z->offset]) {
179 			(void) fprintf(stderr, "Out of memory in buffer\n");
180 			return 0;
181 		}
182 		len = z->zstream.next_out - &z->out[z->offset];
183 		if (len > length) {
184 			len = length;
185 		}
186 		(void) memcpy(&cdest[cc], &z->out[z->offset], len);
187 		z->offset += len;
188 	}
189 
190 	return length;
191 }
192 
193 /* \todo remove code duplication between this and zlib_compressed_data_reader */
194 static int
195 bzip2_compressed_data_reader(void *dest, size_t length,
196 			     __ops_error_t **errors,
197 			     __ops_reader_t *readinfo,
198 			     __ops_callback_data_t *cbinfo)
199 {
200 	bz_decompress_t *bz = __ops_reader_get_arg(readinfo);
201 	size_t		len;
202 	size_t		 cc;
203 	char		*cdest = dest;
204 
205 	if (bz->type != OPS_C_BZIP2) {
206 		(void) fprintf(stderr, "Weird type %d\n", bz->type);
207 		return 0;
208 	}
209 
210 	if (bz->inflate_ret == BZ_STREAM_END &&
211 	    bz->bzstream.next_out == &bz->out[bz->offset]) {
212 		return 0;
213 	}
214 	if (bz->region->length_read == bz->region->length) {
215 		if (bz->inflate_ret != BZ_STREAM_END) {
216 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
217 				"Compressed data didn't end when region ended.");
218 		}
219 	}
220 	for (cc = 0 ; cc < length ; cc += len) {
221 		if (&bz->out[bz->offset] == bz->bzstream.next_out) {
222 			int             ret;
223 
224 			bz->bzstream.next_out = (char *) bz->out;
225 			bz->bzstream.avail_out = sizeof(bz->out);
226 			bz->offset = 0;
227 			if (bz->bzstream.avail_in == 0) {
228 				unsigned        n = bz->region->length;
229 
230 				if (!bz->region->indeterminate) {
231 					n -= bz->region->length_read;
232 					if (n > sizeof(bz->in))
233 						n = sizeof(bz->in);
234 				} else
235 					n = sizeof(bz->in);
236 
237 				if (!__ops_stacked_limited_read((unsigned char *) bz->in, n, bz->region,
238 						     errors, readinfo, cbinfo))
239 					return -1;
240 
241 				bz->bzstream.next_in = bz->in;
242 				bz->bzstream.avail_in = bz->region->indeterminate
243 					? bz->region->last_read : n;
244 			}
245 			ret = BZ2_bzDecompress(&bz->bzstream);
246 			if (ret == BZ_STREAM_END) {
247 				if (!bz->region->indeterminate &&
248 				    bz->region->length_read != bz->region->length)
249 					OPS_ERROR(cbinfo->errors,
250 						OPS_E_P_DECOMPRESSION_ERROR,
251 						"Compressed stream ended before packet end.");
252 			} else if (ret != BZ_OK) {
253 				OPS_ERROR_1(cbinfo->errors,
254 					OPS_E_P_DECOMPRESSION_ERROR,
255 					"Invalid return %d from BZ2_bzDecompress", ret);
256 			}
257 			bz->inflate_ret = ret;
258 		}
259 		if (bz->bzstream.next_out <= &bz->out[bz->offset]) {
260 			(void) fprintf(stderr, "Out of bz memroy\n");
261 			return 0;
262 		}
263 		len = bz->bzstream.next_out - &bz->out[bz->offset];
264 		if (len > length) {
265 			len = length;
266 		}
267 		(void) memcpy(&cdest[cc], &bz->out[bz->offset], len);
268 		bz->offset += len;
269 	}
270 
271 	return length;
272 }
273 
274 /**
275  * \ingroup Core_Compress
276  *
277  * \param *region 	Pointer to a region
278  * \param *parse_info 	How to parse
279  * \param type Which compression type to expect
280 */
281 
282 int
283 __ops_decompress(__ops_region_t *region, __ops_parseinfo_t *parse_info,
284 	       __ops_compression_type_t type)
285 {
286 	z_decompress_t z;
287 	bz_decompress_t bz;
288 	int             ret;
289 
290 	switch (type) {
291 	case OPS_C_ZIP:
292 	case OPS_C_ZLIB:
293 		(void) memset(&z, 0x0, sizeof(z));
294 
295 		z.region = region;
296 		z.offset = 0;
297 		z.type = type;
298 
299 		z.zstream.next_in = Z_NULL;
300 		z.zstream.avail_in = 0;
301 		z.zstream.next_out = z.out;
302 		z.zstream.zalloc = Z_NULL;
303 		z.zstream.zfree = Z_NULL;
304 		z.zstream.opaque = Z_NULL;
305 
306 		break;
307 
308 	case OPS_C_BZIP2:
309 		(void) memset(&bz, 0x0, sizeof(bz));
310 
311 		bz.region = region;
312 		bz.offset = 0;
313 		bz.type = type;
314 
315 		bz.bzstream.next_in = NULL;
316 		bz.bzstream.avail_in = 0;
317 		bz.bzstream.next_out = bz.out;
318 		bz.bzstream.bzalloc = NULL;
319 		bz.bzstream.bzfree = NULL;
320 		bz.bzstream.opaque = NULL;
321 
322 		break;
323 
324 	default:
325 		OPS_ERROR_1(&parse_info->errors,
326 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
327 			"Compression algorithm %d is not yet supported", type);
328 		return 0;
329 	}
330 
331 	switch (type) {
332 	case OPS_C_ZIP:
333 		ret = inflateInit2(&z.zstream, -15);
334 		break;
335 
336 	case OPS_C_ZLIB:
337 		ret = inflateInit(&z.zstream);
338 		break;
339 
340 	case OPS_C_BZIP2:
341 		ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0);
342 		break;
343 
344 	default:
345 		OPS_ERROR_1(&parse_info->errors,
346 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
347 			"Compression algorithm %d is not yet supported", type);
348 		return 0;
349 	}
350 
351 	switch (type) {
352 	case OPS_C_ZIP:
353 	case OPS_C_ZLIB:
354 		if (ret != Z_OK) {
355 			OPS_ERROR_1(&parse_info->errors,
356 				OPS_E_P_DECOMPRESSION_ERROR,
357 				"Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret);
358 			return 0;
359 		}
360 		__ops_reader_push(parse_info, zlib_compressed_data_reader, NULL, &z);
361 		break;
362 
363 	case OPS_C_BZIP2:
364 		if (ret != BZ_OK) {
365 			OPS_ERROR_1(&parse_info->errors,
366 				OPS_E_P_DECOMPRESSION_ERROR,
367 				"Cannot initialise BZIP2 stream for decompression: error=%d", ret);
368 			return 0;
369 		}
370 		__ops_reader_push(parse_info, bzip2_compressed_data_reader, NULL, &bz);
371 		break;
372 
373 	default:
374 		OPS_ERROR_1(&parse_info->errors,
375 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
376 			"Compression algorithm %d is not yet supported", type);
377 		return 0;
378 	}
379 
380 	ret = __ops_parse(parse_info, 0);
381 
382 	__ops_reader_pop(parse_info);
383 
384 	return ret;
385 }
386 
387 /**
388 \ingroup Core_WritePackets
389 \brief Writes Compressed packet
390 \param data Data to write out
391 \param len Length of data
392 \param cinfo Write settings
393 \return true if OK; else false
394 */
395 
396 bool
397 __ops_write_compressed(const unsigned char *data,
398 		     const unsigned int len,
399 		     __ops_createinfo_t *cinfo)
400 {
401 	compress_t	*zip = calloc(1, sizeof(compress_t));
402 	size_t		 sz_in = 0;
403 	size_t		 sz_out = 0;
404 	int              r = 0;
405 
406 	/* compress the data */
407 	const int       level = Z_DEFAULT_COMPRESSION;	/* \todo allow varying
408 							 * levels */
409 	zip->stream.zalloc = Z_NULL;
410 	zip->stream.zfree = Z_NULL;
411 	zip->stream.opaque = NULL;
412 
413 	/* all other fields set to zero by use of calloc */
414 
415 	if (deflateInit(&zip->stream, level) != Z_OK) {
416 		(void) fprintf(stderr,
417 			"__ops_write_compressed: can't initialise\n");
418 		return false;
419 	}
420 	/* do necessary transformation */
421 	/* copy input to maintain const'ness of src */
422 	if (zip->src != NULL || zip->dst != NULL) {
423 		(void) fprintf(stderr,
424 			"__ops_write_compressed: non-null streams\n");
425 		return false;
426 	}
427 
428 	sz_in = len * sizeof(unsigned char);
429 	sz_out = (sz_in * 1.01) + 12;	/* from zlib webpage */
430 	zip->src = calloc(1, sz_in);
431 	zip->dst = calloc(1, sz_out);
432 	(void) memcpy(zip->src, data, len);
433 
434 	/* setup stream */
435 	zip->stream.next_in = zip->src;
436 	zip->stream.avail_in = sz_in;
437 	zip->stream.total_in = 0;
438 
439 	zip->stream.next_out = zip->dst;
440 	zip->stream.avail_out = sz_out;
441 	zip->stream.total_out = 0;
442 
443 	do {
444 		r = deflate(&zip->stream, Z_FINISH);
445 	} while (r != Z_STREAM_END);
446 
447 	/* write it out */
448 	return (__ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo) &&
449 		__ops_write_length((unsigned)(zip->stream.total_out + 1), cinfo) &&
450 		__ops_write_scalar(OPS_C_ZLIB, 1, cinfo) &&
451 		__ops_write(zip->dst, (unsigned)zip->stream.total_out, cinfo));
452 }
453