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
25#include <ctype.h>
26
27#include <apr_md5.h>
28#include <apr_sha1.h>
29
30#include "svn_checksum.h"
31#include "svn_error.h"
32#include "svn_ctype.h"
33
34#include "sha1.h"
35#include "md5.h"
36
37#include "private/svn_subr_private.h"
38
39#include "svn_private_config.h"
40
41
42
43/* Returns the digest size of it's argument. */
44#define DIGESTSIZE(k) ((k) == svn_checksum_md5 ? APR_MD5_DIGESTSIZE : \
45                       (k) == svn_checksum_sha1 ? APR_SHA1_DIGESTSIZE : 0)
46
47
48/* Check to see if KIND is something we recognize.  If not, return
49 * SVN_ERR_BAD_CHECKSUM_KIND */
50static svn_error_t *
51validate_kind(svn_checksum_kind_t kind)
52{
53  if (kind == svn_checksum_md5 || kind == svn_checksum_sha1)
54    return SVN_NO_ERROR;
55  else
56    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
57}
58
59/* Create a svn_checksum_t with everything but the contents of the
60   digest populated. */
61static svn_checksum_t *
62checksum_create_without_digest(svn_checksum_kind_t kind,
63                               apr_size_t digest_size,
64                               apr_pool_t *pool)
65{
66  /* Use apr_palloc() instead of apr_pcalloc() so that the digest
67   * contents are only set once by the caller. */
68  svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size);
69  checksum->digest = (unsigned char *)checksum + sizeof(*checksum);
70  checksum->kind = kind;
71  return checksum;
72}
73
74static svn_checksum_t *
75checksum_create(svn_checksum_kind_t kind,
76                apr_size_t digest_size,
77                const unsigned char *digest,
78                apr_pool_t *pool)
79{
80  svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
81                                                            pool);
82  memcpy((unsigned char *)checksum->digest, digest, digest_size);
83  return checksum;
84}
85
86svn_checksum_t *
87svn_checksum_create(svn_checksum_kind_t kind,
88                    apr_pool_t *pool)
89{
90  svn_checksum_t *checksum;
91  apr_size_t digest_size;
92
93  switch (kind)
94    {
95      case svn_checksum_md5:
96        digest_size = APR_MD5_DIGESTSIZE;
97        break;
98      case svn_checksum_sha1:
99        digest_size = APR_SHA1_DIGESTSIZE;
100        break;
101      default:
102        return NULL;
103    }
104
105  checksum = checksum_create_without_digest(kind, digest_size, pool);
106  memset((unsigned char *) checksum->digest, 0, digest_size);
107  return checksum;
108}
109
110svn_checksum_t *
111svn_checksum__from_digest_md5(const unsigned char *digest,
112                              apr_pool_t *result_pool)
113{
114  return checksum_create(svn_checksum_md5, APR_MD5_DIGESTSIZE, digest,
115                         result_pool);
116}
117
118svn_checksum_t *
119svn_checksum__from_digest_sha1(const unsigned char *digest,
120                               apr_pool_t *result_pool)
121{
122  return checksum_create(svn_checksum_sha1, APR_SHA1_DIGESTSIZE, digest,
123                         result_pool);
124}
125
126svn_error_t *
127svn_checksum_clear(svn_checksum_t *checksum)
128{
129  SVN_ERR(validate_kind(checksum->kind));
130
131  memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind));
132  return SVN_NO_ERROR;
133}
134
135svn_boolean_t
136svn_checksum_match(const svn_checksum_t *checksum1,
137                   const svn_checksum_t *checksum2)
138{
139  if (checksum1 == NULL || checksum2 == NULL)
140    return TRUE;
141
142  if (checksum1->kind != checksum2->kind)
143    return FALSE;
144
145  switch (checksum1->kind)
146    {
147      case svn_checksum_md5:
148        return svn_md5__digests_match(checksum1->digest, checksum2->digest);
149      case svn_checksum_sha1:
150        return svn_sha1__digests_match(checksum1->digest, checksum2->digest);
151      default:
152        /* We really shouldn't get here, but if we do... */
153        return FALSE;
154    }
155}
156
157const char *
158svn_checksum_to_cstring_display(const svn_checksum_t *checksum,
159                                apr_pool_t *pool)
160{
161  switch (checksum->kind)
162    {
163      case svn_checksum_md5:
164        return svn_md5__digest_to_cstring_display(checksum->digest, pool);
165      case svn_checksum_sha1:
166        return svn_sha1__digest_to_cstring_display(checksum->digest, pool);
167      default:
168        /* We really shouldn't get here, but if we do... */
169        return NULL;
170    }
171}
172
173const char *
174svn_checksum_to_cstring(const svn_checksum_t *checksum,
175                        apr_pool_t *pool)
176{
177  if (checksum == NULL)
178    return NULL;
179
180  switch (checksum->kind)
181    {
182      case svn_checksum_md5:
183        return svn_md5__digest_to_cstring(checksum->digest, pool);
184      case svn_checksum_sha1:
185        return svn_sha1__digest_to_cstring(checksum->digest, pool);
186      default:
187        /* We really shouldn't get here, but if we do... */
188        return NULL;
189    }
190}
191
192
193const char *
194svn_checksum_serialize(const svn_checksum_t *checksum,
195                       apr_pool_t *result_pool,
196                       apr_pool_t *scratch_pool)
197{
198  const char *ckind_str;
199
200  SVN_ERR_ASSERT_NO_RETURN(checksum->kind == svn_checksum_md5
201                           || checksum->kind == svn_checksum_sha1);
202  ckind_str = (checksum->kind == svn_checksum_md5 ? "$md5 $" : "$sha1$");
203  return apr_pstrcat(result_pool,
204                     ckind_str,
205                     svn_checksum_to_cstring(checksum, scratch_pool),
206                     (char *)NULL);
207}
208
209
210svn_error_t *
211svn_checksum_deserialize(const svn_checksum_t **checksum,
212                         const char *data,
213                         apr_pool_t *result_pool,
214                         apr_pool_t *scratch_pool)
215{
216  svn_checksum_kind_t ckind;
217  svn_checksum_t *parsed_checksum;
218
219  /* "$md5 $..." or "$sha1$..." */
220  SVN_ERR_ASSERT(strlen(data) > 6);
221
222  ckind = (data[1] == 'm' ? svn_checksum_md5 : svn_checksum_sha1);
223  SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, ckind,
224                                 data + 6, result_pool));
225  *checksum = parsed_checksum;
226
227  return SVN_NO_ERROR;
228}
229
230
231svn_error_t *
232svn_checksum_parse_hex(svn_checksum_t **checksum,
233                       svn_checksum_kind_t kind,
234                       const char *hex,
235                       apr_pool_t *pool)
236{
237  int i, len;
238  char is_nonzero = '\0';
239  char *digest;
240  static const char xdigitval[256] =
241    {
242      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
243      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
244      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
245       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,   /* 0-9 */
246      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* A-F */
247      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
248      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* a-f */
249      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
250      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
251      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
252      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
253      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
254      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
255      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
256      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
257      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
258    };
259
260  if (hex == NULL)
261    {
262      *checksum = NULL;
263      return SVN_NO_ERROR;
264    }
265
266  SVN_ERR(validate_kind(kind));
267
268  *checksum = svn_checksum_create(kind, pool);
269  digest = (char *)(*checksum)->digest;
270  len = DIGESTSIZE(kind);
271
272  for (i = 0; i < len; i++)
273    {
274      char x1 = xdigitval[(unsigned char)hex[i * 2]];
275      char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]];
276      if (x1 == (char)-1 || x2 == (char)-1)
277        return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL);
278
279      digest[i] = (char)((x1 << 4) | x2);
280      is_nonzero |= (char)((x1 << 4) | x2);
281    }
282
283  if (!is_nonzero)
284    *checksum = NULL;
285
286  return SVN_NO_ERROR;
287}
288
289svn_checksum_t *
290svn_checksum_dup(const svn_checksum_t *checksum,
291                 apr_pool_t *pool)
292{
293  /* The duplicate of a NULL checksum is a NULL... */
294  if (checksum == NULL)
295    return NULL;
296
297  /* Without this check on valid checksum kind a NULL svn_checksum_t
298   * pointer is returned which could cause a core dump at an
299   * indeterminate time in the future because callers are not
300   * expecting a NULL pointer.  This commit forces an early abort() so
301   * it's easier to track down where the issue arose. */
302  switch (checksum->kind)
303    {
304      case svn_checksum_md5:
305        return svn_checksum__from_digest_md5(checksum->digest, pool);
306        break;
307      case svn_checksum_sha1:
308        return svn_checksum__from_digest_sha1(checksum->digest, pool);
309        break;
310      default:
311        SVN_ERR_MALFUNCTION_NO_RETURN();
312        break;
313    }
314}
315
316svn_error_t *
317svn_checksum(svn_checksum_t **checksum,
318             svn_checksum_kind_t kind,
319             const void *data,
320             apr_size_t len,
321             apr_pool_t *pool)
322{
323  apr_sha1_ctx_t sha1_ctx;
324
325  SVN_ERR(validate_kind(kind));
326  *checksum = svn_checksum_create(kind, pool);
327
328  switch (kind)
329    {
330      case svn_checksum_md5:
331        apr_md5((unsigned char *)(*checksum)->digest, data, len);
332        break;
333
334      case svn_checksum_sha1:
335        apr_sha1_init(&sha1_ctx);
336        apr_sha1_update(&sha1_ctx, data, (unsigned int)len);
337        apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
338        break;
339
340      default:
341        /* We really shouldn't get here, but if we do... */
342        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
343    }
344
345  return SVN_NO_ERROR;
346}
347
348
349svn_checksum_t *
350svn_checksum_empty_checksum(svn_checksum_kind_t kind,
351                            apr_pool_t *pool)
352{
353  switch (kind)
354    {
355      case svn_checksum_md5:
356        return svn_checksum__from_digest_md5(svn_md5__empty_string_digest(),
357                                             pool);
358
359      case svn_checksum_sha1:
360        return svn_checksum__from_digest_sha1(svn_sha1__empty_string_digest(),
361                                              pool);
362
363      default:
364        /* We really shouldn't get here, but if we do... */
365        SVN_ERR_MALFUNCTION_NO_RETURN();
366    }
367}
368
369struct svn_checksum_ctx_t
370{
371  void *apr_ctx;
372  svn_checksum_kind_t kind;
373};
374
375svn_checksum_ctx_t *
376svn_checksum_ctx_create(svn_checksum_kind_t kind,
377                        apr_pool_t *pool)
378{
379  svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx));
380
381  ctx->kind = kind;
382  switch (kind)
383    {
384      case svn_checksum_md5:
385        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t));
386        apr_md5_init(ctx->apr_ctx);
387        break;
388
389      case svn_checksum_sha1:
390        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t));
391        apr_sha1_init(ctx->apr_ctx);
392        break;
393
394      default:
395        SVN_ERR_MALFUNCTION_NO_RETURN();
396    }
397
398  return ctx;
399}
400
401svn_error_t *
402svn_checksum_update(svn_checksum_ctx_t *ctx,
403                    const void *data,
404                    apr_size_t len)
405{
406  switch (ctx->kind)
407    {
408      case svn_checksum_md5:
409        apr_md5_update(ctx->apr_ctx, data, len);
410        break;
411
412      case svn_checksum_sha1:
413        apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
414        break;
415
416      default:
417        /* We really shouldn't get here, but if we do... */
418        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
419    }
420
421  return SVN_NO_ERROR;
422}
423
424svn_error_t *
425svn_checksum_final(svn_checksum_t **checksum,
426                   const svn_checksum_ctx_t *ctx,
427                   apr_pool_t *pool)
428{
429  *checksum = svn_checksum_create(ctx->kind, pool);
430
431  switch (ctx->kind)
432    {
433      case svn_checksum_md5:
434        apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
435        break;
436
437      case svn_checksum_sha1:
438        apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
439        break;
440
441      default:
442        /* We really shouldn't get here, but if we do... */
443        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
444    }
445
446  return SVN_NO_ERROR;
447}
448
449apr_size_t
450svn_checksum_size(const svn_checksum_t *checksum)
451{
452  return DIGESTSIZE(checksum->kind);
453}
454
455svn_error_t *
456svn_checksum_mismatch_err(const svn_checksum_t *expected,
457                          const svn_checksum_t *actual,
458                          apr_pool_t *scratch_pool,
459                          const char *fmt,
460                          ...)
461{
462  va_list ap;
463  const char *desc;
464
465  va_start(ap, fmt);
466  desc = apr_pvsprintf(scratch_pool, fmt, ap);
467  va_end(ap);
468
469  return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
470                           _("%s:\n"
471                             "   expected:  %s\n"
472                             "     actual:  %s\n"),
473                desc,
474                svn_checksum_to_cstring_display(expected, scratch_pool),
475                svn_checksum_to_cstring_display(actual, scratch_pool));
476}
477
478svn_boolean_t
479svn_checksum_is_empty_checksum(svn_checksum_t *checksum)
480{
481  /* By definition, the NULL checksum matches all others, including the
482     empty one. */
483  if (!checksum)
484    return TRUE;
485
486  switch (checksum->kind)
487    {
488      case svn_checksum_md5:
489        return svn_md5__digests_match(checksum->digest,
490                                      svn_md5__empty_string_digest());
491
492      case svn_checksum_sha1:
493        return svn_sha1__digests_match(checksum->digest,
494                                       svn_sha1__empty_string_digest());
495
496      default:
497        /* We really shouldn't get here, but if we do... */
498        SVN_ERR_MALFUNCTION_NO_RETURN();
499    }
500}
501