xref: /netbsd-src/crypto/external/bsd/netpgp/dist/src/lib/compress.c (revision 93bf6008f8b7982c1d1a9486e4a4a0e687fe36eb)
1 /*
2  * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
3  * All rights reserved.
4  * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
5  * their moral rights under the UK Copyright Design and Patents Act 1988 to
6  * be recorded as the authors of this copyright work.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
9  * use this file except in compliance with the License.
10  *
11  * You may obtain a copy of the License at
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  *
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 /** \file
23  */
24 #include "config.h"
25 
26 #ifdef HAVE_ZLIB_H
27 #include <zlib.h>
28 #endif
29 
30 #ifdef HAVE_BZLIB_H
31 #include <bzlib.h>
32 #endif
33 
34 #ifdef HAVE_ASSERT_H
35 #include <assert.h>
36 #endif
37 
38 #include <string.h>
39 
40 #include "packet-parse.h"
41 #include "crypto.h"
42 #include "errors.h"
43 
44 #include "netpgpdefs.h"
45 #include "parse_local.h"
46 #include "memory.h"
47 
48 #define DECOMPRESS_BUFFER	1024
49 
50 typedef struct {
51 	__ops_compression_type_t type;
52 	__ops_region_t   *region;
53 	unsigned char   in[DECOMPRESS_BUFFER];
54 	unsigned char   out[DECOMPRESS_BUFFER];
55 	z_stream        zstream;/* ZIP and ZLIB */
56 	size_t          offset;
57 	int             inflate_ret;
58 }               z_decompress_t;
59 
60 typedef struct {
61 	__ops_compression_type_t type;
62 	__ops_region_t   *region;
63 	char            in[DECOMPRESS_BUFFER];
64 	char            out[DECOMPRESS_BUFFER];
65 	bz_stream       bzstream;	/* BZIP2 */
66 	size_t          offset;
67 	int             inflate_ret;
68 }               bz_decompress_t;
69 
70 typedef struct {
71 	z_stream        stream;
72 	unsigned char  *src;
73 	unsigned char  *dst;
74 }               compress_t;
75 
76 /*
77  * \todo remove code duplication between this and
78  * bzip2_compressed_data_reader
79  */
80 static int
81 zlib_compressed_data_reader(void *dest, size_t length,
82 			    __ops_error_t ** errors,
83 			    __ops_reader_info_t * rinfo,
84 			    __ops_parse_cb_info_t * cbinfo)
85 {
86 	z_decompress_t *z = __ops_reader_get_arg(rinfo);
87 	size_t           len;
88 	char		*cdest = dest;
89 	int              cc;
90 
91 	assert(z->type == OPS_C_ZIP || z->type == OPS_C_ZLIB);
92 
93 	if (z->inflate_ret == Z_STREAM_END &&
94 	    z->zstream.next_out == &z->out[z->offset]) {
95 		return 0;
96 	}
97 
98 	if (__ops_get_debug_level(__FILE__)) {
99 		(void) fprintf(stderr, "zlib_compressed_data_reader: length %" PRIsize "d\n", length);
100 	}
101 
102 	if (z->region->length_read == z->region->length) {
103 		if (z->inflate_ret != Z_STREAM_END) {
104 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
105 				"Compressed data didn't end when region ended.");
106 		}
107 	}
108 	for (cc = 0 ; cc < length ; cc += len) {
109 		if (&z->out[z->offset] == z->zstream.next_out) {
110 			int             ret;
111 
112 			z->zstream.next_out = z->out;
113 			z->zstream.avail_out = sizeof(z->out);
114 			z->offset = 0;
115 			if (z->zstream.avail_in == 0) {
116 				unsigned        n = z->region->length;
117 
118 				if (!z->region->indeterminate) {
119 					n -= z->region->length_read;
120 					if (n > sizeof(z->in)) {
121 						n = sizeof(z->in);
122 					}
123 				} else {
124 					n = sizeof(z->in);
125 				}
126 
127 				if (!__ops_stacked_limited_read(z->in, n, z->region,
128 						     errors, rinfo, cbinfo)) {
129 					return -1;
130 				}
131 
132 				z->zstream.next_in = z->in;
133 				z->zstream.avail_in = (z->region->indeterminate) ?
134 					z->region->last_read : n;
135 			}
136 			ret = inflate(&z->zstream, Z_SYNC_FLUSH);
137 			if (ret == Z_STREAM_END) {
138 				if (!z->region->indeterminate &&
139 				    z->region->length_read != z->region->length) {
140 					OPS_ERROR(cbinfo->errors,
141 						OPS_E_P_DECOMPRESSION_ERROR,
142 						"Compressed stream ended before packet end.");
143 				}
144 			} else if (ret != Z_OK) {
145 				(void) fprintf(stderr, "ret=%d\n", ret);
146 				OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR, z->zstream.msg);
147 			}
148 			z->inflate_ret = ret;
149 		}
150 		assert(z->zstream.next_out > &z->out[z->offset]);
151 		len = z->zstream.next_out - &z->out[z->offset];
152 		if (len > length) {
153 			len = length;
154 		}
155 		(void) memcpy(&cdest[cc], &z->out[z->offset], len);
156 		z->offset += len;
157 	}
158 
159 	return length;
160 }
161 
162 /* \todo remove code duplication between this and zlib_compressed_data_reader */
163 static int
164 bzip2_compressed_data_reader(void *dest, size_t length,
165 			     __ops_error_t ** errors,
166 			     __ops_reader_info_t * rinfo,
167 			     __ops_parse_cb_info_t * cbinfo)
168 {
169 	bz_decompress_t *bz = __ops_reader_get_arg(rinfo);
170 	size_t		len;
171 	char		*cdest = dest;
172 	int		cc;
173 
174 	assert(bz->type == OPS_C_BZIP2);
175 
176 	if (bz->inflate_ret == BZ_STREAM_END &&
177 	    bz->bzstream.next_out == &bz->out[bz->offset]) {
178 		return 0;
179 	}
180 	if (bz->region->length_read == bz->region->length) {
181 		if (bz->inflate_ret != BZ_STREAM_END) {
182 			OPS_ERROR(cbinfo->errors, OPS_E_P_DECOMPRESSION_ERROR,
183 				"Compressed data didn't end when region ended.");
184 		}
185 	}
186 	for (cc = 0 ; cc < length ; cc += len) {
187 		if (&bz->out[bz->offset] == bz->bzstream.next_out) {
188 			int             ret;
189 
190 			bz->bzstream.next_out = (char *) bz->out;
191 			bz->bzstream.avail_out = sizeof(bz->out);
192 			bz->offset = 0;
193 			if (bz->bzstream.avail_in == 0) {
194 				unsigned        n = bz->region->length;
195 
196 				if (!bz->region->indeterminate) {
197 					n -= bz->region->length_read;
198 					if (n > sizeof(bz->in))
199 						n = sizeof(bz->in);
200 				} else
201 					n = sizeof(bz->in);
202 
203 				if (!__ops_stacked_limited_read((unsigned char *) bz->in, n, bz->region,
204 						     errors, rinfo, cbinfo))
205 					return -1;
206 
207 				bz->bzstream.next_in = bz->in;
208 				bz->bzstream.avail_in = bz->region->indeterminate
209 					? bz->region->last_read : n;
210 			}
211 			ret = BZ2_bzDecompress(&bz->bzstream);
212 			if (ret == BZ_STREAM_END) {
213 				if (!bz->region->indeterminate &&
214 				    bz->region->length_read != bz->region->length)
215 					OPS_ERROR(cbinfo->errors,
216 						OPS_E_P_DECOMPRESSION_ERROR,
217 						"Compressed stream ended before packet end.");
218 			} else if (ret != BZ_OK) {
219 				OPS_ERROR_1(cbinfo->errors,
220 					OPS_E_P_DECOMPRESSION_ERROR,
221 					"Invalid return %d from BZ2_bzDecompress", ret);
222 			}
223 			bz->inflate_ret = ret;
224 		}
225 		assert(bz->bzstream.next_out > &bz->out[bz->offset]);
226 		len = bz->bzstream.next_out - &bz->out[bz->offset];
227 		if (len > length)
228 			len = length;
229 		(void) memcpy(&cdest[cc], &bz->out[bz->offset], len);
230 		bz->offset += len;
231 	}
232 
233 	return length;
234 }
235 
236 /**
237  * \ingroup Core_Compress
238  *
239  * \param *region 	Pointer to a region
240  * \param *parse_info 	How to parse
241  * \param type Which compression type to expect
242 */
243 
244 int
245 __ops_decompress(__ops_region_t *region, __ops_parse_info_t *parse_info,
246 	       __ops_compression_type_t type)
247 {
248 	z_decompress_t z;
249 	bz_decompress_t bz;
250 	int             ret;
251 
252 	switch (type) {
253 	case OPS_C_ZIP:
254 	case OPS_C_ZLIB:
255 		(void) memset(&z, 0x0, sizeof(z));
256 
257 		z.region = region;
258 		z.offset = 0;
259 		z.type = type;
260 
261 		z.zstream.next_in = Z_NULL;
262 		z.zstream.avail_in = 0;
263 		z.zstream.next_out = z.out;
264 		z.zstream.zalloc = Z_NULL;
265 		z.zstream.zfree = Z_NULL;
266 		z.zstream.opaque = Z_NULL;
267 
268 		break;
269 
270 	case OPS_C_BZIP2:
271 		(void) memset(&bz, 0x0, sizeof(bz));
272 
273 		bz.region = region;
274 		bz.offset = 0;
275 		bz.type = type;
276 
277 		bz.bzstream.next_in = NULL;
278 		bz.bzstream.avail_in = 0;
279 		bz.bzstream.next_out = bz.out;
280 		bz.bzstream.bzalloc = NULL;
281 		bz.bzstream.bzfree = NULL;
282 		bz.bzstream.opaque = NULL;
283 
284 		break;
285 
286 	default:
287 		OPS_ERROR_1(&parse_info->errors,
288 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
289 			"Compression algorithm %d is not yet supported", type);
290 		return 0;
291 	}
292 
293 	switch (type) {
294 	case OPS_C_ZIP:
295 		ret = inflateInit2(&z.zstream, -15);
296 		break;
297 
298 	case OPS_C_ZLIB:
299 		ret = inflateInit(&z.zstream);
300 		break;
301 
302 	case OPS_C_BZIP2:
303 		ret = BZ2_bzDecompressInit(&bz.bzstream, 1, 0);
304 		break;
305 
306 	default:
307 		OPS_ERROR_1(&parse_info->errors,
308 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
309 			"Compression algorithm %d is not yet supported", type);
310 		return 0;
311 	}
312 
313 	switch (type) {
314 	case OPS_C_ZIP:
315 	case OPS_C_ZLIB:
316 		if (ret != Z_OK) {
317 			OPS_ERROR_1(&parse_info->errors,
318 				OPS_E_P_DECOMPRESSION_ERROR,
319 				"Cannot initialise ZIP or ZLIB stream for decompression: error=%d", ret);
320 			return 0;
321 		}
322 		__ops_reader_push(parse_info, zlib_compressed_data_reader, NULL, &z);
323 		break;
324 
325 	case OPS_C_BZIP2:
326 		if (ret != BZ_OK) {
327 			OPS_ERROR_1(&parse_info->errors,
328 				OPS_E_P_DECOMPRESSION_ERROR,
329 				"Cannot initialise BZIP2 stream for decompression: error=%d", ret);
330 			return 0;
331 		}
332 		__ops_reader_push(parse_info, bzip2_compressed_data_reader, NULL, &bz);
333 		break;
334 
335 	default:
336 		OPS_ERROR_1(&parse_info->errors,
337 			OPS_E_ALG_UNSUPPORTED_COMPRESS_ALG,
338 			"Compression algorithm %d is not yet supported", type);
339 		return 0;
340 	}
341 
342 	ret = __ops_parse(parse_info);
343 
344 	__ops_reader_pop(parse_info);
345 
346 	return ret;
347 }
348 
349 /**
350 \ingroup Core_WritePackets
351 \brief Writes Compressed packet
352 \param data Data to write out
353 \param len Length of data
354 \param cinfo Write settings
355 \return true if OK; else false
356 */
357 
358 bool
359 __ops_write_compressed(const unsigned char *data,
360 		     const unsigned int len,
361 		     __ops_create_info_t * cinfo)
362 {
363 	int             r = 0;
364 	int             sz_in = 0;
365 	int             sz_out = 0;
366 	compress_t *compression = calloc(1, sizeof(compress_t));
367 
368 	/* compress the data */
369 	const int       level = Z_DEFAULT_COMPRESSION;	/* \todo allow varying
370 							 * levels */
371 	compression->stream.zalloc = Z_NULL;
372 	compression->stream.zfree = Z_NULL;
373 	compression->stream.opaque = NULL;
374 
375 	/* all other fields set to zero by use of calloc */
376 
377 	if (deflateInit(&compression->stream, level) != Z_OK) {
378 		/* can't initialise */
379 		assert(0);
380 	}
381 	/* do necessary transformation */
382 	/* copy input to maintain const'ness of src */
383 	assert(compression->src == NULL);
384 	assert(compression->dst == NULL);
385 
386 	sz_in = len * sizeof(unsigned char);
387 	sz_out = (sz_in * 1.01) + 12;	/* from zlib webpage */
388 	compression->src = calloc(1, sz_in);
389 	compression->dst = calloc(1, sz_out);
390 	(void) memcpy(compression->src, data, len);
391 
392 	/* setup stream */
393 	compression->stream.next_in = compression->src;
394 	compression->stream.avail_in = sz_in;
395 	compression->stream.total_in = 0;
396 
397 	compression->stream.next_out = compression->dst;
398 	compression->stream.avail_out = sz_out;
399 	compression->stream.total_out = 0;
400 
401 	r = deflate(&compression->stream, Z_FINISH);
402 	assert(r == Z_STREAM_END);	/* need to loop if not */
403 
404 	/* write it out */
405 	return (__ops_write_ptag(OPS_PTAG_CT_COMPRESSED, cinfo)
406 		&& __ops_write_length(1 + compression->stream.total_out, cinfo)
407 		&& __ops_write_scalar(OPS_C_ZLIB, 1, cinfo)
408 		&& __ops_write(compression->dst, compression->stream.total_out, cinfo));
409 }
410