Completed
Push — master ( 984ffa...d0785d )
by Florent
05:23 queued 01:15
created

PBKDF2::customPBKDF2()   B

Complexity

Conditions 5
Paths 12

Size

Total Lines 26
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
rs 8.439
cc 5
eloc 16
nc 12
nop 6
1
<?php
2
3
/*
4
 * The MIT License (MIT)
5
 *
6
 * Copyright (c) 2014-2015 Spomky-Labs
7
 *
8
 * This software may be modified and distributed under the terms
9
 * of the MIT license.  See the LICENSE file for details.
10
 */
11
12
namespace PBKDF2;
13
14
/**
15
 * Class representing a JSON Web Token Manager.
16
 */
17
final class PBKDF2
18
{
19
    /**
20
     * PBKDF2 key derivation function as defined by RSA's PKCS #5: RFC 2898
21
     * Test vectors can be found in the RFC 6070
22
     * This implementation of PBKDF2 was originally created by defuse.ca
23
     * With improvements by variations-of-shadow.com.
24
     *
25
     * @param string $algorithm          The hash algorithm to use. For supported hash algorithms, see hash_algos().
26
     * @param string $password           The password.
27
     * @param string $salt               A salt that is unique to the password.
28
     * @param int    $count              Iteration count. Higher is better, but slower.
29
     * @param int    $key_length         The length of the derived key in bytes. Default is 0 (means the length is 1-block).
30
     * @param bool   $raw_output         If true, the result is in binary format, else in hex. Default is false.
31
     * @param bool   $native_php_enabled If true and PHP >= 5.5, this function will use the native PHP function, else, this implementation.
32
     *
33
     * @return string A $key_length-byte key derived from the password and salt.
34
     *
35
     * @see https://www.ietf.org/rfc/rfc2898.txt
36
     * @see https://www.ietf.org/rfc/rfc6070.txt
37
     * @see http://php.net/manual/en/function.hash-hmac.php
38
     * @see http://php.net/manual/en/function.hash-pbkdf2.php
39
     */
40
    public static function deriveKey($algorithm, $password, $salt, $count, $key_length = 0, $raw_output = false, $native_php_enabled = true)
41
    {
42
        if (function_exists('hash_pbkdf2') && $native_php_enabled) {
43
            return self::nativePBKDF2($algorithm, $password, $salt, $count, $key_length, $raw_output);
44
        }
45
46
        self::checkArguments($algorithm, $count, $key_length);
47
        
48
        return self::customPBKDF2($algorithm, $password, $salt, $count, $key_length, $raw_output);
49
    }
50
51
    /**
52
     * Pure PHP PBKDF2 key derivation function.
53
     *
54
     * @param string $algorithm  The hash algorithm to use. For supported hash algorithms, see hash_algos().
55
     * @param string $password   The password.
56
     * @param string $salt       A salt that is unique to the password.
57
     * @param int    $count      Iteration count. Higher is better, but slower.
58
     * @param int    $key_length The length of the derived key in bytes.
59
     * @param bool   $raw_output If true, the result is in binary format, else in hex. Default is false
60
     *
61
     * @return string A $key_length-byte key derived from the password and salt.
62
     */
63
    private static function customPBKDF2($algorithm, $password, $salt, $count, $key_length, $raw_output)
64
    {
65
        $hash_length = strlen(hash($algorithm, '', true));
66
        if (0 === $key_length) {
67
            $key_length = $hash_length;
68
        }
69
        $block_count = ceil($key_length / $hash_length);
70
71
        $output = '';
72
        for ($i = 1; $i <= $block_count; ++$i) {
73
            $last = $salt.pack('N', $i);
74
            $last = $xorsum = hash_hmac($algorithm, $last, $password, true);
75
            for ($j = 1; $j < $count; ++$j) {
76
                $xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
77
            }
78
            $output .= $xorsum;
79
        }
80
81
        $raw = substr($output, 0, $key_length);
82
83
        if ($raw_output) {
84
            return $raw;
85
        }
86
87
        return bin2hex($raw);
88
    }
89
90
    /**
91
     * Native PHP PBKDF2 key derivation function.
92
     *
93
     * @param string $algorithm  The hash algorithm to use. For supported hash algorithms, see hash_algos().
94
     * @param string $password   The password.
95
     * @param string $salt       A salt that is unique to the password.
96
     * @param int    $count      Iteration count. Higher is better, but slower.
97
     * @param int    $key_length The length of the derived key in bytes.
98
     * @param bool   $raw_output If true, the result is in binary format, else in hex. Default is false
99
     *
100
     * @return string A $key_length-byte key derived from the password and salt.
101
     */
102
    private static function nativePBKDF2($algorithm, $password, $salt, $count, $key_length, $raw_output)
103
    {
104
        if (!$raw_output) {
105
            $key_length = $key_length * 2;
106
        }
107
108
        return hash_pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output);
109
    }
110
111
    /**
112
     * Native PHP PBKDF2 key derivation function.
113
     *
114
     * @param string $algorithm  The hash algorithm to use. For supported hash algorithms, see hash_algos().
115
     * @param int    $count      Iteration count. Higher is better, but slower.
116
     * @param int    $key_length The length of the derived key in bytes.
117
     *
118
     * @throws \InvalidArgumentException If algorithm is not supported or if count/key_length parameter are not valid.
119
     */
120
    private static function checkArguments($algorithm, $count, $key_length)
121
    {
122
        if (!in_array($algorithm, hash_algos(), true)) {
123
            throw new \InvalidArgumentException('PBKDF2 ERROR: Invalid hash algorithm.');
124
        }
125
        if ($count <= 0 || $key_length < 0) {
126
            throw new \InvalidArgumentException('PBKDF2 ERROR: Invalid key length parameters.');
127
        }
128
    }
129
}
130