Passed
Push — master ( bf86b6...5c5c76 )
by Maxim
03:30
created

SecurityHelper::unmaskToken()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 6
nc 2
nop 1
1
<?php
2
3
namespace WebComplete\core\utils\helpers;
4
5
class SecurityHelper
6
{
7
8
    /**
9
     * @var StringHelper
10
     */
11
    protected $stringHelper;
12
13
    public function __construct(StringHelper $stringHelper)
14
    {
15
        $this->stringHelper = $stringHelper;
16
    }
17
18
    /**
19
     * @param string $password
20
     * @param string $salt
21
     *
22
     * @return string
23
     */
24
    public function cryptPassword(string $password, string $salt): string
25
    {
26
        return \strtoupper(\md5(\md5($salt) . \md5($password)));
27
    }
28
29
    /**
30
     * Masks a token to make it uncompressible.
31
     * Applies a random mask to the token and prepends the mask used to the result making the string always unique.
32
     * Used to mitigate BREACH attack by randomizing how token is outputted on each request.
33
     *
34
     * @param string $token An unmasked token.
35
     *
36
     * @return string A masked token.
37
     * @throws \RuntimeException
38
     */
39
    public function maskToken($token): string
40
    {
41
        // The number of bytes in a mask is always equal to the number of bytes in a token.
42
        $mask = $this->generateRandomKey($this->stringHelper->byteLength($token));
43
        return $this->stringHelper->base64UrlEncode($mask . ($mask ^ $token));
44
    }
45
46
    /**
47
     * Unmasks a token previously masked by `maskToken`.
48
     * @param string $maskedToken A masked token.
49
     * @return string An unmasked token, or an empty string in case of token format is invalid.
50
     */
51
    public function unmaskToken($maskedToken): string
52
    {
53
        $decoded = $this->stringHelper->base64UrlDecode($maskedToken);
54
        $length = $this->stringHelper->byteLength($decoded) / 2;
55
        // Check if the masked token has an even length.
56
        if (!is_int($length)) {
57
            return '';
58
        }
59
        return $this->stringHelper->byteSubstr($decoded, $length, $length) ^
60
            $this->stringHelper->byteSubstr($decoded, 0, $length);
61
    }
62
63
    /**
64
     * Generates specified number of random bytes.
65
     * Note that output may not be ASCII.
66
     *
67
     * @see generateRandomString() if you need a string.
68
     *
69
     * @param int $length the number of bytes to generate
70
     *
71
     * @return string the generated random bytes
72
     * @throws \RuntimeException
73
     */
74
    public function generateRandomKey(int $length = 32): string
75
    {
76
        if ($length < 1) {
77
            throw new \RuntimeException('First parameter ($length) must be greater than 0');
78
        }
79
80
        // always use random_bytes() if it is available
81
        if (\function_exists('random_bytes')) {
82
            return \random_bytes($length);
83
        }
84
        throw new \RuntimeException('Function random_bytes not found');
85
    }
86
87
    /**
88
     * Generates a random string of specified length.
89
     * The string generated matches [A-Za-z0-9_-]+ and is transparent to URL-encoding.
90
     *
91
     * @param int $length the length of the key in characters
92
     *
93
     * @return string the generated random key
94
     * @throws \RuntimeException
95
     */
96
    public function generateRandomString(int $length = 32): string
97
    {
98
        if ($length < 1) {
99
            throw new \RuntimeException('First parameter ($length) must be greater than 0');
100
        }
101
102
        $bytes = $this->generateRandomKey($length);
103
        return \substr($this->stringHelper->base64UrlEncode($bytes), 0, $length);
104
    }
105
}
106