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