Автор: Enyby 29.7.2010, 1:10
Итак, по порядку. Известно что хаблисты используют схему $Lock-$Key. Основная проблема заключаеться в том, что этот алгоритм чуть-чуть отличается от обычного. Если быть точным, ровно на первый байт.
В интернете видел информацию, что большинство хаблистов благополучно игнорируют правильность этого кода и есть там один, особо задиристый. Однако, чем черт не шутит. Поэтому наваял свою реализацию на PHP.
Но, буду последователен. Исходником послужил код из исходников PtokaX хаба:
Код
uint16_t lport = (uint16_t)ntohs(addr.sin_port);
char cMagic = (char) ((lport&0xFF)+((lport>>8)&0xFF));
// strip the Lock data
char *temp;
if((temp = strchr(sBuffer+6, ' ')) != NULL) {
temp[0] = '\0';
}
// Compute the key
memcpy(sMsg, "$Key ", 5);
sMsg[5] = '\0';
size_t iLen = temp-sBuffer;
char v;
// first make the crypting stuff
for(size_t i = 6; i < iLen; i++) {
if(i == 6) {
v = sBuffer[i] ^ sBuffer[iLen] ^ sBuffer[iLen-1] ^ cMagic;
} else {
v = sBuffer[i] ^ sBuffer[i-1];
}
// Swap nibbles (0xF0 = 11110000, 0x0F = 00001111)
v = (char)(((v << 4) & 0xF0) | ((v >> 4) & 0x0F));
switch(v) {
case 0:
strcat(sMsg, "/%DCN000%/");
break;
case 5:
strcat(sMsg, "/%DCN005%/");
break;
case 36:
strcat(sMsg, "/%DCN036%/");
break;
case 96:
strcat(sMsg, "/%DCN096%/");
break;
case 124:
strcat(sMsg, "/%DCN124%/");
break;
case 126:
strcat(sMsg, "/%DCN126%/");
break;
default:
strncat(sMsg, (char *)&v, 1);
break;
}
}
На старте задается lport как локальный порт хаба, в подключении к хаблисту. У хаблиста порт, естественно, 2501. У нас же, тот что назначит система, а значит случайный.
sBuffer - содержит исходный $Lock код. sMsg - по завершении будет содержать нужный $Key код.
Теперь собственно код на PHP:
Код
function lock2key($_LOCK, $port)
{
$lockLength = strlen($_LOCK);
$LockToKey = '';
for ($j = 0; $j < strlen($_LOCK); $j++)
{
if($j == 0) {
$h = ord($_LOCK{0}) ^ 0 ^ ord( $_LOCK{ $lockLength - 1} ) ^ ($port % 256 + ($port>>8) % 256);
}
else {
$h = ord($_LOCK{$j}) ^ ord($_LOCK{$j-1});
}
$h = $h % 256;
$a = (($h<<4) & 240) | (($h>>4) & 15);
if($a == '126' or $a == '124' or $a == '96' or $a == '36' or $a == '5' or $a == '0') {
$LockToKey .= "/%DCN";
if ($a < 100) $LockToKey .= "0";
if ($a < 10) $LockToKey .= "0";
$LockToKey .= $a;
$LockToKey .= "%/";
}
else {
$LockToKey .= chr($a);
}
}
return $LockToKey;
}
Из заметных отличий от стандартного следующие:
Код
$h = ord($_LOCK{0}) ^ 0 ^ ord( $_LOCK{ $lockLength - 1} ) ^ ($port % 256 + ($port>>8) % 256);
Используется связка первого символа с нулем (вместо последнего символа), а затем с последним символом (вместо предпоследнего). Также, вместо константы 5, используется магический байт вычисляемый как сумма верхнего и нижнего байта шестнадцетиричного представления номера порта.
Все остальные "отличия" - экономия "на спичках" + изменения форматирования, не меняющие алгоритма работы.
Функция принимает два параметра, строку $Lock и номер порта.
Было бы неплохо этот код обосновать в соответствующей теме, описания протокола.
Автор: Nickolya 29.7.2010, 1:57
А код, предоставленный http://mydc.ru/index.html?showtopic=915&view=findpost&p=19477, - не то? Простите, смотреть в чем различия уже не позволяют слипающиеся глаза...
Автор: Enyby 29.7.2010, 4:09
Цитата(Enyby @ 29.7.2010, 0:10)
Основная проблема заключаеться в том, что этот алгоритм чуть-чуть отличается от обычного. Если быть точным, ровно на первый байт.
Под "обычным" и имелось в виду, тот код.
Цитируя http://mydc.ru/topic915.html?p=6717#entry6717
Цитата
Команда $Key также используется, когда хаб регистрируется в хаб-листе, однако, в этом случае ключ вычисляется по иному. Такой обмен ключами является своего рода гарантом того, что соединяемые устройства работают на одном протоколе.
Автор: Setuper 29.7.2010, 10:07
Спасибо за детальный разбор алгоритма.
Обязательно обоснуем в описании протокола.
Автор: Enyby 29.7.2010, 13:04
Кстати. Возвращаясь к теме обычного алгоритма. Он не оптимален, да и просто не эстетично записан.
Сейчас:
Код
function lock2key($_LOCK)
{
$lockLength = strlen($_LOCK);
$h = ord($_LOCK{0}) ^ ord( $_LOCK{ $lockLength - 1} ) ^ ord( $_LOCK{ $lockLength - 2} ) ^ 5;
while($h > 255)
{
$h = $h - 256;
}
$h = (($h<<4) & 240) | (($h>>4) & 15);
$a = $h;
if($a == '126' or
$a == '124' or
$a == '96' or
$a == '36' or
$a == '5' or
$a == '0')
{
$LockToKey = "/%DCN";
if($a < 100) $LockToKey .="0";
if($a < 10) $LockToKey .="0";
$LockToKey .= $a;
$LockToKey .= "%/";
}
else
{
$LockToKey = chr ($a);
}
for ($j = 1; $j < strlen($_LOCK); $j++)
{
$h = ord($_LOCK{$j}) ^ ord($_LOCK{$j-1});
while($h > 255)
{
$h = $h - 256;
}
$h = (($h<<4) & 240) | (($h>>4) & 15);
$a = $h;
if($a == '126' or
$a == '124' or
$a == '96' or
$a == '36' or
$a == '5' or
$a == '0')
{
$LockToKey .= "/%DCN";
if ($a < 100) $LockToKey .="0";
if ($a < 10) $LockToKey .="0";
$LockToKey .= $a;
$LockToKey .= "%/";
}
else
{
$LockToKey .= chr ($a);
}
}
return $LockToKey;
}
А можно так:
Код
function lock2key($_LOCK, $port)
{
$lockLength = strlen($_LOCK);
$LockToKey = '';
for ($j = 0; $j < strlen($_LOCK); $j++)
{
if($j == 0) {
$h = ord($_LOCK{0}) ^ ord( $_LOCK{ $lockLength - 1} ) ^ ord( $_LOCK{ $lockLength - 2} ) ^ 5;
}
else {
$h = ord($_LOCK{$j}) ^ ord($_LOCK{$j-1});
}
$h = $h % 256;
$a = (($h<<4) & 240) | (($h>>4) & 15);
if($a == '126' or $a == '124' or $a == '96' or $a == '36' or $a == '5' or $a == '0') {
$LockToKey .= "/%DCN";
if ($a < 100) $LockToKey .= "0";
if ($a < 10) $LockToKey .= "0";
$LockToKey .= $a;
$LockToKey .= "%/";
}
else {
$LockToKey .= chr($a);
}
}
return $LockToKey;
}
Имхо, так и компактнее и яснее.