1 /*
2 * checksum-test.c: tests checksum functions.
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24 #include <apr_pools.h>
25
26 #include <zlib.h>
27
28 #include "svn_error.h"
29 #include "svn_io.h"
30
31 #include "../svn_test.h"
32
33 /* Verify that DIGEST of checksum type KIND can be parsed and
34 * converted back to a string matching DIGEST. NAME will be used
35 * to identify the type of checksum in error messages.
36 */
37 static svn_error_t *
checksum_parse_kind(const char * digest,svn_checksum_kind_t kind,const char * name,apr_pool_t * pool)38 checksum_parse_kind(const char *digest,
39 svn_checksum_kind_t kind,
40 const char *name,
41 apr_pool_t *pool)
42 {
43 const char *checksum_display;
44 svn_checksum_t *checksum;
45
46 SVN_ERR(svn_checksum_parse_hex(&checksum, kind, digest, pool));
47 checksum_display = svn_checksum_to_cstring_display(checksum, pool);
48
49 if (strcmp(checksum_display, digest) != 0)
50 return svn_error_createf
51 (SVN_ERR_CHECKSUM_MISMATCH, NULL,
52 "verify-checksum: %s checksum mismatch:\n"
53 " expected: %s\n"
54 " actual: %s\n", name, digest, checksum_display);
55
56 return SVN_NO_ERROR;
57 }
58
59 static svn_error_t *
test_checksum_parse(apr_pool_t * pool)60 test_checksum_parse(apr_pool_t *pool)
61 {
62 SVN_ERR(checksum_parse_kind("8518b76f7a45fe4de2d0955085b41f98",
63 svn_checksum_md5, "md5", pool));
64 SVN_ERR(checksum_parse_kind("74d82379bcc6771454377db03b912c2b62704139",
65 svn_checksum_sha1, "sha1", pool));
66 SVN_ERR(checksum_parse_kind("deadbeef",
67 svn_checksum_fnv1a_32, "fnv-1a", pool));
68 SVN_ERR(checksum_parse_kind("cafeaffe",
69 svn_checksum_fnv1a_32x4,
70 "modified fnv-1a", pool));
71
72 return SVN_NO_ERROR;
73 }
74
75 static svn_error_t *
test_checksum_empty(apr_pool_t * pool)76 test_checksum_empty(apr_pool_t *pool)
77 {
78 svn_checksum_kind_t kind;
79 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
80 {
81 svn_checksum_t *checksum;
82 char data = '\0';
83
84 checksum = svn_checksum_empty_checksum(kind, pool);
85 SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum));
86
87 SVN_ERR(svn_checksum(&checksum, kind, &data, 0, pool));
88 SVN_TEST_ASSERT(svn_checksum_is_empty_checksum(checksum));
89 }
90
91 return SVN_NO_ERROR;
92 }
93
94 /* Verify that "zero" checksums work properly for the given checksum KIND.
95 */
96 static svn_error_t *
zero_match_kind(svn_checksum_kind_t kind,apr_pool_t * pool)97 zero_match_kind(svn_checksum_kind_t kind, apr_pool_t *pool)
98 {
99 svn_checksum_t *zero;
100 svn_checksum_t *A;
101 svn_checksum_t *B;
102
103 zero = svn_checksum_create(kind, pool);
104 SVN_ERR(svn_checksum_clear(zero));
105 SVN_ERR(svn_checksum(&A, kind, "A", 1, pool));
106 SVN_ERR(svn_checksum(&B, kind, "B", 1, pool));
107
108 /* Different non-zero don't match. */
109 SVN_TEST_ASSERT(!svn_checksum_match(A, B));
110
111 /* Zero matches anything of the same kind. */
112 SVN_TEST_ASSERT(svn_checksum_match(A, zero));
113 SVN_TEST_ASSERT(svn_checksum_match(zero, B));
114
115 return SVN_NO_ERROR;
116 }
117
118 static svn_error_t *
zero_match(apr_pool_t * pool)119 zero_match(apr_pool_t *pool)
120 {
121 svn_checksum_kind_t kind;
122 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
123 SVN_ERR(zero_match_kind(kind, pool));
124
125 return SVN_NO_ERROR;
126 }
127
128 static svn_error_t *
zero_cross_match(apr_pool_t * pool)129 zero_cross_match(apr_pool_t *pool)
130 {
131 svn_checksum_kind_t i_kind;
132 svn_checksum_kind_t k_kind;
133
134 for (i_kind = svn_checksum_md5;
135 i_kind <= svn_checksum_fnv1a_32x4;
136 ++i_kind)
137 {
138 svn_checksum_t *i_zero;
139 svn_checksum_t *i_A;
140
141 i_zero = svn_checksum_create(i_kind, pool);
142 SVN_ERR(svn_checksum_clear(i_zero));
143 SVN_ERR(svn_checksum(&i_A, i_kind, "A", 1, pool));
144
145 for (k_kind = svn_checksum_md5;
146 k_kind <= svn_checksum_fnv1a_32x4;
147 ++k_kind)
148 {
149 svn_checksum_t *k_zero;
150 svn_checksum_t *k_A;
151 if (i_kind == k_kind)
152 continue;
153
154 k_zero = svn_checksum_create(k_kind, pool);
155 SVN_ERR(svn_checksum_clear(k_zero));
156 SVN_ERR(svn_checksum(&k_A, k_kind, "A", 1, pool));
157
158 /* Different non-zero don't match. */
159 SVN_TEST_ASSERT(!svn_checksum_match(i_A, k_A));
160
161 /* Zero doesn't match anything of a different kind... */
162 SVN_TEST_ASSERT(!svn_checksum_match(i_zero, k_A));
163 SVN_TEST_ASSERT(!svn_checksum_match(i_A, k_zero));
164
165 /* ...even another zero. */
166 SVN_TEST_ASSERT(!svn_checksum_match(i_zero, k_zero));
167 }
168 }
169
170 return SVN_NO_ERROR;
171 }
172
173 static svn_error_t *
zlib_expansion_test(const svn_test_opts_t * opts,apr_pool_t * pool)174 zlib_expansion_test(const svn_test_opts_t *opts,
175 apr_pool_t *pool)
176 {
177 const char *data_path;
178 const char *srcdir;
179 svn_stringbuf_t *deflated;
180 Byte dst_buffer[256 * 1024];
181 Byte *src_buffer;
182 uInt sz;
183
184 SVN_ERR(svn_test_get_srcdir(&srcdir, opts, pool));
185 data_path = svn_dirent_join(srcdir, "zlib.deflated", pool);
186
187 SVN_ERR(svn_stringbuf_from_file2(&deflated, data_path, pool));
188 src_buffer = (Byte*)deflated->data;
189
190 /* Try to decompress the same data with different blocksizes */
191 for (sz = 1; sz < 256; sz++)
192 {
193 z_stream stream;
194 uLong crc = crc32(0, Z_NULL, 0);
195 memset(&stream, 0, sizeof(stream));
196 inflateInit2(&stream, -15 /* DEFLATE_WINDOW_SIZE */);
197
198 stream.avail_in = sz;
199 stream.next_in = src_buffer;
200 stream.avail_out = sizeof(dst_buffer);
201 stream.next_out = dst_buffer;
202
203 do
204 {
205 int zr = inflate(&stream, Z_NO_FLUSH);
206
207 if (zr != Z_OK && zr != Z_STREAM_END)
208 {
209 return svn_error_createf(
210 SVN_ERR_TEST_FAILED, NULL,
211 "Failure decompressing with blocksize %u", sz);
212 }
213 crc = crc32(crc, dst_buffer, sizeof(dst_buffer) - stream.avail_out);
214 stream.avail_out = sizeof(dst_buffer);
215 stream.next_out = dst_buffer;
216 stream.avail_in += sz;
217 } while (stream.next_in + stream.avail_in < src_buffer + deflated->len);
218
219 stream.avail_in = (uInt) (deflated->len - stream.total_in);
220
221 {
222 int zr = inflate(&stream, Z_NO_FLUSH);
223
224 if (zr != Z_STREAM_END)
225 {
226 return svn_error_createf(
227 SVN_ERR_TEST_FAILED, NULL,
228 "Final flush failed with blocksize %u", sz);
229 }
230 crc = crc32(crc, dst_buffer, sizeof(dst_buffer) - stream.avail_out);
231
232 zr = inflateEnd(&stream);
233
234 if (zr != Z_OK)
235 {
236 return svn_error_createf(
237 SVN_ERR_TEST_FAILED, NULL,
238 "End of stream handling failed with blocksize %u",
239 sz);
240 }
241 }
242
243 if (stream.total_out != 242014 || crc != 0x8f03d934)
244 {
245 return svn_error_createf(
246 SVN_ERR_TEST_FAILED, NULL,
247 "Decompressed data doesn't match expected size or crc with "
248 "blocksize %u: Found crc32=0x%08lx, size=%lu.\n"
249 "Verify your ZLib installation, as this should never happen",
250 sz, crc, stream.total_out);
251 }
252 }
253
254 return SVN_NO_ERROR;
255 }
256
257 static svn_error_t *
test_serialization(apr_pool_t * pool)258 test_serialization(apr_pool_t *pool)
259 {
260 svn_checksum_kind_t kind;
261 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
262 {
263 const svn_checksum_t *parsed_checksum;
264 svn_checksum_t *checksum = svn_checksum_empty_checksum(kind, pool);
265 const char *serialized = svn_checksum_serialize(checksum, pool, pool);
266
267 SVN_ERR(svn_checksum_deserialize(&parsed_checksum, serialized, pool,
268 pool));
269
270 SVN_TEST_ASSERT(parsed_checksum->kind == kind);
271 SVN_TEST_ASSERT(svn_checksum_match(checksum, parsed_checksum));
272 }
273
274 return SVN_NO_ERROR;
275 }
276
277 static svn_error_t *
test_checksum_parse_all_zero(apr_pool_t * pool)278 test_checksum_parse_all_zero(apr_pool_t *pool)
279 {
280 svn_checksum_kind_t kind;
281 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
282 {
283 svn_checksum_t *checksum;
284 const char *hex;
285
286 checksum = svn_checksum_create(kind, pool);
287
288 hex = svn_checksum_to_cstring_display(checksum, pool);
289 SVN_ERR(svn_checksum_parse_hex(&checksum, kind, hex, pool));
290
291 /* All zeroes checksum is NULL by definition. See
292 svn_checksum_parse_hex().*/
293 SVN_TEST_ASSERT(checksum == NULL);
294 }
295
296 return SVN_NO_ERROR;
297 }
298
299 static svn_error_t *
test_checksummed_stream_read(apr_pool_t * pool)300 test_checksummed_stream_read(apr_pool_t *pool)
301 {
302 const svn_string_t *str = svn_string_create("abcde", pool);
303 svn_checksum_kind_t kind;
304
305 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
306 {
307 svn_stream_t *stream;
308 svn_checksum_t *expected_checksum;
309 svn_checksum_t *actual_checksum;
310 char buf[64];
311 apr_size_t len;
312
313 stream = svn_stream_from_string(str, pool);
314 stream = svn_stream_checksummed2(stream, &actual_checksum, NULL,
315 kind, TRUE, pool);
316 len = str->len;
317 SVN_ERR(svn_stream_read_full(stream, buf, &len));
318 SVN_TEST_INT_ASSERT((int) len, str->len);
319
320 SVN_ERR(svn_stream_close(stream));
321
322 SVN_ERR(svn_checksum(&expected_checksum, kind,
323 str->data, str->len, pool));
324 SVN_TEST_ASSERT(svn_checksum_match(expected_checksum, actual_checksum));
325 }
326
327 return SVN_NO_ERROR;
328 }
329
330 static svn_error_t *
test_checksummed_stream_reset(apr_pool_t * pool)331 test_checksummed_stream_reset(apr_pool_t *pool)
332 {
333 const svn_string_t *str = svn_string_create("abcde", pool);
334 svn_checksum_kind_t kind;
335
336 for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
337 {
338 svn_stream_t *stream;
339 svn_checksum_t *expected_checksum;
340 svn_checksum_t *actual_checksum;
341 char buf[64];
342 apr_size_t len;
343
344 stream = svn_stream_from_string(str, pool);
345 stream = svn_stream_checksummed2(stream, &actual_checksum, NULL,
346 kind, TRUE, pool);
347 len = str->len;
348 SVN_ERR(svn_stream_read_full(stream, buf, &len));
349 SVN_TEST_INT_ASSERT((int) len, str->len);
350
351 SVN_ERR(svn_stream_reset(stream));
352
353 len = str->len;
354 SVN_ERR(svn_stream_read_full(stream, buf, &len));
355 SVN_TEST_INT_ASSERT((int) len, str->len);
356
357 SVN_ERR(svn_stream_close(stream));
358
359 SVN_ERR(svn_checksum(&expected_checksum, kind,
360 str->data, str->len, pool));
361 SVN_TEST_ASSERT(svn_checksum_match(expected_checksum, actual_checksum));
362 }
363
364 return SVN_NO_ERROR;
365 }
366
367 /* An array of all test functions */
368
369 static int max_threads = 1;
370
371 static struct svn_test_descriptor_t test_funcs[] =
372 {
373 SVN_TEST_NULL,
374 SVN_TEST_PASS2(test_checksum_parse,
375 "checksum parse"),
376 SVN_TEST_PASS2(test_checksum_empty,
377 "checksum emptiness"),
378 SVN_TEST_PASS2(zero_match,
379 "zero checksum matching"),
380 SVN_TEST_OPTS_PASS(zlib_expansion_test,
381 "zlib expansion test (zlib regression)"),
382 SVN_TEST_PASS2(zero_cross_match,
383 "zero checksum cross-type matching"),
384 SVN_TEST_PASS2(test_serialization,
385 "checksum (de-)serialization"),
386 SVN_TEST_PASS2(test_checksum_parse_all_zero,
387 "checksum parse all zero"),
388 SVN_TEST_PASS2(test_checksummed_stream_read,
389 "read from checksummed stream"),
390 SVN_TEST_PASS2(test_checksummed_stream_reset,
391 "reset checksummed stream"),
392 SVN_TEST_NULL
393 };
394
395 SVN_TEST_MAIN
396