Test Failed
Push — master ( fb5eec...70b4d1 )
by Sebastian
04:40 queued 02:12
created

HmacTokenProvider::validate()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 31
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 12
nc 4
nop 1
dl 0
loc 31
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * This file is part of the Linna Csrf Guard.
7
 *
8
 * @author Sebastian Rapetti <[email protected]>
9
 * @copyright (c) 2020, Sebastian Rapetti
10
 * @license http://opensource.org/licenses/MIT MIT License
11
 */
12
13
namespace Linna\CsrfGuard\Provider;
14
15
use Linna\CsrfGuard\Exception\BadExpireException;
16
use Linna\CsrfGuard\Exception\BadExpireTrait;
17
18
/**
19
 * Csrf HMAC Based Token Pattern Provider.
20
 *
21
 * <p>It uses hash_hmac with sha3-384 algorithm and doesn't need storage to work.</p>
22
 */
23
class HmacTokenProvider implements TokenProviderInterface
24
{
25
    use BadExpireTrait;
26
27
    /** @var string $key Secret key for the hmac. */
28
    private string $key = '';
29
30
    /** @var string $value Value will be hashed inside token. */
31
    private string $value = '';
32
33
    /** @var int $expire Token validity in seconds, default 600 -> 10 minutes. */
34
    private int $expire = 0;
35
36
    /**
37
     * Class constructor.
38
     *
39
     * @param string $value  Value will be hashed inside token.
40
     * @param string $key    Secret key for the hmac.
41
     * @param int    $expire Token validity in seconds, default 600 -> 10 minutes.
42
     *
43
     * @throws BadExpireException If <code>$expire</code> is less than 0 and greater than 86400.
44
     */
45
    public function __construct(string $value, string $key, int $expire = 600)
46
    {
47
        // from BadExpireTrait
48
        /** @throws BadExpireException */
49
        $this->checkBadExpire($expire);
50
51
        $this->key = $key;
52
        $this->value = $value;
53
        $this->expire = $expire;
54
    }
55
56
    /**
57
     * Return new Hmac Token.
58
     *
59
     * @return string The token in hex format.
60
     */
61
    public function getToken(): string
62
    {
63
        //get the time for the token
64
        $time = \base_convert((string) \time(), 10, 16);
65
        //random bytes for avoid to wait one second to get a different token
66
        $random = \bin2hex(\random_bytes(2));
67
68
        //return the token
69
        return \hash_hmac('sha3-384', $this->value.$time.$random, $this->key).$time.$random;
70
    }
71
72
    /**
73
     * Validate Hmac Token.
74
     *
75
     * @param string $token Token must be validated.
76
     *
77
     * @return bool True if the token is valid, false otherwise.
78
     */
79
    public function validate(string $token): bool
80
    {
81
        //check for void token
82
        if ($token === "") {
83
            return false;
84
        }
85
86
        //hmac present in token
87
        $hmac_token = \substr($token, 0, 96);
88
        //token time
89
        $time = \substr($token, 96, 8);
90
        //random value
91
        $random = \substr($token, 104);
92
93
        //hmac generate locally with token time
94
        $hmac_local = \hash_hmac('sha3-384', $this->value.$time.$random, $this->key);
95
96
        //hmac check in constant time
97
        if (!\hash_equals($hmac_local, $hmac_token)) {
98
            return false;
99
        }
100
101
        //timestamp from token time
102
        $timestamp = (int) \base_convert($time, 16, 10);
103
104
        //token expiration check
105
        if ($timestamp + $this->expire < \time()) {
106
            return false;
107
        }
108
109
        return true;
110
    }
111
}
112