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) */ 13static int 14rfc1950_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 */ 44static int 45rfc1951_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; 87fail: 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 */ 99static int 100rfc1951_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; 144fail: 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 155int 156fido_compress(fido_blob_t *out, const fido_blob_t *in) 157{ 158 return rfc1951_deflate(out, in); 159} 160 161int 162fido_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