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
24299742Sdim#define APR_WANT_BYTEFUNC
25251881Speter
26251881Speter#include <ctype.h>
27251881Speter
28251881Speter#include <apr_md5.h>
29251881Speter#include <apr_sha1.h>
30251881Speter
31251881Speter#include "svn_checksum.h"
32251881Speter#include "svn_error.h"
33251881Speter#include "svn_ctype.h"
34299742Sdim#include "svn_sorts.h"
35251881Speter
36299742Sdim#include "checksum.h"
37299742Sdim#include "fnv1a.h"
38251881Speter
39251881Speter#include "private/svn_subr_private.h"
40251881Speter
41251881Speter#include "svn_private_config.h"
42251881Speter
43251881Speter
44251881Speter
45299742Sdim/* The MD5 digest for the empty string. */
46299742Sdimstatic const unsigned char md5_empty_string_digest_array[] = {
47299742Sdim  0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
48299742Sdim  0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e
49299742Sdim};
50299742Sdim
51299742Sdim/* The SHA1 digest for the empty string. */
52299742Sdimstatic const unsigned char sha1_empty_string_digest_array[] = {
53299742Sdim  0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55,
54299742Sdim  0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09
55299742Sdim};
56299742Sdim
57299742Sdim/* The FNV-1a digest for the empty string. */
58299742Sdimstatic const unsigned char fnv1a_32_empty_string_digest_array[] = {
59299742Sdim  0x81, 0x1c, 0x9d, 0xc5
60299742Sdim};
61299742Sdim
62299742Sdim/* The FNV-1a digest for the empty string. */
63299742Sdimstatic const unsigned char fnv1a_32x4_empty_string_digest_array[] = {
64299742Sdim  0xcd, 0x6d, 0x9a, 0x85
65299742Sdim};
66299742Sdim
67299742Sdim/* Digests for an empty string, indexed by checksum type */
68299742Sdimstatic const unsigned char * empty_string_digests[] = {
69299742Sdim  md5_empty_string_digest_array,
70299742Sdim  sha1_empty_string_digest_array,
71299742Sdim  fnv1a_32_empty_string_digest_array,
72299742Sdim  fnv1a_32x4_empty_string_digest_array
73299742Sdim};
74299742Sdim
75299742Sdim/* Digest sizes in bytes, indexed by checksum type */
76299742Sdimstatic const apr_size_t digest_sizes[] = {
77299742Sdim  APR_MD5_DIGESTSIZE,
78299742Sdim  APR_SHA1_DIGESTSIZE,
79299742Sdim  sizeof(apr_uint32_t),
80299742Sdim  sizeof(apr_uint32_t)
81299742Sdim};
82299742Sdim
83299742Sdim/* Checksum type prefixes used in serialized checksums. */
84299742Sdimstatic const char *ckind_str[] = {
85299742Sdim  "$md5 $",
86299742Sdim  "$sha1$",
87299742Sdim  "$fnv1$",
88299742Sdim  "$fnvm$",
89299742Sdim};
90299742Sdim
91251881Speter/* Returns the digest size of it's argument. */
92299742Sdim#define DIGESTSIZE(k) \
93299742Sdim  (((k) < svn_checksum_md5 || (k) > svn_checksum_fnv1a_32x4) ? 0 : digest_sizes[k])
94251881Speter
95299742Sdim/* Largest supported digest size */
96299742Sdim#define MAX_DIGESTSIZE (MAX(APR_MD5_DIGESTSIZE,APR_SHA1_DIGESTSIZE))
97251881Speter
98299742Sdimconst unsigned char *
99299742Sdimsvn__empty_string_digest(svn_checksum_kind_t kind)
100299742Sdim{
101299742Sdim  return empty_string_digests[kind];
102299742Sdim}
103299742Sdim
104299742Sdimconst char *
105299742Sdimsvn__digest_to_cstring_display(const unsigned char digest[],
106299742Sdim                               apr_size_t digest_size,
107299742Sdim                               apr_pool_t *pool)
108299742Sdim{
109299742Sdim  static const char *hex = "0123456789abcdef";
110299742Sdim  char *str = apr_palloc(pool, (digest_size * 2) + 1);
111299742Sdim  apr_size_t i;
112299742Sdim
113299742Sdim  for (i = 0; i < digest_size; i++)
114299742Sdim    {
115299742Sdim      str[i*2]   = hex[digest[i] >> 4];
116299742Sdim      str[i*2+1] = hex[digest[i] & 0x0f];
117299742Sdim    }
118299742Sdim  str[i*2] = '\0';
119299742Sdim
120299742Sdim  return str;
121299742Sdim}
122299742Sdim
123299742Sdim
124299742Sdimconst char *
125299742Sdimsvn__digest_to_cstring(const unsigned char digest[],
126299742Sdim                       apr_size_t digest_size,
127299742Sdim                       apr_pool_t *pool)
128299742Sdim{
129299742Sdim  static const unsigned char zeros_digest[MAX_DIGESTSIZE] = { 0 };
130299742Sdim
131299742Sdim  if (memcmp(digest, zeros_digest, digest_size) != 0)
132299742Sdim    return svn__digest_to_cstring_display(digest, digest_size, pool);
133299742Sdim  else
134299742Sdim    return NULL;
135299742Sdim}
136299742Sdim
137299742Sdim
138299742Sdimsvn_boolean_t
139299742Sdimsvn__digests_match(const unsigned char d1[],
140299742Sdim                   const unsigned char d2[],
141299742Sdim                   apr_size_t digest_size)
142299742Sdim{
143299742Sdim  static const unsigned char zeros[MAX_DIGESTSIZE] = { 0 };
144299742Sdim
145299742Sdim  return ((memcmp(d1, d2, digest_size) == 0)
146299742Sdim          || (memcmp(d2, zeros, digest_size) == 0)
147299742Sdim          || (memcmp(d1, zeros, digest_size) == 0));
148299742Sdim}
149299742Sdim
150251881Speter/* Check to see if KIND is something we recognize.  If not, return
151251881Speter * SVN_ERR_BAD_CHECKSUM_KIND */
152251881Speterstatic svn_error_t *
153251881Spetervalidate_kind(svn_checksum_kind_t kind)
154251881Speter{
155299742Sdim  if (kind >= svn_checksum_md5 && kind <= svn_checksum_fnv1a_32x4)
156251881Speter    return SVN_NO_ERROR;
157251881Speter  else
158251881Speter    return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
159251881Speter}
160251881Speter
161251881Speter/* Create a svn_checksum_t with everything but the contents of the
162251881Speter   digest populated. */
163251881Speterstatic svn_checksum_t *
164251881Speterchecksum_create_without_digest(svn_checksum_kind_t kind,
165251881Speter                               apr_size_t digest_size,
166251881Speter                               apr_pool_t *pool)
167251881Speter{
168251881Speter  /* Use apr_palloc() instead of apr_pcalloc() so that the digest
169251881Speter   * contents are only set once by the caller. */
170251881Speter  svn_checksum_t *checksum = apr_palloc(pool, sizeof(*checksum) + digest_size);
171251881Speter  checksum->digest = (unsigned char *)checksum + sizeof(*checksum);
172251881Speter  checksum->kind = kind;
173251881Speter  return checksum;
174251881Speter}
175251881Speter
176299742Sdim/* Return a checksum object, allocated in POOL.  The checksum will be of
177299742Sdim * type KIND and contain the given DIGEST.
178299742Sdim */
179251881Speterstatic svn_checksum_t *
180251881Speterchecksum_create(svn_checksum_kind_t kind,
181251881Speter                const unsigned char *digest,
182251881Speter                apr_pool_t *pool)
183251881Speter{
184299742Sdim  apr_size_t digest_size = DIGESTSIZE(kind);
185251881Speter  svn_checksum_t *checksum = checksum_create_without_digest(kind, digest_size,
186251881Speter                                                            pool);
187251881Speter  memcpy((unsigned char *)checksum->digest, digest, digest_size);
188251881Speter  return checksum;
189251881Speter}
190251881Speter
191251881Spetersvn_checksum_t *
192251881Spetersvn_checksum_create(svn_checksum_kind_t kind,
193251881Speter                    apr_pool_t *pool)
194251881Speter{
195251881Speter  svn_checksum_t *checksum;
196251881Speter  apr_size_t digest_size;
197251881Speter
198251881Speter  switch (kind)
199251881Speter    {
200251881Speter      case svn_checksum_md5:
201251881Speter      case svn_checksum_sha1:
202299742Sdim      case svn_checksum_fnv1a_32:
203299742Sdim      case svn_checksum_fnv1a_32x4:
204299742Sdim        digest_size = digest_sizes[kind];
205251881Speter        break;
206299742Sdim
207251881Speter      default:
208251881Speter        return NULL;
209251881Speter    }
210251881Speter
211251881Speter  checksum = checksum_create_without_digest(kind, digest_size, pool);
212251881Speter  memset((unsigned char *) checksum->digest, 0, digest_size);
213251881Speter  return checksum;
214251881Speter}
215251881Speter
216251881Spetersvn_checksum_t *
217251881Spetersvn_checksum__from_digest_md5(const unsigned char *digest,
218251881Speter                              apr_pool_t *result_pool)
219251881Speter{
220299742Sdim  return checksum_create(svn_checksum_md5, digest, result_pool);
221251881Speter}
222251881Speter
223251881Spetersvn_checksum_t *
224251881Spetersvn_checksum__from_digest_sha1(const unsigned char *digest,
225251881Speter                               apr_pool_t *result_pool)
226251881Speter{
227299742Sdim  return checksum_create(svn_checksum_sha1, digest, result_pool);
228251881Speter}
229251881Speter
230299742Sdimsvn_checksum_t *
231299742Sdimsvn_checksum__from_digest_fnv1a_32(const unsigned char *digest,
232299742Sdim                                   apr_pool_t *result_pool)
233299742Sdim{
234299742Sdim  return checksum_create(svn_checksum_fnv1a_32, digest, result_pool);
235299742Sdim}
236299742Sdim
237299742Sdimsvn_checksum_t *
238299742Sdimsvn_checksum__from_digest_fnv1a_32x4(const unsigned char *digest,
239299742Sdim                                     apr_pool_t *result_pool)
240299742Sdim{
241299742Sdim  return checksum_create(svn_checksum_fnv1a_32x4, digest, result_pool);
242299742Sdim}
243299742Sdim
244251881Spetersvn_error_t *
245251881Spetersvn_checksum_clear(svn_checksum_t *checksum)
246251881Speter{
247251881Speter  SVN_ERR(validate_kind(checksum->kind));
248251881Speter
249251881Speter  memset((unsigned char *) checksum->digest, 0, DIGESTSIZE(checksum->kind));
250251881Speter  return SVN_NO_ERROR;
251251881Speter}
252251881Speter
253251881Spetersvn_boolean_t
254251881Spetersvn_checksum_match(const svn_checksum_t *checksum1,
255251881Speter                   const svn_checksum_t *checksum2)
256251881Speter{
257251881Speter  if (checksum1 == NULL || checksum2 == NULL)
258251881Speter    return TRUE;
259251881Speter
260251881Speter  if (checksum1->kind != checksum2->kind)
261251881Speter    return FALSE;
262251881Speter
263251881Speter  switch (checksum1->kind)
264251881Speter    {
265251881Speter      case svn_checksum_md5:
266251881Speter      case svn_checksum_sha1:
267299742Sdim      case svn_checksum_fnv1a_32:
268299742Sdim      case svn_checksum_fnv1a_32x4:
269299742Sdim        return svn__digests_match(checksum1->digest,
270299742Sdim                                  checksum2->digest,
271299742Sdim                                  digest_sizes[checksum1->kind]);
272299742Sdim
273251881Speter      default:
274251881Speter        /* We really shouldn't get here, but if we do... */
275251881Speter        return FALSE;
276251881Speter    }
277251881Speter}
278251881Speter
279251881Speterconst char *
280251881Spetersvn_checksum_to_cstring_display(const svn_checksum_t *checksum,
281251881Speter                                apr_pool_t *pool)
282251881Speter{
283251881Speter  switch (checksum->kind)
284251881Speter    {
285251881Speter      case svn_checksum_md5:
286251881Speter      case svn_checksum_sha1:
287299742Sdim      case svn_checksum_fnv1a_32:
288299742Sdim      case svn_checksum_fnv1a_32x4:
289299742Sdim        return svn__digest_to_cstring_display(checksum->digest,
290299742Sdim                                              digest_sizes[checksum->kind],
291299742Sdim                                              pool);
292299742Sdim
293251881Speter      default:
294251881Speter        /* We really shouldn't get here, but if we do... */
295251881Speter        return NULL;
296251881Speter    }
297251881Speter}
298251881Speter
299251881Speterconst char *
300251881Spetersvn_checksum_to_cstring(const svn_checksum_t *checksum,
301251881Speter                        apr_pool_t *pool)
302251881Speter{
303251881Speter  if (checksum == NULL)
304251881Speter    return NULL;
305251881Speter
306251881Speter  switch (checksum->kind)
307251881Speter    {
308251881Speter      case svn_checksum_md5:
309251881Speter      case svn_checksum_sha1:
310299742Sdim      case svn_checksum_fnv1a_32:
311299742Sdim      case svn_checksum_fnv1a_32x4:
312299742Sdim        return svn__digest_to_cstring(checksum->digest,
313299742Sdim                                      digest_sizes[checksum->kind],
314299742Sdim                                      pool);
315299742Sdim
316251881Speter      default:
317251881Speter        /* We really shouldn't get here, but if we do... */
318251881Speter        return NULL;
319251881Speter    }
320251881Speter}
321251881Speter
322251881Speter
323251881Speterconst char *
324251881Spetersvn_checksum_serialize(const svn_checksum_t *checksum,
325251881Speter                       apr_pool_t *result_pool,
326251881Speter                       apr_pool_t *scratch_pool)
327251881Speter{
328299742Sdim  SVN_ERR_ASSERT_NO_RETURN(checksum->kind >= svn_checksum_md5
329299742Sdim                           || checksum->kind <= svn_checksum_fnv1a_32x4);
330251881Speter  return apr_pstrcat(result_pool,
331299742Sdim                     ckind_str[checksum->kind],
332251881Speter                     svn_checksum_to_cstring(checksum, scratch_pool),
333299742Sdim                     SVN_VA_NULL);
334251881Speter}
335251881Speter
336251881Speter
337251881Spetersvn_error_t *
338251881Spetersvn_checksum_deserialize(const svn_checksum_t **checksum,
339251881Speter                         const char *data,
340251881Speter                         apr_pool_t *result_pool,
341251881Speter                         apr_pool_t *scratch_pool)
342251881Speter{
343299742Sdim  svn_checksum_kind_t kind;
344251881Speter  svn_checksum_t *parsed_checksum;
345251881Speter
346299742Sdim  /* All prefixes have the same length. */
347299742Sdim  apr_size_t prefix_len = strlen(ckind_str[0]);
348251881Speter
349299742Sdim  /* "$md5 $...", "$sha1$..." or ... */
350299742Sdim  if (strlen(data) <= prefix_len)
351299742Sdim    return svn_error_createf(SVN_ERR_BAD_CHECKSUM_PARSE, NULL,
352299742Sdim                             _("Invalid prefix in checksum '%s'"),
353299742Sdim                             data);
354251881Speter
355299742Sdim  for (kind = svn_checksum_md5; kind <= svn_checksum_fnv1a_32x4; ++kind)
356299742Sdim    if (strncmp(ckind_str[kind], data, prefix_len) == 0)
357299742Sdim      {
358299742Sdim        SVN_ERR(svn_checksum_parse_hex(&parsed_checksum, kind,
359299742Sdim                                       data + prefix_len, result_pool));
360299742Sdim        *checksum = parsed_checksum;
361299742Sdim        return SVN_NO_ERROR;
362299742Sdim      }
363299742Sdim
364299742Sdim  return svn_error_createf(SVN_ERR_BAD_CHECKSUM_KIND, NULL,
365299742Sdim                           "Unknown checksum kind in '%s'", data);
366251881Speter}
367251881Speter
368251881Speter
369251881Spetersvn_error_t *
370251881Spetersvn_checksum_parse_hex(svn_checksum_t **checksum,
371251881Speter                       svn_checksum_kind_t kind,
372251881Speter                       const char *hex,
373251881Speter                       apr_pool_t *pool)
374251881Speter{
375299742Sdim  apr_size_t i, len;
376251881Speter  char is_nonzero = '\0';
377251881Speter  char *digest;
378251881Speter  static const char xdigitval[256] =
379251881Speter    {
380251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
381251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
382251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
383251881Speter       0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,   /* 0-9 */
384251881Speter      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* A-F */
385251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
386251881Speter      -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,   /* a-f */
387251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
388251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
389251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
390251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
391251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
392251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
393251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
394251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
395251881Speter      -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
396251881Speter    };
397251881Speter
398251881Speter  if (hex == NULL)
399251881Speter    {
400251881Speter      *checksum = NULL;
401251881Speter      return SVN_NO_ERROR;
402251881Speter    }
403251881Speter
404251881Speter  SVN_ERR(validate_kind(kind));
405251881Speter
406251881Speter  *checksum = svn_checksum_create(kind, pool);
407251881Speter  digest = (char *)(*checksum)->digest;
408251881Speter  len = DIGESTSIZE(kind);
409251881Speter
410251881Speter  for (i = 0; i < len; i++)
411251881Speter    {
412251881Speter      char x1 = xdigitval[(unsigned char)hex[i * 2]];
413251881Speter      char x2 = xdigitval[(unsigned char)hex[i * 2 + 1]];
414251881Speter      if (x1 == (char)-1 || x2 == (char)-1)
415251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_PARSE, NULL, NULL);
416251881Speter
417251881Speter      digest[i] = (char)((x1 << 4) | x2);
418251881Speter      is_nonzero |= (char)((x1 << 4) | x2);
419251881Speter    }
420251881Speter
421251881Speter  if (!is_nonzero)
422251881Speter    *checksum = NULL;
423251881Speter
424251881Speter  return SVN_NO_ERROR;
425251881Speter}
426251881Speter
427251881Spetersvn_checksum_t *
428251881Spetersvn_checksum_dup(const svn_checksum_t *checksum,
429251881Speter                 apr_pool_t *pool)
430251881Speter{
431251881Speter  /* The duplicate of a NULL checksum is a NULL... */
432251881Speter  if (checksum == NULL)
433251881Speter    return NULL;
434251881Speter
435251881Speter  /* Without this check on valid checksum kind a NULL svn_checksum_t
436251881Speter   * pointer is returned which could cause a core dump at an
437251881Speter   * indeterminate time in the future because callers are not
438251881Speter   * expecting a NULL pointer.  This commit forces an early abort() so
439251881Speter   * it's easier to track down where the issue arose. */
440251881Speter  switch (checksum->kind)
441251881Speter    {
442251881Speter      case svn_checksum_md5:
443251881Speter      case svn_checksum_sha1:
444299742Sdim      case svn_checksum_fnv1a_32:
445299742Sdim      case svn_checksum_fnv1a_32x4:
446299742Sdim        return checksum_create(checksum->kind, checksum->digest, pool);
447299742Sdim
448251881Speter      default:
449251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
450251881Speter        break;
451251881Speter    }
452251881Speter}
453251881Speter
454251881Spetersvn_error_t *
455251881Spetersvn_checksum(svn_checksum_t **checksum,
456251881Speter             svn_checksum_kind_t kind,
457251881Speter             const void *data,
458251881Speter             apr_size_t len,
459251881Speter             apr_pool_t *pool)
460251881Speter{
461251881Speter  apr_sha1_ctx_t sha1_ctx;
462251881Speter
463251881Speter  SVN_ERR(validate_kind(kind));
464251881Speter  *checksum = svn_checksum_create(kind, pool);
465251881Speter
466251881Speter  switch (kind)
467251881Speter    {
468251881Speter      case svn_checksum_md5:
469251881Speter        apr_md5((unsigned char *)(*checksum)->digest, data, len);
470251881Speter        break;
471251881Speter
472251881Speter      case svn_checksum_sha1:
473251881Speter        apr_sha1_init(&sha1_ctx);
474251881Speter        apr_sha1_update(&sha1_ctx, data, (unsigned int)len);
475251881Speter        apr_sha1_final((unsigned char *)(*checksum)->digest, &sha1_ctx);
476251881Speter        break;
477251881Speter
478299742Sdim      case svn_checksum_fnv1a_32:
479299742Sdim        *(apr_uint32_t *)(*checksum)->digest
480299742Sdim          = htonl(svn__fnv1a_32(data, len));
481299742Sdim        break;
482299742Sdim
483299742Sdim      case svn_checksum_fnv1a_32x4:
484299742Sdim        *(apr_uint32_t *)(*checksum)->digest
485299742Sdim          = htonl(svn__fnv1a_32x4(data, len));
486299742Sdim        break;
487299742Sdim
488251881Speter      default:
489251881Speter        /* We really shouldn't get here, but if we do... */
490251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
491251881Speter    }
492251881Speter
493251881Speter  return SVN_NO_ERROR;
494251881Speter}
495251881Speter
496251881Speter
497251881Spetersvn_checksum_t *
498251881Spetersvn_checksum_empty_checksum(svn_checksum_kind_t kind,
499251881Speter                            apr_pool_t *pool)
500251881Speter{
501251881Speter  switch (kind)
502251881Speter    {
503251881Speter      case svn_checksum_md5:
504251881Speter      case svn_checksum_sha1:
505299742Sdim      case svn_checksum_fnv1a_32:
506299742Sdim      case svn_checksum_fnv1a_32x4:
507299742Sdim        return checksum_create(kind, empty_string_digests[kind], pool);
508251881Speter
509251881Speter      default:
510251881Speter        /* We really shouldn't get here, but if we do... */
511251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
512251881Speter    }
513251881Speter}
514251881Speter
515251881Speterstruct svn_checksum_ctx_t
516251881Speter{
517251881Speter  void *apr_ctx;
518251881Speter  svn_checksum_kind_t kind;
519251881Speter};
520251881Speter
521251881Spetersvn_checksum_ctx_t *
522251881Spetersvn_checksum_ctx_create(svn_checksum_kind_t kind,
523251881Speter                        apr_pool_t *pool)
524251881Speter{
525251881Speter  svn_checksum_ctx_t *ctx = apr_palloc(pool, sizeof(*ctx));
526251881Speter
527251881Speter  ctx->kind = kind;
528251881Speter  switch (kind)
529251881Speter    {
530251881Speter      case svn_checksum_md5:
531251881Speter        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_md5_ctx_t));
532251881Speter        apr_md5_init(ctx->apr_ctx);
533251881Speter        break;
534251881Speter
535251881Speter      case svn_checksum_sha1:
536251881Speter        ctx->apr_ctx = apr_palloc(pool, sizeof(apr_sha1_ctx_t));
537251881Speter        apr_sha1_init(ctx->apr_ctx);
538251881Speter        break;
539251881Speter
540299742Sdim      case svn_checksum_fnv1a_32:
541299742Sdim        ctx->apr_ctx = svn_fnv1a_32__context_create(pool);
542299742Sdim        break;
543299742Sdim
544299742Sdim      case svn_checksum_fnv1a_32x4:
545299742Sdim        ctx->apr_ctx = svn_fnv1a_32x4__context_create(pool);
546299742Sdim        break;
547299742Sdim
548251881Speter      default:
549251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
550251881Speter    }
551251881Speter
552251881Speter  return ctx;
553251881Speter}
554251881Speter
555251881Spetersvn_error_t *
556251881Spetersvn_checksum_update(svn_checksum_ctx_t *ctx,
557251881Speter                    const void *data,
558251881Speter                    apr_size_t len)
559251881Speter{
560251881Speter  switch (ctx->kind)
561251881Speter    {
562251881Speter      case svn_checksum_md5:
563251881Speter        apr_md5_update(ctx->apr_ctx, data, len);
564251881Speter        break;
565251881Speter
566251881Speter      case svn_checksum_sha1:
567251881Speter        apr_sha1_update(ctx->apr_ctx, data, (unsigned int)len);
568251881Speter        break;
569251881Speter
570299742Sdim      case svn_checksum_fnv1a_32:
571299742Sdim        svn_fnv1a_32__update(ctx->apr_ctx, data, len);
572299742Sdim        break;
573299742Sdim
574299742Sdim      case svn_checksum_fnv1a_32x4:
575299742Sdim        svn_fnv1a_32x4__update(ctx->apr_ctx, data, len);
576299742Sdim        break;
577299742Sdim
578251881Speter      default:
579251881Speter        /* We really shouldn't get here, but if we do... */
580251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
581251881Speter    }
582251881Speter
583251881Speter  return SVN_NO_ERROR;
584251881Speter}
585251881Speter
586251881Spetersvn_error_t *
587251881Spetersvn_checksum_final(svn_checksum_t **checksum,
588251881Speter                   const svn_checksum_ctx_t *ctx,
589251881Speter                   apr_pool_t *pool)
590251881Speter{
591251881Speter  *checksum = svn_checksum_create(ctx->kind, pool);
592251881Speter
593251881Speter  switch (ctx->kind)
594251881Speter    {
595251881Speter      case svn_checksum_md5:
596251881Speter        apr_md5_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
597251881Speter        break;
598251881Speter
599251881Speter      case svn_checksum_sha1:
600251881Speter        apr_sha1_final((unsigned char *)(*checksum)->digest, ctx->apr_ctx);
601251881Speter        break;
602251881Speter
603299742Sdim      case svn_checksum_fnv1a_32:
604299742Sdim        *(apr_uint32_t *)(*checksum)->digest
605299742Sdim          = htonl(svn_fnv1a_32__finalize(ctx->apr_ctx));
606299742Sdim        break;
607299742Sdim
608299742Sdim      case svn_checksum_fnv1a_32x4:
609299742Sdim        *(apr_uint32_t *)(*checksum)->digest
610299742Sdim          = htonl(svn_fnv1a_32x4__finalize(ctx->apr_ctx));
611299742Sdim        break;
612299742Sdim
613251881Speter      default:
614251881Speter        /* We really shouldn't get here, but if we do... */
615251881Speter        return svn_error_create(SVN_ERR_BAD_CHECKSUM_KIND, NULL, NULL);
616251881Speter    }
617251881Speter
618251881Speter  return SVN_NO_ERROR;
619251881Speter}
620251881Speter
621251881Speterapr_size_t
622251881Spetersvn_checksum_size(const svn_checksum_t *checksum)
623251881Speter{
624251881Speter  return DIGESTSIZE(checksum->kind);
625251881Speter}
626251881Speter
627251881Spetersvn_error_t *
628251881Spetersvn_checksum_mismatch_err(const svn_checksum_t *expected,
629251881Speter                          const svn_checksum_t *actual,
630251881Speter                          apr_pool_t *scratch_pool,
631251881Speter                          const char *fmt,
632251881Speter                          ...)
633251881Speter{
634251881Speter  va_list ap;
635251881Speter  const char *desc;
636251881Speter
637251881Speter  va_start(ap, fmt);
638251881Speter  desc = apr_pvsprintf(scratch_pool, fmt, ap);
639251881Speter  va_end(ap);
640251881Speter
641251881Speter  return svn_error_createf(SVN_ERR_CHECKSUM_MISMATCH, NULL,
642251881Speter                           _("%s:\n"
643251881Speter                             "   expected:  %s\n"
644251881Speter                             "     actual:  %s\n"),
645251881Speter                desc,
646251881Speter                svn_checksum_to_cstring_display(expected, scratch_pool),
647251881Speter                svn_checksum_to_cstring_display(actual, scratch_pool));
648251881Speter}
649251881Speter
650251881Spetersvn_boolean_t
651251881Spetersvn_checksum_is_empty_checksum(svn_checksum_t *checksum)
652251881Speter{
653251881Speter  /* By definition, the NULL checksum matches all others, including the
654251881Speter     empty one. */
655251881Speter  if (!checksum)
656251881Speter    return TRUE;
657251881Speter
658251881Speter  switch (checksum->kind)
659251881Speter    {
660251881Speter      case svn_checksum_md5:
661251881Speter      case svn_checksum_sha1:
662299742Sdim      case svn_checksum_fnv1a_32:
663299742Sdim      case svn_checksum_fnv1a_32x4:
664299742Sdim        return svn__digests_match(checksum->digest,
665299742Sdim                                  svn__empty_string_digest(checksum->kind),
666299742Sdim                                  digest_sizes[checksum->kind]);
667251881Speter
668251881Speter      default:
669251881Speter        /* We really shouldn't get here, but if we do... */
670251881Speter        SVN_ERR_MALFUNCTION_NO_RETURN();
671251881Speter    }
672251881Speter}
673299742Sdim
674299742Sdim/* Checksum calculating stream wrappers.
675299742Sdim */
676299742Sdim
677299742Sdim/* Baton used by write_handler and close_handler to calculate the checksum
678299742Sdim * and return the result to the stream creator.  It accommodates the data
679299742Sdim * needed by svn_checksum__wrap_write_stream_fnv1a_32x4 as well as
680299742Sdim * svn_checksum__wrap_write_stream.
681299742Sdim */
682299742Sdimtypedef struct stream_baton_t
683299742Sdim{
684299742Sdim  /* Stream we are wrapping. Forward write() and close() operations to it. */
685299742Sdim  svn_stream_t *inner_stream;
686299742Sdim
687299742Sdim  /* Build the checksum data in here. */
688299742Sdim  svn_checksum_ctx_t *context;
689299742Sdim
690299742Sdim  /* Write the final checksum here. May be NULL. */
691299742Sdim  svn_checksum_t **checksum;
692299742Sdim
693299742Sdim  /* Copy the digest of the final checksum. May be NULL. */
694299742Sdim  unsigned char *digest;
695299742Sdim
696299742Sdim  /* Allocate the resulting checksum here. */
697299742Sdim  apr_pool_t *pool;
698299742Sdim} stream_baton_t;
699299742Sdim
700299742Sdim/* Implement svn_write_fn_t.
701299742Sdim * Update checksum and pass data on to inner stream.
702299742Sdim */
703299742Sdimstatic svn_error_t *
704299742Sdimwrite_handler(void *baton,
705299742Sdim              const char *data,
706299742Sdim              apr_size_t *len)
707299742Sdim{
708299742Sdim  stream_baton_t *b = baton;
709299742Sdim
710299742Sdim  SVN_ERR(svn_checksum_update(b->context, data, *len));
711299742Sdim  SVN_ERR(svn_stream_write(b->inner_stream, data, len));
712299742Sdim
713299742Sdim  return SVN_NO_ERROR;
714299742Sdim}
715299742Sdim
716299742Sdim/* Implement svn_close_fn_t.
717299742Sdim * Finalize checksum calculation and write results. Close inner stream.
718299742Sdim */
719299742Sdimstatic svn_error_t *
720299742Sdimclose_handler(void *baton)
721299742Sdim{
722299742Sdim  stream_baton_t *b = baton;
723299742Sdim  svn_checksum_t *local_checksum;
724299742Sdim
725299742Sdim  /* Ensure we can always write to *B->CHECKSUM. */
726299742Sdim  if (!b->checksum)
727299742Sdim    b->checksum = &local_checksum;
728299742Sdim
729299742Sdim  /* Get the final checksum. */
730299742Sdim  SVN_ERR(svn_checksum_final(b->checksum, b->context, b->pool));
731299742Sdim
732299742Sdim  /* Extract digest, if wanted. */
733299742Sdim  if (b->digest)
734299742Sdim    {
735299742Sdim      apr_size_t digest_size = DIGESTSIZE((*b->checksum)->kind);
736299742Sdim      memcpy(b->digest, (*b->checksum)->digest, digest_size);
737299742Sdim    }
738299742Sdim
739299742Sdim  /* Done here.  Now, close the underlying stream as well. */
740299742Sdim  return svn_error_trace(svn_stream_close(b->inner_stream));
741299742Sdim}
742299742Sdim
743299742Sdim/* Common constructor function for svn_checksum__wrap_write_stream and
744299742Sdim * svn_checksum__wrap_write_stream_fnv1a_32x4, taking the superset of their
745299742Sdim * respecting parameters.
746299742Sdim *
747299742Sdim * In the current usage, either CHECKSUM or DIGEST will be NULL but this
748299742Sdim * function does not enforce any such restriction.  Also, the caller must
749299742Sdim * make sure that DIGEST refers to a buffer of sufficient length.
750299742Sdim */
751299742Sdimstatic svn_stream_t *
752299742Sdimwrap_write_stream(svn_checksum_t **checksum,
753299742Sdim                  unsigned char *digest,
754299742Sdim                  svn_stream_t *inner_stream,
755299742Sdim                  svn_checksum_kind_t kind,
756299742Sdim                  apr_pool_t *pool)
757299742Sdim{
758299742Sdim  svn_stream_t *outer_stream;
759299742Sdim
760299742Sdim  stream_baton_t *baton = apr_pcalloc(pool, sizeof(*baton));
761299742Sdim  baton->inner_stream = inner_stream;
762299742Sdim  baton->context = svn_checksum_ctx_create(kind, pool);
763299742Sdim  baton->checksum = checksum;
764299742Sdim  baton->digest = digest;
765299742Sdim  baton->pool = pool;
766299742Sdim
767299742Sdim  outer_stream = svn_stream_create(baton, pool);
768299742Sdim  svn_stream_set_write(outer_stream, write_handler);
769299742Sdim  svn_stream_set_close(outer_stream, close_handler);
770299742Sdim
771299742Sdim  return outer_stream;
772299742Sdim}
773299742Sdim
774299742Sdimsvn_stream_t *
775299742Sdimsvn_checksum__wrap_write_stream(svn_checksum_t **checksum,
776299742Sdim                                svn_stream_t *inner_stream,
777299742Sdim                                svn_checksum_kind_t kind,
778299742Sdim                                apr_pool_t *pool)
779299742Sdim{
780299742Sdim  return wrap_write_stream(checksum, NULL, inner_stream, kind, pool);
781299742Sdim}
782299742Sdim
783299742Sdim/* Implement svn_close_fn_t.
784299742Sdim * For FNV-1a-like checksums, we want the checksum as 32 bit integer instead
785299742Sdim * of a big endian 4 byte sequence.  This simply wraps close_handler adding
786299742Sdim * the digest conversion.
787299742Sdim */
788299742Sdimstatic svn_error_t *
789299742Sdimclose_handler_fnv1a_32x4(void *baton)
790299742Sdim{
791299742Sdim  stream_baton_t *b = baton;
792299742Sdim  SVN_ERR(close_handler(baton));
793299742Sdim
794299742Sdim  *(apr_uint32_t *)b->digest = ntohl(*(apr_uint32_t *)b->digest);
795299742Sdim  return SVN_NO_ERROR;
796299742Sdim}
797299742Sdim
798299742Sdimsvn_stream_t *
799299742Sdimsvn_checksum__wrap_write_stream_fnv1a_32x4(apr_uint32_t *digest,
800299742Sdim                                           svn_stream_t *inner_stream,
801299742Sdim                                           apr_pool_t *pool)
802299742Sdim{
803299742Sdim  svn_stream_t *result
804299742Sdim    = wrap_write_stream(NULL, (unsigned char *)digest, inner_stream,
805299742Sdim                        svn_checksum_fnv1a_32x4, pool);
806299742Sdim  svn_stream_set_close(result, close_handler_fnv1a_32x4);
807299742Sdim
808299742Sdim  return result;
809299742Sdim}
810