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