Swift_Transport_Esmtp_Auth_NTLMAuthenticator   C
last analyzed

Complexity

Total Complexity 57

Size/Duplication

Total Lines 689
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Test Coverage

Coverage 64.85%

Importance

Changes 0
Metric Value
dl 0
loc 689
ccs 190
cts 293
cp 0.6485
rs 5.2116
c 0
b 0
f 0
wmc 57
lcom 1
cbo 1

27 Methods

Rating   Name   Duplication   Size   Complexity  
C authenticate() 0 29 7
A getAuthKeyword() 0 4 1
B si2bin() 0 25 6
A sendMessage1() 0 6 1
B parseMessage2() 0 26 1
B readSubBlock() 0 24 3
A sendMessage3() 0 22 2
A createMessage1() 0 6 1
B createMessage3() 0 28 1
A createBlob() 0 10 1
A getDomainAndUsername() 0 15 3
A createLMPassword() 0 20 1
A createNTLMPassword() 0 12 1
A getCorrectTimestamp() 0 15 2
A createLMv2Password() 0 13 2
A createNTLMv2Hash() 0 12 1
B createDesKey() 0 37 4
A createSecurityBuffer() 0 8 2
A readSecurityBuffer() 0 7 1
A castToByte() 0 4 1
A uRShift() 0 8 2
A createByte() 0 10 2
A desEncrypt() 0 5 1
A md5Encrypt() 0 13 2
A md4Encrypt() 0 6 2
A convertTo16bit() 0 4 1
B debug() 0 73 5

How to fix   Complexity   

Complex Class

Complex classes like Swift_Transport_Esmtp_Auth_NTLMAuthenticator often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Swift_Transport_Esmtp_Auth_NTLMAuthenticator, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of SwiftMailer.
5
 * (c) 2004-2009 Chris Corbyn
6
 *
7
 * This authentication is for Exchange servers. We support version 1 & 2.
8
 *
9
 * For the full copyright and license information, please view the LICENSE
10
 * file that was distributed with this source code.
11
 */
12
13
/**
14
 * Handles NTLM authentication.
15
 *
16
 * @author Ward Peeters <[email protected]>
17
 */
18
class Swift_Transport_Esmtp_Auth_NTLMAuthenticator implements Swift_Transport_Esmtp_Authenticator
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
19
{
20
    const NTLMSIG  = "NTLMSSP\x00";
21
    const DESCONST = 'KGS!@#$%';
22
23
    /**
24
     * Get the name of the AUTH mechanism this Authenticator handles.
25
     *
26
     * @return string
27
     */
28 3
    public function getAuthKeyword()
29 1
    {
30 3
        return 'NTLM';
31
    }
32
33
    /**
34
     * Try to authenticate the user with $username and $password.
35
     *
36
     * @param Swift_Transport_SmtpAgent $agent
37
     * @param string                    $username
38
     * @param string                    $password
39
     *
40
     * @return bool
41
     */
42 2
    public function authenticate(Swift_Transport_SmtpAgent $agent, $username, $password)
43
    {
44 2
        if (!function_exists('openssl_random_pseudo_bytes') || !function_exists('openssl_encrypt')) {
45
            throw new LogicException('The OpenSSL extension must be enabled to use the NTLM authenticator.');
46
        }
47
48 2
        if (!function_exists('bcmul')) {
49
            throw new LogicException('The BCMath functions must be enabled to use the NTLM authenticator.');
50
        }
51
52
        try {
53
            // execute AUTH command and filter out the code at the beginning
54
            // AUTH NTLM xxxx
55 2
            $response = base64_decode(substr(trim($this->sendMessage1($agent)), 4));
56
57
            // extra parameters for our unit cases
58 1
            $timestamp = func_num_args() > 3 ? func_get_arg(3) : $this->getCorrectTimestamp(bcmul(microtime(true), '1000'));
59 1
            $client = func_num_args() > 4 ? func_get_arg(4) : \random_bytes(8);
60
61
            // Message 3 response
62 1
            $this->sendMessage3($response, $username, $password, $timestamp, $client, $agent);
63
64 1
            return true;
65 1
        } catch (Swift_TransportException $e) {
66 1
            $agent->executeCommand("RSET\r\n", array(250));
67
68 1
            return false;
69
        }
70
    }
71
72
    /**
73
     * @param string $si
74
     * @param int    $bits
75
     *
76
     * @return null|string
77
     */
78
    protected function si2bin($si, $bits = 32)
79
    {
80
        $bin = null;
81
        if ($si >= -pow(2, $bits - 1) && ($si <= pow(2, $bits - 1))) {
82
            // positive or zero
83
            if ($si >= 0) {
84
                $bin = base_convert($si, 10, 2);
85
                // pad to $bits bit
86
                $bin_length = strlen($bin);
87
                if ($bin_length < $bits) {
88
                    $bin = str_repeat('0', $bits - $bin_length) . $bin;
89
                }
90
            } else {
91
                // negative
92
                $si = -$si - pow(2, $bits);
93
                $bin = base_convert($si, 10, 2);
94
                $bin_length = strlen($bin);
95
                if ($bin_length > $bits) {
96
                    $bin = str_repeat('1', $bits - $bin_length) . $bin;
97
                }
98
            }
99
        }
100
101
        return $bin;
102
    }
103
104
    /**
105
     * Send our auth message and returns the response.
106
     *
107
     * @param Swift_Transport_SmtpAgent $agent
108
     *
109
     * @return string SMTP Response
110
     */
111 2
    protected function sendMessage1(Swift_Transport_SmtpAgent $agent)
112
    {
113 2
        $message = $this->createMessage1();
114
115 2
        return $agent->executeCommand(sprintf("AUTH %s %s\r\n", $this->getAuthKeyword(), base64_encode($message)), array(334));
116
    }
117
118
    /**
119
     * Fetch all details of our response (message 2).
120
     *
121
     * @param string $response
122
     *
123
     * @return array our response parsed
124
     */
125 1
    protected function parseMessage2($response)
126
    {
127 1
        $responseHex = bin2hex($response);
128 1
        $length = floor(hexdec(substr($responseHex, 28, 4)) / 256) * 2;
129 1
        $offset = floor(hexdec(substr($responseHex, 32, 4)) / 256) * 2;
130 1
        $challenge = \hex2bin(substr($responseHex, 48, 16));
131 1
        $context = \hex2bin(substr($responseHex, 64, 16));
132 1
        $targetInfoH = \hex2bin(substr($responseHex, 80, 16));
133 1
        $targetName = \hex2bin(substr($responseHex, $offset, $length));
134 1
        $offset = floor(hexdec(substr($responseHex, 88, 4)) / 256) * 2;
135 1
        $targetInfoBlock = substr($responseHex, $offset);
136 1
        list($domainName, $serverName, $DNSDomainName, $DNSServerName, $terminatorByte) = $this->readSubBlock($targetInfoBlock);
137
138
        return array(
139 1
            $challenge,
140 1
            $context,
141 1
            $targetInfoH,
142 1
            $targetName,
143 1
            $domainName,
144 1
            $serverName,
145 1
            $DNSDomainName,
146 1
            $DNSServerName,
147 1
            \hex2bin($targetInfoBlock),
148 1
            $terminatorByte,
149 1
        );
150
    }
151
152
    /**
153
     * Read the blob information in from message2.
154
     *
155
     * @param string $block
156
     *
157
     * @return string[]
158
     */
159 1
    protected function readSubBlock($block)
160
    {
161
        // remove terminatorByte cause it's always the same
162 1
        $block = substr($block, 0, -8);
163
164 1
        $length = strlen($block);
165 1
        $offset = 0;
166 1
        $data = array();
167 1
        while ($offset < $length) {
168 1
            $blockLength = hexdec(substr(substr($block, $offset, 8), -4)) / 256;
169 1
            $offset += 8;
170 1
            $data[] = \hex2bin(substr($block, $offset, $blockLength * 2));
171 1
            $offset += $blockLength * 2;
172 1
        }
173
174 1
        if (count($data) === 3) {
175 1
            $data[] = $data[2];
176 1
            $data[2] = '';
177 1
        }
178
179 1
        $data[] = $this->createByte('00');
180
181 1
        return $data;
182
    }
183
184
    /**
185
     * Send our final message with all our data.
186
     *
187
     * @param string                    $response Message 1 response (message 2)
188
     * @param string                    $username
189
     * @param string                    $password
190
     * @param string                    $timestamp
191
     * @param string                    $client
192
     * @param Swift_Transport_SmtpAgent $agent
193
     * @param bool                      $v2       Use version2 of the protocol
194
     *
195
     * @return string
196
     */
197 1
    protected function sendMessage3($response, $username, $password, $timestamp, $client, Swift_Transport_SmtpAgent $agent, $v2 = true)
198
    {
199 1
        list($domain, $username) = $this->getDomainAndUsername($username);
200
        //$challenge, $context, $targetInfoH, $targetName, $domainName, $workstation, $DNSDomainName, $DNSServerName, $blob, $ter
0 ignored issues
show
Unused Code Comprehensibility introduced by
68% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
201 1
        list($challenge, , , , , $workstation, , , $blob) = $this->parseMessage2($response);
202
203 1
        if (!$v2) {
204
            // LMv1
205
            $lmResponse = $this->createLMPassword($password, $challenge);
206
            // NTLMv1
207
            $ntlmResponse = $this->createNTLMPassword($password, $challenge);
208
        } else {
209
            // LMv2
210 1
            $lmResponse = $this->createLMv2Password($password, $username, $domain, $challenge, $client);
211
            // NTLMv2
212 1
            $ntlmResponse = $this->createNTLMv2Hash($password, $username, $domain, $challenge, $blob, $timestamp, $client);
213
        }
214
215 1
        $message = $this->createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse);
216
217 1
        return $agent->executeCommand(sprintf("%s\r\n", base64_encode($message)), array(235));
218
    }
219
220
    /**
221
     * Create our message 1.
222
     *
223
     * @return string
224
     */
225 3
    protected function createMessage1()
226
    {
227
        return self::NTLMSIG
228 3
               . $this->createByte('01') // Message 1
229 3
               . $this->createByte('0702'); // Flags
230
    }
231
232
    /**
233
     * Create our message 3.
234
     *
235
     * @param string $domain
236
     * @param string $username
237
     * @param string $workstation
238
     * @param string $lmResponse
239
     * @param string $ntlmResponse
240
     *
241
     * @return string
242
     */
243 3
    protected function createMessage3($domain, $username, $workstation, $lmResponse, $ntlmResponse)
244
    {
245
        // Create security buffers
246 3
        $domainSec = $this->createSecurityBuffer($domain, 64);
247 3
        $domainInfo = $this->readSecurityBuffer(bin2hex($domainSec));
248 3
        $userSec = $this->createSecurityBuffer($username, ($domainInfo[0] + $domainInfo[1]) / 2);
249 3
        $userInfo = $this->readSecurityBuffer(bin2hex($userSec));
250 3
        $workSec = $this->createSecurityBuffer($workstation, ($userInfo[0] + $userInfo[1]) / 2);
251 3
        $workInfo = $this->readSecurityBuffer(bin2hex($workSec));
252 3
        $lmSec = $this->createSecurityBuffer($lmResponse, ($workInfo[0] + $workInfo[1]) / 2, true);
253 3
        $lmInfo = $this->readSecurityBuffer(bin2hex($lmSec));
254 3
        $ntlmSec = $this->createSecurityBuffer($ntlmResponse, ($lmInfo[0] + $lmInfo[1]) / 2, true);
255
256
        return self::NTLMSIG
257 3
               . $this->createByte('03') // TYPE 3 message
258 3
               . $lmSec // LM response header
259 3
               . $ntlmSec // NTLM response header
260 3
               . $domainSec // Domain header
261 3
               . $userSec // User header
262 3
               . $workSec // Workstation header
263 3
               . $this->createByte('000000009a', 8) // session key header (empty)
264 3
               . $this->createByte('01020000') // FLAGS
265 3
               . $this->convertTo16bit($domain) // domain name
266 3
               . $this->convertTo16bit($username) // username
267 3
               . $this->convertTo16bit($workstation) // workstation
268 3
               . $lmResponse
269 3
               . $ntlmResponse;
270
    }
271
272
    /**
273
     * @param string $timestamp Epoch timestamp in microseconds
274
     * @param string $client    Random bytes
275
     * @param string $targetInfo
276
     *
277
     * @return string
278
     */
279 1
    protected function createBlob($timestamp, $client, $targetInfo)
280
    {
281 1
        return $this->createByte('0101')
282 1
               . $this->createByte('00')
283 1
               . $timestamp
284 1
               . $client
285 1
               . $this->createByte('00')
286 1
               . $targetInfo
287 1
               . $this->createByte('00');
288
    }
289
290
    /**
291
     * Get domain and username from our username.
292
     *
293
     * @example DOMAIN\username
294
     *
295
     * @param string $name
296
     *
297
     * @return array
298
     */
299 6
    protected function getDomainAndUsername($name)
300
    {
301 6
        if (strpos($name, '\\') !== false) {
302 2
            return explode('\\', $name);
303
        }
304
305 4
        if (strpos($name, '@') !== false) {
306 3
            list($user, $domain) = explode('@', $name);
307
308 3
            return array($domain, $user);
309
        }
310
311
        // no domain passed
312 1
        return array('', $name);
313
    }
314
315
    /**
316
     * Create LMv1 response.
317
     *
318
     * @param string $password
319
     * @param string $challenge
320
     *
321
     * @return string
322
     */
323 1
    protected function createLMPassword($password, $challenge)
324
    {
325
        // FIRST PART
326 1
        $password = $this->createByte(strtoupper($password), 14, false);
327 1
        list($key1, $key2) = str_split($password, 7);
328
329 1
        $desKey1 = $this->createDesKey($key1);
330 1
        $desKey2 = $this->createDesKey($key2);
331
332 1
        $constantDecrypt = $this->createByte($this->desEncrypt(self::DESCONST, $desKey1) . $this->desEncrypt(self::DESCONST, $desKey2), 21, false);
333
334
        // SECOND PART
335 1
        list($key1, $key2, $key3) = str_split($constantDecrypt, 7);
336
337 1
        $desKey1 = $this->createDesKey($key1);
338 1
        $desKey2 = $this->createDesKey($key2);
339 1
        $desKey3 = $this->createDesKey($key3);
340
341 1
        return $this->desEncrypt($challenge, $desKey1) . $this->desEncrypt($challenge, $desKey2) . $this->desEncrypt($challenge, $desKey3);
342
    }
343
344
    /**
345
     * Create NTLMv1 response.
346
     *
347
     * @param string $password
348
     * @param string $challenge
349
     *
350
     * @return string
351
     */
352
    protected function createNTLMPassword($password, $challenge)
353
    {
354
        // FIRST PART
355
        $ntlmHash = $this->createByte($this->md4Encrypt($password), 21, false);
356
        list($key1, $key2, $key3) = str_split($ntlmHash, 7);
357
358
        $desKey1 = $this->createDesKey($key1);
359
        $desKey2 = $this->createDesKey($key2);
360
        $desKey3 = $this->createDesKey($key3);
361
362
        return $this->desEncrypt($challenge, $desKey1) . $this->desEncrypt($challenge, $desKey2) . $this->desEncrypt($challenge, $desKey3);
363
    }
364
365
    /**
366
     * Convert a normal timestamp to a tenth of a microtime epoch time.
367
     *
368
     * @param string $time
369
     *
370
     * @return string
371
     */
372
    protected function getCorrectTimestamp($time)
373
    {
374
        // Get our timestamp (tricky!)
375
        $time = number_format($time, 0, '.', ''); // save microtime to string
376
        $time = bcadd($time, '11644473600000', 0); // add epoch time
377
        $time = bcmul($time, 10000, 0); // tenths of a microsecond.
378
379
        $binary = $this->si2bin($time, 64); // create 64 bit binary string
380
        $timestamp = '';
381
        for ($i = 0; $i < 8; ++$i) {
382
            $timestamp .= chr(bindec(substr($binary, -(($i + 1) * 8), 8)));
383
        }
384
385
        return $timestamp;
386
    }
387
388
    /**
389
     * Create LMv2 response.
390
     *
391
     * @param string $password
392
     * @param string $username
393
     * @param string $domain
394
     * @param string $challenge NTLM Challenge
395
     * @param string $client    Random string
396
     *
397
     * @return string
398
     */
399 2
    protected function createLMv2Password($password, $username, $domain, $challenge, $client)
400
    {
401 2
        $lmPass = '00'; // by default 00
402
        // if $password > 15 than we can't use this method
403 2
        if (strlen($password) <= 15) {
404 2
            $ntlmHash = $this->md4Encrypt($password);
405 2
            $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username) . $domain));
406
407 2
            $lmPass = bin2hex($this->md5Encrypt($ntml2Hash, $challenge . $client) . $client);
408 2
        }
409
410 2
        return $this->createByte($lmPass, 24);
411
    }
412
413
    /**
414
     * Create NTLMv2 response.
415
     *
416
     * @param string $password
417
     * @param string $username
418
     * @param string $domain
419
     * @param string $challenge  Hex values
420
     * @param string $targetInfo Hex values
421
     * @param string $timestamp
422
     * @param string $client     Random bytes
423
     *
424
     * @return string
425
     *
426
     * @see http://davenport.sourceforge.net/ntlm.html#theNtlmResponse
427
     */
428 1
    protected function createNTLMv2Hash($password, $username, $domain, $challenge, $targetInfo, $timestamp, $client)
429
    {
430 1
        $ntlmHash = $this->md4Encrypt($password);
431 1
        $ntml2Hash = $this->md5Encrypt($ntlmHash, $this->convertTo16bit(strtoupper($username) . $domain));
432
433
        // create blob
434 1
        $blob = $this->createBlob($timestamp, $client, $targetInfo);
435
436 1
        $ntlmv2Response = $this->md5Encrypt($ntml2Hash, $challenge . $blob);
437
438 1
        return $ntlmv2Response . $blob;
439
    }
440
441 1
    protected function createDesKey($key)
442
    {
443 1
        $material = array(bin2hex($key[0]));
444 1
        $len = strlen($key);
445 1
        for ($i = 1; $i < $len; ++$i) {
446 1
            list($high, $low) = str_split(bin2hex($key[$i]));
447 1
            $v = $this->castToByte(ord($key[$i - 1]) << (7 + 1 - $i) | $this->uRShift(hexdec(dechex(hexdec($high) & 0xf) . dechex(hexdec($low) & 0xf)), $i));
448 1
            $material[] = str_pad(substr(dechex($v), -2), 2, '0', STR_PAD_LEFT); // cast to byte
449 1
        }
450 1
        $material[] = str_pad(substr(dechex($this->castToByte(ord($key[6]) << 1)), -2), 2, '0');
451
452
        // odd parity
453 1
        foreach ($material as $k => $v) {
454 1
            $b = $this->castToByte(hexdec($v));
455
            $needsParity = (
456
                               (
457 1
                                   $this->uRShift($b, 7)
458 1
                                   ^ $this->uRShift($b, 6)
459 1
                                   ^ $this->uRShift($b, 5)
460 1
                                   ^ $this->uRShift($b, 4)
461 1
                                   ^ $this->uRShift($b, 3)
462 1
                                   ^ $this->uRShift($b, 2)
463 1
                                   ^ $this->uRShift($b, 1)
464
                               ) & 0x01
465 1
                           ) == 0;
466
467 1
            list($high, $low) = str_split($v);
468
469 1
            if ($needsParity) {
470 1
                $material[$k] = dechex(hexdec($high) | 0x0) . dechex(hexdec($low) | 0x1);
471 1
            } else {
472 1
                $material[$k] = dechex(hexdec($high) & 0xf) . dechex(hexdec($low) & 0xe);
473
            }
474 1
        }
475
476 1
        return \hex2bin(implode('', $material));
477
    }
478
479
    /** HELPER FUNCTIONS */
480
481
    /**
482
     * Create our security buffer depending on length and offset.
483
     *
484
     * @param string $value  Value we want to put in
485
     * @param int    $offset start of value
486
     * @param bool   $is16   Do we 16bit string or not?
487
     *
488
     * @return string
489
     */
490 3
    protected function createSecurityBuffer($value, $offset, $is16 = false)
491
    {
492 3
        $length = strlen(bin2hex($value));
493 3
        $length = $is16 ? $length / 2 : $length;
494 3
        $length = $this->createByte(str_pad(dechex($length), 2, '0', STR_PAD_LEFT), 2);
495
496 3
        return $length . $length . $this->createByte(dechex($offset), 4);
497
    }
498
499
    /**
500
     * Read our security buffer to fetch length and offset of our value.
501
     *
502
     * @param string $value Securitybuffer in hex
503
     *
504
     * @return double[] array with length and offset
505
     */
506 3
    protected function readSecurityBuffer($value)
507
    {
508 3
        $length = floor(hexdec(substr($value, 0, 4)) / 256) * 2;
509 3
        $offset = floor(hexdec(substr($value, 8, 4)) / 256) * 2;
510
511 3
        return array($length, $offset);
512
    }
513
514
    /**
515
     * Cast to byte java equivalent to (byte).
516
     *
517
     * @param int $v
518
     *
519
     * @return int
520
     */
521 1
    protected function castToByte($v)
522
    {
523 1
        return (($v + 128) % 256) - 128;
524
    }
525
526
    /**
527
     * Java unsigned right bitwise
528
     * $a >>> $b.
529
     *
530
     * @param int $a
531
     * @param int $b
532
     *
533
     * @return int
534
     */
535 1
    protected function uRShift($a, $b)
536
    {
537 1
        if ($b == 0) {
538
            return $a;
539
        }
540
541 1
        return ($a >> $b) & ~(1 << (8 * PHP_INT_SIZE - 1) >> ($b - 1));
542
    }
543
544
    /**
545
     * Right padding with 0 to certain length.
546
     *
547
     * @param string $input
548
     * @param int    $bytes Length of bytes
549
     * @param bool   $isHex Did we provided hex value
550
     *
551
     * @return string
552
     */
553 7
    protected function createByte($input, $bytes = 4, $isHex = true)
554
    {
555 7
        if ($isHex) {
556 6
            $byte = \hex2bin(str_pad($input, $bytes * 2, '00'));
557 6
        } else {
558 1
            $byte = str_pad($input, $bytes, "\x00");
559
        }
560
561 7
        return $byte;
562
    }
563
564
    /** ENCRYPTION ALGORITHMS */
565
566
    /**
567
     * DES Encryption.
568
     *
569
     * @param string $value An 8-byte string
570
     * @param string $key
571
     *
572
     * @return string
573
     */
574 1
    protected function desEncrypt($value, $key)
575
    {
576
        // 1 == OPENSSL_RAW_DATA - but constant is only available as of PHP 5.4.
577 1
        return substr(openssl_encrypt($value, 'DES-ECB', $key, 1), 0, 8);
578
    }
579
580
    /**
581
     * MD5 Encryption.
582
     *
583
     * @param string $key Encryption key
584
     * @param string $msg Message to encrypt
585
     *
586
     * @return string
587
     */
588 2
    protected function md5Encrypt($key, $msg)
589
    {
590 2
        $blocksize = 64;
591 2
        if (strlen($key) > $blocksize) {
592
            $key = pack('H*', md5($key));
593
        }
594
595 2
        $key = str_pad($key, $blocksize, "\0");
596 2
        $ipadk = $key ^ str_repeat("\x36", $blocksize);
597 2
        $opadk = $key ^ str_repeat("\x5c", $blocksize);
598
599 2
        return pack('H*', md5($opadk . pack('H*', md5($ipadk . $msg))));
600
    }
601
602
    /**
603
     * MD4 Encryption.
604
     *
605
     * @param string $input
606
     *
607
     * @return string
608
     *
609
     * @see http://php.net/manual/en/ref.hash.php
610
     */
611 2
    protected function md4Encrypt($input)
612
    {
613 2
        $input = $this->convertTo16bit($input);
614
615 2
        return function_exists('hash') ? \hex2bin(hash('md4', $input)) : mhash(MHASH_MD4, $input);
616
    }
617
618
    /**
619
     * Convert UTF-8 to UTF-16.
620
     *
621
     * @param string $input
622
     *
623
     * @return string
624
     */
625 4
    protected function convertTo16bit($input)
626
    {
627 4
        return iconv('UTF-8', 'UTF-16LE', $input);
628
    }
629
630
    /**
631
     * @param string $message
632
     */
633
    protected function debug($message)
634
    {
635
        $message = bin2hex($message);
636
        $messageId = substr($message, 16, 8);
637
        echo substr($message, 0, 16) . " NTLMSSP Signature<br />\n";
638
        echo $messageId . " Type Indicator<br />\n";
639
640
        if ($messageId === '02000000') {
641
            $map = array(
642
                'Challenge',
643
                'Context',
644
                'Target Information Security Buffer',
645
                'Target Name Data',
646
                'NetBIOS Domain Name',
647
                'NetBIOS Server Name',
648
                'DNS Domain Name',
649
                'DNS Server Name',
650
                'BLOB',
651
                'Target Information Terminator',
652
            );
653
654
            $data = $this->parseMessage2(\hex2bin($message));
655
656
            foreach ($map as $key => $value) {
657
                echo bin2hex($data[$key]) . ' - ' . $data[$key] . ' ||| ' . $value . "<br />\n";
658
            }
659
        } elseif ($messageId === '03000000') {
660
            $i = 0;
661
            $data[$i++] = substr($message, 24, 16);
0 ignored issues
show
Coding Style Comprehensibility introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
662
            list($lmLength, $lmOffset) = $this->readSecurityBuffer($data[$i - 1]);
663
664
            $data[$i++] = substr($message, 40, 16);
665
            list($ntmlLength, $ntmlOffset) = $this->readSecurityBuffer($data[$i - 1]);
666
667
            $data[$i++] = substr($message, 56, 16);
668
            list($targetLength, $targetOffset) = $this->readSecurityBuffer($data[$i - 1]);
669
670
            $data[$i++] = substr($message, 72, 16);
671
            list($userLength, $userOffset) = $this->readSecurityBuffer($data[$i - 1]);
672
673
            $data[$i++] = substr($message, 88, 16);
674
            list($workLength, $workOffset) = $this->readSecurityBuffer($data[$i - 1]);
675
676
            $data[$i++] = substr($message, 104, 16);
677
            $data[$i++] = substr($message, 120, 8);
678
            $data[$i++] = substr($message, $targetOffset, $targetLength);
679
            $data[$i++] = substr($message, $userOffset, $userLength);
680
            $data[$i++] = substr($message, $workOffset, $workLength);
681
            $data[$i++] = substr($message, $lmOffset, $lmLength);
682
            $data[$i] = substr($message, $ntmlOffset, $ntmlLength);
683
684
            $map = array(
685
                'LM Response Security Buffer',
686
                'NTLM Response Security Buffer',
687
                'Target Name Security Buffer',
688
                'User Name Security Buffer',
689
                'Workstation Name Security Buffer',
690
                'Session Key Security Buffer',
691
                'Flags',
692
                'Target Name Data',
693
                'User Name Data',
694
                'Workstation Name Data',
695
                'LM Response Data',
696
                'NTLM Response Data',
697
            );
698
699
            foreach ($map as $key => $value) {
700
                echo $data[$key] . ' - ' . \hex2bin($data[$key]) . ' ||| ' . $value . "<br />\n";
701
            }
702
        }
703
704
        echo '<br /><br />';
705
    }
706
}
707