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