xref: /openbsd-src/lib/libfido2/src/compress.c (revision ab19a69ebe1d1275c01611de862453c36b3d15b9)
1 /*
2  * Copyright (c) 2020-2022 Yubico AB. All rights reserved.
3  * Use of this source code is governed by a BSD-style
4  * license that can be found in the LICENSE file.
5  */
6 
7 #include <zlib.h>
8 #include "fido.h"
9 
10 #define BOUND (1024UL * 1024UL)
11 
12 /* zlib inflate (raw + headers) */
13 static int
rfc1950_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)14 rfc1950_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
15 {
16 	u_long ilen, olen;
17 	int z;
18 
19 	memset(out, 0, sizeof(*out));
20 
21 	if (in->len > ULONG_MAX || (ilen = (u_long)in->len) > BOUND ||
22 	    origsiz > ULONG_MAX || (olen = (u_long)origsiz) > BOUND) {
23 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
24 		    in->len, origsiz);
25 		return FIDO_ERR_INVALID_ARGUMENT;
26 	}
27 
28 	if ((out->ptr = calloc(1, olen)) == NULL)
29 		return FIDO_ERR_INTERNAL;
30 	out->len = olen;
31 
32 	if ((z = uncompress(out->ptr, &olen, in->ptr, ilen)) != Z_OK ||
33 	    olen > SIZE_MAX || olen != out->len) {
34 		fido_log_debug("%s: uncompress: %d, olen=%lu, out->len=%zu",
35 		    __func__, z, olen, out->len);
36 		fido_blob_reset(out);
37 		return FIDO_ERR_COMPRESS;
38 	}
39 
40 	return FIDO_OK;
41 }
42 
43 /* raw inflate */
44 static int
rfc1951_inflate(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)45 rfc1951_inflate(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
46 {
47 	z_stream zs;
48 	u_int ilen, olen;
49 	int r, z;
50 
51 	memset(&zs, 0, sizeof(zs));
52 	memset(out, 0, sizeof(*out));
53 
54 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND ||
55 	    origsiz > UINT_MAX || (olen = (u_int)origsiz) > BOUND) {
56 		fido_log_debug("%s: in->len=%zu, origsiz=%zu", __func__,
57 		    in->len, origsiz);
58 		return FIDO_ERR_INVALID_ARGUMENT;
59 	}
60 	if ((z = inflateInit2(&zs, -MAX_WBITS)) != Z_OK) {
61 		fido_log_debug("%s: inflateInit2: %d", __func__, z);
62 		return FIDO_ERR_COMPRESS;
63 	}
64 
65 	if ((out->ptr = calloc(1, olen)) == NULL) {
66 		r = FIDO_ERR_INTERNAL;
67 		goto fail;
68 	}
69 	out->len = olen;
70 	zs.next_in = in->ptr;
71 	zs.avail_in = ilen;
72 	zs.next_out = out->ptr;
73 	zs.avail_out = olen;
74 
75 	if ((z = inflate(&zs, Z_FINISH)) != Z_STREAM_END) {
76 		fido_log_debug("%s: inflate: %d", __func__, z);
77 		r = FIDO_ERR_COMPRESS;
78 		goto fail;
79 	}
80 	if (zs.avail_out != 0) {
81 		fido_log_debug("%s: %u != 0", __func__, zs.avail_out);
82 		r = FIDO_ERR_COMPRESS;
83 		goto fail;
84 	}
85 
86 	r = FIDO_OK;
87 fail:
88 	if ((z = inflateEnd(&zs)) != Z_OK) {
89 		fido_log_debug("%s: inflateEnd: %d", __func__, z);
90 		r = FIDO_ERR_COMPRESS;
91 	}
92 	if (r != FIDO_OK)
93 		fido_blob_reset(out);
94 
95 	return r;
96 }
97 
98 /* raw deflate */
99 static int
rfc1951_deflate(fido_blob_t * out,const fido_blob_t * in)100 rfc1951_deflate(fido_blob_t *out, const fido_blob_t *in)
101 {
102 	z_stream zs;
103 	u_int ilen, olen;
104 	int r, z;
105 
106 	memset(&zs, 0, sizeof(zs));
107 	memset(out, 0, sizeof(*out));
108 
109 	if (in->len > UINT_MAX || (ilen = (u_int)in->len) > BOUND) {
110 		fido_log_debug("%s: in->len=%zu", __func__, in->len);
111 		return FIDO_ERR_INVALID_ARGUMENT;
112 	}
113 	if ((z = deflateInit2(&zs, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
114 	    -MAX_WBITS, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
115 		fido_log_debug("%s: deflateInit2: %d", __func__, z);
116 		return FIDO_ERR_COMPRESS;
117 	}
118 
119 	olen = BOUND;
120 	if ((out->ptr = calloc(1, olen)) == NULL) {
121 		r = FIDO_ERR_INTERNAL;
122 		goto fail;
123 	}
124 	out->len = olen;
125 	zs.next_in = in->ptr;
126 	zs.avail_in = ilen;
127 	zs.next_out = out->ptr;
128 	zs.avail_out = olen;
129 
130 	if ((z = deflate(&zs, Z_FINISH)) != Z_STREAM_END) {
131 		fido_log_debug("%s: inflate: %d", __func__, z);
132 		r = FIDO_ERR_COMPRESS;
133 		goto fail;
134 	}
135 	if (zs.avail_out >= out->len) {
136 		fido_log_debug("%s: %u > %zu", __func__, zs.avail_out,
137 		    out->len);
138 		r = FIDO_ERR_COMPRESS;
139 		goto fail;
140 	}
141 	out->len -= zs.avail_out;
142 
143 	r = FIDO_OK;
144 fail:
145 	if ((z = deflateEnd(&zs)) != Z_OK) {
146 		fido_log_debug("%s: deflateEnd: %d", __func__, z);
147 		r = FIDO_ERR_COMPRESS;
148 	}
149 	if (r != FIDO_OK)
150 		fido_blob_reset(out);
151 
152 	return r;
153 }
154 
155 int
fido_compress(fido_blob_t * out,const fido_blob_t * in)156 fido_compress(fido_blob_t *out, const fido_blob_t *in)
157 {
158 	return rfc1951_deflate(out, in);
159 }
160 
161 int
fido_uncompress(fido_blob_t * out,const fido_blob_t * in,size_t origsiz)162 fido_uncompress(fido_blob_t *out, const fido_blob_t *in, size_t origsiz)
163 {
164 	if (rfc1950_inflate(out, in, origsiz) == FIDO_OK)
165 		return FIDO_OK; /* backwards compat with libfido2 < 1.11 */
166 	return rfc1951_inflate(out, in, origsiz);
167 }
168