Fossil SCM
Add HMAC-SHA1 implementation.
Commit
dcee34b25f4f4a1fc14e15b07c564cb78c2f5a6d
Parent
d4a341b49dd1b70…
1 file changed
+178
+178
| --- src/sha1.c | ||
| +++ src/sha1.c | ||
| @@ -199,10 +199,71 @@ | ||
| 199 | 199 | digest[i] = (unsigned char) |
| 200 | 200 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 201 | 201 | } |
| 202 | 202 | } |
| 203 | 203 | |
| 204 | +typedef struct HMAC_SHA1Context HMAC_SHA1Context; | |
| 205 | +struct HMAC_SHA1Context { | |
| 206 | + SHA1Context inner; | |
| 207 | + SHA1Context outer; | |
| 208 | +}; | |
| 209 | + | |
| 210 | +static void HMAC_SHA1Init( | |
| 211 | + HMAC_SHA1Context *context, | |
| 212 | + const unsigned char *key, | |
| 213 | + unsigned int keylen | |
| 214 | +){ | |
| 215 | + unsigned char pad[64]; /* 64 is SHA-1 block length */ | |
| 216 | + unsigned char keyhash[20]; | |
| 217 | + unsigned int i; | |
| 218 | + const unsigned char *k = key; | |
| 219 | + | |
| 220 | + if( keylen > sizeof(pad) ){ | |
| 221 | + /* Key is too big, hash it, and use this hash as a key */ | |
| 222 | + SHA1Init(&context->inner); | |
| 223 | + SHA1Update(&context->inner, k, keylen); | |
| 224 | + SHA1Final(&context->inner, keyhash); | |
| 225 | + k = keyhash; | |
| 226 | + keylen = sizeof(keyhash); | |
| 227 | + } | |
| 228 | + | |
| 229 | + /* Initialize inner hash */ | |
| 230 | + SHA1Init(&context->inner); | |
| 231 | + memset(pad, 0x36, sizeof(pad)); | |
| 232 | + for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; } | |
| 233 | + SHA1Update(&context->inner, pad, sizeof(pad)); | |
| 234 | + | |
| 235 | + /* Initialize outer hash */ | |
| 236 | + SHA1Init(&context->outer); | |
| 237 | + memset(pad, 0x5C, sizeof(pad)); | |
| 238 | + for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; } | |
| 239 | + SHA1Update(&context->outer, pad, sizeof(pad)); | |
| 240 | + | |
| 241 | + /* Cleanup */ | |
| 242 | + memset(keyhash, 0, sizeof(keyhash)); | |
| 243 | +} | |
| 244 | + | |
| 245 | +static void HMAC_SHA1Update( | |
| 246 | + HMAC_SHA1Context *context, | |
| 247 | + const unsigned char *data, | |
| 248 | + unsigned int len | |
| 249 | +){ | |
| 250 | + SHA1Update(&context->inner, data, len); | |
| 251 | +} | |
| 252 | + | |
| 253 | +static void HMAC_SHA1Final( | |
| 254 | + HMAC_SHA1Context *context, | |
| 255 | + unsigned char digest[20] | |
| 256 | +){ | |
| 257 | + unsigned char innerDigest[20]; | |
| 258 | + SHA1Final(&context->inner, innerDigest); | |
| 259 | + /* Mix inner into outer */ | |
| 260 | + SHA1Update(&context->outer, innerDigest, sizeof(innerDigest)); | |
| 261 | + SHA1Final(&context->outer, digest); | |
| 262 | + /* Cleanup */ | |
| 263 | + memset(innerDigest, 0, sizeof(innerDigest)); | |
| 264 | +} | |
| 204 | 265 | |
| 205 | 266 | /* |
| 206 | 267 | ** Convert a digest into base-16. digest should be declared as |
| 207 | 268 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 208 | 269 | ** digest is stored in the first 20 bytes. zBuf should |
| @@ -350,10 +411,103 @@ | ||
| 350 | 411 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 351 | 412 | SHA1Final(&ctx, zResult); |
| 352 | 413 | DigestToBase16(zResult, zDigest); |
| 353 | 414 | return mprintf("%s", zDigest); |
| 354 | 415 | } |
| 416 | + | |
| 417 | +/* | |
| 418 | +** Compute HMAC-SHA1 of a zero-terminated string. The | |
| 419 | +** result (hex string) is held in memory obtained from mprintf(). | |
| 420 | +*/ | |
| 421 | +char *hmac_sign(const char *zKey, const char *zData){ | |
| 422 | + HMAC_SHA1Context ctx; | |
| 423 | + unsigned char zResult[20]; | |
| 424 | + char zDigest[41]; | |
| 425 | + | |
| 426 | + HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey)); | |
| 427 | + HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData)); | |
| 428 | + HMAC_SHA1Final(&ctx, zResult); | |
| 429 | + DigestToBase16(zResult, zDigest); | |
| 430 | + return mprintf("%s", zDigest); | |
| 431 | +} | |
| 432 | + | |
| 433 | +/* | |
| 434 | +** Like hmac_sign(), but uses double-HMAC construction: | |
| 435 | +** HMAC(HMAC(zKey, zData), zData) | |
| 436 | +*/ | |
| 437 | +char *hmac_double_sign(const char *zKey, const char *zData){ | |
| 438 | + HMAC_SHA1Context ctx; | |
| 439 | + unsigned char zInterKey[20], zResult[20]; | |
| 440 | + char zDigest[41]; | |
| 441 | + | |
| 442 | + /* First derive intermediate key */ | |
| 443 | + HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey)); | |
| 444 | + HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData)); | |
| 445 | + HMAC_SHA1Final(&ctx, zInterKey); | |
| 446 | + /* Use intermediate key to sign data */ | |
| 447 | + HMAC_SHA1Init(&ctx, zInterKey, sizeof(zInterKey)); | |
| 448 | + HMAC_SHA1Update(&ctx, (unsigned const char *)zData, strlen(zData)); | |
| 449 | + HMAC_SHA1Final(&ctx, zResult); | |
| 450 | + | |
| 451 | + DigestToBase16(zResult, zDigest); | |
| 452 | + return mprintf("%s", zDigest); | |
| 453 | +} | |
| 454 | + | |
| 455 | +/* | |
| 456 | +** Constant-time comparison of buffers b1 and b2, which both have length len | |
| 457 | +** Returns 0 on successful verification, any other number on failure. | |
| 458 | +*/ | |
| 459 | +static int verify_bytes( | |
| 460 | + const unsigned char *b1, | |
| 461 | + const unsigned char *b2, | |
| 462 | + unsigned int len | |
| 463 | +){ | |
| 464 | + unsigned int i; | |
| 465 | + unsigned char rc = 0; | |
| 466 | + if( len==0 ) return 1; | |
| 467 | + for( i=0; i<len; i++){ | |
| 468 | + rc |= b1[i] ^ b2[i]; | |
| 469 | + } | |
| 470 | + return rc; | |
| 471 | +} | |
| 472 | + | |
| 473 | +/* | |
| 474 | +** Verify that hex string zDigest (result of hmac_sign) is the one | |
| 475 | +** that was used to sign zData with secret key zKey. | |
| 476 | +** All strings are zero-terminated. | |
| 477 | +** Returns 0 on successful verification, any other number on failure. | |
| 478 | +*/ | |
| 479 | +int hmac_verify(const char *zDigest, const char *zKey, const char *zData) | |
| 480 | +{ | |
| 481 | + char *zCalcDigest; | |
| 482 | + unsigned int len, i, rc = 0; | |
| 483 | + | |
| 484 | + zCalcDigest = hmac_sign(zKey, zData); | |
| 485 | + len = strlen(zCalcDigest); | |
| 486 | + if( len==0 || len != strlen(zDigest) ) return 1; | |
| 487 | + rc = verify_bytes((const unsigned char*)zDigest, | |
| 488 | + (const unsigned char*)zCalcDigest, len); | |
| 489 | + fossil_free(zCalcDigest); | |
| 490 | + return rc; | |
| 491 | +} | |
| 492 | + | |
| 493 | +/* | |
| 494 | +** Like hmac_verify(), but uses double-HMAC. | |
| 495 | +*/ | |
| 496 | +int hmac_double_verify(const char *zDigest, const char *zKey, const char *zData) | |
| 497 | +{ | |
| 498 | + char *zCalcDigest; | |
| 499 | + unsigned int len, i, rc = 0; | |
| 500 | + | |
| 501 | + zCalcDigest = hmac_double_sign(zKey, zData); | |
| 502 | + len = strlen(zCalcDigest); | |
| 503 | + if( len==0 || len != strlen(zDigest) ) return 1; | |
| 504 | + rc = verify_bytes((const unsigned char*)zDigest, | |
| 505 | + (const unsigned char*)zCalcDigest, len); | |
| 506 | + fossil_free(zCalcDigest); | |
| 507 | + return rc; | |
| 508 | +} | |
| 355 | 509 | |
| 356 | 510 | /* |
| 357 | 511 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 358 | 512 | ** |
| 359 | 513 | ** The algorithm here is: |
| @@ -460,5 +614,29 @@ | ||
| 460 | 614 | } |
| 461 | 615 | fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); |
| 462 | 616 | blob_reset(&cksum); |
| 463 | 617 | } |
| 464 | 618 | } |
| 619 | + | |
| 620 | +/* | |
| 621 | +** COMMAND: test-hmac | |
| 622 | +** %fossil test-hmac secret data | |
| 623 | +*/ | |
| 624 | +void hmac_test(void){ | |
| 625 | + assert( g.argc==4 ); | |
| 626 | + fossil_print("HMAC(%s, %s) = ", g.argv[2], g.argv[3]); | |
| 627 | + fossil_print("%s\n", hmac_sign(g.argv[2], g.argv[3])); | |
| 628 | +} | |
| 629 | + | |
| 630 | +/* | |
| 631 | +** COMMAND: test-hmac-verify | |
| 632 | +** %fossil test-hmac-verify digest secret data | |
| 633 | +*/ | |
| 634 | +void hmac_verify_test(void){ | |
| 635 | + assert( g.argc==5 ); | |
| 636 | + if( hmac_verify(g.argv[2], g.argv[3], g.argv[4])==0 ){ | |
| 637 | + fossil_print("successfuly verified\n"); | |
| 638 | + }else{ | |
| 639 | + fossil_print("not verified\n"); | |
| 640 | + } | |
| 641 | +} | |
| 642 | + | |
| 465 | 643 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -199,10 +199,71 @@ | |
| 199 | digest[i] = (unsigned char) |
| 200 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | |
| 205 | /* |
| 206 | ** Convert a digest into base-16. digest should be declared as |
| 207 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 208 | ** digest is stored in the first 20 bytes. zBuf should |
| @@ -350,10 +411,103 @@ | |
| 350 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 351 | SHA1Final(&ctx, zResult); |
| 352 | DigestToBase16(zResult, zDigest); |
| 353 | return mprintf("%s", zDigest); |
| 354 | } |
| 355 | |
| 356 | /* |
| 357 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 358 | ** |
| 359 | ** The algorithm here is: |
| @@ -460,5 +614,29 @@ | |
| 460 | } |
| 461 | fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); |
| 462 | blob_reset(&cksum); |
| 463 | } |
| 464 | } |
| 465 |
| --- src/sha1.c | |
| +++ src/sha1.c | |
| @@ -199,10 +199,71 @@ | |
| 199 | digest[i] = (unsigned char) |
| 200 | ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); |
| 201 | } |
| 202 | } |
| 203 | |
| 204 | typedef struct HMAC_SHA1Context HMAC_SHA1Context; |
| 205 | struct HMAC_SHA1Context { |
| 206 | SHA1Context inner; |
| 207 | SHA1Context outer; |
| 208 | }; |
| 209 | |
| 210 | static void HMAC_SHA1Init( |
| 211 | HMAC_SHA1Context *context, |
| 212 | const unsigned char *key, |
| 213 | unsigned int keylen |
| 214 | ){ |
| 215 | unsigned char pad[64]; /* 64 is SHA-1 block length */ |
| 216 | unsigned char keyhash[20]; |
| 217 | unsigned int i; |
| 218 | const unsigned char *k = key; |
| 219 | |
| 220 | if( keylen > sizeof(pad) ){ |
| 221 | /* Key is too big, hash it, and use this hash as a key */ |
| 222 | SHA1Init(&context->inner); |
| 223 | SHA1Update(&context->inner, k, keylen); |
| 224 | SHA1Final(&context->inner, keyhash); |
| 225 | k = keyhash; |
| 226 | keylen = sizeof(keyhash); |
| 227 | } |
| 228 | |
| 229 | /* Initialize inner hash */ |
| 230 | SHA1Init(&context->inner); |
| 231 | memset(pad, 0x36, sizeof(pad)); |
| 232 | for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; } |
| 233 | SHA1Update(&context->inner, pad, sizeof(pad)); |
| 234 | |
| 235 | /* Initialize outer hash */ |
| 236 | SHA1Init(&context->outer); |
| 237 | memset(pad, 0x5C, sizeof(pad)); |
| 238 | for( i=0; i<keylen; i++ ){ pad[i] ^= k[i]; } |
| 239 | SHA1Update(&context->outer, pad, sizeof(pad)); |
| 240 | |
| 241 | /* Cleanup */ |
| 242 | memset(keyhash, 0, sizeof(keyhash)); |
| 243 | } |
| 244 | |
| 245 | static void HMAC_SHA1Update( |
| 246 | HMAC_SHA1Context *context, |
| 247 | const unsigned char *data, |
| 248 | unsigned int len |
| 249 | ){ |
| 250 | SHA1Update(&context->inner, data, len); |
| 251 | } |
| 252 | |
| 253 | static void HMAC_SHA1Final( |
| 254 | HMAC_SHA1Context *context, |
| 255 | unsigned char digest[20] |
| 256 | ){ |
| 257 | unsigned char innerDigest[20]; |
| 258 | SHA1Final(&context->inner, innerDigest); |
| 259 | /* Mix inner into outer */ |
| 260 | SHA1Update(&context->outer, innerDigest, sizeof(innerDigest)); |
| 261 | SHA1Final(&context->outer, digest); |
| 262 | /* Cleanup */ |
| 263 | memset(innerDigest, 0, sizeof(innerDigest)); |
| 264 | } |
| 265 | |
| 266 | /* |
| 267 | ** Convert a digest into base-16. digest should be declared as |
| 268 | ** "unsigned char digest[20]" in the calling function. The SHA1 |
| 269 | ** digest is stored in the first 20 bytes. zBuf should |
| @@ -350,10 +411,103 @@ | |
| 411 | SHA1Update(&ctx, (unsigned const char*)zIn, strlen(zIn)); |
| 412 | SHA1Final(&ctx, zResult); |
| 413 | DigestToBase16(zResult, zDigest); |
| 414 | return mprintf("%s", zDigest); |
| 415 | } |
| 416 | |
| 417 | /* |
| 418 | ** Compute HMAC-SHA1 of a zero-terminated string. The |
| 419 | ** result (hex string) is held in memory obtained from mprintf(). |
| 420 | */ |
| 421 | char *hmac_sign(const char *zKey, const char *zData){ |
| 422 | HMAC_SHA1Context ctx; |
| 423 | unsigned char zResult[20]; |
| 424 | char zDigest[41]; |
| 425 | |
| 426 | HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey)); |
| 427 | HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData)); |
| 428 | HMAC_SHA1Final(&ctx, zResult); |
| 429 | DigestToBase16(zResult, zDigest); |
| 430 | return mprintf("%s", zDigest); |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | ** Like hmac_sign(), but uses double-HMAC construction: |
| 435 | ** HMAC(HMAC(zKey, zData), zData) |
| 436 | */ |
| 437 | char *hmac_double_sign(const char *zKey, const char *zData){ |
| 438 | HMAC_SHA1Context ctx; |
| 439 | unsigned char zInterKey[20], zResult[20]; |
| 440 | char zDigest[41]; |
| 441 | |
| 442 | /* First derive intermediate key */ |
| 443 | HMAC_SHA1Init(&ctx, (unsigned const char *)zKey, strlen(zKey)); |
| 444 | HMAC_SHA1Update(&ctx, (unsigned const char*)zData, strlen(zData)); |
| 445 | HMAC_SHA1Final(&ctx, zInterKey); |
| 446 | /* Use intermediate key to sign data */ |
| 447 | HMAC_SHA1Init(&ctx, zInterKey, sizeof(zInterKey)); |
| 448 | HMAC_SHA1Update(&ctx, (unsigned const char *)zData, strlen(zData)); |
| 449 | HMAC_SHA1Final(&ctx, zResult); |
| 450 | |
| 451 | DigestToBase16(zResult, zDigest); |
| 452 | return mprintf("%s", zDigest); |
| 453 | } |
| 454 | |
| 455 | /* |
| 456 | ** Constant-time comparison of buffers b1 and b2, which both have length len |
| 457 | ** Returns 0 on successful verification, any other number on failure. |
| 458 | */ |
| 459 | static int verify_bytes( |
| 460 | const unsigned char *b1, |
| 461 | const unsigned char *b2, |
| 462 | unsigned int len |
| 463 | ){ |
| 464 | unsigned int i; |
| 465 | unsigned char rc = 0; |
| 466 | if( len==0 ) return 1; |
| 467 | for( i=0; i<len; i++){ |
| 468 | rc |= b1[i] ^ b2[i]; |
| 469 | } |
| 470 | return rc; |
| 471 | } |
| 472 | |
| 473 | /* |
| 474 | ** Verify that hex string zDigest (result of hmac_sign) is the one |
| 475 | ** that was used to sign zData with secret key zKey. |
| 476 | ** All strings are zero-terminated. |
| 477 | ** Returns 0 on successful verification, any other number on failure. |
| 478 | */ |
| 479 | int hmac_verify(const char *zDigest, const char *zKey, const char *zData) |
| 480 | { |
| 481 | char *zCalcDigest; |
| 482 | unsigned int len, i, rc = 0; |
| 483 | |
| 484 | zCalcDigest = hmac_sign(zKey, zData); |
| 485 | len = strlen(zCalcDigest); |
| 486 | if( len==0 || len != strlen(zDigest) ) return 1; |
| 487 | rc = verify_bytes((const unsigned char*)zDigest, |
| 488 | (const unsigned char*)zCalcDigest, len); |
| 489 | fossil_free(zCalcDigest); |
| 490 | return rc; |
| 491 | } |
| 492 | |
| 493 | /* |
| 494 | ** Like hmac_verify(), but uses double-HMAC. |
| 495 | */ |
| 496 | int hmac_double_verify(const char *zDigest, const char *zKey, const char *zData) |
| 497 | { |
| 498 | char *zCalcDigest; |
| 499 | unsigned int len, i, rc = 0; |
| 500 | |
| 501 | zCalcDigest = hmac_double_sign(zKey, zData); |
| 502 | len = strlen(zCalcDigest); |
| 503 | if( len==0 || len != strlen(zDigest) ) return 1; |
| 504 | rc = verify_bytes((const unsigned char*)zDigest, |
| 505 | (const unsigned char*)zCalcDigest, len); |
| 506 | fossil_free(zCalcDigest); |
| 507 | return rc; |
| 508 | } |
| 509 | |
| 510 | /* |
| 511 | ** Convert a cleartext password for a specific user into a SHA1 hash. |
| 512 | ** |
| 513 | ** The algorithm here is: |
| @@ -460,5 +614,29 @@ | |
| 614 | } |
| 615 | fossil_print("%s %s\n", blob_str(&cksum), g.argv[i]); |
| 616 | blob_reset(&cksum); |
| 617 | } |
| 618 | } |
| 619 | |
| 620 | /* |
| 621 | ** COMMAND: test-hmac |
| 622 | ** %fossil test-hmac secret data |
| 623 | */ |
| 624 | void hmac_test(void){ |
| 625 | assert( g.argc==4 ); |
| 626 | fossil_print("HMAC(%s, %s) = ", g.argv[2], g.argv[3]); |
| 627 | fossil_print("%s\n", hmac_sign(g.argv[2], g.argv[3])); |
| 628 | } |
| 629 | |
| 630 | /* |
| 631 | ** COMMAND: test-hmac-verify |
| 632 | ** %fossil test-hmac-verify digest secret data |
| 633 | */ |
| 634 | void hmac_verify_test(void){ |
| 635 | assert( g.argc==5 ); |
| 636 | if( hmac_verify(g.argv[2], g.argv[3], g.argv[4])==0 ){ |
| 637 | fossil_print("successfuly verified\n"); |
| 638 | }else{ |
| 639 | fossil_print("not verified\n"); |
| 640 | } |
| 641 | } |
| 642 | |
| 643 |