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 ( 95b19d...1b067e )
by Joni
03:24
created

GCM   A

Complexity

Total Complexity 19

Size/Duplication

Total Lines 266
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 98.55%

Importance

Changes 0
Metric Value
wmc 19
lcom 1
cbo 3
dl 0
loc 266
ccs 68
cts 69
cp 0.9855
rs 10
c 0
b 0
f 0

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 2
A encrypt() 0 11 1
A decrypt() 0 15 2
A _generateJ0() 0 10 2
B _gctr() 0 24 3
A _computeAuthTag() 0 8 1
A _pad128() 0 8 2
A _inc32() 0 9 1
A _uint64() 0 8 2
A strToGMP() 0 4 1
1
<?php
2
3
declare(strict_types = 1);
4
5
namespace Sop\GCM;
6
7
use Sop\GCM\Cipher\Cipher;
8
use Sop\GCM\Exception\AuthenticationException;
9
10
/**
11
 * Implements encryption and decryption in Galois/Counter Mode.
12
 *
13
 * @link
14
 *       http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
15
 * @link http://csrc.nist.gov/publications/nistpubs/800-38D/SP-800-38D.pdf
16
 */
17
class GCM
18
{
19
    /**
20
     * Block of 64 zero bits.
21
     *
22
     * @var string
23
     */
24
    const ZB_64 = "\0\0\0\0\0\0\0\0";
25
    
26
    /**
27
     * Block of 128 zero bits.
28
     *
29
     * @var string
30
     */
31
    const ZB_128 = self::ZB_64 . self::ZB_64;
32
    
33
    /**
34
     * Array of supported t-values, that is, the bit length of the
35
     * authentication tag.
36
     *
37
     * See NIST SP-800-38D section 5.2.1.2 for the details.
38
     *
39
     * @internal
40
     *
41
     * @var array
42
     */
43
    const SUPPORTED_T_LEN = array(128, 120, 112, 104, 96, 64, 32);
44
    
45
    /**
46
     * Cipher.
47
     *
48
     * @var Cipher $_cipher
49
     */
50
    protected $_cipher;
51
    
52
    /**
53
     * Authentication tag length in bytes.
54
     *
55
     * @var int
56
     */
57
    protected $_tagLength;
58
    
59
    /**
60
     * Constructor.
61
     *
62
     * @param Cipher $cipher Cipher implementation
63
     * @param int $tag_length Authentication tag length in bytes
64
     * @throws \DomainException If tag length is not supported
65
     */
66 46
    public function __construct(Cipher $cipher, int $tag_length = 16)
67
    {
68 46
        if (!in_array($tag_length << 3, self::SUPPORTED_T_LEN)) {
69 1
            throw new \DomainException(
70 1
                "Tag length $tag_length is not supported.");
71
        }
72 45
        $this->_cipher = $cipher;
73 45
        $this->_tagLength = $tag_length;
74 45
    }
75
    
76
    /**
77
     * Encrypt plaintext.
78
     *
79
     * @param string $P Plaintext
80
     * @param string $A Additional authenticated data
81
     * @param string $K Encryption key
82
     * @param string $IV Initialization vector
83
     * @throws \RuntimeException For generic errors
84
     * @return array Tuple of ciphertext <code>C</code> and authentication tag
85
     *         <code>T</code>
86
     */
87 46
    public function encrypt(string $P, string $A, string $K, string $IV): array
88
    {
89 46
        $ghash = new GHASH($this->_cipher->encrypt(self::ZB_128, $K));
90
        // generate pre-counter block
91 46
        $J0 = $this->_generateJ0($IV, $ghash);
92
        // encrypt
93 46
        $C = $this->_gctr(self::_inc32($J0), $P, $K);
94
        // generate authentication tag
95 46
        $T = $this->_computeAuthTag($A, $C, $J0, $K, $ghash);
96 46
        return [$C, $T];
97
    }
98
    
99
    /**
100
     * Decrypt ciphertext.
101
     *
102
     * @param string $C Ciphertext
103
     * @param string $T Authentication tag
104
     * @param string $A Additional authenticated data
105
     * @param string $K Encryption key
106
     * @param string $IV Initialization vector
107
     * @throws AuthenticationException If message authentication fails
108
     * @throws \RuntimeException For generic errors
109
     * @return string Plaintext <code>P</code>
110
     */
111 44
    public function decrypt(string $C, string $T, string $A, string $K,
112
        string $IV): string
113
    {
114 44
        $ghash = new GHASH($this->_cipher->encrypt(self::ZB_128, $K));
115
        // generate pre-counter block
116 44
        $J0 = $this->_generateJ0($IV, $ghash);
117
        // generate authentication tag
118 44
        $T2 = $this->_computeAuthTag($A, $C, $J0, $K, $ghash);
119
        // check that authentication tag matches
120 44
        if ($T !== $T2) {
121 1
            throw new AuthenticationException("Authentication failed.");
122
        }
123
        // decrypt
124 43
        return $this->_gctr(self::_inc32($J0), $C, $K);
125
    }
126
    
127
    /**
128
     * Generate pre-counter block.
129
     *
130
     * See NIST SP-300-38D section 7.1 step 2 for the details.
131
     *
132
     * @param string $IV Initialization vector
133
     * @param GHASH $ghash GHASH functor
134
     * @return string
135
     */
136 49
    private function _generateJ0(string $IV, GHASH $ghash): string
137
    {
138
        // if len(IV) = 96
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
139 49
        if (12 == strlen($IV)) {
140 41
            return $IV . "\0\0\0\1";
141
        }
142 8
        $data = self::_pad128($IV) . self::ZB_64 . self::_uint64(
143 8
            strlen($IV) << 3);
144 8
        return $ghash($data);
145
    }
146
    
147
    /**
148
     * Apply GCTR algorithm.
149
     *
150
     * See NIST SP-300-38D section 6.5 for the details.
151
     *
152
     * @param string $ICB Initial counter block
153
     * @param string $X Input data
154
     * @param string $K Encryption key
155
     * @return string Output data
156
     */
157 49
    private function _gctr(string $ICB, string $X, string $K): string
158
    {
159
        // if data is an empty string, return an empty string
160 49
        if ("" == $X) {
161 13
            return "";
162
        }
163
        // number of blocks
164 49
        $n = ceil(strlen($X) / 16);
165 49
        $CB = $ICB;
166 49
        $Y = "";
167 49
        for ($i = 0; $i < $n - 1; ++$i) {
168
            // plaintext block
169 30
            $xi = substr($X, $i << 4, 16);
170
            // encrypt block and append to Y
171 30
            $Y .= $xi ^ $this->_cipher->encrypt($CB, $K);
172
            // increment counter block
173 30
            $CB = self::_inc32($CB);
174
        }
175
        // final block
176 49
        $xn = substr($X, $i << 4);
177
        // XOR against partial block
178 49
        $Y .= $xn ^ substr($this->_cipher->encrypt($CB, $K), 0, strlen($xn));
179 49
        return $Y;
180
    }
181
    
182
    /**
183
     * Compute authentication tag
184
     *
185
     * See NIST SP-300-38D section 7.1 steps 5-6 for the details.
186
     *
187
     * @param string $A Additional authenticated data
188
     * @param string $C Ciphertext
189
     * @param string $J0 Pre-counter block
190
     * @param string $K Encryption key
191
     * @param GHASH $ghash GHASH functor
192
     * @return string Authentication tag <code>T</code>
193
     */
194 49
    private function _computeAuthTag(string $A, string $C, string $J0, string $K,
195
        GHASH $ghash): string
196
    {
197 49
        $data = self::_pad128($A) . self::_pad128($C) .
198 49
             self::_uint64(strlen($A) << 3) . self::_uint64(strlen($C) << 3);
199 49
        $S = $ghash($data);
200 49
        return substr($this->_gctr($J0, $S, $K), 0, $this->_tagLength);
201
    }
202
    
203
    /**
204
     * Pad data to 128 bit block boundary.
205
     *
206
     * @param string $data
207
     * @return string
208
     */
209 49
    private static function _pad128($data)
210
    {
211 49
        $padlen = 16 - strlen($data) % 16;
212 49
        if (16 != $padlen) {
213 39
            $data .= str_repeat("\0", $padlen);
214
        }
215 49
        return $data;
216
    }
217
    
218
    /**
219
     * Increment 32 rightmost bits of the counter block.
220
     *
221
     * See NIST SP-300-38D section 6.2 for the details.
222
     *
223
     * @param string $X
224
     * @return string
225
     */
226 49
    private static function _inc32(string $X): string
227
    {
228 49
        $Y = substr($X, 0, -4);
229
        // increment counter
230 49
        $n = self::strToGMP(substr($X, -4)) + 1;
231
        // wrap by using only the 32 rightmost bits
232 49
        $Y .= substr(self::gmpToStr($n, 4), -4);
233 49
        return $Y;
234
    }
235
    
236
    /**
237
     * Convert integer to 64 bit big endian binary string.
238
     *
239
     * @param int $num
240
     * @return string
241
     */
242 49
    private static function _uint64(int $num): string
243
    {
244
        // truncate on 32 bit hosts
245 49
        if (PHP_INT_SIZE < 8) {
246
            return "\0\0\0\0" . pack("N", $num);
247
        }
248 49
        return pack("J", $num);
249
    }
250
    
251
    /**
252
     * Convert string to GMP number.
253
     *
254
     * String is interpreted as an unsigned integer with big endian order and
255
     * the most significant byte first.
256
     *
257
     * @param string $data Binary data
258
     * @return \GMP
259
     */
260 53
    public static function strToGMP(string $data): \GMP
261
    {
262 53
        return gmp_import($data, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
263
    }
264
    
265
    /**
266
     * Convert GMP number to string.
267
     *
268
     * Returned string represents an unsigned integer with big endian order and
269
     * the most significant byte first.
270
     *
271
     * @param \GMP $num GMP number
272
     * @param int $size Width of the string in bytes
273
     * @return string Binary data
274
     */
275 53
    public static function gmpToStr(\GMP $num, int $size): string
276
    {
277 53
        $data = gmp_export($num, 1, GMP_MSW_FIRST | GMP_BIG_ENDIAN);
278 53
        $len = strlen($data);
279 53
        if ($len < $size) {
280 43
            $data = str_repeat("\0", $size - $len) . $data;
281
        }
282 53
        return $data;
283
    }
284
}
285