Rename trunk directory to public_html
[ldapsaisie.git] / public_html / includes / libs / class.smbHash.php
1 <?php
2 /*
3
4   This code is part of LDAP Account Manager (http://www.sourceforge.net/projects/lam)
5   Copyright (C) 2004 Roland Gruber
6
7   This program is free software; you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation; either version 2 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 */
22
23 /**
24 * This class provides functions to calculate Samba NT and LM hashes.
25 *
26 * The code is a conversion from createntlm.pl (Benjamin Kuit) and smbdes.c/md4.c (Andrew Tridgell).
27 *
28 * @author Roland Gruber
29 *
30 * @package modules
31 */
32
33 /**
34 * Calculates NT and LM hashes.
35 *
36 * The important functions are lmhash($password) and nthash($password).
37 *
38 * @package modules
39 */
40 class smbHash {
41
42 # Contants used in lanlam hash calculations
43 # Ported from SAMBA/source/libsmb/smbdes.c:perm1[56]
44 var $perm1 = array(57, 49, 41, 33, 25, 17,  9,
45         1, 58, 50, 42, 34, 26, 18,
46         10,  2, 59, 51, 43, 35, 27,
47         19, 11,  3, 60, 52, 44, 36,
48         63, 55, 47, 39, 31, 23, 15,
49         7, 62, 54, 46, 38, 30, 22,
50         14,  6, 61, 53, 45, 37, 29,
51         21, 13,  5, 28, 20, 12,  4);
52 # Ported from SAMBA/source/libsmb/smbdes.c:perm2[48]
53 var $perm2 = array(14, 17, 11, 24,  1,  5,
54         3, 28, 15,  6, 21, 10,
55         23, 19, 12,  4, 26,  8,
56         16,  7, 27, 20, 13,  2,
57         41, 52, 31, 37, 47, 55,
58         30, 40, 51, 45, 33, 48,
59         44, 49, 39, 56, 34, 53,
60         46, 42, 50, 36, 29, 32);
61 # Ported from SAMBA/source/libsmb/smbdes.c:perm3[64]
62 var $perm3 = array(58, 50, 42, 34, 26, 18, 10,  2,
63         60, 52, 44, 36, 28, 20, 12,  4,
64         62, 54, 46, 38, 30, 22, 14,  6,
65         64, 56, 48, 40, 32, 24, 16,  8,
66         57, 49, 41, 33, 25, 17,  9,  1,
67         59, 51, 43, 35, 27, 19, 11,  3,
68         61, 53, 45, 37, 29, 21, 13,  5,
69         63, 55, 47, 39, 31, 23, 15,  7);
70 # Ported from SAMBA/source/libsmb/smbdes.c:perm4[48]
71 var $perm4 = array(32,  1,  2,  3,  4,  5,
72         4,  5,  6,  7,  8,  9,
73         8,  9, 10, 11, 12, 13,
74         12, 13, 14, 15, 16, 17,
75         16, 17, 18, 19, 20, 21,
76         20, 21, 22, 23, 24, 25,
77         24, 25, 26, 27, 28, 29,
78         28, 29, 30, 31, 32,  1);
79 # Ported from SAMBA/source/libsmb/smbdes.c:perm5[32]
80 var $perm5 = array(16,  7, 20, 21,
81         29, 12, 28, 17,
82         1, 15, 23, 26,
83         5, 18, 31, 10,
84         2,  8, 24, 14,
85         32, 27,  3,  9,
86         19, 13, 30,  6,
87         22, 11,  4, 25);
88 # Ported from SAMBA/source/libsmb/smbdes.c:perm6[64]
89 var $perm6 = array(40,  8, 48, 16, 56, 24, 64, 32,
90         39,  7, 47, 15, 55, 23, 63, 31,
91         38,  6, 46, 14, 54, 22, 62, 30,
92         37,  5, 45, 13, 53, 21, 61, 29,
93         36,  4, 44, 12, 52, 20, 60, 28,
94         35,  3, 43, 11, 51, 19, 59, 27,
95         34,  2, 42, 10, 50, 18, 58, 26,
96         33,  1, 41,  9, 49, 17, 57, 25);
97 # Ported from SAMBA/source/libsmb/smbdes.c:sc[16]
98 var $sc = array(1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1);
99 # Ported from SAMBA/source/libsmb/smbdes.c:sbox[8][4][16]
100 # Side note, I used cut and paste for all these numbers, I did NOT
101 # type them all in =)
102 var $sbox = array(array(array(14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7),
103                 array( 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8),
104                 array( 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0),
105                 array(15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13)),
106         array(array(15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10),
107                 array( 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5),
108                 array( 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15),
109                 array(13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9)),
110         array(array(10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8),
111                 array(13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1),
112                 array(13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7),
113                 array( 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12)),
114         array(array( 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15),
115                 array(13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9),
116                 array(10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4),
117                 array( 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14)),
118         array(array( 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9),
119                 array(14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6),
120                 array( 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14),
121                 array(11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3)),
122         array(array(12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11),
123                 array(10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8),
124                 array( 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6),
125                 array( 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13)),
126         array(array( 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1),
127                 array(13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6),
128                 array( 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2),
129                 array( 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12)),
130         array(array(13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7),
131                 array( 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2),
132                 array( 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8),
133                 array( 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11)));
134
135         /**
136         * Fixes too large numbers
137         */
138         function x($i) {
139                 if ($i < 0) return 4294967296 - $i;
140                 else return $i;
141         }
142
143         /**
144         * @param integer count
145         * @param array $data
146         * @return array
147         */
148         function lshift($count, $data) {
149                 $ret = array();
150                 for ($i = 0; $i < sizeof($data); $i++) {
151                         $ret[$i] = $data[($i + $count)%sizeof($data)];
152                 }
153                 return $ret;
154         }
155
156         /**
157         * @param array in input data
158         * @param array p permutation
159         * @return array
160         */
161         function permute($in, $p, $n) {
162                 $ret = array();
163                 for ($i = 0; $i < $n; $i++) {
164                         $ret[$i] = $in[$p[$i] - 1]?1:0;
165                 }
166                 return $ret;
167         }
168
169         /**
170         * @param array $in1
171         * @param array $in2
172         * @return array
173         */
174         function mxor($in1, $in2) {
175                 $ret = array();
176                 for ($i = 0; $i < sizeof($in1); $i++) {
177                         $ret[$i] = $in1[$i] ^ $in2[$i];
178                 }
179                 return $ret;
180         }
181
182         /**
183         * @param array $in
184         * @param array $key
185         * @param boolean $forw
186         * @return array
187         */
188         function doHash($in, $key, $forw) {
189                 $ki = array();
190         
191                 $pk1 = $this->permute($key, $this->perm1, 56);
192                 
193                 $c = array();
194                 $d = array();
195                 for ($i = 0; $i < 28; $i++) {
196                         $c[$i] = $pk1[$i];
197                         $d[$i] = $pk1[28 + $i];
198                 }
199                 
200                 for ($i = 0; $i < 16; $i++) {
201                         $c = $this->lshift($this->sc[$i], $c);
202                         $d = $this->lshift($this->sc[$i], $d);
203                         
204                         $cd = $c;
205                         for ($k = 0; $k < sizeof($d); $k++) $cd[] = $d[$k];
206                         $ki[$i] = $this->permute($cd, $this->perm2, 48);
207                 }
208                 
209                 $pd1 = $this->permute($in, $this->perm3, 64);
210                 
211                 $l = array();
212                 $r = array();
213                 for ($i = 0; $i < 32; $i++) {
214                         $l[$i] = $pd1[$i];
215                         $r[$i] = $pd1[32 + $i];
216                 }
217                 
218                 for ($i = 0; $i < 16; $i++) {
219                         $er = $this->permute($r, $this->perm4, 48);
220                         if ($forw) $erk = $this->mxor($er, $ki[$i]);
221                         else $erk = $this->mxor($er, $ki[15 - $i]);
222                         
223                         for ($j = 0; $j < 8; $j++) {
224                                 for ($k = 0; $k < 6; $k++) {
225                                         $b[$j][$k] = $erk[($j * 6) + $k];
226                                 }
227                         }
228                         for ($j = 0; $j < 8; $j++) {
229                                 $m = array();
230                                 $n = array();
231                                 $m = ($b[$j][0] << 1) | $b[$j][5];
232                                 $n = ($b[$j][1] << 3) | ($b[$j][2] << 2) | ($b[$j][3] << 1) | $b[$j][4];
233                                 
234                                 for ($k = 0; $k < 4; $k++) {
235                                         $b[$j][$k]=($this->sbox[$j][$m][$n] & (1 << (3-$k)))?1:0;
236                                 }
237                         }
238                         
239                         for ($j = 0; $j < 8; $j++) {
240                                 for ($k = 0; $k < 4; $k++) {
241                                         $cb[($j * 4) + $k] = $b[$j][$k];
242                                 }
243                         }
244                         $pcb = $this->permute($cb, $this->perm5, 32);
245                         $r2 = $this->mxor($l, $pcb);
246                         for ($k = 0; $k < 32; $k++) $l[$k] = $r[$k];
247                         for ($k = 0; $k < 32; $k++) $r[$k] = $r2[$k];
248                 }
249                 $rl = $r;
250                 for ($i = 0; $i < sizeof($l); $i++) $rl[] = $l[$i];
251                 return $this->permute($rl, $this->perm6, 64);
252         }
253
254         function str_to_key($str) {
255                 $key[0] = $this->unsigned_shift_r($str[0], 1);
256                 $key[1] = (($str[0]&0x01)<<6) | $this->unsigned_shift_r($str[1], 2);
257                 $key[2] = (($str[1]&0x03)<<5) | $this->unsigned_shift_r($str[2], 3);
258                 $key[3] = (($str[2]&0x07)<<4) | $this->unsigned_shift_r($str[3], 4);
259                 $key[4] = (($str[3]&0x0F)<<3) | $this->unsigned_shift_r($str[4], 5);
260                 $key[5] = (($str[4]&0x1F)<<2) | $this->unsigned_shift_r($str[5], 6);
261                 $key[6] = (($str[5]&0x3F)<<1) | $this->unsigned_shift_r($str[6], 7);
262                 $key[7] = $str[6]&0x7F;
263                 for ($i = 0; $i < 8; $i++) {
264                         $key[$i] = ($key[$i] << 1);
265                 }
266                 return $key;
267         }
268
269         function smb_hash($in, $key, $forw){
270                 $key2 = $this->str_to_key($key);
271         
272                 for ($i = 0; $i < 64; $i++) {
273                         $inb[$i] = ($in[$i/8] & (1<<(7-($i%8)))) ? 1:0;
274                         $keyb[$i] = ($key2[$i/8] & (1<<(7-($i%8)))) ? 1:0;
275                         $outb[$i] = 0;
276                 }
277                 $outb = $this->dohash($inb, $keyb, $forw);
278                 for ($i = 0; $i < 8; $i++) {
279                         $out[$i] = 0;
280                 }
281                 for ($i = 0; $i < 65; $i++) {
282                         if ( isset($outb[$i]) && $outb[$i] )  {
283                                 $out[$i/8] |= (1<<(7-($i%8)));
284                         }
285                 }
286                 return $out;
287         }
288
289         function E_P16($in) {
290                 $p14 = array_values(unpack("C*",$in));
291                 $sp8 = array(0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25);
292                 $p14_1 = array();
293                 $p14_2 = array();
294                 for ($i = 0; $i < 7; $i++) {
295                         $p14_1[$i] = $p14[$i];
296                         $p14_2[$i] = $p14[$i + 7];
297                 }
298                 $p16_1 = $this->smb_hash($sp8, $p14_1, true);
299                 $p16_2 = $this->smb_hash($sp8, $p14_2, true);
300                 $p16 = $p16_1;
301                 for ($i = 0; $i < sizeof($p16_2); $i++) {
302                         $p16[] = $p16_2[$i];
303                 }
304                 return $p16;
305         }
306
307         /**
308         * Calculates the LM hash of a given password.
309         *
310         * @param string $password password
311         * @return string hash value
312         */
313         function lmhash($password = "") {
314                 $password = strtoupper($password);
315                 $password = substr($password,0,14);
316                 $password = str_pad($password, 14, chr(0));
317                 $p16 = $this->E_P16($password);
318                 for ($i = 0; $i < sizeof($p16); $i++) {
319                         $p16[$i] = sprintf("%02X", $p16[$i]);
320                 }
321                 return join("", $p16);
322         }
323
324         /**
325         * Calculates the NT hash of a given password.
326         *
327         * @param string $password password
328         * @return string hash value
329         */
330         function nthash($password = "") {
331                 $password = substr($password,0,128);
332                 $password2 = "";
333                 for ($i = 0; $i < strlen($password); $i++) $password2 .= $password[$i] . chr(0);
334                 $password = $password2;
335                 $hex = $this->mdfour($password);
336                 for ($i = 0; $i < sizeof($hex); $i++) {
337                         $hex[$i] = sprintf("%02X", $hex[$i]);
338                 }
339                 return join("", $hex);
340         }
341
342         # Support functions
343         # Ported from SAMBA/source/lib/md4.c:F,G and H respectfully
344         function F($X, $Y, $Z) {
345                 $ret = (($X&$Y) | ((~((int)$X))&$Z));
346                 if ($this->x($ret) > 4294967296) {
347                         $ret = (2*4294967296) - $this->x($ret);
348                 }
349                 return $ret;
350         }
351
352         function G($X, $Y, $Z) {
353                 return ($X&$Y) | ($X&$Z) | ($Y&$Z);
354         }
355         
356         function H($X, $Y, $Z) {
357                 return $X^$Y^$Z;
358         }
359
360         # Ported from SAMBA/source/lib/md4.c:mdfour
361         function mdfour($in) {
362                 $in = unpack("C*",$in);
363                 $in = array_values($in);
364                 $b = sizeof($in) * 8;
365                 $A = array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
366                 while (sizeof($in) > 64 ) {
367                         $M = $this->copy64($in);
368                         $this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
369                         $new_in = array();
370                         for ($i = 64; $i < sizeof($in); $i++) $new_in[] = $in[$i];
371                         $in = $new_in;
372                 }
373                 $buf = $in;
374                 $buf[] = 0x80;
375                 for ($i = sizeof($buf) - 1; $i < 127; $i++) $buf[] = 0;
376                 if ( sizeof($in) <= 55 ) {
377                         $temp = $this->copy4($b);
378                         $buf[56] = $temp[0];
379                         $buf[57] = $temp[1];
380                         $buf[58] = $temp[2];
381                         $buf[59] = $temp[3];
382                         $M = $this->copy64($buf);
383                         $this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
384                 }
385                 else {
386                         $temp = $this->copy4($b);
387                         $buf[120] = $temp[0];
388                         $buf[121] = $temp[1];
389                         $buf[122] = $temp[2];
390                         $buf[123] = $temp[3];
391                         $M = $this->copy64($buf);
392                         $this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
393                         $temp = array();
394                         for ($i = 64; $i < sizeof($buf); $i++) $temp[] = $buf[$i];
395                         $M = $this->copy64($temp);
396                         $this->mdfour64($A[0], $A[1], $A[2], $A[3], $M);
397                 }
398                 $out = array();
399                 $temp = $this->copy4($A[0]);
400                 for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
401                 $temp = $this->copy4($A[1]);
402                 for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
403                 $temp = $this->copy4($A[2]);
404                 for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
405                 $temp = $this->copy4($A[3]);
406                 for ($i = 0; $i < 4; $i++) $out[] = $temp[$i];
407                 return $out;
408         }
409
410         # Ported from SAMBA/source/lib/md4.c:copy4
411         function copy4($x) {
412                 $out = array();
413                 $out[0] = $x&0xFF;
414                 $out[1] = $this->unsigned_shift_r($x, 8)&0xFF;
415                 $out[2] = $this->unsigned_shift_r($x, 16)&0xFF;
416                 $out[3] = $this->unsigned_shift_r($x, 24)&0xFF;
417                 return $out;
418         }
419
420         # Ported from SAMBA/source/lib/md4.c:copy64
421         function copy64($in) {
422                 for ($i = 0; $i < 16; $i++) {
423                         $M[$i] = ($in[$i*4+3]<<24) | ($in[$i*4+2]<<16) | ($in[$i*4+1]<<8) | ($in[$i*4+0]<<0);
424                 }
425                 return $M;
426         }
427
428         # Ported from SAMBA/source/lib/md4.c:mdfour64
429         function mdfour64(&$A, &$B, &$C, &$D, $M) {
430                 $X = array();
431                 for ($i = 0; $i < 16; $i++) $X[] = $M[$i];
432                 $AA=$A;
433                 $BB=$B;
434                 $CC=$C;
435                 $DD=$D;
436                 $this->ROUND1($A,$B,$C,$D,  0,  3, $X);
437                 $this->ROUND1($D,$A,$B,$C,  1,  7, $X);
438                 $this->ROUND1($C,$D,$A,$B,  2, 11, $X);
439                 $this->ROUND1($B,$C,$D,$A,  3, 19, $X);
440                 $this->ROUND1($A,$B,$C,$D,  4,  3, $X);  $this->ROUND1($D,$A,$B,$C,  5,  7, $X);
441                 $this->ROUND1($C,$D,$A,$B,  6, 11, $X);  $this->ROUND1($B,$C,$D,$A,  7, 19, $X);
442                 $this->ROUND1($A,$B,$C,$D,  8,  3, $X);  $this->ROUND1($D,$A,$B,$C,  9,  7, $X);
443                 $this->ROUND1($C,$D,$A,$B, 10, 11, $X);  $this->ROUND1($B,$C,$D,$A, 11, 19, $X);
444                 $this->ROUND1($A,$B,$C,$D, 12,  3, $X);  $this->ROUND1($D,$A,$B,$C, 13,  7, $X);
445                 $this->ROUND1($C,$D,$A,$B, 14, 11, $X);  $this->ROUND1($B,$C,$D,$A, 15, 19, $X);
446                 $this->ROUND2($A,$B,$C,$D,  0,  3, $X);  $this->ROUND2($D,$A,$B,$C,  4,  5, $X);
447                 $this->ROUND2($C,$D,$A,$B,  8,  9, $X);  $this->ROUND2($B,$C,$D,$A, 12, 13, $X);
448                 $this->ROUND2($A,$B,$C,$D,  1,  3, $X);  $this->ROUND2($D,$A,$B,$C,  5,  5, $X);
449                 $this->ROUND2($C,$D,$A,$B,  9,  9, $X);  $this->ROUND2($B,$C,$D,$A, 13, 13, $X);
450                 $this->ROUND2($A,$B,$C,$D,  2,  3, $X);  $this->ROUND2($D,$A,$B,$C,  6,  5, $X);
451                 $this->ROUND2($C,$D,$A,$B, 10,  9, $X);  $this->ROUND2($B,$C,$D,$A, 14, 13, $X);
452                 $this->ROUND2($A,$B,$C,$D,  3,  3, $X);  $this->ROUND2($D,$A,$B,$C,  7,  5, $X);
453                 $this->ROUND2($C,$D,$A,$B, 11,  9, $X);  $this->ROUND2($B,$C,$D,$A, 15, 13, $X);
454                 $this->ROUND3($A,$B,$C,$D,  0,  3, $X);  $this->ROUND3($D,$A,$B,$C,  8,  9, $X);
455                 $this->ROUND3($C,$D,$A,$B,  4, 11, $X);  $this->ROUND3($B,$C,$D,$A, 12, 15, $X);
456                 $this->ROUND3($A,$B,$C,$D,  2,  3, $X);  $this->ROUND3($D,$A,$B,$C, 10,  9, $X);
457                 $this->ROUND3($C,$D,$A,$B,  6, 11, $X);  $this->ROUND3($B,$C,$D,$A, 14, 15, $X);
458                 $this->ROUND3($A,$B,$C,$D,  1,  3, $X);  $this->ROUND3($D,$A,$B,$C,  9,  9, $X);
459                 $this->ROUND3($C,$D,$A,$B,  5, 11, $X);  $this->ROUND3($B,$C,$D,$A, 13, 15, $X);
460                 $this->ROUND3($A,$B,$C,$D,  3,  3, $X);  $this->ROUND3($D,$A,$B,$C, 11,  9, $X);
461                 $this->ROUND3($C,$D,$A,$B,  7, 11, $X);  $this->ROUND3($B,$C,$D,$A, 15, 15, $X);
462                 
463                 $A = $this->add32(array($A, $AA)); $B = $this->add32(array($B, $BB));
464                 $C = $this->add32(array($C, $CC)); $D = $this->add32(array($D, $DD));
465         }
466
467         # Needed? because perl seems to choke on overflowing when doing bitwise
468         # operations on numbers larger than 32 bits. Well, it did on my machine =)
469         function add32($v) {
470                 $sum = array();
471                 for ($i = 0; $i < sizeof($v); $i++) {
472                         $v[$i] = array($this->unsigned_shift_r(($v[$i]&0xffff0000), 16), ($v[$i]&0xffff));
473                 }
474                 for ($i = 0; $i < sizeof($v); $i++) {
475                         @$sum[0] += $v[$i][0];
476                         @$sum[1] += $v[$i][1];
477                 }
478                 $sum[0] += ($sum[1]&0xffff0000)>>16;
479                 $sum[1] &= 0xffff;
480                 $sum[0] &= 0xffff;
481                 $ret = ($sum[0]<<16) | $sum[1];
482                 if ($this->x($ret) > 4294967296) {
483                         $ret = (2*4294967296) - $this->x($ret);
484                 }
485                 return $ret;
486         }
487
488         # Ported from SAMBA/source/lib/md4.c:ROUND1
489         function ROUND1(&$a,$b,$c,$d,$k,$s,$X) {
490                 $a = $this->md4lshift($this->add32(array($a, $this->F($b,$c,$d), $X[$k])), $s);
491                 return $a;
492         }
493
494         # Ported from SAMBA/source/lib/md4.c:ROUND2
495         function ROUND2(&$a,$b,$c,$d,$k,$s,$X) {
496                 $a = $this->md4lshift($this->add32(array($a, $this->G($b,$c,$d), $X[$k] + 0x5A827999)), $s);
497                 return $a;
498         }
499
500         # Ported from SAMBA/source/lib/md4.c:ROUND3
501         function ROUND3(&$a,$b,$c,$d,$k,$s,$X) {
502                 $a = $this->md4lshift($this->add32(array($a + $this->H($b,$c,$d) + $X[$k] + 0x6ED9EBA1)), $s);
503                 return $a;
504         }
505
506         # Ported from SAMBA/source/lib/md4.c:lshift
507         # Renamed to prevent clash with SAMBA/source/libsmb/smbdes.c:lshift
508         function md4lshift($x, $s) {
509                 $x &= 0xFFFFFFFF;
510                 if ($this->x($x) > 4294967296) {
511                         $x = (2*4294967296) - $this->x($x);
512                 }
513                 $ret = ((($x<<$s)&0xFFFFFFFF) | $this->unsigned_shift_r($x, (32-$s)));
514                 if ($this->x($ret) > 4294967296) {
515                         $ret = (2*4294967296) - $this->x($ret);
516                 }
517                 return $ret;
518         }
519
520         /**
521         * Unsigned shift operation for 32bit values.
522         *
523         * PHP 4 only supports signed shifts by default.
524         */
525         function unsigned_shift_r($a, $b) { 
526                 $z = 0x80000000; 
527                 if ($z & $a) { 
528                         $a = ($a >> 1); 
529                         $a &= (~$z); 
530                         $a |= 0x40000000; 
531                         $a = ($a >> ($b - 1)); 
532                 } 
533                 else { 
534                         $a = ($a >> $b); 
535                 } 
536                 return $a; 
537         } 
538
539 }
540
541 ?>