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