Реализация MD5 в PHP - где я ошибаюсь?

Я знаю, что это может быть длинным, но может ли кто-нибудь сказать мне, где моя текущая реализация алгоритма MD5 в PHP идет не так? Я просто не могу понять, что с ним не так.

Он возвращает шестнадцатеричную строку 32 символа (25% от времени, когда она создает строку длиной менее 32 символов), но она не создает те же 32 символа, что и встроенная функция MD5.

Большое спасибо.


<?php

function MD($string){
$a = "67452301";
$b = "EFCDAB89";
$c = "98BADCFE";
$d = "10325476";

$words = init($string);

for($i = 0; $i <= count($words)/16-1; $i++){
    $A = $a;
    $B = $b;
    $C = $c;
    $D = $d;

    /* ROUND 1 */
    FF ($A, $B, $C, $D, $words[0 + ($i * 16)], 7, "d76aa478"); 
    FF ($D, $A, $B, $C, $words[1 + ($i * 16)], 12, "e8c7b756"); 
    FF ($C, $D, $A, $B, $words[2 + ($i * 16)], 17, "242070db"); 
    FF ($B, $C, $D, $A, $words[3 + ($i * 16)], 22, "c1bdceee"); 
    FF ($A, $B, $C, $D, $words[4 + ($i * 16)], 7, "f57c0faf"); 
    FF ($D, $A, $B, $C, $words[5 + ($i * 16)], 12, "4787c62a"); 
    FF ($C, $D, $A, $B, $words[6 + ($i * 16)], 17, "a8304613"); 
    FF ($B, $C, $D, $A, $words[7 + ($i * 16)], 22, "fd469501"); 
    FF ($A, $B, $C, $D, $words[8 + ($i * 16)], 7, "698098d8"); 
    FF ($D, $A, $B, $C, $words[9 + ($i * 16)], 12, "8b44f7af"); 
    FF ($C, $D, $A, $B, $words[10 + ($i * 16)], 17, "ffff5bb1"); 
    FF ($B, $C, $D, $A, $words[11 + ($i * 16)], 22, "895cd7be"); 
    FF ($A, $B, $C, $D, $words[12 + ($i * 16)], 7, "6b901122"); 
    FF ($D, $A, $B, $C, $words[13 + ($i * 16)], 12, "fd987193"); 
    FF ($C, $D, $A, $B, $words[14 + ($i * 16)], 17, "a679438e"); 
    FF ($B, $C, $D, $A, $words[15 + ($i * 16)], 22, "49b40821"); 

    /* ROUND 2 */
    GG ($A, $B, $C, $D, $words[1 + ($i * 16)], 5, "f61e2562"); 
    GG ($D, $A, $B, $C, $words[6 + ($i * 16)], 9, "c040b340"); 
    GG ($C, $D, $A, $B, $words[11 + ($i * 16)], 14, "265e5a51"); 
    GG ($B, $C, $D, $A, $words[0 + ($i * 16)], 20, "e9b6c7aa"); 
    GG ($A, $B, $C, $D, $words[5 + ($i * 16)], 5, "d62f105d"); 
    GG ($D, $A, $B, $C, $words[10 + ($i * 16)], 9, "02441453"); 
    GG ($C, $D, $A, $B, $words[15 + ($i * 16)], 14, "d8a1e681"); 
    GG ($B, $C, $D, $A, $words[4 + ($i * 16)], 20, "e7d3fbc8"); 
    GG ($A, $B, $C, $D, $words[9 + ($i * 16)], 5, "21e1cde6"); 
    GG ($D, $A, $B, $C, $words[14 + ($i * 16)], 9, "c33707d6"); 
    GG ($C, $D, $A, $B, $words[3 + ($i * 16)], 14, "f4d50d87"); 
    GG ($B, $C, $D, $A, $words[8 + ($i * 16)], 20, "455a14ed"); 
    GG ($A, $B, $C, $D, $words[13 + ($i * 16)], 5, "a9e3e905"); 
    GG ($D, $A, $B, $C, $words[2 + ($i * 16)], 9, "fcefa3f8"); 
    GG ($C, $D, $A, $B, $words[7 + ($i * 16)], 14, "676f02d9"); 
    GG ($B, $C, $D, $A, $words[12 + ($i * 16)], 20, "8d2a4c8a"); 

    /* ROUND 3 */
    HH ($A, $B, $C, $D, $words[5 + ($i * 16)], 4, "fffa3942"); 
    HH ($D, $A, $B, $C, $words[8 + ($i * 16)], 11, "8771f681"); 
    HH ($C, $D, $A, $B, $words[11 + ($i * 16)], 16, "6d9d6122"); 
    HH ($B, $C, $D, $A, $words[14 + ($i * 16)], 23, "fde5380c"); 
    HH ($A, $B, $C, $D, $words[1 + ($i * 16)], 4, "a4beea44"); 
    HH ($D, $A, $B, $C, $words[4 + ($i * 16)], 11, "4bdecfa9"); 
    HH ($C, $D, $A, $B, $words[7 + ($i * 16)], 16, "f6bb4b60"); 
    HH ($B, $C, $D, $A, $words[10 + ($i * 16)], 23, "bebfbc70"); 
    HH ($A, $B, $C, $D, $words[13 + ($i * 16)], 4, "289b7ec6"); 
    HH ($D, $A, $B, $C, $words[0 + ($i * 16)], 11, "eaa127fa"); 
    HH ($C, $D, $A, $B, $words[3 + ($i * 16)], 16, "d4ef3085"); 
    HH ($B, $C, $D, $A, $words[6 + ($i * 16)], 23, "04881d05"); 
    HH ($A, $B, $C, $D, $words[9 + ($i * 16)], 4, "d9d4d039"); 
    HH ($D, $A, $B, $C, $words[12 + ($i * 16)], 11, "e6db99e5"); 
    HH ($C, $D, $A, $B, $words[15 + ($i * 16)], 16, "1fa27cf8"); 
    HH ($B, $C, $D, $A, $words[2 + ($i * 16)], 23, "c4ac5665"); 

    /* ROUND 4 */
    II ($A, $B, $C, $D, $words[0 + ($i * 16)], 6, "f4292244"); 
    II ($D, $A, $B, $C, $words[7 + ($i * 16)], 10, "432aff97"); 
    II ($C, $D, $A, $B, $words[14 + ($i * 16)], 15, "ab9423a7"); 
    II ($B, $C, $D, $A, $words[5 + ($i * 16)], 21, "fc93a039"); 
    II ($A, $B, $C, $D, $words[12 + ($i * 16)], 6, "655b59c3"); 
    II ($D, $A, $B, $C, $words[3 + ($i * 16)], 10, "8f0ccc92"); 
    II ($C, $D, $A, $B, $words[10 + ($i * 16)], 15, "ffeff47d"); 
    II ($B, $C, $D, $A, $words[1 + ($i * 16)], 21, "85845dd1"); 
    II ($A, $B, $C, $D, $words[8 + ($i * 16)], 6, "6fa87e4f"); 
    II ($D, $A, $B, $C, $words[15 + ($i * 16)], 10, "fe2ce6e0"); 
    II ($C, $D, $A, $B, $words[6 + ($i * 16)], 15, "a3014314"); 
    II ($B, $C, $D, $A, $words[13 + ($i * 16)], 21, "4e0811a1"); 
    II ($A, $B, $C, $D, $words[4 + ($i * 16)], 6, "f7537e82"); 
    II ($D, $A, $B, $C, $words[11 + ($i * 16)], 10, "bd3af235"); 
    II ($C, $D, $A, $B, $words[2 + ($i * 16)], 15, "2ad7d2bb"); 
    II ($B, $C, $D, $A, $words[9 + ($i * 16)], 21, "eb86d391"); 

    addVars($a, $b, $c, $d, $A, $B, $C, $D);    
}
    $MD5 = $a.$b.$c.$d;
    return $MD5;
}

/* General functions */

function hexbin($str){
    $hexbinmap = array("0" => "0000"
                     , "1" => "0001"
                     , "2" => "0010"
                     , "3" => "0011"
                     , "4" => "0100"
                     , "5" => "0101"
                     , "6" => "0110"
                     , "7" => "0111"
                     , "8" => "1000"
                     , "9" => "1001"
                     , "A" => "1010"
                     , "a" => "1010"
                     , "B" => "1011"
                     , "b" => "1011"
                     , "C" => "1100"
                     , "c" => "1100"
                     , "D" => "1101"
                     , "d" => "1101"
                     , "E" => "1110"
                     , "e" => "1110"
                     , "F" => "1111"
                     , "f" => "1111");                    
    $bin = "";
    for ($i = 0; $i < strlen($str); $i++)
    {
        $bin .= $hexbinmap[$str[$i]];
    }
    $bin = ltrim($bin, '0'); 
    // echo "Original: ".$str."  New: ".$bin."<br />";
    return $bin;
}

function strhex($str){
    $hex = "";
    for ($i = 0; $i < strlen($str); $i++)
    {
        $hex = $hex.dechex(ord($str[$i]));
    }
    return $hex;
}


/* MD5-specific functions */

function init($string){
    $len = strlen($string);
    $hex = strhex($string); // convert ascii string to hex
    $bin = hexbin($hex); // convert hex string to bin
    $padded = pad($bin);
    $padded = pad($padded, 1, $len);
    $block = str_split($padded, 32);
    return $block;
}

function pad($bin, $type=0, $len = 0){
    if($type == 0){
        $bin = $bin."1";
        $buff = strlen($bin) % 512;
        if($buff != 448){
            while(strlen($bin) % 512 != 448){
                $bin = $bin."0";
            }
        }
    }
    // append length (b) of string to latter 64 bits
    elseif($type == 1){
        $bLen = decbin($len);
        if(strlen($bLen) > 64){
            $words = truncate64($bLen);
            $bin .= $words[1].$words[0];
        }
        else{
            while(strlen($bLen) < 64){
                $bLen .= "0";
            }
            $words = str_split ($bLen, 32);
            $bin .= $words[1].$words[0];
        }

    }
    return $bin;
}

function truncate64($string){
    $trunc = substr($string, strlen($string) - 64, 64);
    $trunc = str_split ($trunc, 32);
    return $trunc;
}


/* MD5 base functions */

function F($X, $Y, $Z){
    $X = hexbin($X);
    $Y = hexbin($Y);
    $Z = hexbin($Z);
    $calc = ($X & $Y) | ((~ $X) & $Z); // X AND Y OR NOT X AND Z
    $calc = bindec($calc);
    return  $calc; 
}

function G($X, $Y, $Z){
    $X = hexbin($X);
    $Y = hexbin($Y);
    $Z = hexbin($Z);
    $calc = ($X & $Z) | ($Y & (~ $Z)) ; // X AND Z OR Y AND NOT Z
    $calc = bindec($calc);
    return  $calc; 
}

function H($X, $Y, $Z){
    $X = hexbin($X);
    $Y = hexbin($Y);
    $Z = hexbin($Z);
    $calc = $X ^ $Y ^ $Z; // X XOR Y XOR Z
    $calc = bindec($calc);
    return  $calc; 
}

function I($X, $Y, $Z){
    $X = hexbin($X);
    $Y = hexbin($Y);
    $Z = hexbin($Z);
    $calc = $Y ^ ($X | (~ $Z)) ; // Y XOR (X OR NOT Z)
    $calc = bindec($calc);
    return  $calc; 
}

/* MD5 round functions */

/*
$A - hex, $B - hex, $C - hex, $D - hex (F - dec)
$M - binary
$s - decimal
$t - hex
*/
function FF(&$A, $B, $C, $D, $M, $s, $t){
    $A = hexdec($A);
    $t = hexdec($t);
    $M = bindec($M);
    $A = hexdec($B) + (($A + F($B, $C, $D) + $M + $t)); //decimal
    $A = rotate($A, $s);
}

function GG(&$A, $B, $C, $D, $M, $s, $t){
    $A = hexdec($A);
    $t = hexdec($t);
    $M = bindec($M);
    $A = hexdec($B) + (($A + G($B, $C, $D) + $M + $t)); //decimal
    $A = rotate($A, $s);
}

function HH(&$A, $B, $C, $D, $M, $s, $t){
    $A = hexdec($A);
    $t = hexdec($t);
    $M = bindec($M);
    $A = hexdec($B) + (($A + H($B, $C, $D) + $M + $t)); //decimal
    $A = rotate($A, $s);
}

function II(&$A, $B, $C, $D, $M, $s, $t){
    $A = hexdec($A);
    $t = hexdec($t);
    $M = bindec($M);
    $A = hexdec($B) + (($A + I($B, $C, $D) + $M + $t)); //decimal
    $A = rotate($A, $s);
}

// shift
function rotate($decimal, $bits) { //returns hex
  $binary = decbin($decimal);
  $shifted = substr($binary, $bits).substr($binary, 0, $bits);
  $hexshift = base_convert($shifted, 2, 16);
  return $hexshift;
}

function addVars(&$a, &$b, &$c, &$d, $A, $B, $C, $D){
    $A = hexdec($A);
    $B = hexdec($B);
    $C = hexdec($C);
    $D = hexdec($D);
    $aa = hexdec($a);
    $bb = hexdec($b);
    $cc = hexdec($c);
    $dd = hexdec($d);

    $aa = $aa + $A;
    $bb = $bb + $A;
    $cc = $cc + $A;
    $dd = $dd + $A;

    $a = dechex($aa);
    $b = dechex($bb);
    $c = dechex($cc);
    $d = dechex($dd);
}

?>

Ответ 1

Почему-то этот вопрос не оставил меня в покое, поэтому я прошел ваш код и исправил ошибки до тех пор, пока они не сработали:

Прежде чем вы пройдете это, у меня есть два совета:

  • Не конвертируйте между значениями int и hex/bin представления; преобразовать в значения int до того, как любая обработка будет сделанный; делает код более удобочитаемым.

  • Используйте call_user_func() и реализуйте функции GG → G, II → я только один раз.

Кроме того, остается одна тонкая ошибка; строка ввода, состоящая исключительно из нулевых символов, не будет правильно закодирована; Я оставлю это в качестве упражнения для читателя: -).

  • В init():

Добавляется длина незакрепленного сообщения в количестве бит, а не количество символов:

- $len = strlen($string)
+ $len = strlen($string) * 8;
  $hex = strhex($string); // convert ascii string to hex

Кроме того, вам нужно заполнить то, что вы получаете от hexbin, в противном случае последующий вызов str_split приведет к неправильной настройке:

- $bin = hexbin($hex);
+ $bin = leftpad(hexbin($hex), $len); // convert hex string to bin
  $block = str_split($padded, 32);

также порядок байтов малозначен:

+ foreach ($block as &$b) {
+     $b = implode('', array_reverse(str_split($b, 8)));
+ }
  • В strhex():

Здесь много ошибок заполнения; dechex(ord("\1")) '1' not '01':

- $hex = $hex.dechex(ord($str[$i]));
+ $hex = $hex.leftpad(dechex(ord($str[$i])), 2);
  • В pad():

байт -order является litte endian (вы делитесь на 32-битные слова), а truncate64() полностью не совпадает с картой: -):

-   $bLen = decbin($len);
-  if(strlen($bLen) > 64){
-    $words = truncate64($bLen);
-    $bin .= $words[1].$words[0];
-  }
-  else{
-      while(strlen($bLen) < 64){
-        $bLen .= "0";
-      }
-    $words = str_split ($bLen, 32);
-    $bin .= $words[1].$words[0];
-  }
+ $bLen = leftpad(decbin($len), 64);
+ $bin .= implode('', array_reverse(str_split($bLen, 8)));
  • В F(), G(), H(), I():

Побитовые операторы не могут использоваться для двоичных строк:

- $X = hexbin($X);
- $Y = hexbin($Y);
- $Z = hexbin($Z);
- $calc = ...
- $calc = bindec($calc);
+ $X = hexdec($X);
+ $Y = hexdec($Y);
+ $Z = hexdec($Z);
+ $calc = ...
  • В FF(), GG(), HH(), II():

Вы добавляете $B перед вращением, его следует добавить потом; также, поскольку вы переходите вперед и назад между строковыми и int-представлениями и PHP_INT_SIZE может быть больше 4 (например, на 64-разрядных платформах), вы должны убедиться, что используете только нижние 32-бит:

- $A = hexdec($B) + (($A + H($B, $C, $D) + $M + $t)); //decimal
+ $A = ($A + H($B, $C, $D) + $M + $t) & 0xffffffff; //decimal
+ $A = rotate($A, $s);
+ $A = dechex((hexdec($B) + hexdec($A)) & 0xffffffff);
  • В addVars():

$A повторяется для каждого добавления, возможно, артефакт copy-paste: -):

- $aa = $aa + $A;
- $bb = $bb + $A;
- $cc = $cc + $A;
- $dd = $dd + $A;
+ $aa = ($aa + $A) & 0xffffffff;
+ $bb = ($bb + $B) & 0xffffffff;
+ $cc = ($cc + $C) & 0xffffffff;
+ $dd = ($dd + $D) & 0xffffffff;
  • В rotate():

В вашей функции вращения есть дополнительная ошибка (снова). Просто выбросьте его и замените:

+ function rotate ($decimal, $bits) { //returns hex
+     return dechex((($decimal << $bits) |  ($decimal >> (32 - $bits))) & 0xffffffff);
+ }
  • В MD():

И последнее, но не менее важное: вам нужно снова преобразовать в маленький конец:

- $MD5 = $a.$b.$c.$d;
+ $MD5 = '';
+ foreach (array($a, $b, $c, $d) as $x) {
+     $MD5 .= implode('', array_reverse(str_split(leftpad($x, 8), 2)));
+ }

Отсутствует функция leftpad():

+ function leftpad($needs_padding, $alignment)
+ {
+   if (strlen($needs_padding) % $alignment) {
+       $pad_amount    = $alignment - strlen($needs_padding) % $alignment;
+       $left_pad      = implode('', array_fill(0, $pad_amount, '0'));
+       $needs_padding = $left_pad . $needs_padding;
+   }
+   return $needs_padding;
+ }

Полностью отредактированный источник:

<?php

function MD($string){
$a = "67452301";
$b = "EFCDAB89";
$c = "98BADCFE";
$d = "10325476";

$words = init($string);

for($i = 0; $i <= count($words)/16-1; $i++){
        $A = $a;
        $B = $b;
        $C = $c;
        $D = $d;


        /* ROUND 1 */
        FF ($A, $B, $C, $D, $words[0 + ($i * 16)], 7, "d76aa478"); 
        FF ($D, $A, $B, $C, $words[1 + ($i * 16)], 12, "e8c7b756"); 
        FF ($C, $D, $A, $B, $words[2 + ($i * 16)], 17, "242070db"); 
        FF ($B, $C, $D, $A, $words[3 + ($i * 16)], 22, "c1bdceee"); 
        FF ($A, $B, $C, $D, $words[4 + ($i * 16)], 7, "f57c0faf"); 
        FF ($D, $A, $B, $C, $words[5 + ($i * 16)], 12, "4787c62a"); 
        FF ($C, $D, $A, $B, $words[6 + ($i * 16)], 17, "a8304613"); 
        FF ($B, $C, $D, $A, $words[7 + ($i * 16)], 22, "fd469501"); 
        FF ($A, $B, $C, $D, $words[8 + ($i * 16)], 7, "698098d8"); 
        FF ($D, $A, $B, $C, $words[9 + ($i * 16)], 12, "8b44f7af"); 
        FF ($C, $D, $A, $B, $words[10 + ($i * 16)], 17, "ffff5bb1"); 
        FF ($B, $C, $D, $A, $words[11 + ($i * 16)], 22, "895cd7be"); 
        FF ($A, $B, $C, $D, $words[12 + ($i * 16)], 7, "6b901122"); 
        FF ($D, $A, $B, $C, $words[13 + ($i * 16)], 12, "fd987193"); 
        FF ($C, $D, $A, $B, $words[14 + ($i * 16)], 17, "a679438e"); 
        FF ($B, $C, $D, $A, $words[15 + ($i * 16)], 22, "49b40821"); 

        /* ROUND 2 */
        GG ($A, $B, $C, $D, $words[1 + ($i * 16)], 5, "f61e2562"); 
        GG ($D, $A, $B, $C, $words[6 + ($i * 16)], 9, "c040b340"); 
        GG ($C, $D, $A, $B, $words[11 + ($i * 16)], 14, "265e5a51"); 
        GG ($B, $C, $D, $A, $words[0 + ($i * 16)], 20, "e9b6c7aa"); 
        GG ($A, $B, $C, $D, $words[5 + ($i * 16)], 5, "d62f105d"); 
        GG ($D, $A, $B, $C, $words[10 + ($i * 16)], 9, "02441453"); 
        GG ($C, $D, $A, $B, $words[15 + ($i * 16)], 14, "d8a1e681"); 
        GG ($B, $C, $D, $A, $words[4 + ($i * 16)], 20, "e7d3fbc8"); 
        GG ($A, $B, $C, $D, $words[9 + ($i * 16)], 5, "21e1cde6"); 
        GG ($D, $A, $B, $C, $words[14 + ($i * 16)], 9, "c33707d6"); 
        GG ($C, $D, $A, $B, $words[3 + ($i * 16)], 14, "f4d50d87"); 
        GG ($B, $C, $D, $A, $words[8 + ($i * 16)], 20, "455a14ed"); 
        GG ($A, $B, $C, $D, $words[13 + ($i * 16)], 5, "a9e3e905"); 
        GG ($D, $A, $B, $C, $words[2 + ($i * 16)], 9, "fcefa3f8"); 
        GG ($C, $D, $A, $B, $words[7 + ($i * 16)], 14, "676f02d9"); 
        GG ($B, $C, $D, $A, $words[12 + ($i * 16)], 20, "8d2a4c8a"); 

        /* ROUND 3 */
        HH ($A, $B, $C, $D, $words[5 + ($i * 16)], 4, "fffa3942"); 
        HH ($D, $A, $B, $C, $words[8 + ($i * 16)], 11, "8771f681"); 
        HH ($C, $D, $A, $B, $words[11 + ($i * 16)], 16, "6d9d6122"); 
        HH ($B, $C, $D, $A, $words[14 + ($i * 16)], 23, "fde5380c"); 
        HH ($A, $B, $C, $D, $words[1 + ($i * 16)], 4, "a4beea44"); 
        HH ($D, $A, $B, $C, $words[4 + ($i * 16)], 11, "4bdecfa9"); 
        HH ($C, $D, $A, $B, $words[7 + ($i * 16)], 16, "f6bb4b60"); 
        HH ($B, $C, $D, $A, $words[10 + ($i * 16)], 23, "bebfbc70"); 
        HH ($A, $B, $C, $D, $words[13 + ($i * 16)], 4, "289b7ec6"); 
        HH ($D, $A, $B, $C, $words[0 + ($i * 16)], 11, "eaa127fa"); 
        HH ($C, $D, $A, $B, $words[3 + ($i * 16)], 16, "d4ef3085"); 
        HH ($B, $C, $D, $A, $words[6 + ($i * 16)], 23, "04881d05"); 
        HH ($A, $B, $C, $D, $words[9 + ($i * 16)], 4, "d9d4d039"); 
        HH ($D, $A, $B, $C, $words[12 + ($i * 16)], 11, "e6db99e5"); 
        HH ($C, $D, $A, $B, $words[15 + ($i * 16)], 16, "1fa27cf8"); 
        HH ($B, $C, $D, $A, $words[2 + ($i * 16)], 23, "c4ac5665"); 

        /* ROUND 4 */
        II ($A, $B, $C, $D, $words[0 + ($i * 16)], 6, "f4292244"); 
        II ($D, $A, $B, $C, $words[7 + ($i * 16)], 10, "432aff97"); 
        II ($C, $D, $A, $B, $words[14 + ($i * 16)], 15, "ab9423a7"); 
        II ($B, $C, $D, $A, $words[5 + ($i * 16)], 21, "fc93a039"); 
        II ($A, $B, $C, $D, $words[12 + ($i * 16)], 6, "655b59c3"); 
        II ($D, $A, $B, $C, $words[3 + ($i * 16)], 10, "8f0ccc92"); 
        II ($C, $D, $A, $B, $words[10 + ($i * 16)], 15, "ffeff47d"); 
        II ($B, $C, $D, $A, $words[1 + ($i * 16)], 21, "85845dd1"); 
        II ($A, $B, $C, $D, $words[8 + ($i * 16)], 6, "6fa87e4f"); 
        II ($D, $A, $B, $C, $words[15 + ($i * 16)], 10, "fe2ce6e0"); 
        II ($C, $D, $A, $B, $words[6 + ($i * 16)], 15, "a3014314"); 
        II ($B, $C, $D, $A, $words[13 + ($i * 16)], 21, "4e0811a1"); 
        II ($A, $B, $C, $D, $words[4 + ($i * 16)], 6, "f7537e82"); 
        II ($D, $A, $B, $C, $words[11 + ($i * 16)], 10, "bd3af235"); 
        II ($C, $D, $A, $B, $words[2 + ($i * 16)], 15, "2ad7d2bb"); 
        II ($B, $C, $D, $A, $words[9 + ($i * 16)], 21, "eb86d391"); 

        addVars($a, $b, $c, $d, $A, $B, $C, $D);        
}
  $MD5 = '';
  foreach (array($a, $b, $c, $d) as $x) {
      $MD5 .= implode('', array_reverse(str_split(leftpad($x, 8), 2)));
  }

        return $MD5;
}

/* General functions */

function hexbin($str){
        $hexbinmap = array("0" => "0000"
                                                , "1" => "0001"
                                                , "2" => "0010"
                                                , "3" => "0011"
                                                , "4" => "0100"
                                                , "5" => "0101"
                                                , "6" => "0110"
                                                , "7" => "0111"
                                                , "8" => "1000"
                                                , "9" => "1001"
                                                , "A" => "1010"
                                                , "a" => "1010"
                                                , "B" => "1011"
                                                , "b" => "1011"
                                                , "C" => "1100"
                                                , "c" => "1100"
                                                , "D" => "1101"
                                                , "d" => "1101"
                                                , "E" => "1110"
                                                , "e" => "1110"
                                                , "F" => "1111"
                                                , "f" => "1111");

        $bin = "";
    for ($i = 0; $i < strlen($str); $i++)
    {
        $bin .= $hexbinmap[$str[$i]];
    }
    $bin = ltrim($bin, '0'); 
        // echo "Original: ".$str."  New: ".$bin."<br />";
    return $bin;
}

function strhex($str){
    $hex = "";
    for ($i = 0; $i < strlen($str); $i++)
    {
        $hex = $hex.leftpad(dechex(ord($str[$i])), 2);
    }
    return $hex;
}


/* MD5-specific functions */

function init($string){
        $len = strlen($string) * 8;
        $hex = strhex($string); // convert ascii string to hex
        $bin = leftpad(hexbin($hex), $len); // convert hex string to bin
        $padded = pad($bin);
        $padded = pad($padded, 1, $len);
        $block = str_split($padded, 32);

        foreach ($block as &$b) {
            $b = implode('', array_reverse(str_split($b, 8)));
        }

        return $block;
}

function pad($bin, $type=0, $len = 0){
        if($type == 0){
        $bin = $bin."1";
        $buff = strlen($bin) % 512;
        if($buff != 448){
                while(strlen($bin) % 512 != 448){

                        $bin = $bin."0";
                }
        }
        }
        // append length (b) of string to latter 64 bits
        elseif($type == 1){
            $bLen = leftpad(decbin($len), 64);
            $bin .= implode('', array_reverse(str_split($bLen, 8)));
        }
        return $bin;
}

/* MD5 base functions */

function F($X, $Y, $Z){
        $X = hexdec($X);
        $Y = hexdec($Y);
        $Z = hexdec($Z);
        $calc = (($X & $Y) | ((~ $X) & $Z)); // X AND Y OR NOT X AND Z
        return  $calc; 
}

function G($X, $Y, $Z){
        $X = hexdec($X);
        $Y = hexdec($Y);
        $Z = hexdec($Z);
        $calc = (($X & $Z) | ($Y & (~ $Z))); // X AND Z OR Y AND NOT Z
        return  $calc; 
}

function H($X, $Y, $Z){
        $X = hexdec($X);
        $Y = hexdec($Y);
        $Z = hexdec($Z);
        $calc = ($X ^ $Y ^ $Z); // X XOR Y XOR Z
        return  $calc; 
}

function I($X, $Y, $Z){
        $X = hexdec($X);
        $Y = hexdec($Y);
        $Z = hexdec($Z);
        $calc = ($Y ^ ($X | (~ $Z))) ; // Y XOR (X OR NOT Z)
        return  $calc; 
}

/* MD5 round functions */

/*
$A - hex, $B - hex, $C - hex, $D - hex (F - dec)
$M - binary
$s - decimal
$t - hex
*/
function FF(&$A, $B, $C, $D, $M, $s, $t){
        $A = hexdec($A);
        $t = hexdec($t);
        $M = bindec($M);
        $A = ($A + F($B, $C, $D) + $M + $t) & 0xffffffff; //decimal
        $A = rotate($A, $s);
        $A = dechex((hexdec($B) + hexdec($A)) & 0xffffffff);
}

function GG(&$A, $B, $C, $D, $M, $s, $t){
        $A = hexdec($A);
        $t = hexdec($t);
        $M = bindec($M);
        $A = ($A + G($B, $C, $D) + $M + $t) & 0xffffffff; //decimal
        $A = rotate($A, $s);
        $A = dechex((hexdec($B) + hexdec($A)) & 0xffffffff);
}

function HH(&$A, $B, $C, $D, $M, $s, $t){
        $A = hexdec($A);
        $t = hexdec($t);
        $M = bindec($M);
        $A = ($A + H($B, $C, $D) + $M + $t) & 0xffffffff; //decimal
        $A = rotate($A, $s);
        $A = dechex((hexdec($B) + hexdec($A)) & 0xffffffff);
}

function II(&$A, $B, $C, $D, $M, $s, $t){
        $A = hexdec($A);
        $t = hexdec($t);
        $M = bindec($M);
        $A = ($A + I($B, $C, $D) + $M + $t) & 0xffffffff; //decimal
        $A = rotate($A, $s);
        $A = dechex((hexdec($B) + hexdec($A)) & 0xffffffff);
}

// shift
function rotate ($decimal, $bits) { //returns hex
    return dechex((($decimal << $bits) |  ($decimal >> (32 - $bits))) & 0xffffffff);
}

function addVars(&$a, &$b, &$c, &$d, $A, $B, $C, $D){
        $A = hexdec($A);
        $B = hexdec($B);
        $C = hexdec($C);
        $D = hexdec($D);
        $aa = hexdec($a);
        $bb = hexdec($b);
        $cc = hexdec($c);
        $dd = hexdec($d);

        $aa = ($aa + $A) & 0xffffffff;
        $bb = ($bb + $B) & 0xffffffff;
        $cc = ($cc + $C) & 0xffffffff;
        $dd = ($dd + $D) & 0xffffffff;

        $a = dechex($aa);
        $b = dechex($bb);
        $c = dechex($cc);
        $d = dechex($dd);
}

function leftpad($needs_padding, $alignment)
{
    if (strlen($needs_padding) % $alignment) {
      $pad_amount    = $alignment - strlen($needs_padding) % $alignment;
      $left_pad      = implode('', array_fill(0, $pad_amount, '0'));
      $needs_padding = $left_pad . $needs_padding;
  }
  return $needs_padding;
}

Ответ 2

Хорошо, что вы пытаетесь! У меня был подобный опыт, один раз назад я реализовал алгоритм MD5 в Tcl. Лучший способ, который я нашел для его отладки, заключался в том, чтобы проследить его по строкам, зная, какая операция должна быть выполнена, и убедившись, вручную вычислив, действительно ли была выполнена правильная операция.

Там нет простого ответа на этот вопрос, и невозможно сказать, что может быть неправильным из кода, который вы отправили без детального анализа.

(Я предполагаю, что вы уже знаете о стандартной функции md5(), и вы делаете это для учебных целей.)

Ответ 3

Спасибо @@Inshallah.
Если код @Inshallah не работает, используйте вместо этого код:

<?php

function MD($string){
    $a = "67452301";
    $b = "efcdab89";
    $c = "98badcfe";
    $d = "10325476";

    $A = $a ;
    $B = $b ;
    $C = $c ;
    $D = $d ;
    $words = ConvertToArray($string);    
    for($i = 0; $i <= count($words)/16-1; $i++){
            $a  = $A;
            $b  = $B;
            $c  = $C;
            $d  = $D;

            /* ROUND 1 */  
            FF ($A, $B, $C, $D, $words[0 + ($i * 16)], 7, "d76aa478"); 
            FF ($D, $A, $B, $C, $words[1 + ($i * 16)], 12, "e8c7b756"); 
            FF ($C, $D, $A, $B, $words[2 + ($i * 16)], 17, "242070db"); 
            FF ($B, $C, $D, $A, $words[3 + ($i * 16)], 22, "c1bdceee"); 
            FF ($A, $B, $C, $D, $words[4 + ($i * 16)], 7, "f57c0faf");  
            FF ($D, $A, $B, $C, $words[5 + ($i * 16)], 12, "4787c62a"); 
            FF ($C, $D, $A, $B, $words[6 + ($i * 16)], 17, "a8304613"); 
            FF ($B, $C, $D, $A, $words[7 + ($i * 16)], 22, "fd469501"); 
            FF ($A, $B, $C, $D, $words[8 + ($i * 16)], 7, "698098d8"); 
            FF ($D, $A, $B, $C, $words[9 + ($i * 16)], 12, "8b44f7af"); 
            FF ($C, $D, $A, $B, $words[10 + ($i * 16)], 17, "ffff5bb1"); 
            FF ($B, $C, $D, $A, $words[11 + ($i * 16)], 22, "895cd7be");  
            FF ($A, $B, $C, $D, $words[12 + ($i * 16)], 7, "6b901122"); 
            FF ($D, $A, $B, $C, $words[13 + ($i * 16)], 12, "fd987193"); 
            FF ($C, $D, $A, $B, $words[14 + ($i * 16)], 17, "a679438e"); 
            FF ($B, $C, $D, $A, $words[15 + ($i * 16)], 22, "49b40821"); 

            /* ROUND 2 */
            GG ($A, $B, $C, $D, $words[1 + ($i * 16)], 5, "f61e2562"); 
            GG ($D, $A, $B, $C, $words[6 + ($i * 16)], 9, "c040b340"); 
            GG ($C, $D, $A, $B, $words[11 + ($i * 16)], 14, "265e5a51"); 
            GG ($B, $C, $D, $A, $words[0 + ($i * 16)], 20, "e9b6c7aa"); 
            GG ($A, $B, $C, $D, $words[5 + ($i * 16)], 5, "d62f105d"); 
            GG ($D, $A, $B, $C, $words[10 + ($i * 16)], 9, "2441453"); 
            GG ($C, $D, $A, $B, $words[15 + ($i * 16)], 14, "d8a1e681"); 
            GG ($B, $C, $D, $A, $words[4 + ($i * 16)], 20, "e7d3fbc8"); 
            GG ($A, $B, $C, $D, $words[9 + ($i * 16)], 5, "21e1cde6"); 
            GG ($D, $A, $B, $C, $words[14 + ($i * 16)], 9, "c33707d6"); 
            GG ($C, $D, $A, $B, $words[3 + ($i * 16)], 14, "f4d50d87"); 
            GG ($B, $C, $D, $A, $words[8 + ($i * 16)], 20, "455a14ed"); 
            GG ($A, $B, $C, $D, $words[13 + ($i * 16)], 5, "a9e3e905"); 
            GG ($D, $A, $B, $C, $words[2 + ($i * 16)], 9, "fcefa3f8"); 
            GG ($C, $D, $A, $B, $words[7 + ($i * 16)], 14, "676f02d9"); 
            GG ($B, $C, $D, $A, $words[12 + ($i * 16)], 20, "8d2a4c8a"); 

            /* ROUND 3 */
            HH ($A, $B, $C, $D, $words[5 + ($i * 16)], 4, "fffa3942"); 
            HH ($D, $A, $B, $C, $words[8 + ($i * 16)], 11, "8771f681"); 
            HH ($C, $D, $A, $B, $words[11 + ($i * 16)], 16, "6d9d6122"); 
            HH ($B, $C, $D, $A, $words[14 + ($i * 16)], 23, "fde5380c"); 
            HH ($A, $B, $C, $D, $words[1 + ($i * 16)], 4, "a4beea44"); 
            HH ($D, $A, $B, $C, $words[4 + ($i * 16)], 11, "4bdecfa9"); 
            HH ($C, $D, $A, $B, $words[7 + ($i * 16)], 16, "f6bb4b60"); 
            HH ($B, $C, $D, $A, $words[10 + ($i * 16)], 23, "bebfbc70"); 
            HH ($A, $B, $C, $D, $words[13 + ($i * 16)], 4, "289b7ec6"); 
            HH ($D, $A, $B, $C, $words[0 + ($i * 16)], 11, "eaa127fa"); 
            HH ($C, $D, $A, $B, $words[3 + ($i * 16)], 16, "d4ef3085"); 
            HH ($B, $C, $D, $A, $words[6 + ($i * 16)], 23, "4881d05"); 
            HH ($A, $B, $C, $D, $words[9 + ($i * 16)], 4, "d9d4d039"); 
            HH ($D, $A, $B, $C, $words[12 + ($i * 16)], 11, "e6db99e5"); 
            HH ($C, $D, $A, $B, $words[15 + ($i * 16)], 16, "1fa27cf8"); 
            HH ($B, $C, $D, $A, $words[2 + ($i * 16)], 23, "c4ac5665"); 

            /* ROUND 4 */
            II ($A, $B, $C, $D, $words[0 + ($i * 16)], 6, "f4292244"); 
            II ($D, $A, $B, $C, $words[7 + ($i * 16)], 10, "432aff97"); 
            II ($C, $D, $A, $B, $words[14 + ($i * 16)], 15, "ab9423a7"); 
            II ($B, $C, $D, $A, $words[5 + ($i * 16)], 21, "fc93a039"); 
            II ($A, $B, $C, $D, $words[12 + ($i * 16)], 6, "655b59c3"); 
            II ($D, $A, $B, $C, $words[3 + ($i * 16)], 10, "8f0ccc92"); 
            II ($C, $D, $A, $B, $words[10 + ($i * 16)], 15, "ffeff47d"); 
            II ($B, $C, $D, $A, $words[1 + ($i * 16)], 21, "85845dd1"); 
            II ($A, $B, $C, $D, $words[8 + ($i * 16)], 6, "6fa87e4f"); 
            II ($D, $A, $B, $C, $words[15 + ($i * 16)], 10, "fe2ce6e0"); 
            II ($C, $D, $A, $B, $words[6 + ($i * 16)], 15, "a3014314"); 
            II ($B, $C, $D, $A, $words[13 + ($i * 16)], 21, "4e0811a1"); 
            II ($A, $B, $C, $D, $words[4 + ($i * 16)], 6, "f7537e82"); 
            II ($D, $A, $B, $C, $words[11 + ($i * 16)], 10, "bd3af235"); 
            II ($C, $D, $A, $B, $words[2 + ($i * 16)], 15, "2ad7d2bb"); 
            II ($B, $C, $D, $A, $words[9 + ($i * 16)], 21, "eb86d391"); 

            $A=AddUnsigned(hexdec2($A),hexdec2($a));
            $B=AddUnsigned(hexdec2($B),hexdec2($b));
            $C=AddUnsigned(hexdec2($C),hexdec2($c));
            $D=AddUnsigned(hexdec2($D),hexdec2($d));    
    }

   $MD5 = WordToHex($A).WordToHex($B).WordToHex($C).WordToHex($D);
   return $MD5;
}

function WordToHex($lValue) { 
    $WordToHexValue = "";
    for ($lCount = 0;$lCount<=3;$lCount++) { 
        $lByte = (hexdec2($lValue)>>($lCount*8)) & 255; 
        $C = dechex($lByte);
        $WordToHexValue .= (strlen($C)=='1')?"0".dechex($lByte):dechex($lByte); 
    }
    return $WordToHexValue;
}

function F($X, $Y, $Z){    
        $X = hexdec2($X); 
        $Y = hexdec2($Y);
        $Z = hexdec2($Z);
        $calc = (($X & $Y) | ((~ $X) & $Z)); // X AND Y OR NOT X AND Z
        return  $calc; 
}

function G($X, $Y, $Z){
        $X = hexdec2($X);
        $Y = hexdec2($Y);
        $Z = hexdec2($Z);
        $calc = (($X & $Z) | ($Y & (~ $Z))); // X AND Z OR Y AND NOT Z
        return  $calc; 
}

function H($X, $Y, $Z){
        $X = hexdec2($X);
        $Y = hexdec2($Y);
        $Z = hexdec2($Z);
        $calc = ($X ^ $Y ^ $Z); // X XOR Y XOR Z
        return  $calc; 
}

function I($X, $Y, $Z){
        $X = hexdec2($X);
        $Y = hexdec2($Y);
        $Z = hexdec2($Z);
        $calc = ($Y ^ ($X | (~ $Z))) ; // Y XOR (X OR NOT Z)
        return  $calc; 
}

function AddUnsigned($lX,$lY) { 
    $lX8 = ($lX & 0x80000000);
    $lY8 = ($lY & 0x80000000);
    $lX4 = ($lX & 0x40000000);
    $lY4 = ($lY & 0x40000000);
    $lResult = ($lX & 0x3FFFFFFF)+($lY & 0x3FFFFFFF);
    if ($lX4 & $lY4) {
        $res = ($lResult ^ 0x80000000 ^ $lX8 ^ $lY8);
        if($res < 0)
            return '-'.dechex(abs($res));
        else
            return dechex($res); 
    }
    if ($lX4 | $lY4) {
        if ($lResult & 0x40000000) {
            $res = ($lResult ^ 0xC0000000 ^ $lX8 ^ $lY8);
            if($res < 0)
                return '-'.dechex(abs($res));
            else
                return dechex($res); 
        } else {
            $res = ($lResult ^ 0x40000000 ^ $lX8 ^ $lY8);
            if($res < 0)
                return '-'.dechex(abs($res));
            else
                return dechex($res);
        }
    } else {
        $res = ($lResult ^ $lX8 ^ $lY8);
        if($res < 0)
            return '-'.dechex(abs($res));
        else
            return dechex($res);
    }
}
function hexdec2($hex , $debug = false)
{  
    if(substr($hex, 0,1) == "-")
    {
        return doubleval('-'.hexdec("0x". str_replace("-", "", $hex )));
    }
    return hexdec("0x".$hex);
}

function FF(&$A, $B, $C, $D, $M, $s, $t){  
        $Level1 = hexdec2(AddUnsigned( F($B, $C, $D) , bindec($M) ));
        $level2 = hexdec2(AddUnsigned($Level1, hexdec2($t)));  
        $A = hexdec2(AddUnsigned(hexdec2($A),$level2));
        $A = rotate($A, $s); 
        $A =  AddUnsigned($A , hexdec2($B)) ; 
}

function GG(&$A, $B, $C, $D, $M, $s, $t){
        $Level1 = hexdec2(AddUnsigned( G($B, $C, $D) , bindec($M) ));
        $level2 = hexdec2(AddUnsigned($Level1, hexdec2($t)));  
        $A = hexdec2(AddUnsigned(hexdec2($A),$level2));
        $A = rotate($A, $s); 
        $A =  AddUnsigned($A , hexdec2($B)) ;  
}

function HH(&$A, $B, $C, $D, $M, $s, $t){
        $Level1 = hexdec2(AddUnsigned( H($B, $C, $D) , bindec($M) ));
        $level2 = hexdec2(AddUnsigned($Level1, hexdec2($t)));  
        $A = hexdec2(AddUnsigned(hexdec2($A),$level2));
        $A = rotate($A, $s); 
        $A =  AddUnsigned($A , hexdec2($B)) ;  
}

function II(&$A, $B, $C, $D, $M, $s, $t){
        $Level1 = hexdec2(AddUnsigned( I($B, $C, $D) , bindec($M) ));
        $level2 = hexdec2(AddUnsigned($Level1, hexdec2($t)));  
        $A = hexdec2(AddUnsigned(hexdec2($A),$level2));
        $A = rotate($A, $s); 
        $A =  AddUnsigned($A , hexdec2($B)) ;  
}

function rotate ($decimal, $bits , $debug = false) { 
    return  (($decimal << $bits) |  shiftright($decimal, (32 - $bits))  & 0xffffffff);
}
function shiftright($decimal , $right)
{ 
    if($decimal < 0)
    {
        $res = decbin($decimal >> $right);
        for ($i=0; $i < $right; $i++) { 
            $res[$i] = "";
        }
        return bindec($res) ;
    } else 
    {
        return ($decimal >> $right);
    }
}

function ConvertToArray($string) {
    $lWordCount;
    $lMessageLength = strlen($string);
    $lNumberOfWords_temp1=$lMessageLength + 8;
    $lNumberOfWords_temp2=($lNumberOfWords_temp1-($lNumberOfWords_temp1 % 64))/64;
    $lNumberOfWords = ($lNumberOfWords_temp2+1)*16; 
    $lWordArray=Array("");
    $lBytePosition = 0;
    $lByteCount = 0;
    while ( $lByteCount < $lMessageLength ) {
        $lWordCount = ($lByteCount-($lByteCount % 4))/4;
        $lBytePosition = ($lByteCount % 4)*8;
        if(!isset($lWordArray[$lWordCount]))
            $lWordArray[$lWordCount] = 0;
        $lWordArray[$lWordCount] = ($lWordArray[$lWordCount] | (ord($string[$lByteCount])<<$lBytePosition));
        $lByteCount++;
    }
    $lWordCount = ($lByteCount-($lByteCount % 4))/4;
    $lBytePosition = ($lByteCount % 4)*8;
    if(!isset($lWordArray[$lWordCount]))
        $lWordArray[$lWordCount] = 0;
    $lWordArray[$lWordCount] = $lWordArray[$lWordCount] | (0x80<<$lBytePosition);
    $lWordArray[$lNumberOfWords-2] = $lMessageLength<<3;
    $lWordArray[$lNumberOfWords-1] = $lMessageLength>>29; 
    for ($i=0; $i < $lNumberOfWords; $i++) { 
        if(isset($lWordArray[$i]))
            $lWordArray[$i] = decbin($lWordArray[$i]);
        else
            $lWordArray[$i] = '0';
    }  
    return $lWordArray;
}; 
$str='string';
echo md5($str);
echo'<br>';
echo MD($str);

Ответ 4

здесь моя версия, упрощающая основную итерацию

function MD($string)
    {
    $A = $a = "67452301";
    $B = $b = "efcdab89";
    $C = $c = "98badcfe";
    $D = $d = "10325476";

    $words = str2blks_MD5($string);   

    $torot=array("A","B","C","D");    
    $it=array(7,12,17,22,5,9,14,20,4,11,16,23,6,10,15,21);
    $funcs=array("F","G","H","I");    

    $moduls=array(0,1,1,5,5,3,0,7);

    $acs=array(     "d76aa478","e8c7b756","242070db","c1bdceee",
                    "f57c0faf","4787c62a","a8304613","fd469501",
                    "698098d8","8b44f7af","ffff5bb1","895cd7be",
                    "6b901122","fd987193","a679438e","49b40821",

                    "f61e2562","c040b340","265e5a51","e9b6c7aa",
                    "d62f105d","2441453","d8a1e681","e7d3fbc8",
                    "21e1cde6","c33707d6","f4d50d87","455a14ed",
                    "a9e3e905","fcefa3f8","676f02d9","8d2a4c8a",

                    "fffa3942","8771f681","6d9d6122","fde5380c",
                    "a4beea44","4bdecfa9","f6bb4b60","bebfbc70",
                    "289b7ec6","eaa127fa","d4ef3085","4881d05",
                    "d9d4d039","e6db99e5","1fa27cf8","c4ac5665",  

                    "f4292244","432aff97","ab9423a7","fc93a039",
                    "655b59c3","8f0ccc92","ffeff47d","85845dd1",
                    "6fa87e4f","fe2ce6e0","a3014314","4e0811a1",
                    "f7537e82","bd3af235","2ad7d2bb","eb86d391"); 

    for($i = 0; $i < count($words)/16; $i++)
        {
            $a  = $A;
            $b  = $B;
            $c  = $C;
            $d  = $D;
            $n  = 0; 

        for ($rot3=0;$rot3<4;$rot3++)
            {
            $minit=$moduls[$rot3*2];
        $madd=$moduls[$rot3*2+1];   

            for ($rot2=0;$rot2<4;$rot2++)
                {
                for ($rot=0;$rot<4;$rot++)
                {
                $word=$words[$minit + ($i * 16)];
                $nit=$it[$rot+4*$rot3];

                FGHI (${"$torot[0]"}, ${"$torot[1]"}, ${"$torot[2]"}, ${"$torot[3]"}, $word, $nit, $acs[$n],$funcs[$rot3]); 

                    array_unshift($torot,$torot[3]);
                    array_pop($torot);

                $minit=($minit+$madd)%16;
                ++$n;
                }       
                 }
            }

            $A=AddUnsigned(hexdec2($A),hexdec2($a));
            $B=AddUnsigned(hexdec2($B),hexdec2($b));
            $C=AddUnsigned(hexdec2($C),hexdec2($c));
            $D=AddUnsigned(hexdec2($D),hexdec2($d));    
            }
    return WordToHex($A).WordToHex($B).WordToHex($C).WordToHex($D);
    }
function WordToHex($lValue) 
   { 
    $WordToHexValue = "";
    for ($lCount = 0;$lCount<4;$lCount++) 
        { 
        $lByte = hexdec2($lValue)>>($lCount*8) & 255; 
        $WordToHexValue.= sprintf("%02x",$lByte);
        }
    return $WordToHexValue;
    }
function FGHI(&$A, $B, $C, $D, $M, $s, $t, $func)
        {
        $Level1 = hexdec2(AddUnsigned(FGHI2($B, $C, $D, $func) , bindec($M) ));
        $level2 = hexdec2(AddUnsigned($Level1, hexdec2($t)));  
        $A = hexdec2(AddUnsigned(hexdec2($A),$level2));
        $A = rotate($A, $s); 
        $A = AddUnsigned($A , hexdec2($B)) ; 
        }
function FGHI2($X, $Y, $Z,$func)
        {    
        $X = hexdec2($X); 
        $Y = hexdec2($Y);
        $Z = hexdec2($Z);

    switch ($func)
        {
        case "F":
                $calc = (($X & $Y) | ((~ $X) & $Z));break;
        case "G":
            $calc = (($X & $Z) | ($Y & (~ $Z)));break;
        case "H":
            $calc = ($X ^ $Y ^ $Z);break;
        case "I":
            $calc = ($Y ^ ($X | (~ $Z)));
        }
        return  $calc; 
    }
function dectohex($res)
    {
        if($res < 0)
            return '-'.dechex(abs($res));
        return dechex($res);    
    }

function hexdec2($hex)
    {  
    if($hex[0] == "-")   
        return doubleval('-'.hexdec(str_replace("-", "", $hex )));

    return hexdec($hex);
    }

function AddUnsigned($lX,$lY) 
   { 
    $lX8 = ($lX & 0x80000000);
    $lY8 = ($lY & 0x80000000);
    $lX4 = ($lX & 0x40000000);
    $lY4 = ($lY & 0x40000000);

    $lResult = ($lX & 0x3FFFFFFF)+($lY & 0x3FFFFFFF);

    $res=$lResult ^ $lX8 ^ $lY8;

    if ($lX4 & $lY4)                       return dectohex($res ^ 0x80000000);   
    if ($lX4 | $lY4) 
    {
        if ($lResult & 0x40000000)         return dectohex($res ^ 0xC0000000);
        else                               return dectohex($res ^ 0x40000000);       
        }                                  return dectohex($res);
    }
function rotate ($decimal, $bits) 
    { 
    return  (($decimal << $bits) |  shiftright($decimal, (32 - $bits))  & 0xffffffff);
    }
function shiftright($decimal , $right)
    { 
    $shift=$decimal >> $right;

    if($decimal >= 0) return $shift;
    return bindec(substr(decbin($shift),$right));
    }

function str2blks_MD5($str)
  {
  $nblk = ((strlen($str) + 8) >> 6) + 1;

  $blks = array($nblk * 16);

  for($i = 0; $i < $nblk * 16; $i++) 
                $blks[$i] = 0;

  for($i = 0; $i < strlen($str); $i++)
            $blks[$i >> 2] |= ord($str[$i]) << (($i % 4) * 8);

  $blks[$i >> 2] |= 0x80 << (($i % 4) * 8);

  $blks[$nblk * 16 - 2] = strlen($str) * 8;

  for ($i=0; $i < $nblk * 16; $i++)             
        $blks[$i] = decbin($blks[$i]);

  return $blks;
  }
$str='test';
echo md5($str); // 098f6bcd4621d373cade4e832627b4f6
echo'<br>';
echo MD($str); //  098f6bcd4621d373cade4e832627b4f6