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