xref: /netbsd-src/external/bsd/libfido2/dist/src/compress.c (revision 2d40c4512a84c0d064ec30a492c5e2a14d230bc3)
195dbdf32Schristos /*
2*2d40c451Schristos  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
395dbdf32Schristos  * Use of this source code is governed by a BSD-style
495dbdf32Schristos  * license that can be found in the LICENSE file.
5*2d40c451Schristos  * SPDX-License-Identifier: BSD-2-Clause
695dbdf32Schristos  */
795dbdf32Schristos 
895dbdf32Schristos #include <zlib.h>
995dbdf32Schristos #include "fido.h"
1095dbdf32Schristos 
1195dbdf32Schristos #define BOUND (1024UL * 1024UL)
1295dbdf32Schristos 
13*2d40c451Schristos /* zlib inflate (raw + headers) */
1495dbdf32Schristos static int
rfc1950_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)15*2d40c451Schristos rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
1695dbdf32Schristos {
1795dbdf32Schristos 	u_long ilen, olen;
18*2d40c451Schristos 	int z;
1995dbdf32Schristos 
2095dbdf32Schristos 	memset(out, 0, sizeof(*out));
21*2d40c451Schristos 
2295dbdf32Schristos 	if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
23*2d40c451Schristos 	    origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
24*2d40c451Schristos 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
25*2d40c451Schristos 		    in->len, origsiz);
2695dbdf32Schristos 		return FIDO_ERR_INVALID_ARGUMENT;
27*2d40c451Schristos 	}
28*2d40c451Schristos 
2995dbdf32Schristos 	if ((out->ptr = calloc(1, olen)) == NULL)
3095dbdf32Schristos 		return FIDO_ERR_INTERNAL;
3195dbdf32Schristos 	out->len = olen;
32*2d40c451Schristos 
33*2d40c451Schristos 	if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
34*2d40c451Schristos 	    olen > SIZE_MAX || olen != out->len) {
35*2d40c451Schristos 		fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
36*2d40c451Schristos 		    __func__, z, olen, out->len);
3795dbdf32Schristos 		fido_blob_reset(out);
3895dbdf32Schristos 		return FIDO_ERR_COMPRESS;
3995dbdf32Schristos 	}
4095dbdf32Schristos 
4195dbdf32Schristos 	return FIDO_OK;
4295dbdf32Schristos }
4395dbdf32Schristos 
44*2d40c451Schristos /* raw inflate */
45*2d40c451Schristos static int
rfc1951_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)46*2d40c451Schristos rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
47*2d40c451Schristos {
48*2d40c451Schristos 	z_stream zs;
49*2d40c451Schristos 	u_int ilen, olen;
50*2d40c451Schristos 	int r, z;
51*2d40c451Schristos 
52*2d40c451Schristos 	memset(&zs, 0, sizeof(zs));
53*2d40c451Schristos 	memset(out, 0, sizeof(*out));
54*2d40c451Schristos 
55*2d40c451Schristos 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
56*2d40c451Schristos 	    origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
57*2d40c451Schristos 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
58*2d40c451Schristos 		    in->len, origsiz);
59*2d40c451Schristos 		return FIDO_ERR_INVALID_ARGUMENT;
60*2d40c451Schristos 	}
61*2d40c451Schristos 	if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
62*2d40c451Schristos 		fido_log_debug("%s: inflateInit2: %d", __func__, z);
63*2d40c451Schristos 		return FIDO_ERR_COMPRESS;
64*2d40c451Schristos 	}
65*2d40c451Schristos 
66*2d40c451Schristos 	if ((out->ptr = calloc(1, olen)) == NULL) {
67*2d40c451Schristos 		r = FIDO_ERR_INTERNAL;
68*2d40c451Schristos 		goto fail;
69*2d40c451Schristos 	}
70*2d40c451Schristos 	out->len = olen;
71*2d40c451Schristos 	zs.next_in = in->ptr;
72*2d40c451Schristos 	zs.avail_in = ilen;
73*2d40c451Schristos 	zs.next_out = out->ptr;
74*2d40c451Schristos 	zs.avail_out = olen;
75*2d40c451Schristos 
76*2d40c451Schristos 	if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
77*2d40c451Schristos 		fido_log_debug("%s: inflate: %d", __func__, z);
78*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
79*2d40c451Schristos 		goto fail;
80*2d40c451Schristos 	}
81*2d40c451Schristos 	if (zs.avail_out != 0) {
82*2d40c451Schristos 		fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
83*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
84*2d40c451Schristos 		goto fail;
85*2d40c451Schristos 	}
86*2d40c451Schristos 
87*2d40c451Schristos 	r = FIDO_OK;
88*2d40c451Schristos fail:
89*2d40c451Schristos 	if ((z = inflateEnd(&zs)) != Z_OK) {
90*2d40c451Schristos 		fido_log_debug("%s: inflateEnd: %d", __func__, z);
91*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
92*2d40c451Schristos 	}
93*2d40c451Schristos 	if (r != FIDO_OK)
94*2d40c451Schristos 		fido_blob_reset(out);
95*2d40c451Schristos 
96*2d40c451Schristos 	return r;
97*2d40c451Schristos }
98*2d40c451Schristos 
99*2d40c451Schristos /* raw deflate */
100*2d40c451Schristos static int
rfc1951_deflate(fido_blob_t * out,const fido_blob_t * in)101*2d40c451Schristos rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
102*2d40c451Schristos {
103*2d40c451Schristos 	z_stream zs;
104*2d40c451Schristos 	u_int ilen, olen;
105*2d40c451Schristos 	int r, z;
106*2d40c451Schristos 
107*2d40c451Schristos 	memset(&zs, 0, sizeof(zs));
108*2d40c451Schristos 	memset(out, 0, sizeof(*out));
109*2d40c451Schristos 
110*2d40c451Schristos 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
111*2d40c451Schristos 		fido_log_debug("%s: in->len=%zu", __func__, in->len);
112*2d40c451Schristos 		return FIDO_ERR_INVALID_ARGUMENT;
113*2d40c451Schristos 	}
114*2d40c451Schristos 	if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
115*2d40c451Schristos 	    -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
116*2d40c451Schristos 		fido_log_debug("%s: deflateInit2: %d", __func__, z);
117*2d40c451Schristos 		return FIDO_ERR_COMPRESS;
118*2d40c451Schristos 	}
119*2d40c451Schristos 
120*2d40c451Schristos 	olen = BOUND;
121*2d40c451Schristos 	if ((out->ptr = calloc(1, olen)) == NULL) {
122*2d40c451Schristos 		r = FIDO_ERR_INTERNAL;
123*2d40c451Schristos 		goto fail;
124*2d40c451Schristos 	}
125*2d40c451Schristos 	out->len = olen;
126*2d40c451Schristos 	zs.next_in = in->ptr;
127*2d40c451Schristos 	zs.avail_in = ilen;
128*2d40c451Schristos 	zs.next_out = out->ptr;
129*2d40c451Schristos 	zs.avail_out = olen;
130*2d40c451Schristos 
131*2d40c451Schristos 	if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
132*2d40c451Schristos 		fido_log_debug("%s: inflate: %d", __func__, z);
133*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
134*2d40c451Schristos 		goto fail;
135*2d40c451Schristos 	}
136*2d40c451Schristos 	if (zs.avail_out >= out->len) {
137*2d40c451Schristos 		fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
138*2d40c451Schristos 		    out->len);
139*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
140*2d40c451Schristos 		goto fail;
141*2d40c451Schristos 	}
142*2d40c451Schristos 	out->len -= zs.avail_out;
143*2d40c451Schristos 
144*2d40c451Schristos 	r = FIDO_OK;
145*2d40c451Schristos fail:
146*2d40c451Schristos 	if ((z = deflateEnd(&zs)) != Z_OK) {
147*2d40c451Schristos 		fido_log_debug("%s: deflateEnd: %d", __func__, z);
148*2d40c451Schristos 		r = FIDO_ERR_COMPRESS;
149*2d40c451Schristos 	}
150*2d40c451Schristos 	if (r != FIDO_OK)
151*2d40c451Schristos 		fido_blob_reset(out);
152*2d40c451Schristos 
153*2d40c451Schristos 	return r;
154*2d40c451Schristos }
155*2d40c451Schristos 
15695dbdf32Schristos int
fido_compress(fido_blob_t * out,const fido_blob_t * in)15795dbdf32Schristos fido_compress(fido_blob_t *out, const fido_blob_t *in)
15895dbdf32Schristos {
159*2d40c451Schristos 	return rfc1951_deflate(out, in);
16095dbdf32Schristos }
16195dbdf32Schristos 
16295dbdf32Schristos int
fido_uncompress(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)16395dbdf32Schristos fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
16495dbdf32Schristos {
165*2d40c451Schristos 	if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
166*2d40c451Schristos 		return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
167*2d40c451Schristos 	return rfc1951_inflate(out, in, origsiz);
16895dbdf32Schristos }
169