I need to generate a hash in PHP that matches the output from the HASHBYTES function in T-SQL.
Here is the code that was originally used to generate the hash in T-SQL:
declare @input varchar(15) = [this is the incoming value entered by the user]
declare @salt binary(32) = CRYPT_GEN_RANDOM(32)
declare @saltchars nvarchar(32) = cast(@salt as nvarchar(32) )
declare @inputchars nvarchar(50) = cast(@input as nvarchar(50) )
declare @sha binary(32) = HASHBYTES('SHA2_256', @passwordchars + @saltchars)
For the input "pete1234matt", we get an output of:
salt = 0x75D6215CA44339B645E86BE790FF562D3EE4BC51034FDC6A9697F767BB961B5B
sha = 0x979EEAC93B5F6624269634202D9F3B5B8008113B03A83D104784184E0F9361F8
Now here's the PHP code that I've written, I take the provided and input, then encode those as UTF-16LE as I expect the cast to nvarchar does. Is that correct?
$password = 'pete1234matt';
$salt = 0x75D6215CA44339B645E86BE790FF562D3EE4BC51034FDC6A9697F767BB961B5B;
$saltchars = mb_convert_encoding($salt, 'UTF-16LE');
$passwordchars = mb_convert_encoding($password, 'UTF-16LE');
$sha = hash('sha256', $passwordchars . $saltchars);
//$sha == 17b87bd0491f32f1e474ad9293e2fe5db56ed0a5c9939435f0984fdc04806dc5
//Expected: 979EEAC93B5F6624269634202D9F3B5B8008113B03A83D104784184E0F9361F8
I'm leaning towards there being an encoding issue with the salt but can't quite figure out where i'm going wrong. Can anyone with more knowledge on character encoding & T-SQl lend me a hand here!!
hex2bin()
.$password = 'pete1234matt';
$salt = hex2bin('75D6215CA44339B645E86BE790FF562D3EE4BC51034FDC6A9697F767BB961B5B');
$saltchars = $salt;
$passwordchars = mb_convert_encoding($password, 'UTF-16LE');
$sha = hash('sha256', $passwordchars . $saltchars);
//Expected: 979EEAC93B5F6624269634202D9F3B5B8008113B03A83D104784184E0F9361F8
var_dump($sha);
Output:
string(64) "979eeac93b5f6624269634202d9f3b5b8008113b03a83d104784184e0f9361f8"
Don't make a habit of calling any charset conversion function without explicitly specifying the source encoding. PHP strings are essentially just byte arrays with no concept of encoding. PHP has config settings that control the assumed default charset, but calling mb_convert_encoding($salt, 'UTF-16LE');
with the properly-decoded hex would interpret the string according to the PHP default, likely UTF8 or ISO-8859-1, and turn it into mojibake. When, in reality, that string is already UTF16LE.
Eg:
$test_8 = 'hello 🍆';
$test_16 = mb_convert_encoding($test_8, 'UTF-16LE', 'UTF-8');
$mojibake = mb_convert_encoding($test_16, 'UTF-16LE');
var_dump(
bin2hex($test_8),
bin2hex($test_16),
bin2hex($mojibake)
);
Output:
string(20) "68656c6c6f20f09f8d86"
string(32) "680065006c006c006f0020003cd846df"
string(64) "68000000650000006c0000006c0000006f000000200000003c003f0046003f00"