1 /*
2  * checksum.c:   checksum routines
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 #define APR_WANT_BYTEFUNC
25 
26 #include <ctype.h>
27 
28 #include <apr_md5.h>
29 #include <apr_sha1.h>
30 
31 #include "svn_checksum.h"
32 #include "svn_error.h"
33 #include "svn_ctype.h"
34 #include "svn_sorts.h"
35 
36 #include "checksum.h"
37 #include "fnv1a.h"
38 
39 #include "private/svn_subr_private.h"
40 
41 #include "svn_private_config.h"
42 
43 
44 
45 /* The MD5 digest for the empty string. */
46 static const unsigned char md5_empty_string_digest_array[] = {
47   0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
48   0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
49 };
50 
51 /* The SHA1 digest for the empty string. */
52 static const unsigned char sha1_empty_string_digest_array[] = {
53   0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
54   0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
55 };
56 
57 /* The FNV-1a digest for the empty string. */
58 static const unsigned char fnv1a_32_empty_string_digest_array[] = {
59   0x81, 0x1c, 0x9d, 0xc5
60 };
61 
62 /* The FNV-1a digest for the empty string. */
63 static const unsigned char fnv1a_32x4_empty_string_digest_array[] = {
64   0xcd, 0x6d, 0x9a, 0x85
65 };
66 
67 /* Digests for an empty string, indexed by checksum type */
68 static const unsigned char * empty_string_digests[] = {
69   md5_empty_string_digest_array,
70   sha1_empty_string_digest_array,
71   fnv1a_32_empty_string_digest_array,
72   fnv1a_32x4_empty_string_digest_array
73 };
74 
75 /* Digest sizes in bytes, indexed by checksum type */
76 static const apr_size_t digest_sizes[] = {
77   APR_MD5_DIGESTSIZE,
78   APR_SHA1_DIGESTSIZE,
79   sizeof(apr_uint32_t),
80   sizeof(apr_uint32_t)
81 };
82 
83 /* Checksum type prefixes used in serialized checksums. */
84 static const char *ckind_str[] = {
85   "$md5 $",
86   "$sha1$",
87   "$fnv1$",
88   "$fnvm$",
89   /* ### svn_checksum_deserialize() assumes all these have the same strlen() */
90 };
91 
92 /* Returns the digest size of it's argument. */
93 #define DIGESTSIZE(k) \
94   (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k])
95 
96 /* Largest supported digest size */
97 #define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE))
98 
99 const unsigned char *
svn__empty_string_digest(svn_checksum_kind_t kind)100 svn__empty_string_digest(svn_checksum_kind_t kind)
101 {
102   return empty_string_digests[kind];
103 }
104 
105 const char *
svn__digest_to_cstring_display(const unsigned char digest[],apr_size_t digest_size,apr_pool_t * pool)106 svn__digest_to_cstring_display(const unsigned char digest[],
107                                apr_size_t digest_size,
108                                apr_pool_t *pool)
109 {
110   static const char *hex = "0123456789abcdef";
111   char *str = apr_palloc(pool, (digest_size * 2) + 1);
112   apr_size_t i;
113 
114   for (i = 0; i < digest_size; i++)
115     {
116       str[i*2]   = hex[digest[i] >> 4];
117       str[i*2+1] = hex[digest[i] & 0x0f];
118     }
119   str[i*2] = '\0';
120 
121   return str;
122 }
123 
124 
125 const char *
svn__digest_to_cstring(const unsigned char digest[],apr_size_t digest_size,apr_pool_t * pool)126 svn__digest_to_cstring(const unsigned char digest[],
127                        apr_size_t digest_size,
128                        apr_pool_t *pool)
129 {
130   static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 };
131 
132   if (memcmp(digest, zeros_digest, digest_size) != 0)
133     return svn__digest_to_cstring_display(digest, digest_size, pool);
134   else
135     return NULL;
136 }
137 
138 
139 svn_boolean_t
svn__digests_match(const unsigned char d1[],const unsigned char d2[],apr_size_t digest_size)140 svn__digests_match(const unsigned char d1[],
141                    const unsigned char d2[],
142                    apr_size_t digest_size)
143 {
144   static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 };
145 
146   return ((memcmp(d1, d2, digest_size) == 0)
147           || (memcmp(d2, zeros, digest_size) == 0)
148           || (memcmp(d1, zeros, digest_size) == 0));
149 }
150 
151 /* Check to see if KIND is something we recognize.  If not, return
152  * SVN_ERR_BAD_CHECKSUM_KIND */
153 static svn_error_t *
validate_kind(svn_checksum_kind_t kind)154 validate_kind(svn_checksum_kind_t kind)
155 {
156   if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4)
157     return SVN_NO_ERROR;
158   else
159     return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
160 }
161 
162 /* Create a svn_checksum_t with everything but the contents of the
163    digest populated. */
164 static svn_checksum_t *
checksum_create_without_digest(svn_checksum_kind_t kind,apr_size_t digest_size,apr_pool_t * pool)165 checksum_create_without_digest(svn_checksum_kind_t kind,
166                                apr_size_t digest_size,
167                                apr_pool_t *pool)
168 {
169   /* Use apr_palloc() instead of apr_pcalloc() so that the digest
170    * contents are only set once by the caller. */
171   svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size);
172   checksum->digest = (unsigned char *)checksum + sizeof(*checksum);
173   checksum->kind = kind;
174   return checksum;
175 }
176 
177 /* Return a checksum object, allocated in POOL.  The checksum will be of
178  * type KIND and contain the given DIGEST.
179  */
180 static svn_checksum_t *
checksum_create(svn_checksum_kind_t kind,const unsigned char * digest,apr_pool_t * pool)181 checksum_create(svn_checksum_kind_t kind,
182                 const unsigned char *digest,
183                 apr_pool_t *pool)
184 {
185   apr_size_t digest_size = DIGESTSIZE(kind);
186   svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
187                                                             pool);
188   memcpy((unsigned char *)checksum->digest, digest, digest_size);
189   return checksum;
190 }
191 
192 svn_checksum_t *
svn_checksum_create(svn_checksum_kind_t kind,apr_pool_t * pool)193 svn_checksum_create(svn_checksum_kind_t kind,
194                     apr_pool_t *pool)
195 {
196   svn_checksum_t *checksum;
197   apr_size_t digest_size;
198 
199   switch (kind)
200     {
201       case svn_checksum_md5:
202       case svn_checksum_sha1:
203       case svn_checksum_fnv1a_32:
204       case svn_checksum_fnv1a_32x4:
205         digest_size = digest_sizes[kind];
206         break;
207 
208       default:
209         return NULL;
210     }
211 
212   checksum = checksum_create_without_digest(kind, digest_size, pool);
213   memset((unsigned char *) checksum->digest, 0, digest_size);
214   return checksum;
215 }
216 
217 svn_checksum_t *
svn_checksum__from_digest_md5(const unsigned char * digest,apr_pool_t * result_pool)218 svn_checksum__from_digest_md5(const unsigned char *digest,
219                               apr_pool_t *result_pool)
220 {
221   return checksum_create(svn_checksum_md5, digest, result_pool);
222 }
223 
224 svn_checksum_t *
svn_checksum__from_digest_sha1(const unsigned char * digest,apr_pool_t * result_pool)225 svn_checksum__from_digest_sha1(const unsigned char *digest,
226                                apr_pool_t *result_pool)
227 {
228   return checksum_create(svn_checksum_sha1, digest, result_pool);
229 }
230 
231 svn_checksum_t *
svn_checksum__from_digest_fnv1a_32(const unsigned char * digest,apr_pool_t * result_pool)232 svn_checksum__from_digest_fnv1a_32(const unsigned char *digest,
233                                    apr_pool_t *result_pool)
234 {
235   return checksum_create(svn_checksum_fnv1a_32, digest, result_pool);
236 }
237 
238 svn_checksum_t *
svn_checksum__from_digest_fnv1a_32x4(const unsigned char * digest,apr_pool_t * result_pool)239 svn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest,
240                                      apr_pool_t *result_pool)
241 {
242   return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool);
243 }
244 
245 svn_error_t *
svn_checksum_clear(svn_checksum_t * checksum)246 svn_checksum_clear(svn_checksum_t *checksum)
247 {
248   SVN_ERR(validate_kind(checksum->kind));
249 
250   memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind));
251   return SVN_NO_ERROR;
252 }
253 
254 svn_boolean_t
svn_checksum_match(const svn_checksum_t * checksum1,const svn_checksum_t * checksum2)255 svn_checksum_match(const svn_checksum_t *checksum1,
256                    const svn_checksum_t *checksum2)
257 {
258   if (checksum1 == NULL || checksum2 == NULL)
259     return TRUE;
260 
261   if (checksum1->kind != checksum2->kind)
262     return FALSE;
263 
264   switch (checksum1->kind)
265     {
266       case svn_checksum_md5:
267       case svn_checksum_sha1:
268       case svn_checksum_fnv1a_32:
269       case svn_checksum_fnv1a_32x4:
270         return svn__digests_match(checksum1->digest,
271                                   checksum2->digest,
272                                   digest_sizes[checksum1->kind]);
273 
274       default:
275         /* We really shouldn't get here, but if we do... */
276         return FALSE;
277     }
278 }
279 
280 const char *
svn_checksum_to_cstring_display(const svn_checksum_t * checksum,apr_pool_t * pool)281 svn_checksum_to_cstring_display(const svn_checksum_t *checksum,
282                                 apr_pool_t *pool)
283 {
284   switch (checksum->kind)
285     {
286       case svn_checksum_md5:
287       case svn_checksum_sha1:
288       case svn_checksum_fnv1a_32:
289       case svn_checksum_fnv1a_32x4:
290         return svn__digest_to_cstring_display(checksum->digest,
291                                               digest_sizes[checksum->kind],
292                                               pool);
293 
294       default:
295         /* We really shouldn't get here, but if we do... */
296         return NULL;
297     }
298 }
299 
300 const char *
svn_checksum_to_cstring(const svn_checksum_t * checksum,apr_pool_t * pool)301 svn_checksum_to_cstring(const svn_checksum_t *checksum,
302                         apr_pool_t *pool)
303 {
304   if (checksum == NULL)
305     return NULL;
306 
307   switch (checksum->kind)
308     {
309       case svn_checksum_md5:
310       case svn_checksum_sha1:
311       case svn_checksum_fnv1a_32:
312       case svn_checksum_fnv1a_32x4:
313         return svn__digest_to_cstring(checksum->digest,
314                                       digest_sizes[checksum->kind],
315                                       pool);
316 
317       default:
318         /* We really shouldn't get here, but if we do... */
319         return NULL;
320     }
321 }
322 
323 
324 const char *
svn_checksum_serialize(const svn_checksum_t * checksum,apr_pool_t * result_pool,apr_pool_t * scratch_pool)325 svn_checksum_serialize(const svn_checksum_t *checksum,
326                        apr_pool_t *result_pool,
327                        apr_pool_t *scratch_pool)
328 {
329   SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5
330                            || checksum->kind <= svn_checksum_fnv1a_32x4);
331   return apr_pstrcat(result_pool,
332                      ckind_str[checksum->kind],
333                      svn_checksum_to_cstring(checksum, scratch_pool),
334                      SVN_VA_NULL);
335 }
336 
337 
338 svn_error_t *
svn_checksum_deserialize(const svn_checksum_t ** checksum,const char * data,apr_pool_t * result_pool,apr_pool_t * scratch_pool)339 svn_checksum_deserialize(const svn_checksum_t **checksum,
340                          const char *data,
341                          apr_pool_t *result_pool,
342                          apr_pool_t *scratch_pool)
343 {
344   svn_checksum_kind_t kind;
345   svn_checksum_t *parsed_checksum;
346 
347   /* All prefixes have the same length. */
348   apr_size_t prefix_len = strlen(ckind_str[0]);
349 
350   /* "$md5 $...", "$sha1$..." or ... */
351   if (strlen(data) <= prefix_len)
352     return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL,
353                              _("Invalid prefix in checksum '%s'"),
354                              data);
355 
356   for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
357     if (strncmp(ckind_str[kind], data, prefix_len) == 0)
358       {
359         SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind,
360                                        data + prefix_len, result_pool));
361         *checksum = parsed_checksum;
362         return SVN_NO_ERROR;
363       }
364 
365   return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
366                            "Unknown checksum kind in '%s'", data);
367 }
368 
369 
370 svn_error_t *
svn_checksum_parse_hex(svn_checksum_t ** checksum,svn_checksum_kind_t kind,const char * hex,apr_pool_t * pool)371 svn_checksum_parse_hex(svn_checksum_t **checksum,
372                        svn_checksum_kind_t kind,
373                        const char *hex,
374                        apr_pool_t *pool)
375 {
376   apr_size_t i, len;
377   unsigned char is_nonzero = 0;
378   unsigned char *digest;
379   static const unsigned char xdigitval[256] =
380     {
381       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
382       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
383       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
384       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
385       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
386       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
387       0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,   /* 0-7 */
388       0x08,0x09,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,   /* 8-9 */
389       0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,   /* A-F */
390       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
391       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
392       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
393       0xFF,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0xFF,   /* a-f */
394       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
395       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
396       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
397       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
398       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
399       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
400       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
401       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
402       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
403       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
404       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
405       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
406       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
407       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
408       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
409       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
410       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
411       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
412       0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
413     };
414 
415   if (hex == NULL)
416     {
417       *checksum = NULL;
418       return SVN_NO_ERROR;
419     }
420 
421   SVN_ERR(validate_kind(kind));
422 
423   *checksum = svn_checksum_create(kind, pool);
424   digest = (unsigned char *)(*checksum)->digest;
425   len = DIGESTSIZE(kind);
426 
427   for (i = 0; i < len; i++)
428     {
429       unsigned char x1 = xdigitval[(unsigned char)hex[i * 2]];
430       unsigned char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]];
431       if (x1 == 0xFF || x2 == 0xFF)
432         return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL);
433 
434       digest[i] = (x1 << 4) | x2;
435       is_nonzero |= digest[i];
436     }
437 
438   if (!is_nonzero)
439     *checksum = NULL;
440 
441   return SVN_NO_ERROR;
442 }
443 
444 svn_checksum_t *
svn_checksum_dup(const svn_checksum_t * checksum,apr_pool_t * pool)445 svn_checksum_dup(const svn_checksum_t *checksum,
446                  apr_pool_t *pool)
447 {
448   /* The duplicate of a NULL checksum is a NULL... */
449   if (checksum == NULL)
450     return NULL;
451 
452   /* Without this check on valid checksum kind a NULL svn_checksum_t
453    * pointer is returned which could cause a core dump at an
454    * indeterminate time in the future because callers are not
455    * expecting a NULL pointer.  This commit forces an early abort() so
456    * it's easier to track down where the issue arose. */
457   switch (checksum->kind)
458     {
459       case svn_checksum_md5:
460       case svn_checksum_sha1:
461       case svn_checksum_fnv1a_32:
462       case svn_checksum_fnv1a_32x4:
463         return checksum_create(checksum->kind, checksum->digest, pool);
464 
465       default:
466         SVN_ERR_MALFUNCTION_NO_RETURN();
467         break;
468     }
469 }
470 
471 svn_error_t *
svn_checksum(svn_checksum_t ** checksum,svn_checksum_kind_t kind,const void * data,apr_size_t len,apr_pool_t * pool)472 svn_checksum(svn_checksum_t **checksum,
473              svn_checksum_kind_t kind,
474              const void *data,
475              apr_size_t len,
476              apr_pool_t *pool)
477 {
478   apr_sha1_ctx_t sha1_ctx;
479 
480   SVN_ERR(validate_kind(kind));
481   *checksum = svn_checksum_create(kind, pool);
482 
483   switch (kind)
484     {
485       case svn_checksum_md5:
486         apr_md5((unsigned char *)(*checksum)->digest, data, len);
487         break;
488 
489       case svn_checksum_sha1:
490         apr_sha1_init(&sha1_ctx);
491         apr_sha1_update(&sha1_ctx, data, (unsigned int)len);
492         apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
493         break;
494 
495       case svn_checksum_fnv1a_32:
496         *(apr_uint32_t *)(*checksum)->digest
497           = htonl(svn__fnv1a_32(data, len));
498         break;
499 
500       case svn_checksum_fnv1a_32x4:
501         *(apr_uint32_t *)(*checksum)->digest
502           = htonl(svn__fnv1a_32x4(data, len));
503         break;
504 
505       default:
506         /* We really shouldn't get here, but if we do... */
507         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
508     }
509 
510   return SVN_NO_ERROR;
511 }
512 
513 
514 svn_checksum_t *
svn_checksum_empty_checksum(svn_checksum_kind_t kind,apr_pool_t * pool)515 svn_checksum_empty_checksum(svn_checksum_kind_t kind,
516                             apr_pool_t *pool)
517 {
518   switch (kind)
519     {
520       case svn_checksum_md5:
521       case svn_checksum_sha1:
522       case svn_checksum_fnv1a_32:
523       case svn_checksum_fnv1a_32x4:
524         return checksum_create(kind, empty_string_digests[kind], pool);
525 
526       default:
527         /* We really shouldn't get here, but if we do... */
528         SVN_ERR_MALFUNCTION_NO_RETURN();
529     }
530 }
531 
532 struct svn_checksum_ctx_t
533 {
534   void *apr_ctx;
535   svn_checksum_kind_t kind;
536 };
537 
538 svn_checksum_ctx_t *
svn_checksum_ctx_create(svn_checksum_kind_t kind,apr_pool_t * pool)539 svn_checksum_ctx_create(svn_checksum_kind_t kind,
540                         apr_pool_t *pool)
541 {
542   svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx));
543 
544   ctx->kind = kind;
545   switch (kind)
546     {
547       case svn_checksum_md5:
548         ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t));
549         apr_md5_init(ctx->apr_ctx);
550         break;
551 
552       case svn_checksum_sha1:
553         ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t));
554         apr_sha1_init(ctx->apr_ctx);
555         break;
556 
557       case svn_checksum_fnv1a_32:
558         ctx->apr_ctx = svn_fnv1a_32__context_create(pool);
559         break;
560 
561       case svn_checksum_fnv1a_32x4:
562         ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool);
563         break;
564 
565       default:
566         SVN_ERR_MALFUNCTION_NO_RETURN();
567     }
568 
569   return ctx;
570 }
571 
572 svn_error_t *
svn_checksum_ctx_reset(svn_checksum_ctx_t * ctx)573 svn_checksum_ctx_reset(svn_checksum_ctx_t *ctx)
574 {
575   switch (ctx->kind)
576     {
577       case svn_checksum_md5:
578         memset(ctx->apr_ctx, 0, sizeof(apr_md5_ctx_t));
579         apr_md5_init(ctx->apr_ctx);
580         break;
581 
582       case svn_checksum_sha1:
583         memset(ctx->apr_ctx, 0, sizeof(apr_sha1_ctx_t));
584         apr_sha1_init(ctx->apr_ctx);
585         break;
586 
587       case svn_checksum_fnv1a_32:
588         svn_fnv1a_32__context_reset(ctx->apr_ctx);
589         break;
590 
591       case svn_checksum_fnv1a_32x4:
592         svn_fnv1a_32x4__context_reset(ctx->apr_ctx);
593         break;
594 
595       default:
596         SVN_ERR_MALFUNCTION();
597     }
598 
599   return SVN_NO_ERROR;
600 }
601 
602 svn_error_t *
svn_checksum_update(svn_checksum_ctx_t * ctx,const void * data,apr_size_t len)603 svn_checksum_update(svn_checksum_ctx_t *ctx,
604                     const void *data,
605                     apr_size_t len)
606 {
607   switch (ctx->kind)
608     {
609       case svn_checksum_md5:
610         apr_md5_update(ctx->apr_ctx, data, len);
611         break;
612 
613       case svn_checksum_sha1:
614         apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
615         break;
616 
617       case svn_checksum_fnv1a_32:
618         svn_fnv1a_32__update(ctx->apr_ctx, data, len);
619         break;
620 
621       case svn_checksum_fnv1a_32x4:
622         svn_fnv1a_32x4__update(ctx->apr_ctx, data, len);
623         break;
624 
625       default:
626         /* We really shouldn't get here, but if we do... */
627         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
628     }
629 
630   return SVN_NO_ERROR;
631 }
632 
633 svn_error_t *
svn_checksum_final(svn_checksum_t ** checksum,const svn_checksum_ctx_t * ctx,apr_pool_t * pool)634 svn_checksum_final(svn_checksum_t **checksum,
635                    const svn_checksum_ctx_t *ctx,
636                    apr_pool_t *pool)
637 {
638   *checksum = svn_checksum_create(ctx->kind, pool);
639 
640   switch (ctx->kind)
641     {
642       case svn_checksum_md5:
643         apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
644         break;
645 
646       case svn_checksum_sha1:
647         apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
648         break;
649 
650       case svn_checksum_fnv1a_32:
651         *(apr_uint32_t *)(*checksum)->digest
652           = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx));
653         break;
654 
655       case svn_checksum_fnv1a_32x4:
656         *(apr_uint32_t *)(*checksum)->digest
657           = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx));
658         break;
659 
660       default:
661         /* We really shouldn't get here, but if we do... */
662         return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
663     }
664 
665   return SVN_NO_ERROR;
666 }
667 
668 apr_size_t
svn_checksum_size(const svn_checksum_t * checksum)669 svn_checksum_size(const svn_checksum_t *checksum)
670 {
671   return DIGESTSIZE(checksum->kind);
672 }
673 
674 svn_error_t *
svn_checksum_mismatch_err(const svn_checksum_t * expected,const svn_checksum_t * actual,apr_pool_t * scratch_pool,const char * fmt,...)675 svn_checksum_mismatch_err(const svn_checksum_t *expected,
676                           const svn_checksum_t *actual,
677                           apr_pool_t *scratch_pool,
678                           const char *fmt,
679                           ...)
680 {
681   va_list ap;
682   const char *desc;
683 
684   va_start(ap, fmt);
685   desc = apr_pvsprintf(scratch_pool, fmt, ap);
686   va_end(ap);
687 
688   return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
689                            _("%s:\n"
690                              "   expected:  %s\n"
691                              "     actual:  %s\n"),
692                 desc,
693                 svn_checksum_to_cstring_display(expected, scratch_pool),
694                 svn_checksum_to_cstring_display(actual, scratch_pool));
695 }
696 
697 svn_boolean_t
svn_checksum_is_empty_checksum(svn_checksum_t * checksum)698 svn_checksum_is_empty_checksum(svn_checksum_t *checksum)
699 {
700   /* By definition, the NULL checksum matches all others, including the
701      empty one. */
702   if (!checksum)
703     return TRUE;
704 
705   switch (checksum->kind)
706     {
707       case svn_checksum_md5:
708       case svn_checksum_sha1:
709       case svn_checksum_fnv1a_32:
710       case svn_checksum_fnv1a_32x4:
711         return svn__digests_match(checksum->digest,
712                                   svn__empty_string_digest(checksum->kind),
713                                   digest_sizes[checksum->kind]);
714 
715       default:
716         /* We really shouldn't get here, but if we do... */
717         SVN_ERR_MALFUNCTION_NO_RETURN();
718     }
719 }
720 
721 /* Checksum calculating stream wrappers.
722  */
723 
724 /* Baton used by write_handler and close_handler to calculate the checksum
725  * and return the result to the stream creator.  It accommodates the data
726  * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as
727  * svn_checksum__wrap_write_stream.
728  */
729 typedef struct stream_baton_t
730 {
731   /* Stream we are wrapping. Forward write() and close() operations to it. */
732   svn_stream_t *inner_stream;
733 
734   /* Build the checksum data in here. */
735   svn_checksum_ctx_t *context;
736 
737   /* Write the final checksum here. May be NULL. */
738   svn_checksum_t **checksum;
739 
740   /* Copy the digest of the final checksum. May be NULL. */
741   unsigned char *digest;
742 
743   /* Allocate the resulting checksum here. */
744   apr_pool_t *pool;
745 } stream_baton_t;
746 
747 /* Implement svn_write_fn_t.
748  * Update checksum and pass data on to inner stream.
749  */
750 static svn_error_t *
write_handler(void * baton,const char * data,apr_size_t * len)751 write_handler(void *baton,
752               const char *data,
753               apr_size_t *len)
754 {
755   stream_baton_t *b = baton;
756 
757   SVN_ERR(svn_checksum_update(b->context, data, *len));
758   SVN_ERR(svn_stream_write(b->inner_stream, data, len));
759 
760   return SVN_NO_ERROR;
761 }
762 
763 /* Implement svn_close_fn_t.
764  * Finalize checksum calculation and write results. Close inner stream.
765  */
766 static svn_error_t *
close_handler(void * baton)767 close_handler(void *baton)
768 {
769   stream_baton_t *b = baton;
770   svn_checksum_t *local_checksum;
771 
772   /* Ensure we can always write to *B->CHECKSUM. */
773   if (!b->checksum)
774     b->checksum = &local_checksum;
775 
776   /* Get the final checksum. */
777   SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool));
778 
779   /* Extract digest, if wanted. */
780   if (b->digest)
781     {
782       apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind);
783       memcpy(b->digest, (*b->checksum)->digest, digest_size);
784     }
785 
786   /* Done here.  Now, close the underlying stream as well. */
787   return svn_error_trace(svn_stream_close(b->inner_stream));
788 }
789 
790 /* Common constructor function for svn_checksum__wrap_write_stream and
791  * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their
792  * respecting parameters.
793  *
794  * In the current usage, either CHECKSUM or DIGEST will be NULL but this
795  * function does not enforce any such restriction.  Also, the caller must
796  * make sure that DIGEST refers to a buffer of sufficient length.
797  */
798 static svn_stream_t *
wrap_write_stream(svn_checksum_t ** checksum,unsigned char * digest,svn_stream_t * inner_stream,svn_checksum_kind_t kind,apr_pool_t * pool)799 wrap_write_stream(svn_checksum_t **checksum,
800                   unsigned char *digest,
801                   svn_stream_t *inner_stream,
802                   svn_checksum_kind_t kind,
803                   apr_pool_t *pool)
804 {
805   svn_stream_t *outer_stream;
806 
807   stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
808   baton->inner_stream = inner_stream;
809   baton->context = svn_checksum_ctx_create(kind, pool);
810   baton->checksum = checksum;
811   baton->digest = digest;
812   baton->pool = pool;
813 
814   outer_stream = svn_stream_create(baton, pool);
815   svn_stream_set_write(outer_stream, write_handler);
816   svn_stream_set_close(outer_stream, close_handler);
817 
818   return outer_stream;
819 }
820 
821 svn_stream_t *
svn_checksum__wrap_write_stream(svn_checksum_t ** checksum,svn_stream_t * inner_stream,svn_checksum_kind_t kind,apr_pool_t * pool)822 svn_checksum__wrap_write_stream(svn_checksum_t **checksum,
823                                 svn_stream_t *inner_stream,
824                                 svn_checksum_kind_t kind,
825                                 apr_pool_t *pool)
826 {
827   return wrap_write_stream(checksum, NULL, inner_stream, kind, pool);
828 }
829 
830 /* Implement svn_close_fn_t.
831  * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead
832  * of a big endian 4 byte sequence.  This simply wraps close_handler adding
833  * the digest conversion.
834  */
835 static svn_error_t *
close_handler_fnv1a_32x4(void * baton)836 close_handler_fnv1a_32x4(void *baton)
837 {
838   stream_baton_t *b = baton;
839   SVN_ERR(close_handler(baton));
840 
841   *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest);
842   return SVN_NO_ERROR;
843 }
844 
845 svn_stream_t *
svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t * digest,svn_stream_t * inner_stream,apr_pool_t * pool)846 svn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest,
847                                            svn_stream_t *inner_stream,
848                                            apr_pool_t *pool)
849 {
850   svn_stream_t *result
851     = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream,
852                         svn_checksum_fnv1a_32x4, pool);
853   svn_stream_set_close(result, close_handler_fnv1a_32x4);
854 
855   return result;
856 }
857