1251881Speter/*
2251881Speter * checksum.c:   checksum routines
3251881Speter *
4251881Speter * ====================================================================
5251881Speter *    Licensed to the Apache Software Foundation (ASF) under one
6251881Speter *    or more contributor license agreements.  See the NOTICE file
7251881Speter *    distributed with this work for additional information
8251881Speter *    regarding copyright ownership.  The ASF licenses this file
9251881Speter *    to you under the Apache License, Version 2.0 (the
10251881Speter *    "License"); you may not use this file except in compliance
11251881Speter *    with the License.  You may obtain a copy of the License at
12251881Speter *
13251881Speter *      http://www.apache.org/licenses/LICENSE-2.0
14251881Speter *
15251881Speter *    Unless required by applicable law or agreed to in writing,
16251881Speter *    software distributed under the License is distributed on an
17251881Speter *    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18251881Speter *    KIND, either express or implied.  See the License for the
19251881Speter *    specific language governing permissions and limitations
20251881Speter *    under the License.
21251881Speter * ====================================================================
22251881Speter */
23251881Speter
24251881Speter
25251881Speter#include <ctype.h>
26251881Speter
27251881Speter#include <apr_md5.h>
28251881Speter#include <apr_sha1.h>
29251881Speter
30251881Speter#include "svn_checksum.h"
31251881Speter#include "svn_error.h"
32251881Speter#include "svn_ctype.h"
33251881Speter
34251881Speter#include "sha1.h"
35251881Speter#include "md5.h"
36251881Speter
37251881Speter#include "private/svn_subr_private.h"
38251881Speter
39251881Speter#include "svn_private_config.h"
40251881Speter
41251881Speter
42251881Speter
43251881Speter/* Returns the digest size of it's argument. */
44251881Speter#define DIGESTSIZE(k) ((k) == svn_checksum_md5 ? APR_MD5_DIGESTSIZE : \
45251881Speter                       (k) == svn_checksum_sha1 ? APR_SHA1_DIGESTSIZE : 0)
46251881Speter
47251881Speter
48251881Speter/* Check to see if KIND is something we recognize.  If not, return
49251881Speter * SVN_ERR_BAD_CHECKSUM_KIND */
50251881Speterstatic svn_error_t *
51251881Spetervalidate_kind(svn_checksum_kind_t kind)
52251881Speter{
53251881Speter  if (kind == svn_checksum_md5 || kind == svn_checksum_sha1)
54251881Speter    return SVN_NO_ERROR;
55251881Speter  else
56251881Speter    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
57251881Speter}
58251881Speter
59251881Speter/* Create a svn_checksum_t with everything but the contents of the
60251881Speter   digest populated. */
61251881Speterstatic svn_checksum_t *
62251881Speterchecksum_create_without_digest(svn_checksum_kind_t kind,
63251881Speter                               apr_size_t digest_size,
64251881Speter                               apr_pool_t *pool)
65251881Speter{
66251881Speter  /* Use apr_palloc() instead of apr_pcalloc() so that the digest
67251881Speter   * contents are only set once by the caller. */
68251881Speter  svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size);
69251881Speter  checksum->digest = (unsigned char *)checksum + sizeof(*checksum);
70251881Speter  checksum->kind = kind;
71251881Speter  return checksum;
72251881Speter}
73251881Speter
74251881Speterstatic svn_checksum_t *
75251881Speterchecksum_create(svn_checksum_kind_t kind,
76251881Speter                apr_size_t digest_size,
77251881Speter                const unsigned char *digest,
78251881Speter                apr_pool_t *pool)
79251881Speter{
80251881Speter  svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
81251881Speter                                                            pool);
82251881Speter  memcpy((unsigned char *)checksum->digest, digest, digest_size);
83251881Speter  return checksum;
84251881Speter}
85251881Speter
86251881Spetersvn_checksum_t *
87251881Spetersvn_checksum_create(svn_checksum_kind_t kind,
88251881Speter                    apr_pool_t *pool)
89251881Speter{
90251881Speter  svn_checksum_t *checksum;
91251881Speter  apr_size_t digest_size;
92251881Speter
93251881Speter  switch (kind)
94251881Speter    {
95251881Speter      case svn_checksum_md5:
96251881Speter        digest_size = APR_MD5_DIGESTSIZE;
97251881Speter        break;
98251881Speter      case svn_checksum_sha1:
99251881Speter        digest_size = APR_SHA1_DIGESTSIZE;
100251881Speter        break;
101251881Speter      default:
102251881Speter        return NULL;
103251881Speter    }
104251881Speter
105251881Speter  checksum = checksum_create_without_digest(kind, digest_size, pool);
106251881Speter  memset((unsigned char *) checksum->digest, 0, digest_size);
107251881Speter  return checksum;
108251881Speter}
109251881Speter
110251881Spetersvn_checksum_t *
111251881Spetersvn_checksum__from_digest_md5(const unsigned char *digest,
112251881Speter                              apr_pool_t *result_pool)
113251881Speter{
114251881Speter  return checksum_create(svn_checksum_md5, APR_MD5_DIGESTSIZE, digest,
115251881Speter                         result_pool);
116251881Speter}
117251881Speter
118251881Spetersvn_checksum_t *
119251881Spetersvn_checksum__from_digest_sha1(const unsigned char *digest,
120251881Speter                               apr_pool_t *result_pool)
121251881Speter{
122251881Speter  return checksum_create(svn_checksum_sha1, APR_SHA1_DIGESTSIZE, digest,
123251881Speter                         result_pool);
124251881Speter}
125251881Speter
126251881Spetersvn_error_t *
127251881Spetersvn_checksum_clear(svn_checksum_t *checksum)
128251881Speter{
129251881Speter  SVN_ERR(validate_kind(checksum->kind));
130251881Speter
131251881Speter  memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind));
132251881Speter  return SVN_NO_ERROR;
133251881Speter}
134251881Speter
135251881Spetersvn_boolean_t
136251881Spetersvn_checksum_match(const svn_checksum_t *checksum1,
137251881Speter                   const svn_checksum_t *checksum2)
138251881Speter{
139251881Speter  if (checksum1 == NULL || checksum2 == NULL)
140251881Speter    return TRUE;
141251881Speter
142251881Speter  if (checksum1->kind != checksum2->kind)
143251881Speter    return FALSE;
144251881Speter
145251881Speter  switch (checksum1->kind)
146251881Speter    {
147251881Speter      case svn_checksum_md5:
148251881Speter        return svn_md5__digests_match(checksum1->digest, checksum2->digest);
149251881Speter      case svn_checksum_sha1:
150251881Speter        return svn_sha1__digests_match(checksum1->digest, checksum2->digest);
151251881Speter      default:
152251881Speter        /* We really shouldn't get here, but if we do... */
153251881Speter        return FALSE;
154251881Speter    }
155251881Speter}
156251881Speter
157251881Speterconst char *
158251881Spetersvn_checksum_to_cstring_display(const svn_checksum_t *checksum,
159251881Speter                                apr_pool_t *pool)
160251881Speter{
161251881Speter  switch (checksum->kind)
162251881Speter    {
163251881Speter      case svn_checksum_md5:
164251881Speter        return svn_md5__digest_to_cstring_display(checksum->digest, pool);
165251881Speter      case svn_checksum_sha1:
166251881Speter        return svn_sha1__digest_to_cstring_display(checksum->digest, pool);
167251881Speter      default:
168251881Speter        /* We really shouldn't get here, but if we do... */
169251881Speter        return NULL;
170251881Speter    }
171251881Speter}
172251881Speter
173251881Speterconst char *
174251881Spetersvn_checksum_to_cstring(const svn_checksum_t *checksum,
175251881Speter                        apr_pool_t *pool)
176251881Speter{
177251881Speter  if (checksum == NULL)
178251881Speter    return NULL;
179251881Speter
180251881Speter  switch (checksum->kind)
181251881Speter    {
182251881Speter      case svn_checksum_md5:
183251881Speter        return svn_md5__digest_to_cstring(checksum->digest, pool);
184251881Speter      case svn_checksum_sha1:
185251881Speter        return svn_sha1__digest_to_cstring(checksum->digest, pool);
186251881Speter      default:
187251881Speter        /* We really shouldn't get here, but if we do... */
188251881Speter        return NULL;
189251881Speter    }
190251881Speter}
191251881Speter
192251881Speter
193251881Speterconst char *
194251881Spetersvn_checksum_serialize(const svn_checksum_t *checksum,
195251881Speter                       apr_pool_t *result_pool,
196251881Speter                       apr_pool_t *scratch_pool)
197251881Speter{
198251881Speter  const char *ckind_str;
199251881Speter
200251881Speter  SVN_ERR_ASSERT_NO_RETURN(checksum->kind == svn_checksum_md5
201251881Speter                           || checksum->kind == svn_checksum_sha1);
202251881Speter  ckind_str = (checksum->kind == svn_checksum_md5 ? "$md5 $" : "$sha1$");
203251881Speter  return apr_pstrcat(result_pool,
204251881Speter                     ckind_str,
205251881Speter                     svn_checksum_to_cstring(checksum, scratch_pool),
206251881Speter                     (char *)NULL);
207251881Speter}
208251881Speter
209251881Speter
210251881Spetersvn_error_t *
211251881Spetersvn_checksum_deserialize(const svn_checksum_t **checksum,
212251881Speter                         const char *data,
213251881Speter                         apr_pool_t *result_pool,
214251881Speter                         apr_pool_t *scratch_pool)
215251881Speter{
216251881Speter  svn_checksum_kind_t ckind;
217251881Speter  svn_checksum_t *parsed_checksum;
218251881Speter
219251881Speter  /* "$md5 $..." or "$sha1$..." */
220251881Speter  SVN_ERR_ASSERT(strlen(data) > 6);
221251881Speter
222251881Speter  ckind = (data[1] == 'm' ? svn_checksum_md5 : svn_checksum_sha1);
223251881Speter  SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, ckind,
224251881Speter                                 data + 6, result_pool));
225251881Speter  *checksum = parsed_checksum;
226251881Speter
227251881Speter  return SVN_NO_ERROR;
228251881Speter}
229251881Speter
230251881Speter
231251881Spetersvn_error_t *
232251881Spetersvn_checksum_parse_hex(svn_checksum_t **checksum,
233251881Speter                       svn_checksum_kind_t kind,
234251881Speter                       const char *hex,
235251881Speter                       apr_pool_t *pool)
236251881Speter{
237251881Speter  int i, len;
238251881Speter  char is_nonzero = '\0';
239251881Speter  char *digest;
240251881Speter  static const char xdigitval[256] =
241251881Speter    {
242251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
243251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
244251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
245251881Speter       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,   /* 0-9 */
246251881Speter      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* A-F */
247251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
248251881Speter      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* a-f */
249251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
250251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
251251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
252251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
253251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
254251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
255251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
256251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
257251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
258251881Speter    };
259251881Speter
260251881Speter  if (hex == NULL)
261251881Speter    {
262251881Speter      *checksum = NULL;
263251881Speter      return SVN_NO_ERROR;
264251881Speter    }
265251881Speter
266251881Speter  SVN_ERR(validate_kind(kind));
267251881Speter
268251881Speter  *checksum = svn_checksum_create(kind, pool);
269251881Speter  digest = (char *)(*checksum)->digest;
270251881Speter  len = DIGESTSIZE(kind);
271251881Speter
272251881Speter  for (i = 0; i < len; i++)
273251881Speter    {
274251881Speter      char x1 = xdigitval[(unsigned char)hex[i * 2]];
275251881Speter      char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]];
276251881Speter      if (x1 == (char)-1 || x2 == (char)-1)
277251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL);
278251881Speter
279251881Speter      digest[i] = (char)((x1 << 4) | x2);
280251881Speter      is_nonzero |= (char)((x1 << 4) | x2);
281251881Speter    }
282251881Speter
283251881Speter  if (!is_nonzero)
284251881Speter    *checksum = NULL;
285251881Speter
286251881Speter  return SVN_NO_ERROR;
287251881Speter}
288251881Speter
289251881Spetersvn_checksum_t *
290251881Spetersvn_checksum_dup(const svn_checksum_t *checksum,
291251881Speter                 apr_pool_t *pool)
292251881Speter{
293251881Speter  /* The duplicate of a NULL checksum is a NULL... */
294251881Speter  if (checksum == NULL)
295251881Speter    return NULL;
296251881Speter
297251881Speter  /* Without this check on valid checksum kind a NULL svn_checksum_t
298251881Speter   * pointer is returned which could cause a core dump at an
299251881Speter   * indeterminate time in the future because callers are not
300251881Speter   * expecting a NULL pointer.  This commit forces an early abort() so
301251881Speter   * it's easier to track down where the issue arose. */
302251881Speter  switch (checksum->kind)
303251881Speter    {
304251881Speter      case svn_checksum_md5:
305251881Speter        return svn_checksum__from_digest_md5(checksum->digest, pool);
306251881Speter        break;
307251881Speter      case svn_checksum_sha1:
308251881Speter        return svn_checksum__from_digest_sha1(checksum->digest, pool);
309251881Speter        break;
310251881Speter      default:
311251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
312251881Speter        break;
313251881Speter    }
314251881Speter}
315251881Speter
316251881Spetersvn_error_t *
317251881Spetersvn_checksum(svn_checksum_t **checksum,
318251881Speter             svn_checksum_kind_t kind,
319251881Speter             const void *data,
320251881Speter             apr_size_t len,
321251881Speter             apr_pool_t *pool)
322251881Speter{
323251881Speter  apr_sha1_ctx_t sha1_ctx;
324251881Speter
325251881Speter  SVN_ERR(validate_kind(kind));
326251881Speter  *checksum = svn_checksum_create(kind, pool);
327251881Speter
328251881Speter  switch (kind)
329251881Speter    {
330251881Speter      case svn_checksum_md5:
331251881Speter        apr_md5((unsigned char *)(*checksum)->digest, data, len);
332251881Speter        break;
333251881Speter
334251881Speter      case svn_checksum_sha1:
335251881Speter        apr_sha1_init(&sha1_ctx);
336251881Speter        apr_sha1_update(&sha1_ctx, data, (unsigned int)len);
337251881Speter        apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
338251881Speter        break;
339251881Speter
340251881Speter      default:
341251881Speter        /* We really shouldn't get here, but if we do... */
342251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
343251881Speter    }
344251881Speter
345251881Speter  return SVN_NO_ERROR;
346251881Speter}
347251881Speter
348251881Speter
349251881Spetersvn_checksum_t *
350251881Spetersvn_checksum_empty_checksum(svn_checksum_kind_t kind,
351251881Speter                            apr_pool_t *pool)
352251881Speter{
353251881Speter  switch (kind)
354251881Speter    {
355251881Speter      case svn_checksum_md5:
356251881Speter        return svn_checksum__from_digest_md5(svn_md5__empty_string_digest(),
357251881Speter                                             pool);
358251881Speter
359251881Speter      case svn_checksum_sha1:
360251881Speter        return svn_checksum__from_digest_sha1(svn_sha1__empty_string_digest(),
361251881Speter                                              pool);
362251881Speter
363251881Speter      default:
364251881Speter        /* We really shouldn't get here, but if we do... */
365251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
366251881Speter    }
367251881Speter}
368251881Speter
369251881Speterstruct svn_checksum_ctx_t
370251881Speter{
371251881Speter  void *apr_ctx;
372251881Speter  svn_checksum_kind_t kind;
373251881Speter};
374251881Speter
375251881Spetersvn_checksum_ctx_t *
376251881Spetersvn_checksum_ctx_create(svn_checksum_kind_t kind,
377251881Speter                        apr_pool_t *pool)
378251881Speter{
379251881Speter  svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx));
380251881Speter
381251881Speter  ctx->kind = kind;
382251881Speter  switch (kind)
383251881Speter    {
384251881Speter      case svn_checksum_md5:
385251881Speter        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t));
386251881Speter        apr_md5_init(ctx->apr_ctx);
387251881Speter        break;
388251881Speter
389251881Speter      case svn_checksum_sha1:
390251881Speter        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t));
391251881Speter        apr_sha1_init(ctx->apr_ctx);
392251881Speter        break;
393251881Speter
394251881Speter      default:
395251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
396251881Speter    }
397251881Speter
398251881Speter  return ctx;
399251881Speter}
400251881Speter
401251881Spetersvn_error_t *
402251881Spetersvn_checksum_update(svn_checksum_ctx_t *ctx,
403251881Speter                    const void *data,
404251881Speter                    apr_size_t len)
405251881Speter{
406251881Speter  switch (ctx->kind)
407251881Speter    {
408251881Speter      case svn_checksum_md5:
409251881Speter        apr_md5_update(ctx->apr_ctx, data, len);
410251881Speter        break;
411251881Speter
412251881Speter      case svn_checksum_sha1:
413251881Speter        apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
414251881Speter        break;
415251881Speter
416251881Speter      default:
417251881Speter        /* We really shouldn't get here, but if we do... */
418251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
419251881Speter    }
420251881Speter
421251881Speter  return SVN_NO_ERROR;
422251881Speter}
423251881Speter
424251881Spetersvn_error_t *
425251881Spetersvn_checksum_final(svn_checksum_t **checksum,
426251881Speter                   const svn_checksum_ctx_t *ctx,
427251881Speter                   apr_pool_t *pool)
428251881Speter{
429251881Speter  *checksum = svn_checksum_create(ctx->kind, pool);
430251881Speter
431251881Speter  switch (ctx->kind)
432251881Speter    {
433251881Speter      case svn_checksum_md5:
434251881Speter        apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
435251881Speter        break;
436251881Speter
437251881Speter      case svn_checksum_sha1:
438251881Speter        apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
439251881Speter        break;
440251881Speter
441251881Speter      default:
442251881Speter        /* We really shouldn't get here, but if we do... */
443251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
444251881Speter    }
445251881Speter
446251881Speter  return SVN_NO_ERROR;
447251881Speter}
448251881Speter
449251881Speterapr_size_t
450251881Spetersvn_checksum_size(const svn_checksum_t *checksum)
451251881Speter{
452251881Speter  return DIGESTSIZE(checksum->kind);
453251881Speter}
454251881Speter
455251881Spetersvn_error_t *
456251881Spetersvn_checksum_mismatch_err(const svn_checksum_t *expected,
457251881Speter                          const svn_checksum_t *actual,
458251881Speter                          apr_pool_t *scratch_pool,
459251881Speter                          const char *fmt,
460251881Speter                          ...)
461251881Speter{
462251881Speter  va_list ap;
463251881Speter  const char *desc;
464251881Speter
465251881Speter  va_start(ap, fmt);
466251881Speter  desc = apr_pvsprintf(scratch_pool, fmt, ap);
467251881Speter  va_end(ap);
468251881Speter
469251881Speter  return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
470251881Speter                           _("%s:\n"
471251881Speter                             "   expected:  %s\n"
472251881Speter                             "     actual:  %s\n"),
473251881Speter                desc,
474251881Speter                svn_checksum_to_cstring_display(expected, scratch_pool),
475251881Speter                svn_checksum_to_cstring_display(actual, scratch_pool));
476251881Speter}
477251881Speter
478251881Spetersvn_boolean_t
479251881Spetersvn_checksum_is_empty_checksum(svn_checksum_t *checksum)
480251881Speter{
481251881Speter  /* By definition, the NULL checksum matches all others, including the
482251881Speter     empty one. */
483251881Speter  if (!checksum)
484251881Speter    return TRUE;
485251881Speter
486251881Speter  switch (checksum->kind)
487251881Speter    {
488251881Speter      case svn_checksum_md5:
489251881Speter        return svn_md5__digests_match(checksum->digest,
490251881Speter                                      svn_md5__empty_string_digest());
491251881Speter
492251881Speter      case svn_checksum_sha1:
493251881Speter        return svn_sha1__digests_match(checksum->digest,
494251881Speter                                       svn_sha1__empty_string_digest());
495251881Speter
496251881Speter      default:
497251881Speter        /* We really shouldn't get here, but if we do... */
498251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
499251881Speter    }
500251881Speter}
501