Passed
Push — development ( b3bbd1...d832f6 )
by Nils
05:00 queued 01:04
created

Aes::subBytes()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
nc 3
nop 2
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
namespace Encryption\Crypt;
3
4
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
5
/*  AES counter (CTR) mode implementation in PHP (c) Chris Veness 2005-2010. Right of free use is */
6
/*    granted for all commercial or non-commercial use under CC-BY licence. No warranty of any    */
7
/*    form is offered.                                                                            */
8
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
9
10
class Aes
11
{
12
    /**
13
     * AES Cipher function: encrypt 'input' with Rijndael algorithm
14
     *
15
     * @param input message as byte-array (16 bytes)
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\message was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
     * @param w     key schedule as 2D byte-array (Nr+1 x Nb bytes) -
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\key was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
     *              generated from the cipher key by keyExpansion()
18
     * @return ciphertext as byte-array (16 bytes)
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\ciphertext was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
19
     */
20
    public static function cipher($input, $w)
21
    {
22
        // main cipher function [é5.1]
23
        $Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
24
        $Nr = count($w) / $Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys
25
26
        $state = array(); // initialise 4xNb byte-array 'state' with input [é3.4]
27
        for ($i = 0; $i < 4 * $Nb; $i++) {
28
            $state[$i % 4][floor($i / 4)] = $input[$i];
29
        }
30
31
        $state = self::addRoundKey($state, $w, 0, $Nb);
32
33
        for ($round = 1; $round < $Nr; $round++) {  // apply Nr rounds
34
            $state = self::subBytes($state, $Nb);
35
            $state = self::shiftRows($state, $Nb);
36
            $state = self::mixColumns($state, $Nb);
37
            $state = self::addRoundKey($state, $w, $round, $Nb);
38
        }
39
40
        $state = self::subBytes($state, $Nb);
41
        $state = self::shiftRows($state, $Nb);
42
        $state = self::addRoundKey($state, $w, $Nr, $Nb);
43
44
        $output = array(4 * $Nb); // convert state to 1-d array before returning [é3.4]
45
        for ($i = 0; $i < 4 * $Nb; $i++) {
46
            $output[$i] = $state[$i % 4][floor($i / 4)];
47
        }
48
49
        return $output;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $output returns the type array<integer,integer> which is incompatible with the documented return type Encryption\Crypt\ciphertext.
Loading history...
50
    }
51
52
    /**
53
     * @param integer $rnd
54
     * @param integer $Nb
55
     */
56
    private static function addRoundKey($state, $w, $rnd, $Nb)
57
    {
58
        // xor Round Key into state S [é5.1.4]
59
        for ($r = 0; $r < 4; $r++) {
60
            for ($c = 0; $c < $Nb; $c++) {
61
                $state[$r][$c] ^= $w[$rnd * 4 + $c][$r];
62
            }
63
        }
64
65
        return $state;
66
    }
67
68
    /**
69
     * @param integer $Nb
70
     */
71
    private static function subBytes($s, $Nb)
72
    {
73
        // apply SBox to state S [é5.1.1]
74
        for ($r = 0; $r < 4; $r++) {
75
            for ($c = 0; $c < $Nb; $c++) {
76
                $s[$r][$c] = self::$sBox[$s[$r][$c]];
77
            }
78
        }
79
80
        return $s;
81
    }
82
83
    /**
84
     * @param integer $Nb
85
     */
86
    private static function shiftRows($s, $Nb)
87
    {
88
        // shift row r of state S left by r bytes [é5.1.2]
89
        $t = array(4);
90
        for ($r = 1; $r < 4; $r++) {
91
            for ($c = 0; $c < 4; $c++) {
92
                $t[$c] = $s[$r][($c + $r) % $Nb]; // shift into temp copy
93
            }
94
            for ($c = 0; $c < 4; $c++) {
95
                $s[$r][$c] = $t[$c]; // and copy back
96
            }
97
        }          // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES):
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% 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...
98
        return $s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf
99
    }
100
101
    /**
102
     * @param integer $Nb
103
     */
104
    private static function mixColumns($s, $Nb)
0 ignored issues
show
Unused Code introduced by
The parameter $Nb is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

104
    private static function mixColumns($s, /** @scrutinizer ignore-unused */ $Nb)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
105
    {
106
        // combine bytes of each col of state S [é5.1.3]
107
        for ($c = 0; $c < 4; $c++) {
108
            $a = array(4); // 'a' is a copy of the current column from 's'
109
            $b = array(4); // 'b' is aé{02} in GF(2^8)
0 ignored issues
show
Unused Code Comprehensibility introduced by
50% 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...
110
            for ($i = 0; $i < 4; $i++) {
111
                $a[$i] = $s[$i][$c];
112
                $b[$i] = $s[$i][$c] & 0x80 ? $s[$i][$c] << 1 ^ 0x011b : $s[$i][$c] << 1;
113
            }
114
            // a[n] ^ b[n] is aé{03} in GF(2^8)
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
115
            $s[0][$c] = $b[0] ^ $a[1] ^ $b[1] ^ $a[2] ^ $a[3]; // 2*a0 + 3*a1 + a2 + a3
116
            $s[1][$c] = $a[0] ^ $b[1] ^ $a[2] ^ $b[2] ^ $a[3]; // a0 * 2*a1 + 3*a2 + a3
117
            $s[2][$c] = $a[0] ^ $a[1] ^ $b[2] ^ $a[3] ^ $b[3]; // a0 + a1 + 2*a2 + 3*a3
118
            $s[3][$c] = $a[0] ^ $b[0] ^ $a[1] ^ $a[2] ^ $b[3]; // 3*a0 + a1 + a2 + 2*a3
119
        }
120
121
        return $s;
122
    }
123
124
    /**
125
     * Key expansion for Rijndael cipher(): performs key expansion on cipher key
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\cipher was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
126
     * to generate a key schedule
127
     *
128
     * @param key cipher key byte-array (16 bytes)
129
     * @return key schedule as 2D byte-array (Nr+1 x Nb bytes)
130
     */
131
    public static function keyExpansion($key)
132
    {
133
        // generate Key Schedule from Cipher Key [é5.2]
134
        $Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES)
135
        $Nk = count($key) / 4; // key length (in words): 4/6/8 for 128/192/256-bit keys
136
        $Nr = $Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys
137
138
        $w = array();
139
        $temp = array();
140
141
        for ($i = 0; $i < $Nk; $i++) {
142
            $r = array($key[4 * $i], $key[4 * $i + 1], $key[4 * $i + 2], $key[4 * $i + 3]);
143
            $w[$i] = $r;
144
        }
145
146
        for ($i = $Nk; $i < ($Nb * ($Nr + 1)); $i++) {
147
            $w[$i] = array();
148
            for ($t = 0; $t < 4; $t++) {
149
                $temp[$t] = $w[$i - 1][$t];
150
            }
151
            if ($i % $Nk == 0) {
152
                $temp = self::subWord(self::rotWord($temp));
153
                for ($t = 0; $t < 4; $t++) {
154
                    $temp[$t] ^= self::$rCon[$i / $Nk][$t];
155
                }
156
            } elseif ($Nk > 6 && $i % $Nk == 4) {
157
                $temp = self::subWord($temp);
158
            }
159
            for ($t = 0; $t < 4; $t++) {
160
                $w[$i][$t] = $w[$i - $Nk][$t] ^ $temp[$t];
161
            }
162
        }
163
164
        return $w;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $w returns the type array which is incompatible with the documented return type Encryption\Crypt\key.
Loading history...
165
    }
166
167
    private static function subWord($w)
168
    {
169
        // apply SBox to 4-byte word w
170
        for ($i = 0; $i < 4; $i++) {
171
            $w[$i] = self::$sBox[$w[$i]];
172
        }
173
174
        return $w;
175
    }
176
177
    private static function rotWord($w)
178
    {
179
        // rotate 4-byte word w left by one byte
180
        $tmp = $w[0];
181
        for ($i = 0; $i < 3; $i++) {
182
            $w[$i] = $w[$i + 1];
183
        }
184
        $w[3] = $tmp;
185
186
        return $w;
187
    }
188
189
    // sBox is pre-computed multiplicative inverse in GF(2^8) used in subBytes and keyExpansion [é5.1.1]
190
    private static $sBox = array(
191
            0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
192
            0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
193
            0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
194
            0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
195
            0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
196
            0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
197
            0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
198
            0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
199
            0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
200
            0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
201
            0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
202
            0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
203
            0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
204
            0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
205
            0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
206
            0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
207
    );
208
209
    // rCon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [é5.2]
210
    private static $rCon = array(
211
            array(0x00, 0x00, 0x00, 0x00),
212
            array(0x01, 0x00, 0x00, 0x00),
213
            array(0x02, 0x00, 0x00, 0x00),
214
            array(0x04, 0x00, 0x00, 0x00),
215
            array(0x08, 0x00, 0x00, 0x00),
216
            array(0x10, 0x00, 0x00, 0x00),
217
            array(0x20, 0x00, 0x00, 0x00),
218
            array(0x40, 0x00, 0x00, 0x00),
219
            array(0x80, 0x00, 0x00, 0x00),
220
            array(0x1b, 0x00, 0x00, 0x00),
221
            array(0x36, 0x00, 0x00, 0x00)
222
    );
223
224
}
225
226
class aesctr extends Aes
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
227
{
228
229
    /**
230
     * Encrypt a text using AES encryption in Counter mode of operation
231
     *  - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
232
     *
233
     * Unicode multi-byte character safe
234
     *
235
     * @param plaintext string text to be encrypted
236
     * @param password  the password to use to generate a key
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\the was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
237
     * @param nBits     integer of bits to be used in the key (128, 192, or 256)
238
     * @return          string text
239
     */
240
    public static function encrypt($plaintext, $password, $nBits)
241
    {
242
        $blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
243
        if (!($nBits == 128 || $nBits == 192 || $nBits == 256)) {
244
            return ''; // standard allows 128/192/256 bit keys
245
        }
246
        // note PHP (5) gives us plaintext and password in UTF8 encoding!
247
248
        // use AES itself to encrypt password to get cipher key (using plain password as source for
249
        // key expansion) - gives us well encrypted key
250
        $nBytes = $nBits / 8; // no bytes in key
251
        $pwBytes = array();
252
        for ($i = 0; $i < $nBytes; $i++) {
253
            $pwBytes[$i] = ord(substr($password, $i, 1)) & 0xff;
254
        }
255
        $key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
0 ignored issues
show
Bug introduced by
$pwBytes of type array is incompatible with the type Encryption\Crypt\cipher expected by parameter $key of Encryption\Crypt\Aes::keyExpansion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

255
        $key = Aes::cipher($pwBytes, Aes::keyExpansion(/** @scrutinizer ignore-type */ $pwBytes));
Loading history...
Bug introduced by
$pwBytes of type array is incompatible with the type Encryption\Crypt\message expected by parameter $input of Encryption\Crypt\Aes::cipher(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

255
        $key = Aes::cipher(/** @scrutinizer ignore-type */ $pwBytes, Aes::keyExpansion($pwBytes));
Loading history...
256
        $key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
0 ignored issues
show
Bug introduced by
$key of type Encryption\Crypt\ciphertext is incompatible with the type array expected by parameter $array of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

256
        $key = array_merge($key, array_slice(/** @scrutinizer ignore-type */ $key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
Loading history...
Bug introduced by
$key of type Encryption\Crypt\ciphertext is incompatible with the type array expected by parameter $array1 of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

256
        $key = array_merge(/** @scrutinizer ignore-type */ $key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
Loading history...
257
258
        // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in
259
        // 1st 8 bytes, block counter in 2nd 8 bytes
260
        $counterBlock = array();
261
        $nonce = floor(microtime(true) * 1000); // timestamp: milliseconds since 1-Jan-1970
262
        $nonceSec = floor($nonce / 1000);
263
        $nonceMs = $nonce % 1000;
264
        // encode nonce with seconds in 1st 4 bytes, and (repeated) ms part filling 2nd 4 bytes
265
        for ($i = 0; $i < 4; $i++) {
266
            $counterBlock[$i] = self::urs($nonceSec, $i * 8) & 0xff;
267
        }
268
        for ($i = 0; $i < 4; $i++) {
269
            $counterBlock[$i + 4] = $nonceMs & 0xff;
270
        }
271
        // and convert it to a string to go on the front of the ciphertext
272
        $ctrTxt = '';
273
        for ($i = 0; $i < 8; $i++) {
274
            $ctrTxt .= chr($counterBlock[$i]);
275
        }
276
277
        // generate key schedule - an expansion of the key into distinct Key Rounds for each round
278
        $keySchedule = Aes::keyExpansion($key);
0 ignored issues
show
Bug introduced by
$key of type array is incompatible with the type Encryption\Crypt\cipher expected by parameter $key of Encryption\Crypt\Aes::keyExpansion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

278
        $keySchedule = Aes::keyExpansion(/** @scrutinizer ignore-type */ $key);
Loading history...
279
280
        $blockCount = ceil(strlen($plaintext) / $blockSize);
281
        $ciphertxt = array(); // ciphertext as array of strings
282
283
        for ($b = 0; $b < $blockCount; $b++) {
284
            // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
285
            // done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB)
286
            for ($c = 0; $c < 4; $c++) {
287
                $counterBlock[15 - $c] = self::urs($b, $c * 8) & 0xff;
288
            }
289
            for ($c = 0; $c < 4; $c++) {
290
                $counterBlock[15 - $c - 4] = self::urs($b / 0x100000000, $c * 8);
291
            }
292
293
            $cipherCntr = Aes::cipher($counterBlock, $keySchedule); // -- encrypt counter block --
294
295
            // block size is reduced on final block
296
            $blockLength = $b < $blockCount - 1 ? $blockSize : (strlen($plaintext) - 1) % $blockSize + 1;
297
            $cipherByte = array();
298
299
            for ($i = 0; $i < $blockLength; $i++) {  // -- xor plaintext with ciphered counter byte-by-byte --
300
                $cipherByte[$i] = $cipherCntr[$i] ^ ord(substr($plaintext, $b * $blockSize + $i, 1));
301
                $cipherByte[$i] = chr($cipherByte[$i]);
302
            }
303
            $ciphertxt[$b] = implode('', $cipherByte); // escape troublesome characters in ciphertext
304
        }
305
306
        // implode is more efficient than repeated string concatenation
307
        $ciphertext = $ctrTxt.implode('', $ciphertxt);
308
        $ciphertext = base64_encode($ciphertext);
309
310
        return $ciphertext;
311
    }
312
313
    /**
314
     * Decrypt a text encrypted by AES in counter mode of operation
315
     *
316
     * @param ciphertext source text to be decrypted
0 ignored issues
show
Bug introduced by
The type Encryption\Crypt\source was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
317
     * @param password   the password to use to generate a key
318
     * @param nBits      integer of bits to be used in the key (128, 192, or 256)
319
     * @return           string text
320
     */
321
    public static function decrypt($ciphertext, $password, $nBits)
322
    {
323
        $blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES
324
        if (!($nBits == 128 || $nBits == 192 || $nBits == 256)) {
325
            return ''; // standard allows 128/192/256 bit keys
326
        }
327
        $ciphertext = base64_decode($ciphertext);
328
329
        // use AES to encrypt password (mirroring encrypt routine)
330
        $nBytes = $nBits / 8; // no bytes in key
331
        $pwBytes = array();
332
        for ($i = 0; $i < $nBytes; $i++) {
333
            $pwBytes[$i] = ord(substr($password, $i, 1)) & 0xff;
334
        }
335
        $key = Aes::cipher($pwBytes, Aes::keyExpansion($pwBytes));
0 ignored issues
show
Bug introduced by
$pwBytes of type array is incompatible with the type Encryption\Crypt\message expected by parameter $input of Encryption\Crypt\Aes::cipher(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

335
        $key = Aes::cipher(/** @scrutinizer ignore-type */ $pwBytes, Aes::keyExpansion($pwBytes));
Loading history...
Bug introduced by
$pwBytes of type array is incompatible with the type Encryption\Crypt\cipher expected by parameter $key of Encryption\Crypt\Aes::keyExpansion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

335
        $key = Aes::cipher($pwBytes, Aes::keyExpansion(/** @scrutinizer ignore-type */ $pwBytes));
Loading history...
336
        $key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
0 ignored issues
show
Bug introduced by
$key of type Encryption\Crypt\ciphertext is incompatible with the type array expected by parameter $array of array_slice(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

336
        $key = array_merge($key, array_slice(/** @scrutinizer ignore-type */ $key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
Loading history...
Bug introduced by
$key of type Encryption\Crypt\ciphertext is incompatible with the type array expected by parameter $array1 of array_merge(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

336
        $key = array_merge(/** @scrutinizer ignore-type */ $key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
Loading history...
337
338
        // recover nonce from 1st element of ciphertext
339
        $counterBlock = array();
340
        $ctrTxt = substr($ciphertext, 0, 8);
341
        for ($i = 0; $i < 8; $i++) {
342
            $counterBlock[$i] = ord(substr($ctrTxt, $i, 1));
343
        }
344
345
        // generate key schedule
346
        $keySchedule = Aes::keyExpansion($key);
0 ignored issues
show
Bug introduced by
$key of type array is incompatible with the type Encryption\Crypt\cipher expected by parameter $key of Encryption\Crypt\Aes::keyExpansion(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

346
        $keySchedule = Aes::keyExpansion(/** @scrutinizer ignore-type */ $key);
Loading history...
347
348
        // separate ciphertext into blocks (skipping past initial 8 bytes)
349
        $nBlocks = ceil((strlen($ciphertext) - 8) / $blockSize);
350
        $ct = array();
351
        for ($b = 0; $b < $nBlocks; $b++) {
352
            $ct[$b] = substr($ciphertext, 8 + $b * $blockSize, 16);
353
        }
354
        $ciphertext = $ct; // ciphertext is now array of block-length strings
355
356
        // plaintext will get generated block-by-block into array of block-length strings
357
        $plaintxt = array();
358
359
        for ($b = 0; $b < $nBlocks; $b++) {
360
            // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes)
361
            for ($c = 0; $c < 4; $c++) {
362
                $counterBlock[15 - $c] = self::urs($b, $c * 8) & 0xff;
363
            }
364
            for ($c = 0; $c < 4; $c++) {
365
                $counterBlock[15 - $c - 4] = self::urs(($b + 1) / 0x100000000 - 1, $c * 8) & 0xff;
366
            }
367
368
            $cipherCntr = Aes::cipher($counterBlock, $keySchedule); // encrypt counter block
369
370
            $plaintxtByte = array();
371
            for ($i = 0; $i < strlen($ciphertext[$b]); $i++) {
372
                // -- xor plaintext with ciphered counter byte-by-byte --
373
                $plaintxtByte[$i] = $cipherCntr[$i] ^ ord(substr($ciphertext[$b], $i, 1));
374
                $plaintxtByte[$i] = chr($plaintxtByte[$i]);
375
            }
376
            $plaintxt[$b] = implode('', $plaintxtByte);
377
        }
378
379
        // join array of blocks into single plaintext string
380
        $plaintext = implode('', $plaintxt);
381
382
        return $plaintext;
383
    }
384
385
    /*
386
    * Unsigned right shift function, since PHP has neither >>> operator nor unsigned ints
387
    *
388
    * @param a  number to be shifted (32-bit integer)
389
    * @param b  number of bits to shift a to the right (0..31)
390
    * @return   a right-shifted and zero-filled by b bits
391
    */
392
    private static function urs($a, $b)
393
    {
394
        $a &= 0xffffffff;
395
        $b &= 0x1f; // (bounds check)
396
        if ($a & 0x80000000 && $b > 0) {   // if left-most bit set
397
            $a = ($a >> 1) & 0x7fffffff; //   right-shift one bit & clear left-most bit
398
            $a = $a >> ($b - 1); //   remaining right-shifts
399
        } else {                       // otherwise
400
            $a = ($a >> $b); //   use normal right-shift
401
        }
402
403
        return $a;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $a returns the type integer which is incompatible with the documented return type Encryption\Crypt\a.
Loading history...
404
    }
405
}
406
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -  */
407