Skip to content

Commit

Permalink
Use non-multibyte functions for non-mb-string hashes
Browse files Browse the repository at this point in the history
  • Loading branch information
kambereBr committed Jul 9, 2024
1 parent 2ae3775 commit 200f4d5
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 52 deletions.
18 changes: 9 additions & 9 deletions lib/crypt.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ public static function plaintext($string, $key) {
$string = base64_decode($string);

/* bail if the crypt text is invalid */
if (!$string || mb_strlen($string, '8bit') <= 200) {
if (!$string || strlen($string) <= 200) {
return false;
}

/* get the payload and salt */
$crypt_string = mb_substr($string, 192, null, '8bit');
$salt = mb_substr($string, 0, 128, '8bit');
$crypt_string = substr($string, 192);
$salt = substr($string, 0, 128);

/* check the signature. Temporarily allow the same key for hmac validation, eventually remove the $encryption_rounds
* check and require the hmac_rounds check only! */
if (!self::check_hmac($crypt_string, mb_substr($string, 128, 64, '8bit'), $salt, $key, self::$hmac_rounds) &&
!self::check_hmac($crypt_string, mb_substr($string, 128, 64, '8bit'), $salt, $key, self::$encryption_rounds)) {
if (!self::check_hmac($crypt_string, substr($string, 128, 64), $salt, $key, self::$hmac_rounds) &&
!self::check_hmac($crypt_string, substr($string, 128, 64), $salt, $key, self::$encryption_rounds)) {
Hm_Debug::add('HMAC verification failed');
return false;
}
Expand Down Expand Up @@ -160,7 +160,7 @@ public static function generate_salt() {
*/
private static function hash_equals($a, $b) {
$res = 0;
$len = mb_strlen($a, '8bit');
$len = strlen($a);
for ($i = 0; $i < $len; $i++) {
$res |= ord($a[$i]) ^ ord($b[$i]);
}
Expand All @@ -176,7 +176,7 @@ private static function hash_equals($a, $b) {
* @return bool
*/
public static function hash_compare($a, $b) {
if (!is_string($a) || !is_string($b) || mb_strlen($a, '8bit') !== mb_strlen($b, '8bit')) {
if (!is_string($a) || !is_string($b) || strlen($a) !== strlen($b)) {
return false;
}
/* requires PHP >= 7.4 */
Expand Down Expand Up @@ -212,7 +212,7 @@ public static function pbkdf2($key, $salt, $length, $count, $algo) {
}

/* manual version */
$size = mb_strlen(hash($algo, '', true), '8bit');
$size = strlen(hash($algo, '', true));
$len = ceil($length / $size);
$result = '';
for ($i = 1; $i <= $len; $i++) {
Expand All @@ -224,7 +224,7 @@ public static function pbkdf2($key, $salt, $length, $count, $algo) {
}
$result .= $res;
}
return mb_substr($result, 0, $length, '8bit');
return substr($result, 0, $length);
}

/**
Expand Down
8 changes: 4 additions & 4 deletions lib/crypt_sodium.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ public static function plaintext($string, $key) {
}
$res = false;
$raw_string = base64_decode($string);
if (!$raw_string || mb_strlen($raw_string, '8bit') < 60) {
if (!$raw_string || strlen($raw_string) < 60) {
return false;
}
list($salt, $crypt_key) = self::keygen($key, mb_substr($raw_string, 0, 24, '8bit'));
$hmac = mb_substr($raw_string, 24, 32, '8bit');
$crypt_string = mb_substr($raw_string, 56, null, '8bit');
list($salt, $crypt_key) = self::keygen($key, substr($raw_string, 0, 24));
$hmac = substr($raw_string, 24, 32);
$crypt_string = substr($raw_string, 56);

if (Hm_Sodium_Compat::crypto_auth_verify($hmac, $crypt_string, $crypt_key)) {
$res = Hm_Sodium_Compat::crypto_secretbox_open($crypt_string, $salt, $crypt_key);
Expand Down
10 changes: 5 additions & 5 deletions lib/scram.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ private function log($message) {
}
public function generateClientProof($username, $password, $salt, $clientNonce, $serverNonce, $algorithm) {
$iterations = 4096;
$keyLength = mb_strlen(hash($algorithm, '', true), '8bit'); // Dynamically determine key length based on algorithm
$keyLength = strlen(hash($algorithm, '', true)); // Dynamically determine key length based on algorithm

$passwordBytes = hash($algorithm, $password, true);
$saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, $iterations, $keyLength, true);
Expand All @@ -56,8 +56,8 @@ public function authenticateScram($scramAlgorithm, $username, $password, $getSer
// Extract salt and server nonce from the server's challenge
$serverChallenge = base64_decode(mb_substr($response[0], 2));
$parts = explode(',', $serverChallenge);
$serverNonce = base64_decode(mb_substr($parts[0], mb_strpos($parts[0], "=", 0, '8bit') + 1, null, '8bit'));
$salt = base64_decode(mb_substr($parts[1], mb_strpos($parts[1], "=", 0, '8bit') + 1, null, '8bit'));
$serverNonce = base64_decode(substr($parts[0], strpos($parts[0], "=") + 1));
$salt = base64_decode(substr($parts[1], strpos($parts[1], "=") + 1));

// Generate client nonce
$clientNonce = base64_encode(random_bytes(32));
Expand All @@ -79,11 +79,11 @@ public function authenticateScram($scramAlgorithm, $username, $password, $getSer
if (!empty($response) && mb_substr($response[0], 0, 2) == '+ ') {
$serverFinalMessage = base64_decode(mb_substr($response[0], 2));
$parts = explode(',', $serverFinalMessage);
$serverProof = mb_substr($parts[0], mb_strpos($parts[0], "=", 0, '8bit') + 1, null, '8bit');
$serverProof = substr($parts[0], strpos($parts[0], "=") + 1);

// Generate server key
$passwordBytes = hash($algorithm, $password, true);
$saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, 4096, mb_strlen(hash($algorithm, '', true), '8bit'), true);
$saltedPassword = hash_pbkdf2($algorithm, $passwordBytes, $salt, 4096, strlen(hash($algorithm, '', true)), true);
$serverKey = hash_hmac($algorithm, "Server Key", $saltedPassword, true);

// Calculate server signature
Expand Down
6 changes: 3 additions & 3 deletions modules/2fa/modules.php
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,9 @@ function check_2fa_pin($pin, $secret, $pass_len=6) {
$time = pack('N', $time);
$time = str_pad($time, 8, chr(0), STR_PAD_LEFT);
$hash = hash_hmac('sha1', $time, $secret, true);
$offset = ord(mb_substr($hash,-1, null, '8bit')) & 0xF;
$input = mb_substr($hash, $offset, mb_strlen($hash, '8bit') - $offset, '8bit');
$input = unpack("N",mb_substr($input, 0, 4, '8bit'));
$offset = ord(substr($hash,-1)) & 0xF;
$input = substr($hash, $offset, strlen($hash) - $offset);
$input = unpack("N",substr($input, 0, 4));
$inthash = $input[1] & 0x7FFFFFFF;
return $pin === str_pad($inthash % $pin_mod, 6, "0", STR_PAD_LEFT);
}}
Expand Down
4 changes: 2 additions & 2 deletions modules/imap/hm-imap.php
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ public function authenticate($username, $password) {
$cram1 = 'AUTHENTICATE CRAM-MD5' . "\r\n";
$this->send_command($cram1);
$response = $this->get_response();
$challenge = base64_decode(mb_substr(trim($response[0]), 1, null, '8bit'));
$pass = str_repeat(chr(0x00), (64-mb_strlen($password, '8bit')));
$challenge = base64_decode(substr(trim($response[0]), 1));
$pass = str_repeat(chr(0x00), (64-strlen($password)));
$ipad = str_repeat(chr(0x36), 64);
$opad = str_repeat(chr(0x5c), 64);
$digest = bin2hex(pack("H*", md5(($pass ^ $opad) . pack("H*", md5(($pass ^ $ipad) . $challenge)))));
Expand Down
2 changes: 1 addition & 1 deletion modules/imap/hm-jmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public function start_message_stream($uid, $message_part) {
return 0;
}
$this->streaming_msg = $this->get_raw_message_content($blob_id, $name);
return mb_strlen($this->streaming_msg, '8bit');
return strlen($this->streaming_msg);
}

/**
Expand Down
48 changes: 24 additions & 24 deletions modules/smtp/hm-smtp.php
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ function authenticate($username, $password, $mech) {
$result = 'FATAL: SMTP server does not support AUTH CRAM-MD5';
} else {
$challenge = base64_decode(trim($response[0][1][0]));
$password .= str_repeat(chr(0x00), (64-mb_strlen($password, '8bit')));
$password .= str_repeat(chr(0x00), (64-strlen($password)));
$ipad = str_repeat(chr(0x36), 64);
$opad = str_repeat(chr(0x5c), 64);
$digest = bin2hex(pack('H*', md5(($password ^ $opad).pack('H*', md5(($password ^ $ipad).$challenge)))));
Expand Down Expand Up @@ -429,20 +429,20 @@ function authenticate($username, $password, $mech) {
function parse_ntlm_type_two($bin_str) {
$res = array();
$res['vals'] = unpack('a8prefix/Vtype/vname_len/vname_space/Vname_offset/Vflags/A8challenge/A8context/vtarget_len/vtarget_space/Vtarget_offset', base64_decode($bin_str));
$res['name'] = unpack('A'.$res['vals']['name_len'].'name', mb_substr(base64_decode($bin_str), $res['vals']['name_offset'], $res['vals']['name_len'], '8bit'));
$target = mb_substr(base64_decode($bin_str), $res['vals']['target_offset'], $res['vals']['target_len'], '8bit');
$res['name'] = unpack('A'.$res['vals']['name_len'].'name', substr(base64_decode($bin_str), $res['vals']['name_offset'], $res['vals']['name_len']));
$target = substr(base64_decode($bin_str), $res['vals']['target_offset'], $res['vals']['target_len']);
$flds = array(2 => 'domain', 1 => 'server', 4 => 'dns_domain', 3 => 'dns_server');
$names = array('domain' => '', 'server' => '', 'dns_domain' => '', 'dns_server' => '');
while ($target) {
$atts = unpack('vfld/vlen', $target);
if ($atts['fld'] == 0) {
break;
}
$fld = unpack('A'.$atts['len'], mb_substr($target, 4, null, '8bit'));
$fld = unpack('A'.$atts['len'], substr($target, 4));
if (isset($flds[$atts['fld']])) {
$names[$flds[$atts['fld']]] = $fld;
}
$target = mb_substr($target, (4 + $atts['len']), null, '8bit');
$target = substr($target, (4 + $atts['len']));
}
$res['names'] = $names;
return $res;
Expand All @@ -466,17 +466,17 @@ function build_ntlm_type_three($msg_data, $username, $password) {
$lm_response = $this->build_lm_response($msg_data, $username, $password);
$ntlm_response = $this->build_ntlm_response($msg_data, $username, $password);
$flags = pack('V', 0x00000201);
$offset = mb_strlen($pre.$type, '8bit')+52;
$target_sec = $this->ntlm_security_buffer(mb_strlen($target, '8bit'), $offset);
$offset += mb_strlen($target, '8bit');
$offset = strlen($pre.$type)+52;
$target_sec = $this->ntlm_security_buffer(strlen($target), $offset);
$offset += strlen($target);
$user_sec = $this->ntlm_security_buffer(mb_strlen($username), $offset);
$offset += mb_strlen($username);
$host_sec = $this->ntlm_security_buffer(mb_strlen($host, '8bit'), $offset);
$host_sec = $this->ntlm_security_buffer(strlen($host), $offset);
$offset += mb_strlen($host);
$lm_sec = $this->ntlm_security_buffer(mb_strlen($lm_response, '8bit'), $offset);
$offset += mb_strlen($lm_response, '8bit');
$ntlm_sec = $this->ntlm_security_buffer(mb_strlen($ntlm_response, '8bit'), $offset);
$offset += mb_strlen($ntlm_response, '8bit');
$lm_sec = $this->ntlm_security_buffer(strlen($lm_response), $offset);
$offset += strlen($lm_response);
$ntlm_sec = $this->ntlm_security_buffer(strlen($ntlm_response), $offset);
$offset += strlen($ntlm_response);
$sess_sec = $this->ntlm_security_buffer(0, $offset);
return base64_encode($pre.$type.$lm_sec.$ntlm_sec.$target_sec.$user_sec.$host_sec.$sess_sec.$flags.$target.$username.$host.$lm_response.$ntlm_response);
}
Expand All @@ -488,17 +488,17 @@ function ntlm_security_buffer($len, $offset) {

/* build the NTLM lm hash then ecnrypt the challenge string with it */
function build_lm_response($msg_data, $username, $password){
$pass = mb_strtoupper($password, '8bit');
while (mb_strlen($pass, '8bit') < 14) {
$pass = strtoupper($password);
while (strlen($pass) < 14) {
$pass .= chr(0);
}
if (mb_strlen($pass, '8bit') > 14) {
if (strlen($pass) > 14) {
return str_repeat(chr(0), 16);
}
$p1 = mb_substr($pass, 0, 7, '8bit');
$p2 = mb_substr($pass, 7, null, '8bit');
$p1 = substr($pass, 0, 7);
$p2 = substr($pass, 7);
$lm_hash = $this->des_encrypt($p1).$this->des_encrypt($p2);
while (mb_strlen($lm_hash, '8bit') < 21) {
while (strlen($lm_hash) < 21) {
$lm_hash .= chr(0);
}
return $this->apply_ntlm_hash($msg_data['vals']['challenge'], $lm_hash);
Expand All @@ -508,17 +508,17 @@ function build_lm_response($msg_data, $username, $password){
function build_ntlm_response($msg_data, $username, $password){
$password = iconv('UTF-8', 'UTF-16LE', $password);
$ntlm_hash = hash('md4', $password, true);
while (mb_strlen($ntlm_hash, '8bit') < 21) {
while (strlen($ntlm_hash) < 21) {
$ntlm_hash .= chr(0);
}
return $this->apply_ntlm_hash($msg_data['vals']['challenge'], $ntlm_hash);
}

/* encrypt the challenge string with the lm/ntlm hash */
function apply_ntlm_hash($challenge, $hash) {
$p1 = mb_substr($hash, 0, 7, '8bit');
$p2 = mb_substr($hash, 7, 7, '8bit');
$p3 = mb_substr($hash, 14, 7, '8bit');
$p1 = substr($hash, 0, 7);
$p2 = substr($hash, 7, 7);
$p3 = substr($hash, 14, 7);
return $this->des_encrypt($p1, $challenge).
$this->des_encrypt($p2, $challenge).
$this->des_encrypt($p3, $challenge);
Expand All @@ -528,7 +528,7 @@ function apply_ntlm_hash($challenge, $hash) {
function des_encrypt($string, $challenge='KGS!@#$%') {
$key = array();
$tmp = array();
$len = mb_strlen($string, '8bit');
$len = strlen($string);
for ($i=0; $i<7; ++$i)
$tmp[] = $i < $len ? ord($string[$i]) : 0;
$key[] = $tmp[0] & 254;
Expand Down
8 changes: 4 additions & 4 deletions tests/phpunit/crypt_base.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function setUp(): void {
*/
public function test_random() {
$bytes = Hm_Crypt::random(10);
$this->assertTrue(mb_strlen($bytes, '8bit') == 10);
$this->assertTrue(strlen($bytes) == 10);
Hm_Functions::$rand_bytes = 'bad';
try {
Hm_Crypt::random(10);
Expand Down Expand Up @@ -104,8 +104,8 @@ public function test_pbkdf2() {
* @runInSeparateProcess
*/
public function test_unique_id() {
$this->assertEquals(24, mb_strlen(base64_decode(Hm_Crypt::unique_id(24)), '8bit'));
$this->assertEquals(48, mb_strlen(base64_decode(Hm_Crypt::unique_id(48)), '8bit'));
$this->assertEquals(128, mb_strlen(base64_decode(Hm_Crypt::unique_id()), '8bit'));
$this->assertEquals(24, strlen(base64_decode(Hm_Crypt::unique_id(24))));
$this->assertEquals(48, strlen(base64_decode(Hm_Crypt::unique_id(48))));
$this->assertEquals(128, strlen(base64_decode(Hm_Crypt::unique_id())));
}
}

0 comments on commit 200f4d5

Please sign in to comment.