Реализация PHP Pack/Unpack в несоответствии Javascript


Согласно соответствующему ответу на этот вопрос , я пытаюсь собрать решение для упаковки/распаковки, напоминающее этот процесс PHP, однако в Nodejs (Javascript) с использованием md5 и буферный пакет

Вот подход PHP (адаптированный из Daloradius:

  $challenge = 'c731395aca5dcf45446c0ae83db5319e';
  $uamsecret = 'secret';
  $password = 'password';

  $hexchal = pack ("H32", $challenge);
  $newchal = pack ("H*", md5($hexchal . $uamsecret));
  $response = md5("\0" . $password . $newchal);
  $newpwd = pack("a32", $password);
  $pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));

  echo "Response: ---> ", $response, "\n";
  echo "New Password: ---> ", $newpwd, "\n";
  echo "Pap Password: ---> ", $pappassword, "\n";

Вышесказанное перекликается с этим:

Responses PHP

Выше открытым текстом:

Response: ---> 2d4bd27184f5eb032641137f728c6043
New Password: ---> password
Pap Password: ---> 356a1fb08f909fc400dfe448fc483ce3

В Javascript вот что я сейчас делаю:

  var challenge = 'c731395aca5dcf45446c0ae83db5319e';
  var uamsecret = 'secret';
  var password = 'password';

  var hexchal = pack.pack("H32", challenge);
  var newchal = pack.pack("H*", md5(hexchal + uamsecret));
  var response = md5("\0" + password + newchal);
  var newpwd = pack.pack("a32", password);
  var pappassword = pack.unpack("H32", (newpwd ^ newchal)).join("");

  console.log("Response: --> ", response);
  console.log("New Password: -->", newpwd);
  console.log("Pap Password: --->", pappassword);

, который дает результат:

Response Javascript

В JSON:

Response Javascript JSON format

Открытым текстом:

Response: -->  e8a54a55cbcd81dbc2bdfd9b197d62af
New Password: --> <Buffer >
Pap Password: ---> NaN

Все приведенные выше фрагменты доступны здесь: Радиусы

Мое понимание всего этого процесса не самое лучшее, и я буду признателен за понимание и за то, где я ошибаюсь.

Почему существует несоответствие?

Author: Community, 2017-01-15

1 answers

Перевод не работает, потому что функция PHP Pack использует строки другого формата и возвращает строки, в то время как модуль Javascript bufferpack возвращает массивы. Также вы не можете удалять строки в Javascript.

Хотя могут быть модули для выполнения того, что вы хотите, у меня есть свои собственные функции для анализа шестнадцатеричных строк. Также мне нравится изменять прототипы, с которыми не все согласны, но они могут быть преобразованы в обычные функции.

String.prototype.pad = function( length ,padding ) {

    var padding = typeof padding === 'string' && padding.length > 0 ? padding[0] : '\x00'
        ,length = isNaN( length ) ? 0 : ~~length;

    return this.length < length ? this + Array( length - this.length + 1 ).join( padding ) : this;

}

String.prototype.packHex = function() {

    var source = this.length % 2 ? this + '0' : this
        ,result = '';

    for( var i = 0; i < source.length; i = i + 2 ) {
        result += String.fromCharCode( parseInt( source.substr( i , 2 ) ,16 ) );
    }

    return result;

}

var challenge = 'c731395aca5dcf45446c0ae83db5319e'
    ,uamsecret = 'secret'
    ,password = 'password';

var hexchal = challenge.packHex();
var newchal = md5( hexchal + uamsecret ).packHex();
var response = md5( '\0' + password + newchal );
var newpwd = password.pad( 32 );
var pappassword = '';
for( var i = 0; i < newchal.length; i++ ) {
    pappassword += ( newpwd.charCodeAt( i ) ^ newchal.charCodeAt( i ) ).toString( 16 );
}

console.log("Response: --> ", response);
console.log("New Password: -->", newpwd);
console.log("Pap Password: --->", pappassword);

В прототипе строки определены две функции для замены использования функции pack:

.pad( 32, string ) используется для заполнения строки нулями, чтобы получить те же результаты, что и pack( 'a32', string ). Хотя здесь это и не требуется, он также принимает второй параметр, если требуется дополнить строку символом, отличным от нулей.

.packHex является эквивалентом pack( 'H*' ,string ) и преобразует код каждой пары шестнадцатеричных символов в символ. Функция в идеале нуждается в большем проверка для проверки строки является допустимой шестнадцатеричной, если она будет использоваться.

После определения входных данных в следующих четырех строках вместо этого задаются переменные, использующие эти функции, а не pack.

Поскольку Javascript изначально не может использовать строки xor, вам необходимо использовать цикл для извлечения каждого символа, преобразования его в числовое значение, xor этих значений, а затем преобразовать результат обратно в символ, чтобы создать строку pappassword.

Который вернется, ибо я:

Response: -->  – "fbfd42ffde05fcf8dbdd02b7e8ae2d90"
New Password: --> – "password������������������������"
Pap Password: ---> – "dcbdacb03f5d38ca33c128b931c272a"

Что является результатом, но, к сожалению, отличается от кода PHP.

Это связано с тем, что моя установка PHP настроена на внутреннее использование кодировки ISO-8859-1, в то время как Javascript изначально использует UTF-16.

Это не проблема при обычном использовании, но это означает, что соответствующие функции md5 будут видеть разные значения и, следовательно, возвращать другой хэш.

Предполагая, что вы пишете процедуру аутентификации с использованием Серверная часть PHP вам, очевидно, понадобятся последовательные результаты. Могут быть доступны модули для преобразования кодировки значений Javscript для совместимости, но гораздо проще вносить изменения в PHP-код.

Поскольку мы знаем, что шестнадцатеричные строки будут состоять из одного байта, Javascript эффективно использует UTF-8, поэтому PHP может сделать то же самое с помощью функции utf8_encode() для преобразования упакованных шестнадцатеричных строк перед их обработкой md5.

Первоначально я думал, что Javascript был внутреннее преобразование закодированных шестнадцатеричных символов в их эквиваленты в юникоде из-за этого, но это было не так. Вместо этого это был модуль md5, используемый в Javascript, который выполнял преобразование UTF-8 на входе.

Это оставляет два возможных варианта.


1. Используйте PHP в формате UTF-8

Если возможно, вы можете перенастроить свой PHP-сервер на использование кодировки UTF-8. Или вы можете изменить свой сценарий, чтобы использовать функцию utf8_encode() для зеркального отображения того же процесса как это происходит в Javascript, и преобразуйте шестнадцатеричные упакованные строки в UTF-8, прежде чем передавать их в md5()

$challenge = 'c731395aca5dcf45446c0ae83db5319e';
$uamsecret = 'secret';
$password = 'password';

$hexchal = pack ("H32", $challenge);
$newchal = pack ("H*", md5(utf8_encode($hexchal) . $uamsecret));
$response = md5("\0" . $password . utf8_encode($newchal));
$newpwd = pack("a32", $password);
$pappassword = implode ("", unpack("H32", ($newpwd ^ $newchal)));

echo "Response: ---> ", $response, "\n";
echo "New Password: ---> ", $newpwd, "\n";
echo "Pap Password: ---> ", $pappassword, "\n";

Затем это возвращает те же результаты, что и Javscript:

Response: ---> fbfd42ffde05fcf8dbdd02b7e8ae2d90
New Password: ---> password
Pap Password: ---> dcbdacb03f5d38ca33c128b9310c272a



2. Измените модуль md5 в Javascript

Я предполагаю, что вы используете модуль bluimp JavaScript-MD5, так как это то, что он использовал в подпрограмме DaloRADIUS, которую вы связали. Вы можете исправить это, чтобы обойти преобразование UTF-8.

Существуют различные способы вы можете исправить это, но в строке 259 находится определение самой функции md5(). Это просто набор операторов if для анализа входных параметров и вызова соответствующей внутренней функции.

Однако функции, вызываемые этим блоком, просто обеспечивают преобразование входных данных UTF-8 с помощью функции str2rstrUTF8(), прежде чем вызывать соответствующие функции для обеспечения фактического хэширования. Поэтому вы можете захотеть исправить md5(), чтобы принять третий параметр, указывающий, следует ли следует применить преобразование UTF-8, а затем вызвать другие функции по мере необходимости.

Однако, чтобы просто полностью удалить преобразование, проще всего изменить str2rstrUTF8(), чтобы вернуть входные данные без изменений. Эту функцию можно найти в строке 239, изменение ее на просто читать следующим образом остановит преобразование:

function str2rstrUTF8 (input) {
  return input
}

В качестве альтернативы, чтобы удалить избыточный вызов функции, вы можете вместо этого просто удалить ссылки на нее. Измените функцию, начинающуюся в строке 246, на следующую следует:

function rawMD5 (s) {
  return rstrMD5(s)
}

Функция rawHMACMD5() в строке 252 также включает вызовы функции str2rstrUTF8(), которую вы также можете исправить для обеспечения согласованности, но это не требуется для описанной выше процедуры. Эта функция вызывается вместо этого, когда передается второй параметр для предоставления хэша ключа , функции, недоступной в собственной функции PHP md5().

После внесения любого из этих изменений процедура Javascript теперь возвращает тот же результат, что и ваш оригинал (с использованием ISO-8859-1) PHP-код:

Response: -->  – "2d4bd27184f5eb032641137f728c6043"
New Password: --> – "password������������������������"
Pap Password: ---> – "356a1fb08f909fc40dfe448fc483ce3"
 12
Author: Michael, 2017-02-04 13:19:59