Coverage Report

Created: 2025-10-13 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/opensc/src/libopensc/compression.c
Line
Count
Source
1
/*
2
 * compression.c: Generic wrapper for compression of data
3
 *
4
 * Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8
 * License as published by the Free Software Foundation; either
9
 * version 2.1 of the License, or (at your option) any later version.
10
 *
11
 * This library is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public
17
 * License along with this library; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19
 */
20
21
#ifdef HAVE_CONFIG_H
22
#include "config.h"
23
#endif
24
25
#ifdef ENABLE_ZLIB  /* empty file without zlib */
26
#include <zlib.h>
27
#include <string.h>
28
#include <stdlib.h>
29
30
#include "internal.h"
31
#include "errors.h"
32
#include "compression.h"
33
34
858
static int zerr_to_opensc(int err) {
35
858
  switch(err) {
36
0
  case Z_OK:
37
337
  case Z_STREAM_END:
38
337
    return SC_SUCCESS;
39
44
  case Z_UNKNOWN:
40
44
    return SC_ERROR_UNKNOWN;
41
362
  case Z_DATA_ERROR:
42
477
  case Z_BUF_ERROR:
43
477
    return SC_ERROR_UNKNOWN_DATA_RECEIVED;
44
0
  case Z_MEM_ERROR:
45
0
    return SC_ERROR_OUT_OF_MEMORY;
46
0
  case Z_VERSION_ERROR:
47
0
  case Z_STREAM_ERROR:
48
  /* case Z_NEED_DICT: */
49
0
  default:
50
0
    return SC_ERROR_INTERNAL;
51
858
  }
52
858
}
53
1.67k
static int detect_method(const u8* in, size_t inLen) {
54
1.67k
  if (in != NULL && inLen > 1) {
55
1.46k
    if (in[0] == 0x1f && in[1] == 0x8b)
56
29
      return COMPRESSION_GZIP;
57
    /*
58
     * A zlib stream has the following structure:
59
     *   0   1
60
     * +---+---+
61
     * |CMF|FLG|   (more-->)
62
     * +---+---+
63
     *
64
     * FLG (FLaGs)
65
     *  This flag byte is divided as follows:
66
     *
67
     *  bits 0 to 4  FCHECK  (check bits for CMF and FLG)
68
     *  bit  5       FDICT   (preset dictionary)
69
     *  bits 6 to 7  FLEVEL  (compression level)
70
     *
71
     *  The FCHECK value must be such that CMF and FLG, when viewed as
72
     *  a 16-bit unsigned integer stored in MSB order (CMF*256 + FLG),
73
     *  is a multiple of 31.
74
     */
75
1.43k
    if ((((uint16_t) in[0])*256 + in[1]) % 31 == 0)
76
829
      return COMPRESSION_ZLIB;
77
1.43k
  }
78
815
  return COMPRESSION_UNKNOWN;
79
1.67k
}
80
81
0
static int sc_compress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) {
82
  /* Since compress does not offer a way to make it compress gzip... manually set it up */
83
0
  z_stream gz;
84
0
  int err;
85
0
  int window_size = 15 + 0x10;
86
0
  memset(&gz, 0, sizeof(gz));
87
88
0
  gz.next_in = (u8*)in;
89
0
  gz.avail_in = (unsigned)inLen;
90
0
  gz.next_out = out;
91
0
  gz.avail_out = (unsigned)*outLen;
92
93
0
  err = deflateInit2(&gz, Z_BEST_COMPRESSION, Z_DEFLATED, window_size, 9, Z_DEFAULT_STRATEGY);
94
0
  if (err != Z_OK) {
95
0
    deflateEnd(&gz);
96
0
    return zerr_to_opensc(err);
97
0
  }
98
0
  err = deflate(&gz, Z_FINISH);
99
0
  if (err != Z_STREAM_END) {
100
0
    deflateEnd(&gz);
101
0
    return zerr_to_opensc(err);
102
0
  }
103
0
  *outLen = gz.total_out;
104
105
0
  err = deflateEnd(&gz);
106
0
  return zerr_to_opensc(err);
107
0
}
108
109
0
static int sc_decompress_gzip(u8* out, size_t* outLen, const u8* in, size_t inLen) {
110
  /* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */
111
0
  z_stream gz;
112
0
  int err;
113
0
  int window_size = 15 + 0x20;
114
0
  memset(&gz, 0, sizeof(gz));
115
116
0
  gz.next_in = (u8*)in;
117
0
  gz.avail_in = (unsigned)inLen;
118
0
  gz.next_out = out;
119
0
  gz.avail_out = (unsigned)*outLen;
120
121
0
  *outLen = 0;
122
123
0
  err = inflateInit2(&gz, window_size);
124
0
  if (err != Z_OK) return zerr_to_opensc(err);
125
0
  err = inflate(&gz, Z_FINISH);
126
0
  if(err != Z_STREAM_END) {
127
0
    inflateEnd(&gz);
128
0
    return zerr_to_opensc(err);
129
0
  }
130
0
  *outLen = gz.total_out;
131
132
0
  err = inflateEnd(&gz);
133
0
  if (*outLen == 0) {
134
0
    return SC_ERROR_UNKNOWN_DATA_RECEIVED;
135
0
  }
136
0
  return zerr_to_opensc(err);
137
0
}
138
139
0
int sc_compress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method) {
140
0
  unsigned long zlib_outlen;
141
0
  int rc;
142
143
0
  switch(method) {
144
0
  case COMPRESSION_ZLIB:
145
0
    zlib_outlen = *outLen;
146
0
    rc = zerr_to_opensc(compress(out, &zlib_outlen, in, inLen));
147
0
    *outLen = zlib_outlen;
148
0
    return rc;
149
0
  case COMPRESSION_GZIP:
150
0
    return sc_compress_gzip(out, outLen, in, inLen);
151
0
  default:
152
0
    return SC_ERROR_INVALID_ARGUMENTS;
153
0
  }
154
0
}
155
156
int sc_decompress(u8* out, size_t* outLen, const u8* in, size_t inLen, int method)
157
0
{
158
0
  unsigned long zlib_outlen;
159
0
  int rc;
160
161
0
  if (in == NULL || out == NULL) {
162
0
    return SC_ERROR_UNKNOWN_DATA_RECEIVED;
163
0
  }
164
165
0
  if (method == COMPRESSION_AUTO) {
166
0
    method = detect_method(in, inLen);
167
0
    if (method == COMPRESSION_UNKNOWN) {
168
0
      *outLen = 0;
169
0
      return SC_ERROR_UNKNOWN_DATA_RECEIVED;
170
0
    }
171
0
  }
172
0
  switch (method) {
173
0
  case COMPRESSION_ZLIB:
174
0
    zlib_outlen = *outLen;
175
0
    rc = zerr_to_opensc(uncompress(out, &zlib_outlen, in, inLen));
176
0
    *outLen = zlib_outlen;
177
0
    return rc;
178
0
  case COMPRESSION_GZIP:
179
0
    return sc_decompress_gzip(out, outLen, in, inLen);
180
0
  default:
181
0
    return SC_ERROR_INVALID_ARGUMENTS;
182
0
  }
183
0
}
184
185
858
static int sc_decompress_zlib_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int gzip) {
186
  /* Since uncompress does not offer a way to make it uncompress gzip... manually set it up */
187
858
  z_stream gz;
188
858
  int err;
189
858
  int window_size = 15;
190
858
  const size_t startSize = inLen < 1024 ? 2048 : inLen * 2;
191
858
  const size_t blockSize = inLen < 1024 ? 512 : inLen / 2;
192
858
  size_t bufferSize = startSize;
193
858
  if (gzip)
194
29
    window_size += 0x20;
195
858
  memset(&gz, 0, sizeof(gz));
196
197
858
  if (!out || !outLen)
198
0
    return SC_ERROR_INVALID_ARGUMENTS;
199
200
858
  gz.next_in = (u8*)in;
201
858
  gz.avail_in = (unsigned)inLen;
202
203
858
  err = inflateInit2(&gz, window_size);
204
858
  if (err != Z_OK)
205
0
    return zerr_to_opensc(err);
206
207
858
  *outLen = 0;
208
209
1.21k
  while (1) {
210
    /* Setup buffer... */
211
1.21k
    size_t num;
212
1.21k
    u8* buf = realloc(*out, bufferSize);
213
1.21k
    if (!buf) {
214
0
      free(*out);
215
0
      *out = NULL;
216
0
      inflateEnd(&gz);
217
0
      return SC_ERROR_OUT_OF_MEMORY;
218
0
    }
219
1.21k
    *out = buf;
220
1.21k
    gz.next_out = buf + *outLen;
221
1.21k
    gz.avail_out = (unsigned)(bufferSize - *outLen);
222
223
1.21k
    err = inflate(&gz, Z_FULL_FLUSH);
224
1.21k
    if (err != Z_STREAM_END && err != Z_OK) {
225
521
      free(*out);
226
521
      *out = NULL;
227
521
      break;
228
521
    }
229
691
    num = *outLen + gz.avail_out;
230
691
    if (bufferSize > num) {
231
660
      *outLen += bufferSize - num;
232
660
      bufferSize += bufferSize - num + blockSize;
233
660
    }
234
691
    if (err == Z_STREAM_END) {
235
337
      if (*outLen > 0) {
236
        /* Shrink it down, if it fails, just use old data */
237
337
        buf = realloc(buf, *outLen);
238
337
        if (buf) {
239
337
          *out = buf;
240
337
        }
241
337
      } else {
242
0
        free(*out);
243
0
        *out = NULL;
244
0
        err = Z_DATA_ERROR;
245
0
      }
246
337
      break;
247
337
    }
248
691
  }
249
858
  inflateEnd(&gz);
250
858
  return zerr_to_opensc(err);
251
858
}
252
253
int sc_decompress_alloc(u8** out, size_t* outLen, const u8* in, size_t inLen, int method)
254
1.67k
{
255
1.67k
  if (in == NULL || out == NULL) {
256
0
    return SC_ERROR_UNKNOWN_DATA_RECEIVED;
257
0
  }
258
259
1.67k
  if (method == COMPRESSION_AUTO) {
260
1.67k
    method = detect_method(in, inLen);
261
1.67k
    if (method == COMPRESSION_UNKNOWN) {
262
815
      return SC_ERROR_UNKNOWN_DATA_RECEIVED;
263
815
    }
264
1.67k
  }
265
266
858
  switch (method) {
267
829
  case COMPRESSION_ZLIB:
268
829
    return sc_decompress_zlib_alloc(out, outLen, in, inLen, 0);
269
29
  case COMPRESSION_GZIP:
270
29
    return sc_decompress_zlib_alloc(out, outLen, in, inLen, 1);
271
0
  default:
272
0
    return SC_ERROR_INVALID_ARGUMENTS;
273
858
  }
274
858
}
275
#endif /* ENABLE_ZLIB */