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