Completed
Push — master ( 3ae3f3...1debc7 )
by Michael
06:55
created

Str::substr()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 13
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 4

Importance

Changes 2
Bugs 1 Features 0
Metric Value
c 2
b 1
f 0
dl 0
loc 13
ccs 6
cts 6
cp 1
rs 9.2
cc 4
eloc 6
nc 3
nop 3
crap 4
1
<?php
2
3
/**
4
 * Str.php
5
 * 
6
 * PHP version 5
7
 * 
8
 * @category Dcrypt
9
 * @package  Dcrypt
10
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
11
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
12
 * @link     https://github.com/mmeyer2k/dcrypt
13
 */
14
15
namespace Dcrypt;
16
17
/**
18
 * Provides time-safe string comparison facilities, and safe string operations
19
 * on systems that have mb_* function overloading enabled.
20
 * 
21
 * The functions in this class were inspired by the symfony's StringUtils class. 
22
 * 
23
 * @category Dcrypt
24
 * @package  Dcrypt
25
 * @author   Michael Meyer (mmeyer2k) <[email protected]>
26
 * @license  http://opensource.org/licenses/MIT The MIT License (MIT)
27
 * @link     https://github.com/mmeyer2k/dcrypt
28
 * @link     https://github.com/symfony/Security/blob/master/Core/Util/StringUtils.php
29
 * @link     https://php.net/manual/en/mbstring.overload.php
30
 * @link     https://apigen.ci/github/mmeyer2k/dcrypt/namespace-Dcrypt.html
31
 */
32
final class Str
33
{
34
35
    /**
36
     * Private constant-time strcmp method to use when hash_equals is unavailable.
37
     *
38
     * @param string $knownHash Hash of the known string
39
     * @param string $givenHash Hash of the given string
40
     *
41
     * @return bool true if the two strings are the same, false otherwise
42
     */
43 12
    private static function strcmp($knownHash, $givenHash)
44
    {
45 12
        $result = 0;
46
47
        // XOR the bytes of the 2 input hashes and loop over them.
48
        // Each byte value is then added to a running total...
49 12
        foreach (\str_split($knownHash ^ $givenHash) as $xbyte) {
50 12
            $result += \ord($xbyte);
51 12
        }
52
53
        // Strings are equal if the final result is exactly zero
54 12
        return 0 === $result;
55
    }
56
57
    /**
58
     * Compares two strings in constant time. Strings are hashed before 
59
     * comparison so information is not leaked when strings are not of
60
     * equal length.
61
     *
62
     * @param string $known       The string of known length to compare against
63
     * @param string $given       The string that the user can control
64
     *
65
     * @return bool
66
     */
67 12
    public static function equal($known, $given)
68
    {
69
        // We hash the 2 inputs at this point because hash_equals is still 
70
        // vulnerable to timing attacks when the inputs have different sizes.
71
        // Inputs are also cast to string like in symfony stringutils.
72 12
        $nonce = Random::bytes(32);
73
74 12
        $known = \hash_hmac('sha256', (string) $known, $nonce, true);
75 12
        $given = \hash_hmac('sha256', (string) $given, $nonce, true);
76
77 12
        if (\function_exists('hash_equals')) {
78
            return \hash_equals($known, $given); // @codeCoverageIgnore
79
        }
80
81 12
        return self::strcmp($known, $given);
82
    }
83
84
    /**
85
     * Determine the length of the output of a given hash algorithm in bytes.
86
     * 
87
     * @param string $algo Name of algorithm to look up
88
     * 
89
     * @return int
90
     */
91 10
    public static function hashSize($algo)
92
    {
93 10
        return self::strlen(\hash($algo, 'hash me', true));
94
    }
95
96
    /**
97
     * Returns the number of bytes in a string.
98
     *
99
     * @param string $string The string whose length we wish to obtain
100
     *
101
     * @return int
102
     */
103 20
    public static function strlen($string)
104
    {
105 20
        if (\function_exists('mb_strlen')) {
106 20
            return \mb_strlen($string, '8bit');
107
        }
108
109
        return \strlen($string); // @codeCoverageIgnore
110
    }
111
112
    /**
113
     * Returns part of a string.
114
     *
115
     * @param string $string The string whose length we wish to obtain
116
     * @param int    $start
117
     * @param int    $length
118
     * 
119
     * @return string the extracted part of string; or FALSE on failure, or an empty string.
120
     */
121 14
    public static function substr($string, $start, $length = null)
122
    {
123 14
        if (\function_exists('mb_substr')) {
124
            // Fix a weird quirk in PHP versions prior to 5.4.8
125 14
            if ($length === null && \version_compare('5.4.8', PHP_VERSION)) {
126 9
                $length = self::strlen($string);
127 9
            }
128
129 14
            return \mb_substr($string, $start, $length, '8bit');
130
        }
131
132
        return \substr($string, $start, $length); // @codeCoverageIgnore
133
    }
134
135
}
136