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