Passed
Branch wip_sessions (2e0cc8)
by Nils
04:59
created

Aes::subWord()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
nc 2
nop 1
dl 0
loc 8
rs 10
c 1
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
Documentation Bug introduced by
The doc comment $ciphertext at position 0 could not be parsed: Unknown type name '$ciphertext' at position 0 in $ciphertext.
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;
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):
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)
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)
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
126
     * to generate a key schedule
127
     *
128
     * @param $key cipher key byte-array (16 bytes)
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...
129
     * @return $key schedule as 2D byte-array (Nr+1 x Nb bytes)
0 ignored issues
show
Documentation Bug introduced by
The doc comment $key at position 0 could not be parsed: Unknown type name '$key' at position 0 in $key.
Loading history...
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;
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
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));
256
        $key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
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);
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));
336
        $key = array_merge($key, array_slice($key, 0, $nBytes - 16)); // expand key to 16/24/32 bytes long
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);
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