xref: /freebsd/contrib/libfido2/src/compress.c (revision 2ccfa855b2fc331819953e3de1b1c15ce5b95a7e)
10afa8e06SEd Maste /*
2*2ccfa855SEd Maste  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
30afa8e06SEd Maste  * Use of this source code is governed by a BSD-style
40afa8e06SEd Maste  * license that can be found in the LICENSE file.
5*2ccfa855SEd Maste  * SPDX-License-Identifier: BSD-2-Clause
60afa8e06SEd Maste  */
70afa8e06SEd Maste 
80afa8e06SEd Maste #include <zlib.h>
90afa8e06SEd Maste #include "fido.h"
100afa8e06SEd Maste 
110afa8e06SEd Maste #define BOUND (1024UL * 1024UL)
120afa8e06SEd Maste 
13*2ccfa855SEd Maste /* zlib inflate (raw + headers) */
140afa8e06SEd Maste static int
rfc1950_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)15*2ccfa855SEd Maste rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
160afa8e06SEd Maste {
170afa8e06SEd Maste 	u_long ilen, olen;
18*2ccfa855SEd Maste 	int z;
190afa8e06SEd Maste 
200afa8e06SEd Maste 	memset(out, 0, sizeof(*out));
21*2ccfa855SEd Maste 
220afa8e06SEd Maste 	if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
23*2ccfa855SEd Maste 	    origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
24*2ccfa855SEd Maste 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
25*2ccfa855SEd Maste 		    in->len, origsiz);
260afa8e06SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
27*2ccfa855SEd Maste 	}
28*2ccfa855SEd Maste 
290afa8e06SEd Maste 	if ((out->ptr = calloc(1, olen)) == NULL)
300afa8e06SEd Maste 		return FIDO_ERR_INTERNAL;
310afa8e06SEd Maste 	out->len = olen;
32*2ccfa855SEd Maste 
33*2ccfa855SEd Maste 	if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
34*2ccfa855SEd Maste 	    olen > SIZE_MAX || olen != out->len) {
35*2ccfa855SEd Maste 		fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
36*2ccfa855SEd Maste 		    __func__, z, olen, out->len);
370afa8e06SEd Maste 		fido_blob_reset(out);
380afa8e06SEd Maste 		return FIDO_ERR_COMPRESS;
390afa8e06SEd Maste 	}
400afa8e06SEd Maste 
410afa8e06SEd Maste 	return FIDO_OK;
420afa8e06SEd Maste }
430afa8e06SEd Maste 
44*2ccfa855SEd Maste /* raw inflate */
45*2ccfa855SEd Maste static int
rfc1951_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)46*2ccfa855SEd Maste rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
47*2ccfa855SEd Maste {
48*2ccfa855SEd Maste 	z_stream zs;
49*2ccfa855SEd Maste 	u_int ilen, olen;
50*2ccfa855SEd Maste 	int r, z;
51*2ccfa855SEd Maste 
52*2ccfa855SEd Maste 	memset(&zs, 0, sizeof(zs));
53*2ccfa855SEd Maste 	memset(out, 0, sizeof(*out));
54*2ccfa855SEd Maste 
55*2ccfa855SEd Maste 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
56*2ccfa855SEd Maste 	    origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
57*2ccfa855SEd Maste 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
58*2ccfa855SEd Maste 		    in->len, origsiz);
59*2ccfa855SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
60*2ccfa855SEd Maste 	}
61*2ccfa855SEd Maste 	if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
62*2ccfa855SEd Maste 		fido_log_debug("%s: inflateInit2: %d", __func__, z);
63*2ccfa855SEd Maste 		return FIDO_ERR_COMPRESS;
64*2ccfa855SEd Maste 	}
65*2ccfa855SEd Maste 
66*2ccfa855SEd Maste 	if ((out->ptr = calloc(1, olen)) == NULL) {
67*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
68*2ccfa855SEd Maste 		goto fail;
69*2ccfa855SEd Maste 	}
70*2ccfa855SEd Maste 	out->len = olen;
71*2ccfa855SEd Maste 	zs.next_in = in->ptr;
72*2ccfa855SEd Maste 	zs.avail_in = ilen;
73*2ccfa855SEd Maste 	zs.next_out = out->ptr;
74*2ccfa855SEd Maste 	zs.avail_out = olen;
75*2ccfa855SEd Maste 
76*2ccfa855SEd Maste 	if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
77*2ccfa855SEd Maste 		fido_log_debug("%s: inflate: %d", __func__, z);
78*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
79*2ccfa855SEd Maste 		goto fail;
80*2ccfa855SEd Maste 	}
81*2ccfa855SEd Maste 	if (zs.avail_out != 0) {
82*2ccfa855SEd Maste 		fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
83*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
84*2ccfa855SEd Maste 		goto fail;
85*2ccfa855SEd Maste 	}
86*2ccfa855SEd Maste 
87*2ccfa855SEd Maste 	r = FIDO_OK;
88*2ccfa855SEd Maste fail:
89*2ccfa855SEd Maste 	if ((z = inflateEnd(&zs)) != Z_OK) {
90*2ccfa855SEd Maste 		fido_log_debug("%s: inflateEnd: %d", __func__, z);
91*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
92*2ccfa855SEd Maste 	}
93*2ccfa855SEd Maste 	if (r != FIDO_OK)
94*2ccfa855SEd Maste 		fido_blob_reset(out);
95*2ccfa855SEd Maste 
96*2ccfa855SEd Maste 	return r;
97*2ccfa855SEd Maste }
98*2ccfa855SEd Maste 
99*2ccfa855SEd Maste /* raw deflate */
100*2ccfa855SEd Maste static int
rfc1951_deflate(fido_blob_t * out,const fido_blob_t * in)101*2ccfa855SEd Maste rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
102*2ccfa855SEd Maste {
103*2ccfa855SEd Maste 	z_stream zs;
104*2ccfa855SEd Maste 	u_int ilen, olen;
105*2ccfa855SEd Maste 	int r, z;
106*2ccfa855SEd Maste 
107*2ccfa855SEd Maste 	memset(&zs, 0, sizeof(zs));
108*2ccfa855SEd Maste 	memset(out, 0, sizeof(*out));
109*2ccfa855SEd Maste 
110*2ccfa855SEd Maste 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
111*2ccfa855SEd Maste 		fido_log_debug("%s: in->len=%zu", __func__, in->len);
112*2ccfa855SEd Maste 		return FIDO_ERR_INVALID_ARGUMENT;
113*2ccfa855SEd Maste 	}
114*2ccfa855SEd Maste 	if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
115*2ccfa855SEd Maste 	    -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
116*2ccfa855SEd Maste 		fido_log_debug("%s: deflateInit2: %d", __func__, z);
117*2ccfa855SEd Maste 		return FIDO_ERR_COMPRESS;
118*2ccfa855SEd Maste 	}
119*2ccfa855SEd Maste 
120*2ccfa855SEd Maste 	olen = BOUND;
121*2ccfa855SEd Maste 	if ((out->ptr = calloc(1, olen)) == NULL) {
122*2ccfa855SEd Maste 		r = FIDO_ERR_INTERNAL;
123*2ccfa855SEd Maste 		goto fail;
124*2ccfa855SEd Maste 	}
125*2ccfa855SEd Maste 	out->len = olen;
126*2ccfa855SEd Maste 	zs.next_in = in->ptr;
127*2ccfa855SEd Maste 	zs.avail_in = ilen;
128*2ccfa855SEd Maste 	zs.next_out = out->ptr;
129*2ccfa855SEd Maste 	zs.avail_out = olen;
130*2ccfa855SEd Maste 
131*2ccfa855SEd Maste 	if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
132*2ccfa855SEd Maste 		fido_log_debug("%s: inflate: %d", __func__, z);
133*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
134*2ccfa855SEd Maste 		goto fail;
135*2ccfa855SEd Maste 	}
136*2ccfa855SEd Maste 	if (zs.avail_out >= out->len) {
137*2ccfa855SEd Maste 		fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
138*2ccfa855SEd Maste 		    out->len);
139*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
140*2ccfa855SEd Maste 		goto fail;
141*2ccfa855SEd Maste 	}
142*2ccfa855SEd Maste 	out->len -= zs.avail_out;
143*2ccfa855SEd Maste 
144*2ccfa855SEd Maste 	r = FIDO_OK;
145*2ccfa855SEd Maste fail:
146*2ccfa855SEd Maste 	if ((z = deflateEnd(&zs)) != Z_OK) {
147*2ccfa855SEd Maste 		fido_log_debug("%s: deflateEnd: %d", __func__, z);
148*2ccfa855SEd Maste 		r = FIDO_ERR_COMPRESS;
149*2ccfa855SEd Maste 	}
150*2ccfa855SEd Maste 	if (r != FIDO_OK)
151*2ccfa855SEd Maste 		fido_blob_reset(out);
152*2ccfa855SEd Maste 
153*2ccfa855SEd Maste 	return r;
154*2ccfa855SEd Maste }
155*2ccfa855SEd Maste 
1560afa8e06SEd Maste int
fido_compress(fido_blob_t * out,const fido_blob_t * in)1570afa8e06SEd Maste fido_compress(fido_blob_t *out, const fido_blob_t *in)
1580afa8e06SEd Maste {
159*2ccfa855SEd Maste 	return rfc1951_deflate(out, in);
1600afa8e06SEd Maste }
1610afa8e06SEd Maste 
1620afa8e06SEd Maste int
fido_uncompress(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)1630afa8e06SEd Maste fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
1640afa8e06SEd Maste {
165*2ccfa855SEd Maste 	if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
166*2ccfa855SEd Maste 		return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
167*2ccfa855SEd Maste 	return rfc1951_inflate(out, in, origsiz);
1680afa8e06SEd Maste }
169