|
21 | 21 |
|
22 | 22 | #include <math.h> |
23 | 23 | #include "php_hash.h" |
| 24 | +#include "php_hash_sha.h" |
24 | 25 | #include "ext/standard/info.h" |
25 | 26 | #include "ext/standard/file.h" |
26 | 27 | #include "ext/standard/php_var.h" |
27 | 28 |
|
| 29 | +/* Internal helper from hash_sha.c, not part of the public hash API. */ |
| 30 | +void php_hash_sha256_final32_from_context(unsigned char digest[32], const PHP_SHA256_CTX *context, const unsigned char data[32]); |
| 31 | + |
28 | 32 | #include "zend_attributes.h" |
29 | 33 | #include "zend_exceptions.h" |
30 | 34 | #include "zend_interfaces.h" |
@@ -502,6 +506,127 @@ static inline void php_hash_hmac_round_with_copy(unsigned char *final, const php |
502 | 506 | ops->hash_final(final, context); |
503 | 507 | } |
504 | 508 |
|
| 509 | +static const unsigned char php_hash_sha_padding[128] = { |
| 510 | + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 511 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 512 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 513 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 514 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 515 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 516 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, |
| 517 | + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 |
| 518 | +}; |
| 519 | + |
| 520 | +static inline void php_hash_sha256_encode(unsigned char digest[32], const uint32_t state[8]) { |
| 521 | + for (size_t i = 0; i < 8; i++) { |
| 522 | + digest[i * 4] = (unsigned char) ((state[i] >> 24) & 0xff); |
| 523 | + digest[(i * 4) + 1] = (unsigned char) ((state[i] >> 16) & 0xff); |
| 524 | + digest[(i * 4) + 2] = (unsigned char) ((state[i] >> 8) & 0xff); |
| 525 | + digest[(i * 4) + 3] = (unsigned char) (state[i] & 0xff); |
| 526 | + } |
| 527 | +} |
| 528 | + |
| 529 | +static inline void php_hash_sha256_final_no_zero(unsigned char digest[32], PHP_SHA256_CTX *context) { |
| 530 | + unsigned char bits[8]; |
| 531 | + unsigned int index, pad_len; |
| 532 | + |
| 533 | + bits[7] = (unsigned char) (context->count[0] & 0xFF); |
| 534 | + bits[6] = (unsigned char) ((context->count[0] >> 8) & 0xFF); |
| 535 | + bits[5] = (unsigned char) ((context->count[0] >> 16) & 0xFF); |
| 536 | + bits[4] = (unsigned char) ((context->count[0] >> 24) & 0xFF); |
| 537 | + bits[3] = (unsigned char) (context->count[1] & 0xFF); |
| 538 | + bits[2] = (unsigned char) ((context->count[1] >> 8) & 0xFF); |
| 539 | + bits[1] = (unsigned char) ((context->count[1] >> 16) & 0xFF); |
| 540 | + bits[0] = (unsigned char) ((context->count[1] >> 24) & 0xFF); |
| 541 | + |
| 542 | + index = (unsigned int) ((context->count[0] >> 3) & 0x3f); |
| 543 | + pad_len = (index < 56) ? (56 - index) : (120 - index); |
| 544 | + PHP_SHA256Update(context, php_hash_sha_padding, pad_len); |
| 545 | + PHP_SHA256Update(context, bits, 8); |
| 546 | + |
| 547 | + php_hash_sha256_encode(digest, context->state); |
| 548 | +} |
| 549 | + |
| 550 | +static zend_string *php_hash_pbkdf2_sha256(const char *pass, size_t pass_len, const char *salt, size_t salt_len, zend_long iterations, zend_long length, bool raw_output) { |
| 551 | + zend_string *returnval; |
| 552 | + unsigned char *digest, *result, *K = NULL, counter[4]; |
| 553 | + zend_long loops, i, j, digest_length = 0; |
| 554 | + const size_t digest_size = 32; |
| 555 | + const size_t block_size = 64; |
| 556 | + PHP_SHA256_CTX inner_context, outer_context, context; |
| 557 | + |
| 558 | + memset(&context, 0, sizeof(PHP_SHA256_CTX)); |
| 559 | + K = emalloc(block_size); |
| 560 | + digest = emalloc(digest_size); |
| 561 | + |
| 562 | + php_hash_hmac_prep_key(K, &php_hash_sha256_ops, &context, (const unsigned char *) pass, pass_len); |
| 563 | + |
| 564 | + PHP_SHA256Init(&inner_context); |
| 565 | + PHP_SHA256Update(&inner_context, K, block_size); |
| 566 | + |
| 567 | + php_hash_string_xor_char(K, K, 0x6A, block_size); |
| 568 | + PHP_SHA256Init(&outer_context); |
| 569 | + PHP_SHA256Update(&outer_context, K, block_size); |
| 570 | + |
| 571 | + if (length == 0) { |
| 572 | + length = digest_size; |
| 573 | + if (!raw_output) { |
| 574 | + length = length * 2; |
| 575 | + } |
| 576 | + } |
| 577 | + digest_length = length; |
| 578 | + if (!raw_output) { |
| 579 | + digest_length = (zend_long) ceil((float) length / 2.0); |
| 580 | + } |
| 581 | + |
| 582 | + loops = (zend_long) ceil((float) digest_length / (float) digest_size); |
| 583 | + |
| 584 | + result = safe_emalloc(loops, digest_size, 0); |
| 585 | + |
| 586 | + for (i = 1; i <= loops; i++) { |
| 587 | + unsigned char *result_block = result + ((i - 1) * digest_size); |
| 588 | + |
| 589 | + counter[0] = (unsigned char) (i >> 24); |
| 590 | + counter[1] = (unsigned char) ((i & 0xFF0000) >> 16); |
| 591 | + counter[2] = (unsigned char) ((i & 0xFF00) >> 8); |
| 592 | + counter[3] = (unsigned char) (i & 0xFF); |
| 593 | + |
| 594 | + context = inner_context; |
| 595 | + PHP_SHA256Update(&context, (const unsigned char *) salt, salt_len); |
| 596 | + PHP_SHA256Update(&context, counter, sizeof(counter)); |
| 597 | + php_hash_sha256_final_no_zero(digest, &context); |
| 598 | + |
| 599 | + php_hash_sha256_final32_from_context(digest, &outer_context, digest); |
| 600 | + memcpy(result_block, digest, digest_size); |
| 601 | + |
| 602 | + for (j = 1; j < iterations; j++) { |
| 603 | + php_hash_sha256_final32_from_context(digest, &inner_context, digest); |
| 604 | + php_hash_sha256_final32_from_context(digest, &outer_context, digest); |
| 605 | + |
| 606 | + php_hash_string_xor(result_block, result_block, digest, digest_size); |
| 607 | + } |
| 608 | + } |
| 609 | + |
| 610 | + ZEND_SECURE_ZERO(K, block_size); |
| 611 | + ZEND_SECURE_ZERO(digest, digest_size); |
| 612 | + ZEND_SECURE_ZERO(&inner_context, sizeof(PHP_SHA256_CTX)); |
| 613 | + ZEND_SECURE_ZERO(&outer_context, sizeof(PHP_SHA256_CTX)); |
| 614 | + ZEND_SECURE_ZERO(&context, sizeof(PHP_SHA256_CTX)); |
| 615 | + efree(K); |
| 616 | + efree(digest); |
| 617 | + |
| 618 | + returnval = zend_string_alloc(length, 0); |
| 619 | + if (raw_output) { |
| 620 | + memcpy(ZSTR_VAL(returnval), result, length); |
| 621 | + } else { |
| 622 | + php_hash_bin2hex(ZSTR_VAL(returnval), result, digest_length); |
| 623 | + } |
| 624 | + ZSTR_VAL(returnval)[length] = 0; |
| 625 | + efree(result); |
| 626 | + |
| 627 | + return returnval; |
| 628 | +} |
| 629 | + |
505 | 630 | static void php_hash_do_hash_hmac( |
506 | 631 | zval *return_value, zend_string *algo, char *data, size_t data_len, char *key, size_t key_len, bool raw_output, bool isfilename |
507 | 632 | ) /* {{{ */ { |
@@ -1029,6 +1154,10 @@ PHP_FUNCTION(hash_pbkdf2) |
1029 | 1154 | RETURN_THROWS(); |
1030 | 1155 | } |
1031 | 1156 |
|
| 1157 | + if (ops == &php_hash_sha256_ops) { |
| 1158 | + RETURN_NEW_STR(php_hash_pbkdf2_sha256(pass, pass_len, salt, salt_len, iterations, length, raw_output)); |
| 1159 | + } |
| 1160 | + |
1032 | 1161 | inner_context = php_hash_alloc_context(ops); |
1033 | 1162 | outer_context = php_hash_alloc_context(ops); |
1034 | 1163 | context = php_hash_alloc_context(ops); |
|
0 commit comments