Completed
Push — master ( 63fbb3...c28691 )
by Oscar
05:29
created

CryptTrait::hkdf()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 15
Code Lines 9

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 15
rs 9.4286
cc 2
eloc 9
nc 2
nop 2
1
<?php
2
3
namespace Psr7Middlewares\Utils;
4
5
use RuntimeException;
6
7
/**
8
 * Trait used by all middlewares that needs encrypt/decrypt functions.
9
 */
10
trait CryptTrait
11
{
12
    private $key;
13
    private $authentication;
14
15
    /**
16
     * Set the keys to encrypt and authenticate.
17
     * 
18
     * @param string $key
19
     *
20
     * @return self
21
     */
22
    public function key($key)
23
    {
24
        $this->key = self::hkdf($key, 'KeyForEncryption');
25
        $this->authentication = self::hkdf($key, 'KeyForAuthentication');
26
27
        return $this;
28
    }
29
30
    /**
31
     * Encrypt the given value.
32
     *
33
     * @param string $value
34
     * 
35
     * @return string
36
     */
37
    private function encrypt($value)
38
    {
39
        if (empty($this->key) || empty($this->authentication)) {
40
            throw new RuntimeException('No crypt keys provided');
41
        }
42
43
        $iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
44
        $cipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, json_encode($value), 'ctr', $iv);
45
        $hmac = hash_hmac('sha256', $iv.$cipher, $this->authentication, true);
46
47
        return base64_encode($hmac.$iv.$cipher);
48
    }
49
50
    /**
51
     * Decrypt the given value.
52
     *
53
     * @param string $value
54
     * 
55
     * @return string
56
     */
57
    private function decrypt($value)
58
    {
59
        if (empty($this->key) || empty($this->authentication)) {
60
            throw new RuntimeException('No crypt keys provided');
61
        }
62
63
        $decoded = base64_decode($value);
64
        $hmac = mb_substr($decoded, 0, 32, '8bit');
65
        $iv = mb_substr($decoded, 32, 16, '8bit');
66
        $cipher = mb_substr($decoded, 48, null, '8bit');
67
        $calculated = hash_hmac('sha256', $iv.$cipher, $this->authentication, true);
68
69
        if (Helpers::hashEquals($hmac, $calculated)) {
70
            $value = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $cipher, 'ctr', $iv), "\0");
71
72
            return json_decode($value, true);
73
        }
74
    }
75
76
    /**
77
     * Get derived key
78
     * http://tools.ietf.org/html/rfc5869.
79
     * 
80
     * @param string $ikm  Initial Keying Material
81
     * @param string $info What sort of key are we deriving?
82
     * 
83
     * @return string
84
     */
85
    private static function hkdf($ikm, $info = '')
86
    {
87
        $salt = str_repeat("\x00", 32);
88
        $prk = hash_hmac('sha256', $ikm, $salt, true);
89
90
        $t = '';
91
        $last_block = '';
92
93
        for ($block_index = 1; mb_strlen($t, '8bit') < 16; ++$block_index) {
94
            $last_block = hash_hmac('sha256', $last_block.$info.chr($block_index), $prk, true);
95
            $t .= $last_block;
96
        }
97
98
        return mb_substr($t, 0, 16, '8bit');
99
    }
100
}
101