GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( c4f121...b9594d )
by Joni
05:22
created

AESCBCAlgorithm   A

Complexity

Total Complexity 21

Size/Duplication

Total Lines 209
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Test Coverage

Coverage 95.16%

Importance

Changes 0
Metric Value
wmc 21
lcom 1
cbo 2
dl 0
loc 209
ccs 59
cts 62
cp 0.9516
rs 10
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
_cipherMethod() 0 1 ?
_hashAlgo() 0 1 ?
_encKeyLen() 0 1 ?
_macKeyLen() 0 1 ?
_tagLen() 0 1 ?
A _getCipherMethod() 0 14 3
A _validateKey() 0 6 2
A _validateIV() 0 7 2
A _macKey() 0 4 1
A _encKey() 0 4 1
A _aadLen() 0 8 2
A _computeAuthTag() 0 5 1
A encrypt() 0 15 2
A decrypt() 0 17 3
A _getLastOpenSSLError() 0 8 2
A ivSize() 0 4 1
A headerParameters() 0 4 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace JWX\JWE\EncryptionAlgorithm;
6
7
use JWX\JWE\ContentEncryptionAlgorithm;
8
use JWX\JWE\Exception\AuthenticationException;
9
use JWX\JWT\Parameter\EncryptionAlgorithmParameter;
10
11
/**
12
 * Base class for algorithms implementing AES in CBC mode with HMAC-SHA.
13
 *
14
 * @link https://tools.ietf.org/html/rfc7518#section-5.2
15
 */
16
abstract class AESCBCAlgorithm implements ContentEncryptionAlgorithm
17
{
18
    /**
19
     * Get cipher method name that is recognized by OpenSSL.
20
     *
21
     * @return string
22
     */
23
    abstract protected function _cipherMethod(): string;
24
    
25
    /**
26
     * Get algorithm name that is recognized by the Hash extension.
27
     *
28
     * @return string
29
     */
30
    abstract protected function _hashAlgo(): string;
31
    
32
    /**
33
     * Get length of the encryption key.
34
     *
35
     * @return int
36
     */
37
    abstract protected function _encKeyLen(): int;
38
    
39
    /**
40
     * Get length of the MAC key.
41
     *
42
     * @return int
43
     */
44
    abstract protected function _macKeyLen(): int;
45
    
46
    /**
47
     * Get length of the authentication tag.
48
     *
49
     * @return int
50
     */
51
    abstract protected function _tagLen(): int;
52
    
53
    /**
54
     * Get cipher method and verify that it's supported.
55
     *
56
     * @throws \RuntimeException
57
     * @return string
58
     */
59 38
    final protected function _getCipherMethod(): string
60
    {
61 38
        static $supported_ciphers;
62 38
        if (!isset($supported_ciphers)) {
63 4
            $supported_ciphers = array_flip(openssl_get_cipher_methods());
64
        }
65 38
        $method = $this->_cipherMethod();
66 38
        if (!isset($supported_ciphers[$method])) {
67 1
            throw new \RuntimeException(
68 1
                "Cipher method $method is not" .
69 1
                     " supported by this version of OpenSSL.");
70
        }
71 37
        return $method;
72
    }
73
    
74
    /**
75
     * Check that key is valid.
76
     *
77
     * @param string $key
78
     * @throws \RuntimeException
79
     */
80 39
    final protected function _validateKey(string $key)
81
    {
82 39
        if (strlen($key) != $this->keySize()) {
83 1
            throw new \RuntimeException("Invalid key size.");
84
        }
85 38
    }
86
    
87
    /**
88
     * Check that IV is valid.
89
     *
90
     * @param string $iv
91
     * @throws \RuntimeException
92
     */
93 38
    final protected function _validateIV(string $iv)
94
    {
95 38
        $len = openssl_cipher_iv_length($this->_getCipherMethod());
96 37
        if ($len != strlen($iv)) {
97 1
            throw new \RuntimeException("Invalid IV length.");
98
        }
99 36
    }
100
    
101
    /**
102
     * Get MAC key from CEK.
103
     *
104
     * @param string $key
105
     * @return string
106
     */
107 36
    final protected function _macKey(string $key): string
108
    {
109 36
        return substr($key, 0, $this->_macKeyLen());
110
    }
111
    
112
    /**
113
     * Get encryption key from CEK.
114
     *
115
     * @param string $key
116
     * @return string
117
     */
118 36
    final protected function _encKey(string $key): string
119
    {
120 36
        return substr($key, -$this->_encKeyLen());
121
    }
122
    
123
    /**
124
     * Compute AL value.
125
     *
126
     * @param string $aad
127
     * @return string 64 bits
128
     */
129 36
    final protected function _aadLen(string $aad): string
130
    {
131
        // truncate on 32 bit hosts
132 36
        if (PHP_INT_SIZE < 8) {
133
            return "\0\0\0\0" . pack("N", strlen($aad) * 8);
134
        }
135 36
        return pack("J", strlen($aad) * 8);
136
    }
137
    
138
    /**
139
     * Compute authentication tag.
140
     *
141
     * @param string $data
142
     * @param string $key CEK
143
     * @return string
144
     */
145 36
    final protected function _computeAuthTag(string $data, string $key): string
146
    {
147 36
        $tag = hash_hmac($this->_hashAlgo(), $data, $this->_macKey($key), true);
148 36
        return substr($tag, 0, $this->_tagLen());
149
    }
150
    
151
    /**
152
     *
153
     * {@inheritdoc}
154
     */
155 23
    public function encrypt(string $plaintext, string $key, string $iv,
156
        string $aad): array
157
    {
158 23
        $this->_validateKey($key);
159 22
        $this->_validateIV($iv);
160 20
        $ciphertext = openssl_encrypt($plaintext, $this->_getCipherMethod(),
161 20
            $this->_encKey($key), OPENSSL_RAW_DATA, $iv);
162 20
        if (false === $ciphertext) {
163
            throw new \RuntimeException(
164
                "openssl_encrypt() failed: " . $this->_getLastOpenSSLError());
165
        }
166 20
        $auth_data = $aad . $iv . $ciphertext . $this->_aadLen($aad);
167 20
        $auth_tag = $this->_computeAuthTag($auth_data, $key);
168 20
        return [$ciphertext, $auth_tag];
169
    }
170
    
171
    /**
172
     *
173
     * {@inheritdoc}
174
     */
175 17
    public function decrypt(string $ciphertext, string $key, string $iv,
176
        string $aad, string $auth_tag): string
177
    {
178 17
        $this->_validateKey($key);
179 17
        $this->_validateIV($iv);
180 17
        $auth_data = $aad . $iv . $ciphertext . $this->_aadLen($aad);
181 17
        if ($this->_computeAuthTag($auth_data, $key) != $auth_tag) {
182 1
            throw new AuthenticationException("Message authentication failed.");
183
        }
184 16
        $plaintext = openssl_decrypt($ciphertext, $this->_getCipherMethod(),
185 16
            $this->_encKey($key), OPENSSL_RAW_DATA, $iv);
186 16
        if (false === $plaintext) {
187 1
            throw new \RuntimeException(
188 1
                "openssl_decrypt() failed: " . $this->_getLastOpenSSLError());
189
        }
190 15
        return $plaintext;
191
    }
192
    
193
    /**
194
     * Get last OpenSSL error message.
195
     *
196
     * @return string|null
197
     */
198 1
    protected function _getLastOpenSSLError()
199
    {
200 1
        $msg = null;
201 1
        while (false !== ($err = openssl_error_string())) {
202 1
            $msg = $err;
203
        }
204 1
        return $msg;
205
    }
206
    
207
    /**
208
     *
209
     * {@inheritdoc}
210
     */
211 13
    public function ivSize(): int
212
    {
213 13
        return 16;
214
    }
215
    
216
    /**
217
     *
218
     * {@inheritdoc}
219
     */
220 11
    public function headerParameters(): array
221
    {
222 11
        return array(EncryptionAlgorithmParameter::fromAlgorithm($this));
223
    }
224
}
225