CryptTrait   A
last analyzed

Complexity

Total Complexity 13

Size/Duplication

Total Lines 126
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 0
Metric Value
wmc 13
lcom 1
cbo 1
dl 0
loc 126
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A checkKey() 0 13 4
A key() 0 7 1
A hkdf() 0 16 2
A encrypt() 0 10 1
A decrypt() 0 16 2
A secureRandomKey() 0 13 3
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 The binary 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
        $this->checkKey();
40
41
        $iv = mcrypt_create_iv(16, MCRYPT_DEV_URANDOM);
42
        $cipher = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $this->key, json_encode($value), 'ctr', $iv);
43
        $hmac = hash_hmac('sha256', $iv.$cipher, $this->authentication, true);
44
45
        return base64_encode($hmac.$iv.$cipher);
46
    }
47
48
    /**
49
     * Decrypt the given value.
50
     *
51
     * @param string $value
52
     *
53
     * @return string
54
     */
55
    private function decrypt($value)
56
    {
57
        $this->checkKey();
58
59
        $decoded = base64_decode($value);
60
        $hmac = mb_substr($decoded, 0, 32, '8bit');
61
        $iv = mb_substr($decoded, 32, 16, '8bit');
62
        $cipher = mb_substr($decoded, 48, null, '8bit');
63
        $calculated = hash_hmac('sha256', $iv.$cipher, $this->authentication, true);
64
65
        if (Helpers::hashEquals($hmac, $calculated)) {
66
            $value = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $this->key, $cipher, 'ctr', $iv), "\0");
67
68
            return json_decode($value, true);
69
        }
70
    }
71
72
    /**
73
     * Check whether the key exists or not.
74
     *
75
     * @throws RuntimeException
76
     */
77
    private function checkKey()
78
    {
79
        if (empty($this->key) || empty($this->authentication)) {
80
            $key = $this->secureRandomKey();
81
            $message = 'No binary key provided to encrypt/decrypt data.';
82
83
            if ($key !== null) {
84
                $message .= sprintf(" For example: base64_decode('%s')", base64_encode($key));
85
            }
86
87
            throw new RuntimeException($message);
88
        }
89
    }
90
91
    /**
92
     * Generate a secure random key.
93
     *
94
     * @return string|null
95
     */
96
    private static function secureRandomKey()
97
    {
98
        if (!function_exists('openssl_random_pseudo_bytes')) {
99
            return;
100
        }
101
102
        $secure = false;
103
        $random = openssl_random_pseudo_bytes(16, $secure);
104
105
        if ($secure) {
106
            return $random;
107
        }
108
    }
109
110
    /**
111
     * Get derived key
112
     * http://tools.ietf.org/html/rfc5869.
113
     *
114
     * @param string $ikm  Initial Keying Material
115
     * @param string $info What sort of key are we deriving?
116
     *
117
     * @return string
118
     */
119
    private static function hkdf($ikm, $info = '')
120
    {
121
        $salt = str_repeat("\x00", 32);
122
        $prk = hash_hmac('sha256', $ikm, $salt, true);
123
124
        $t = $last_block = '';
125
        $length = 0;
126
127
        for ($block_index = 1; $length < 16; ++$block_index) {
128
            $last_block = hash_hmac('sha256', $last_block.$info.chr($block_index), $prk, true);
129
            $t .= $last_block;
130
            $length = mb_strlen($t, '8bit');
131
        }
132
133
        return mb_substr($t, 0, 16, '8bit');
134
    }
135
}
136