1<?php
2
3/**
4 * Pure-PHP implementation of Blowfish.
5 *
6 * Uses mcrypt, if available, and an internal implementation, otherwise.
7 *
8 * PHP version 5
9 *
10 * Useful resources are as follows:
11 *
12 *  - {@link http://en.wikipedia.org/wiki/Blowfish_(cipher) Wikipedia description of Blowfish}
13 *
14 * # An overview of bcrypt vs Blowfish
15 *
16 * OpenSSH private keys use a customized version of bcrypt. Specifically, instead of
17 * encrypting OrpheanBeholderScryDoubt 64 times OpenSSH's bcrypt variant encrypts
18 * OxychromaticBlowfishSwatDynamite 64 times. so we can't use crypt().
19 *
20 * bcrypt is basically Blowfish but instead of performing the key expansion once it performs
21 * the expansion 129 times for each round, with the first key expansion interleaving the salt
22 * and password. This renders OpenSSL unusable and forces us to use a pure-PHP implementation
23 * of blowfish.
24 *
25 * # phpseclib's four different _encryptBlock() implementations
26 *
27 * When using Blowfish as an encryption algorithm, _encryptBlock() is called 9 + 512 +
28 * (the number of blocks in the plaintext) times.
29 *
30 * Each of the first 9 calls to _encryptBlock() modify the P-array. Each of the next 512
31 * calls modify the S-boxes. The remaining _encryptBlock() calls operate on the plaintext to
32 * produce the ciphertext. In the pure-PHP implementation of Blowfish these remaining
33 * _encryptBlock() calls are highly optimized through the use of eval(). Among other things,
34 * P-array lookups are eliminated by hard-coding the key-dependent P-array values, and thus we
35 * have explained 2 of the 4 different _encryptBlock() implementations.
36 *
37 * With bcrypt things are a bit different. _encryptBlock() is called 1,079,296 times,
38 * assuming 16 rounds (which is what OpenSSH's bcrypt defaults to). The eval()-optimized
39 * _encryptBlock() isn't as beneficial because the P-array values are not constant. Well, they
40 * are constant, but only for, at most, 777 _encryptBlock() calls, which is equivalent to ~6KB
41 * of data. The average length of back to back _encryptBlock() calls with a fixed P-array is
42 * 514.12, which is ~4KB of data. Creating an eval()-optimized _encryptBlock() has an upfront
43 * cost, which is CPU dependent and is probably not going to be worth it for just ~4KB of
44 * data. Conseqeuently, bcrypt does not benefit from the eval()-optimized _encryptBlock().
45 *
46 * The regular _encryptBlock() does unpack() and pack() on every call, as well, and that can
47 * begin to add up after one million function calls.
48 *
49 * In theory, one might think that it might be beneficial to rewrite all block ciphers so
50 * that, instead of passing strings to _encryptBlock(), you convert the string to an array of
51 * integers and then pass successive subarrays of that array to _encryptBlock. This, however,
52 * kills PHP's memory use. Like let's say you have a 1MB long string. After doing
53 * $in = str_repeat('a', 1024 * 1024); PHP's memory utilization jumps up by ~1MB. After doing
54 * $blocks = str_split($in, 4); it jumps up by an additional ~16MB. After
55 * $blocks = array_map(fn($x) => unpack('N*', $x), $blocks); it jumps up by an additional
56 * ~90MB, yielding a 106x increase in memory usage. Consequently, it bcrypt calls a different
57 * _encryptBlock() then the regular Blowfish does. That said, the Blowfish _encryptBlock() is
58 * basically just a thin wrapper around the bcrypt _encryptBlock(), so there's that.
59 *
60 * This explains 3 of the 4 _encryptBlock() implementations. the last _encryptBlock()
61 * implementation can best be understood by doing Ctrl + F and searching for where
62 * self::$use_reg_intval is defined.
63 *
64 * # phpseclib's three different _setupKey() implementations
65 *
66 * Every bcrypt round is the equivalent of encrypting 512KB of data. Since OpenSSH uses 16
67 * rounds by default that's ~8MB of data that's essentially being encrypted whenever
68 * you use bcrypt. That's a lot of data, however, bcrypt operates within tighter constraints
69 * than regular Blowfish, so we can use that to our advantage. In particular, whereas Blowfish
70 * supports variable length keys, in bcrypt, the initial "key" is the sha512 hash of the
71 * password. sha512 hashes are 512 bits or 64 bytes long and thus the bcrypt keys are of a
72 * fixed length whereas Blowfish keys are not of a fixed length.
73 *
74 * bcrypt actually has two different key expansion steps. The first one (expandstate) is
75 * constantly XOR'ing every _encryptBlock() parameter against the salt prior _encryptBlock()'s
76 * being called. The second one (expand0state) is more similar to Blowfish's _setupKey()
77 * but it can still use the fixed length key optimization discussed above and can do away with
78 * the pack() / unpack() calls.
79 *
80 * I suppose _setupKey() could be made to be a thin wrapper around expandstate() but idk it's
81 * just a lot of work for very marginal benefits as _setupKey() is only called once for
82 * regular Blowfish vs the 128 times it's called --per round-- with bcrypt.
83 *
84 * # blowfish + bcrypt in the same class
85 *
86 * Altho there's a lot of Blowfish code that bcrypt doesn't re-use, bcrypt does re-use the
87 * initial S-boxes, the initial P-array and the int-only _encryptBlock() implementation.
88 *
89 * # Credit
90 *
91 * phpseclib's bcrypt implementation is based losely off of OpenSSH's implementation:
92 *
93 * https://github.com/openssh/openssh-portable/blob/master/openbsd-compat/bcrypt_pbkdf.c
94 *
95 * Here's a short example of how to use this library:
96 * <code>
97 * <?php
98 *    include 'vendor/autoload.php';
99 *
100 *    $blowfish = new \phpseclib3\Crypt\Blowfish('ctr');
101 *
102 *    $blowfish->setKey('12345678901234567890123456789012');
103 *
104 *    $plaintext = str_repeat('a', 1024);
105 *
106 *    echo $blowfish->decrypt($blowfish->encrypt($plaintext));
107 * ?>
108 * </code>
109 *
110 * @author    Jim Wigginton <terrafrost@php.net>
111 * @author    Hans-Juergen Petrich <petrich@tronic-media.com>
112 * @copyright 2007 Jim Wigginton
113 * @license   http://www.opensource.org/licenses/mit-license.html  MIT License
114 * @link      http://phpseclib.sourceforge.net
115 */
116
117namespace phpseclib3\Crypt;
118
119use phpseclib3\Crypt\Common\BlockCipher;
120
121/**
122 * Pure-PHP implementation of Blowfish.
123 *
124 * @author  Jim Wigginton <terrafrost@php.net>
125 * @author  Hans-Juergen Petrich <petrich@tronic-media.com>
126 */
127class Blowfish extends BlockCipher
128{
129    /**
130     * Block Length of the cipher
131     *
132     * @see \phpseclib3\Crypt\Common\SymmetricKey::block_size
133     * @var int
134     */
135    protected $block_size = 8;
136
137    /**
138     * The mcrypt specific name of the cipher
139     *
140     * @see \phpseclib3\Crypt\Common\SymmetricKey::cipher_name_mcrypt
141     * @var string
142     */
143    protected $cipher_name_mcrypt = 'blowfish';
144
145    /**
146     * Optimizing value while CFB-encrypting
147     *
148     * @see \phpseclib3\Crypt\Common\SymmetricKey::cfb_init_len
149     * @var int
150     */
151    protected $cfb_init_len = 500;
152
153    /**
154     * The fixed subkeys boxes ($sbox0 - $sbox3) with 256 entries each
155     *
156     * S-Box 0
157     *
158     * @var    array
159     */
160    private static $sbox0 = [
161        0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
162        0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
163        0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
164        0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
165        0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
166        0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
167        0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
168        0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
169        0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
170        0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
171        0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
172        0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
173        0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
174        0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
175        0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
176        0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
177        0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
178        0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
179        0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
180        0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
181        0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
182        0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
183        0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
184        0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
185        0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
186        0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
187        0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
188        0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
189        0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
190        0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
191        0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
192        0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a
193    ];
194
195    /**
196     * S-Box 1
197     *
198     * @var    array
199     */
200    private static $sbox1 = [
201        0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
202        0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
203        0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
204        0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
205        0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
206        0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
207        0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
208        0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
209        0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
210        0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
211        0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
212        0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
213        0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
214        0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
215        0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
216        0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
217        0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
218        0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
219        0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
220        0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
221        0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
222        0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
223        0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
224        0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
225        0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
226        0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
227        0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
228        0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
229        0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
230        0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
231        0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
232        0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7
233    ];
234
235    /**
236     * S-Box 2
237     *
238     * @var    array
239     */
240    private static $sbox2 = [
241        0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
242        0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
243        0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
244        0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
245        0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
246        0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
247        0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
248        0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
249        0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
250        0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
251        0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
252        0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
253        0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
254        0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
255        0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
256        0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
257        0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
258        0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
259        0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
260        0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
261        0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
262        0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
263        0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
264        0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
265        0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
266        0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
267        0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
268        0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
269        0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
270        0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
271        0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
272        0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0
273    ];
274
275    /**
276     * S-Box 3
277     *
278     * @var    array
279     */
280    private static $sbox3 = [
281        0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
282        0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
283        0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
284        0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
285        0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
286        0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
287        0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
288        0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
289        0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
290        0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
291        0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
292        0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
293        0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
294        0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
295        0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
296        0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
297        0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
298        0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
299        0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
300        0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
301        0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
302        0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
303        0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
304        0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
305        0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
306        0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
307        0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
308        0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
309        0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
310        0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
311        0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
312        0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
313    ];
314
315    /**
316     * P-Array consists of 18 32-bit subkeys
317     *
318     * @var array
319     */
320    private static $parray = [
321        0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
322        0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
323        0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b
324    ];
325
326    /**
327     * The BCTX-working Array
328     *
329     * Holds the expanded key [p] and the key-depended s-boxes [sb]
330     *
331     * @var array
332     */
333    private $bctx;
334
335    /**
336     * Holds the last used key
337     *
338     * @var array
339     */
340    private $kl;
341
342    /**
343     * The Key Length (in bytes)
344     * {@internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16.  Exists in conjunction with $Nk
345     *    because the encryption / decryption / key schedule creation requires this number and not $key_length.  We could
346     *    derive this from $key_length or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
347     *    of that, we'll just precompute it once.}
348     *
349     * @see \phpseclib3\Crypt\Common\SymmetricKey::setKeyLength()
350     * @var int
351     */
352    protected $key_length = 16;
353
354    /**
355     * Default Constructor.
356     *
357     * @param string $mode
358     * @throws \InvalidArgumentException if an invalid / unsupported mode is provided
359     */
360    public function __construct($mode)
361    {
362        parent::__construct($mode);
363
364        if ($this->mode == self::MODE_STREAM) {
365            throw new \InvalidArgumentException('Block ciphers cannot be ran in stream mode');
366        }
367    }
368
369    /**
370     * Sets the key length.
371     *
372     * Key lengths can be between 32 and 448 bits.
373     *
374     * @param int $length
375     */
376    public function setKeyLength($length)
377    {
378        if ($length < 32 || $length > 448) {
379                throw new \LengthException('Key size of ' . $length . ' bits is not supported by this algorithm. Only keys of sizes between 32 and 448 bits are supported');
380        }
381
382        $this->key_length = $length >> 3;
383
384        parent::setKeyLength($length);
385    }
386
387    /**
388     * Test for engine validity
389     *
390     * This is mainly just a wrapper to set things up for \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
391     *
392     * @see \phpseclib3\Crypt\Common\SymmetricKey::isValidEngine()
393     * @param int $engine
394     * @return bool
395     */
396    protected function isValidEngineHelper($engine)
397    {
398        if ($engine == self::ENGINE_OPENSSL) {
399            if ($this->key_length < 16) {
400                return false;
401            }
402            // quoting https://www.openssl.org/news/openssl-3.0-notes.html, OpenSSL 3.0.1
403            // "Moved all variations of the EVP ciphers CAST5, BF, IDEA, SEED, RC2, RC4, RC5, and DES to the legacy provider"
404            // in theory openssl_get_cipher_methods() should catch this but, on GitHub Actions, at least, it does not
405            if (defined('OPENSSL_VERSION_TEXT') && version_compare(preg_replace('#OpenSSL (\d+\.\d+\.\d+) .*#', '$1', OPENSSL_VERSION_TEXT), '3.0.1', '>=')) {
406                return false;
407            }
408            $this->cipher_name_openssl_ecb = 'bf-ecb';
409            $this->cipher_name_openssl = 'bf-' . $this->openssl_translate_mode();
410        }
411
412        return parent::isValidEngineHelper($engine);
413    }
414
415    /**
416     * Setup the key (expansion)
417     *
418     * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupKey()
419     */
420    protected function setupKey()
421    {
422        if (isset($this->kl['key']) && $this->key === $this->kl['key']) {
423            // already expanded
424            return;
425        }
426        $this->kl = ['key' => $this->key];
427
428        /* key-expanding p[] and S-Box building sb[] */
429        $this->bctx = [
430            'p'  => [],
431            'sb' => [
432                self::$sbox0,
433                self::$sbox1,
434                self::$sbox2,
435                self::$sbox3
436            ]
437        ];
438
439        // unpack binary string in unsigned chars
440        $key  = array_values(unpack('C*', $this->key));
441        $keyl = count($key);
442        // with bcrypt $keyl will always be 16 (because the key is the sha512 of the key you provide)
443        for ($j = 0, $i = 0; $i < 18; ++$i) {
444            // xor P1 with the first 32-bits of the key, xor P2 with the second 32-bits ...
445            for ($data = 0, $k = 0; $k < 4; ++$k) {
446                $data = ($data << 8) | $key[$j];
447                if (++$j >= $keyl) {
448                    $j = 0;
449                }
450            }
451            $this->bctx['p'][] = self::$parray[$i] ^ intval($data);
452        }
453
454        // encrypt the zero-string, replace P1 and P2 with the encrypted data,
455        // encrypt P3 and P4 with the new P1 and P2, do it with all P-array and subkeys
456        $data = "\0\0\0\0\0\0\0\0";
457        for ($i = 0; $i < 18; $i += 2) {
458            list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
459            $this->bctx['p'][$i    ] = $l;
460            $this->bctx['p'][$i + 1] = $r;
461        }
462        for ($i = 0; $i < 4; ++$i) {
463            for ($j = 0; $j < 256; $j += 2) {
464                list($l, $r) = array_values(unpack('N*', $data = $this->encryptBlock($data)));
465                $this->bctx['sb'][$i][$j    ] = $l;
466                $this->bctx['sb'][$i][$j + 1] = $r;
467            }
468        }
469    }
470
471    /**
472     * Initialize Static Variables
473     */
474    protected static function initialize_static_variables()
475    {
476        if (is_float(self::$sbox2[0])) {
477            self::$sbox0 = array_map('intval', self::$sbox0);
478            self::$sbox1 = array_map('intval', self::$sbox1);
479            self::$sbox2 = array_map('intval', self::$sbox2);
480            self::$sbox3 = array_map('intval', self::$sbox3);
481            self::$parray = array_map('intval', self::$parray);
482        }
483
484        parent::initialize_static_variables();
485    }
486
487    /**
488     * bcrypt
489     *
490     * @param string $sha2pass
491     * @param string $sha2salt
492     * @access private
493     * @return string
494     */
495    private static function bcrypt_hash($sha2pass, $sha2salt)
496    {
497        $p = self::$parray;
498        $sbox0 = self::$sbox0;
499        $sbox1 = self::$sbox1;
500        $sbox2 = self::$sbox2;
501        $sbox3 = self::$sbox3;
502
503        $cdata = array_values(unpack('N*', 'OxychromaticBlowfishSwatDynamite'));
504        $sha2pass = array_values(unpack('N*', $sha2pass));
505        $sha2salt = array_values(unpack('N*', $sha2salt));
506
507        self::expandstate($sha2salt, $sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
508        for ($i = 0; $i < 64; $i++) {
509            self::expand0state($sha2salt, $sbox0, $sbox1, $sbox2, $sbox3, $p);
510            self::expand0state($sha2pass, $sbox0, $sbox1, $sbox2, $sbox3, $p);
511        }
512
513        for ($i = 0; $i < 64; $i++) {
514            for ($j = 0; $j < 8; $j += 2) { // count($cdata) == 8
515                list($cdata[$j], $cdata[$j + 1]) = self::encryptBlockHelperFast($cdata[$j], $cdata[$j + 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
516            }
517        }
518
519        return pack('L*', ...$cdata);
520    }
521
522    /**
523     * Performs OpenSSH-style bcrypt
524     *
525     * @param string $pass
526     * @param string $salt
527     * @param int $keylen
528     * @param int $rounds
529     * @access public
530     * @return string
531     */
532    public static function bcrypt_pbkdf($pass, $salt, $keylen, $rounds)
533    {
534        self::initialize_static_variables();
535
536        if (PHP_INT_SIZE == 4) {
537            throw new \RuntimeException('bcrypt is far too slow to be practical on 32-bit versions of PHP');
538        }
539
540        $sha2pass = hash('sha512', $pass, true);
541        $results = [];
542        $count = 1;
543        while (32 * count($results) < $keylen) {
544            $countsalt = $salt . pack('N', $count++);
545            $sha2salt = hash('sha512', $countsalt, true);
546            $out = $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
547            for ($i = 1; $i < $rounds; $i++) {
548                $sha2salt = hash('sha512', $tmpout, true);
549                $tmpout = self::bcrypt_hash($sha2pass, $sha2salt);
550                $out ^= $tmpout;
551            }
552            $results[] = $out;
553        }
554        $output = '';
555        for ($i = 0; $i < 32; $i++) {
556            foreach ($results as $result) {
557                $output .= $result[$i];
558            }
559        }
560        return substr($output, 0, $keylen);
561    }
562
563    /**
564     * Key expansion without salt
565     *
566     * @access private
567     * @param int[] $key
568     * @param int[] $sbox0
569     * @param int[] $sbox1
570     * @param int[] $sbox2
571     * @param int[] $sbox3
572     * @param int[] $p
573     * @see self::_bcrypt_hash()
574     */
575    private static function expand0state(array $key, array &$sbox0, array &$sbox1, array &$sbox2, array &$sbox3, array &$p)
576    {
577        // expand0state is basically the same thing as this:
578        //return self::expandstate(array_fill(0, 16, 0), $key);
579        // but this separate function eliminates a bunch of XORs and array lookups
580
581        $p = [
582            $p[0] ^ $key[0],
583            $p[1] ^ $key[1],
584            $p[2] ^ $key[2],
585            $p[3] ^ $key[3],
586            $p[4] ^ $key[4],
587            $p[5] ^ $key[5],
588            $p[6] ^ $key[6],
589            $p[7] ^ $key[7],
590            $p[8] ^ $key[8],
591            $p[9] ^ $key[9],
592            $p[10] ^ $key[10],
593            $p[11] ^ $key[11],
594            $p[12] ^ $key[12],
595            $p[13] ^ $key[13],
596            $p[14] ^ $key[14],
597            $p[15] ^ $key[15],
598            $p[16] ^ $key[0],
599            $p[17] ^ $key[1]
600        ];
601
602        // @codingStandardsIgnoreStart
603        list( $p[0],  $p[1]) = self::encryptBlockHelperFast(     0,      0, $sbox0, $sbox1, $sbox2, $sbox3, $p);
604        list( $p[2],  $p[3]) = self::encryptBlockHelperFast($p[ 0], $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
605        list( $p[4],  $p[5]) = self::encryptBlockHelperFast($p[ 2], $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
606        list( $p[6],  $p[7]) = self::encryptBlockHelperFast($p[ 4], $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
607        list( $p[8],  $p[9]) = self::encryptBlockHelperFast($p[ 6], $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
608        list($p[10], $p[11]) = self::encryptBlockHelperFast($p[ 8], $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
609        list($p[12], $p[13]) = self::encryptBlockHelperFast($p[10], $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
610        list($p[14], $p[15]) = self::encryptBlockHelperFast($p[12], $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
611        list($p[16], $p[17]) = self::encryptBlockHelperFast($p[14], $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
612        // @codingStandardsIgnoreEnd
613
614        list($sbox0[0], $sbox0[1]) = self::encryptBlockHelperFast($p[16], $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
615        for ($i = 2; $i < 256; $i += 2) {
616            list($sbox0[$i], $sbox0[$i + 1]) = self::encryptBlockHelperFast($sbox0[$i - 2], $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
617        }
618
619        list($sbox1[0], $sbox1[1]) = self::encryptBlockHelperFast($sbox0[254], $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
620        for ($i = 2; $i < 256; $i += 2) {
621            list($sbox1[$i], $sbox1[$i + 1]) = self::encryptBlockHelperFast($sbox1[$i - 2], $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
622        }
623
624        list($sbox2[0], $sbox2[1]) = self::encryptBlockHelperFast($sbox1[254], $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
625        for ($i = 2; $i < 256; $i += 2) {
626            list($sbox2[$i], $sbox2[$i + 1]) = self::encryptBlockHelperFast($sbox2[$i - 2], $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
627        }
628
629        list($sbox3[0], $sbox3[1]) = self::encryptBlockHelperFast($sbox2[254], $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
630        for ($i = 2; $i < 256; $i += 2) {
631            list($sbox3[$i], $sbox3[$i + 1]) = self::encryptBlockHelperFast($sbox3[$i - 2], $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
632        }
633    }
634
635    /**
636     * Key expansion with salt
637     *
638     * @access private
639     * @param int[] $data
640     * @param int[] $key
641     * @param int[] $sbox0
642     * @param int[] $sbox1
643     * @param int[] $sbox2
644     * @param int[] $sbox3
645     * @param int[] $p
646     * @see self::_bcrypt_hash()
647     */
648    private static function expandstate(array $data, array $key, array &$sbox0, array &$sbox1, array &$sbox2, array &$sbox3, array &$p)
649    {
650        $p = [
651            $p[0] ^ $key[0],
652            $p[1] ^ $key[1],
653            $p[2] ^ $key[2],
654            $p[3] ^ $key[3],
655            $p[4] ^ $key[4],
656            $p[5] ^ $key[5],
657            $p[6] ^ $key[6],
658            $p[7] ^ $key[7],
659            $p[8] ^ $key[8],
660            $p[9] ^ $key[9],
661            $p[10] ^ $key[10],
662            $p[11] ^ $key[11],
663            $p[12] ^ $key[12],
664            $p[13] ^ $key[13],
665            $p[14] ^ $key[14],
666            $p[15] ^ $key[15],
667            $p[16] ^ $key[0],
668            $p[17] ^ $key[1]
669        ];
670
671        // @codingStandardsIgnoreStart
672        list( $p[0],  $p[1]) = self::encryptBlockHelperFast($data[ 0]         , $data[ 1]         , $sbox0, $sbox1, $sbox2, $sbox3, $p);
673        list( $p[2],  $p[3]) = self::encryptBlockHelperFast($data[ 2] ^ $p[ 0], $data[ 3] ^ $p[ 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
674        list( $p[4],  $p[5]) = self::encryptBlockHelperFast($data[ 4] ^ $p[ 2], $data[ 5] ^ $p[ 3], $sbox0, $sbox1, $sbox2, $sbox3, $p);
675        list( $p[6],  $p[7]) = self::encryptBlockHelperFast($data[ 6] ^ $p[ 4], $data[ 7] ^ $p[ 5], $sbox0, $sbox1, $sbox2, $sbox3, $p);
676        list( $p[8],  $p[9]) = self::encryptBlockHelperFast($data[ 8] ^ $p[ 6], $data[ 9] ^ $p[ 7], $sbox0, $sbox1, $sbox2, $sbox3, $p);
677        list($p[10], $p[11]) = self::encryptBlockHelperFast($data[10] ^ $p[ 8], $data[11] ^ $p[ 9], $sbox0, $sbox1, $sbox2, $sbox3, $p);
678        list($p[12], $p[13]) = self::encryptBlockHelperFast($data[12] ^ $p[10], $data[13] ^ $p[11], $sbox0, $sbox1, $sbox2, $sbox3, $p);
679        list($p[14], $p[15]) = self::encryptBlockHelperFast($data[14] ^ $p[12], $data[15] ^ $p[13], $sbox0, $sbox1, $sbox2, $sbox3, $p);
680        list($p[16], $p[17]) = self::encryptBlockHelperFast($data[ 0] ^ $p[14], $data[ 1] ^ $p[15], $sbox0, $sbox1, $sbox2, $sbox3, $p);
681        // @codingStandardsIgnoreEnd
682
683        list($sbox0[0], $sbox0[1]) = self::encryptBlockHelperFast($data[2] ^ $p[16], $data[3] ^ $p[17], $sbox0, $sbox1, $sbox2, $sbox3, $p);
684        for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) { // instead of 16 maybe count($data) would be better?
685            list($sbox0[$i], $sbox0[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox0[$i - 2], $data[$j + 1] ^ $sbox0[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
686        }
687
688        list($sbox1[0], $sbox1[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox0[254], $data[3] ^ $sbox0[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
689        for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) {
690            list($sbox1[$i], $sbox1[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox1[$i - 2], $data[$j + 1] ^ $sbox1[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
691        }
692
693        list($sbox2[0], $sbox2[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox1[254], $data[3] ^ $sbox1[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
694        for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) {
695            list($sbox2[$i], $sbox2[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox2[$i - 2], $data[$j + 1] ^ $sbox2[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
696        }
697
698        list($sbox3[0], $sbox3[1]) = self::encryptBlockHelperFast($data[2] ^ $sbox2[254], $data[3] ^ $sbox2[255], $sbox0, $sbox1, $sbox2, $sbox3, $p);
699        for ($i = 2, $j = 4; $i < 256; $i += 2, $j = ($j + 2) % 16) {
700            list($sbox3[$i], $sbox3[$i + 1]) = self::encryptBlockHelperFast($data[$j] ^ $sbox3[$i - 2], $data[$j + 1] ^ $sbox3[$i - 1], $sbox0, $sbox1, $sbox2, $sbox3, $p);
701        }
702    }
703
704    /**
705     * Encrypts a block
706     *
707     * @param string $in
708     * @return string
709     */
710    protected function encryptBlock($in)
711    {
712        $p = $this->bctx['p'];
713        // extract($this->bctx['sb'], EXTR_PREFIX_ALL, 'sb'); // slower
714        $sb_0 = $this->bctx['sb'][0];
715        $sb_1 = $this->bctx['sb'][1];
716        $sb_2 = $this->bctx['sb'][2];
717        $sb_3 = $this->bctx['sb'][3];
718
719        $in = unpack('N*', $in);
720        $l = $in[1];
721        $r = $in[2];
722
723        list($r, $l) = PHP_INT_SIZE == 4 ?
724            self::encryptBlockHelperSlow($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p) :
725            self::encryptBlockHelperFast($l, $r, $sb_0, $sb_1, $sb_2, $sb_3, $p);
726
727        return pack("N*", $r, $l);
728    }
729
730    /**
731     * Fast helper function for block encryption
732     *
733     * @access private
734     * @param int $x0
735     * @param int $x1
736     * @param int[] $sbox0
737     * @param int[] $sbox1
738     * @param int[] $sbox2
739     * @param int[] $sbox3
740     * @param int[] $p
741     * @return int[]
742     */
743    private static function encryptBlockHelperFast($x0, $x1, array $sbox0, array $sbox1, array $sbox2, array $sbox3, array $p)
744    {
745        $x0 ^= $p[0];
746        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
747        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
748        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
749        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
750        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
751        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
752        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
753        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
754        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
755        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
756        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
757        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
758        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
759        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
760        $x1 ^= ((($sbox0[($x0 & 0xFF000000) >> 24] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
761        $x0 ^= ((($sbox0[($x1 & 0xFF000000) >> 24] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
762
763        return [$x1 & 0xFFFFFFFF ^ $p[17], $x0 & 0xFFFFFFFF];
764    }
765
766    /**
767     * Slow helper function for block encryption
768     *
769     * @access private
770     * @param int $x0
771     * @param int $x1
772     * @param int[] $sbox0
773     * @param int[] $sbox1
774     * @param int[] $sbox2
775     * @param int[] $sbox3
776     * @param int[] $p
777     * @return int[]
778     */
779    private static function encryptBlockHelperSlow($x0, $x1, array $sbox0, array $sbox1, array $sbox2, array $sbox3, array $p)
780    {
781        // -16777216 == intval(0xFF000000) on 32-bit PHP installs
782        $x0 ^= $p[0];
783        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[1];
784        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[2];
785        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[3];
786        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[4];
787        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[5];
788        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[6];
789        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[7];
790        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[8];
791        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[9];
792        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[10];
793        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[11];
794        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[12];
795        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[13];
796        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[14];
797        $x1 ^= self::safe_intval((self::safe_intval($sbox0[(($x0 & -16777216) >> 24) & 0xFF] + $sbox1[($x0 & 0xFF0000) >> 16]) ^ $sbox2[($x0 & 0xFF00) >> 8]) + $sbox3[$x0 & 0xFF]) ^ $p[15];
798        $x0 ^= self::safe_intval((self::safe_intval($sbox0[(($x1 & -16777216) >> 24) & 0xFF] + $sbox1[($x1 & 0xFF0000) >> 16]) ^ $sbox2[($x1 & 0xFF00) >> 8]) + $sbox3[$x1 & 0xFF]) ^ $p[16];
799
800        return [$x1 ^ $p[17], $x0];
801    }
802
803    /**
804     * Decrypts a block
805     *
806     * @param string $in
807     * @return string
808     */
809    protected function decryptBlock($in)
810    {
811        $p = $this->bctx['p'];
812        $sb_0 = $this->bctx['sb'][0];
813        $sb_1 = $this->bctx['sb'][1];
814        $sb_2 = $this->bctx['sb'][2];
815        $sb_3 = $this->bctx['sb'][3];
816
817        $in = unpack('N*', $in);
818        $l = $in[1];
819        $r = $in[2];
820
821        for ($i = 17; $i > 2; $i -= 2) {
822            $l ^= $p[$i];
823            $r ^= self::safe_intval((self::safe_intval($sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]) ^
824                  $sb_2[$l >>  8 & 0xff]) +
825                  $sb_3[$l       & 0xff]);
826
827            $r ^= $p[$i - 1];
828            $l ^= self::safe_intval((self::safe_intval($sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]) ^
829                  $sb_2[$r >>  8 & 0xff]) +
830                  $sb_3[$r       & 0xff]);
831        }
832        return pack('N*', $r ^ $p[0], $l ^ $p[1]);
833    }
834
835    /**
836     * Setup the performance-optimized function for de/encrypt()
837     *
838     * @see \phpseclib3\Crypt\Common\SymmetricKey::_setupInlineCrypt()
839     */
840    protected function setupInlineCrypt()
841    {
842        $p = $this->bctx['p'];
843        $init_crypt = '
844            static $sb_0, $sb_1, $sb_2, $sb_3;
845            if (!$sb_0) {
846                $sb_0 = $this->bctx["sb"][0];
847                $sb_1 = $this->bctx["sb"][1];
848                $sb_2 = $this->bctx["sb"][2];
849                $sb_3 = $this->bctx["sb"][3];
850            }
851        ';
852
853        $safeint = self::safe_intval_inline();
854
855        // Generating encrypt code:
856        $encrypt_block = '
857            $in = unpack("N*", $in);
858            $l = $in[1];
859            $r = $in[2];
860        ';
861        for ($i = 0; $i < 16; $i += 2) {
862            $encrypt_block .= '
863                $l^= ' . $p[$i] . ';
864                $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
865                      $sb_2[$l >>  8 & 0xff]) +
866                      $sb_3[$l       & 0xff]') . ';
867
868                $r^= ' . $p[$i + 1] . ';
869                $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . '  ^
870                      $sb_2[$r >>  8 & 0xff]) +
871                      $sb_3[$r       & 0xff]') . ';
872            ';
873        }
874        $encrypt_block .= '
875            $in = pack("N*",
876                $r ^ ' . $p[17] . ',
877                $l ^ ' . $p[16] . '
878            );
879        ';
880         // Generating decrypt code:
881        $decrypt_block = '
882            $in = unpack("N*", $in);
883            $l = $in[1];
884            $r = $in[2];
885        ';
886
887        for ($i = 17; $i > 2; $i -= 2) {
888            $decrypt_block .= '
889                $l^= ' . $p[$i] . ';
890                $r^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$l >> 24 & 0xff] + $sb_1[$l >> 16 & 0xff]') . ' ^
891                      $sb_2[$l >>  8 & 0xff]) +
892                      $sb_3[$l       & 0xff]') . ';
893
894                $r^= ' . $p[$i - 1] . ';
895                $l^= ' . sprintf($safeint, '(' . sprintf($safeint, '$sb_0[$r >> 24 & 0xff] + $sb_1[$r >> 16 & 0xff]') . ' ^
896                      $sb_2[$r >>  8 & 0xff]) +
897                      $sb_3[$r       & 0xff]') . ';
898            ';
899        }
900
901        $decrypt_block .= '
902            $in = pack("N*",
903                $r ^ ' . $p[0] . ',
904                $l ^ ' . $p[1] . '
905            );
906        ';
907
908        $this->inline_crypt = $this->createInlineCryptFunction(
909            [
910               'init_crypt'    => $init_crypt,
911               'init_encrypt'  => '',
912               'init_decrypt'  => '',
913               'encrypt_block' => $encrypt_block,
914               'decrypt_block' => $decrypt_block
915            ]
916        );
917    }
918}
919