|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Core_AES', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Bitsliced implementation of the AES block cipher. |
|
9 * |
|
10 * Based on the implementation provided by BearSSL. |
|
11 * |
|
12 * @internal This should only be used by sodium_compat |
|
13 */ |
|
14 class ParagonIE_Sodium_Core_AES extends ParagonIE_Sodium_Core_Util |
|
15 { |
|
16 /** |
|
17 * @var int[] AES round constants |
|
18 */ |
|
19 private static $Rcon = array( |
|
20 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 |
|
21 ); |
|
22 |
|
23 /** |
|
24 * Mutates the values of $q! |
|
25 * |
|
26 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
27 * @return void |
|
28 */ |
|
29 public static function sbox(ParagonIE_Sodium_Core_AES_Block $q) |
|
30 { |
|
31 /** |
|
32 * @var int $x0 |
|
33 * @var int $x1 |
|
34 * @var int $x2 |
|
35 * @var int $x3 |
|
36 * @var int $x4 |
|
37 * @var int $x5 |
|
38 * @var int $x6 |
|
39 * @var int $x7 |
|
40 */ |
|
41 $x0 = $q[7] & self::U32_MAX; |
|
42 $x1 = $q[6] & self::U32_MAX; |
|
43 $x2 = $q[5] & self::U32_MAX; |
|
44 $x3 = $q[4] & self::U32_MAX; |
|
45 $x4 = $q[3] & self::U32_MAX; |
|
46 $x5 = $q[2] & self::U32_MAX; |
|
47 $x6 = $q[1] & self::U32_MAX; |
|
48 $x7 = $q[0] & self::U32_MAX; |
|
49 |
|
50 $y14 = $x3 ^ $x5; |
|
51 $y13 = $x0 ^ $x6; |
|
52 $y9 = $x0 ^ $x3; |
|
53 $y8 = $x0 ^ $x5; |
|
54 $t0 = $x1 ^ $x2; |
|
55 $y1 = $t0 ^ $x7; |
|
56 $y4 = $y1 ^ $x3; |
|
57 $y12 = $y13 ^ $y14; |
|
58 $y2 = $y1 ^ $x0; |
|
59 $y5 = $y1 ^ $x6; |
|
60 $y3 = $y5 ^ $y8; |
|
61 $t1 = $x4 ^ $y12; |
|
62 $y15 = $t1 ^ $x5; |
|
63 $y20 = $t1 ^ $x1; |
|
64 $y6 = $y15 ^ $x7; |
|
65 $y10 = $y15 ^ $t0; |
|
66 $y11 = $y20 ^ $y9; |
|
67 $y7 = $x7 ^ $y11; |
|
68 $y17 = $y10 ^ $y11; |
|
69 $y19 = $y10 ^ $y8; |
|
70 $y16 = $t0 ^ $y11; |
|
71 $y21 = $y13 ^ $y16; |
|
72 $y18 = $x0 ^ $y16; |
|
73 |
|
74 /* |
|
75 * Non-linear section. |
|
76 */ |
|
77 $t2 = $y12 & $y15; |
|
78 $t3 = $y3 & $y6; |
|
79 $t4 = $t3 ^ $t2; |
|
80 $t5 = $y4 & $x7; |
|
81 $t6 = $t5 ^ $t2; |
|
82 $t7 = $y13 & $y16; |
|
83 $t8 = $y5 & $y1; |
|
84 $t9 = $t8 ^ $t7; |
|
85 $t10 = $y2 & $y7; |
|
86 $t11 = $t10 ^ $t7; |
|
87 $t12 = $y9 & $y11; |
|
88 $t13 = $y14 & $y17; |
|
89 $t14 = $t13 ^ $t12; |
|
90 $t15 = $y8 & $y10; |
|
91 $t16 = $t15 ^ $t12; |
|
92 $t17 = $t4 ^ $t14; |
|
93 $t18 = $t6 ^ $t16; |
|
94 $t19 = $t9 ^ $t14; |
|
95 $t20 = $t11 ^ $t16; |
|
96 $t21 = $t17 ^ $y20; |
|
97 $t22 = $t18 ^ $y19; |
|
98 $t23 = $t19 ^ $y21; |
|
99 $t24 = $t20 ^ $y18; |
|
100 |
|
101 $t25 = $t21 ^ $t22; |
|
102 $t26 = $t21 & $t23; |
|
103 $t27 = $t24 ^ $t26; |
|
104 $t28 = $t25 & $t27; |
|
105 $t29 = $t28 ^ $t22; |
|
106 $t30 = $t23 ^ $t24; |
|
107 $t31 = $t22 ^ $t26; |
|
108 $t32 = $t31 & $t30; |
|
109 $t33 = $t32 ^ $t24; |
|
110 $t34 = $t23 ^ $t33; |
|
111 $t35 = $t27 ^ $t33; |
|
112 $t36 = $t24 & $t35; |
|
113 $t37 = $t36 ^ $t34; |
|
114 $t38 = $t27 ^ $t36; |
|
115 $t39 = $t29 & $t38; |
|
116 $t40 = $t25 ^ $t39; |
|
117 |
|
118 $t41 = $t40 ^ $t37; |
|
119 $t42 = $t29 ^ $t33; |
|
120 $t43 = $t29 ^ $t40; |
|
121 $t44 = $t33 ^ $t37; |
|
122 $t45 = $t42 ^ $t41; |
|
123 $z0 = $t44 & $y15; |
|
124 $z1 = $t37 & $y6; |
|
125 $z2 = $t33 & $x7; |
|
126 $z3 = $t43 & $y16; |
|
127 $z4 = $t40 & $y1; |
|
128 $z5 = $t29 & $y7; |
|
129 $z6 = $t42 & $y11; |
|
130 $z7 = $t45 & $y17; |
|
131 $z8 = $t41 & $y10; |
|
132 $z9 = $t44 & $y12; |
|
133 $z10 = $t37 & $y3; |
|
134 $z11 = $t33 & $y4; |
|
135 $z12 = $t43 & $y13; |
|
136 $z13 = $t40 & $y5; |
|
137 $z14 = $t29 & $y2; |
|
138 $z15 = $t42 & $y9; |
|
139 $z16 = $t45 & $y14; |
|
140 $z17 = $t41 & $y8; |
|
141 |
|
142 /* |
|
143 * Bottom linear transformation. |
|
144 */ |
|
145 $t46 = $z15 ^ $z16; |
|
146 $t47 = $z10 ^ $z11; |
|
147 $t48 = $z5 ^ $z13; |
|
148 $t49 = $z9 ^ $z10; |
|
149 $t50 = $z2 ^ $z12; |
|
150 $t51 = $z2 ^ $z5; |
|
151 $t52 = $z7 ^ $z8; |
|
152 $t53 = $z0 ^ $z3; |
|
153 $t54 = $z6 ^ $z7; |
|
154 $t55 = $z16 ^ $z17; |
|
155 $t56 = $z12 ^ $t48; |
|
156 $t57 = $t50 ^ $t53; |
|
157 $t58 = $z4 ^ $t46; |
|
158 $t59 = $z3 ^ $t54; |
|
159 $t60 = $t46 ^ $t57; |
|
160 $t61 = $z14 ^ $t57; |
|
161 $t62 = $t52 ^ $t58; |
|
162 $t63 = $t49 ^ $t58; |
|
163 $t64 = $z4 ^ $t59; |
|
164 $t65 = $t61 ^ $t62; |
|
165 $t66 = $z1 ^ $t63; |
|
166 $s0 = $t59 ^ $t63; |
|
167 $s6 = $t56 ^ ~$t62; |
|
168 $s7 = $t48 ^ ~$t60; |
|
169 $t67 = $t64 ^ $t65; |
|
170 $s3 = $t53 ^ $t66; |
|
171 $s4 = $t51 ^ $t66; |
|
172 $s5 = $t47 ^ $t65; |
|
173 $s1 = $t64 ^ ~$s3; |
|
174 $s2 = $t55 ^ ~$t67; |
|
175 |
|
176 $q[7] = $s0 & self::U32_MAX; |
|
177 $q[6] = $s1 & self::U32_MAX; |
|
178 $q[5] = $s2 & self::U32_MAX; |
|
179 $q[4] = $s3 & self::U32_MAX; |
|
180 $q[3] = $s4 & self::U32_MAX; |
|
181 $q[2] = $s5 & self::U32_MAX; |
|
182 $q[1] = $s6 & self::U32_MAX; |
|
183 $q[0] = $s7 & self::U32_MAX; |
|
184 } |
|
185 |
|
186 /** |
|
187 * Mutates the values of $q! |
|
188 * |
|
189 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
190 * @return void |
|
191 */ |
|
192 public static function invSbox(ParagonIE_Sodium_Core_AES_Block $q) |
|
193 { |
|
194 self::processInversion($q); |
|
195 self::sbox($q); |
|
196 self::processInversion($q); |
|
197 } |
|
198 |
|
199 /** |
|
200 * This is some boilerplate code needed to invert an S-box. Rather than repeat the code |
|
201 * twice, I moved it to a protected method. |
|
202 * |
|
203 * Mutates $q |
|
204 * |
|
205 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
206 * @return void |
|
207 */ |
|
208 protected static function processInversion(ParagonIE_Sodium_Core_AES_Block $q) |
|
209 { |
|
210 $q0 = (~$q[0]) & self::U32_MAX; |
|
211 $q1 = (~$q[1]) & self::U32_MAX; |
|
212 $q2 = $q[2] & self::U32_MAX; |
|
213 $q3 = $q[3] & self::U32_MAX; |
|
214 $q4 = $q[4] & self::U32_MAX; |
|
215 $q5 = (~$q[5]) & self::U32_MAX; |
|
216 $q6 = (~$q[6]) & self::U32_MAX; |
|
217 $q7 = $q[7] & self::U32_MAX; |
|
218 $q[7] = ($q1 ^ $q4 ^ $q6) & self::U32_MAX; |
|
219 $q[6] = ($q0 ^ $q3 ^ $q5) & self::U32_MAX; |
|
220 $q[5] = ($q7 ^ $q2 ^ $q4) & self::U32_MAX; |
|
221 $q[4] = ($q6 ^ $q1 ^ $q3) & self::U32_MAX; |
|
222 $q[3] = ($q5 ^ $q0 ^ $q2) & self::U32_MAX; |
|
223 $q[2] = ($q4 ^ $q7 ^ $q1) & self::U32_MAX; |
|
224 $q[1] = ($q3 ^ $q6 ^ $q0) & self::U32_MAX; |
|
225 $q[0] = ($q2 ^ $q5 ^ $q7) & self::U32_MAX; |
|
226 } |
|
227 |
|
228 /** |
|
229 * @param int $x |
|
230 * @return int |
|
231 */ |
|
232 public static function subWord($x) |
|
233 { |
|
234 $q = ParagonIE_Sodium_Core_AES_Block::fromArray( |
|
235 array($x, $x, $x, $x, $x, $x, $x, $x) |
|
236 ); |
|
237 $q->orthogonalize(); |
|
238 self::sbox($q); |
|
239 $q->orthogonalize(); |
|
240 return $q[0] & self::U32_MAX; |
|
241 } |
|
242 |
|
243 /** |
|
244 * Calculate the key schedule from a given random key |
|
245 * |
|
246 * @param string $key |
|
247 * @return ParagonIE_Sodium_Core_AES_KeySchedule |
|
248 * @throws SodiumException |
|
249 */ |
|
250 public static function keySchedule($key) |
|
251 { |
|
252 $key_len = self::strlen($key); |
|
253 switch ($key_len) { |
|
254 case 16: |
|
255 $num_rounds = 10; |
|
256 break; |
|
257 case 24: |
|
258 $num_rounds = 12; |
|
259 break; |
|
260 case 32: |
|
261 $num_rounds = 14; |
|
262 break; |
|
263 default: |
|
264 throw new SodiumException('Invalid key length: ' . $key_len); |
|
265 } |
|
266 $skey = array(); |
|
267 $comp_skey = array(); |
|
268 $nk = $key_len >> 2; |
|
269 $nkf = ($num_rounds + 1) << 2; |
|
270 $tmp = 0; |
|
271 |
|
272 for ($i = 0; $i < $nk; ++$i) { |
|
273 $tmp = self::load_4(self::substr($key, $i << 2, 4)); |
|
274 $skey[($i << 1)] = $tmp; |
|
275 $skey[($i << 1) + 1] = $tmp; |
|
276 } |
|
277 |
|
278 for ($i = $nk, $j = 0, $k = 0; $i < $nkf; ++$i) { |
|
279 if ($j === 0) { |
|
280 $tmp = (($tmp & 0xff) << 24) | ($tmp >> 8); |
|
281 $tmp = (self::subWord($tmp) ^ self::$Rcon[$k]) & self::U32_MAX; |
|
282 } elseif ($nk > 6 && $j === 4) { |
|
283 $tmp = self::subWord($tmp); |
|
284 } |
|
285 $tmp ^= $skey[($i - $nk) << 1]; |
|
286 $skey[($i << 1)] = $tmp & self::U32_MAX; |
|
287 $skey[($i << 1) + 1] = $tmp & self::U32_MAX; |
|
288 if (++$j === $nk) { |
|
289 /** @psalm-suppress LoopInvalidation */ |
|
290 $j = 0; |
|
291 ++$k; |
|
292 } |
|
293 } |
|
294 for ($i = 0; $i < $nkf; $i += 4) { |
|
295 $q = ParagonIE_Sodium_Core_AES_Block::fromArray( |
|
296 array_slice($skey, $i << 1, 8) |
|
297 ); |
|
298 $q->orthogonalize(); |
|
299 // We have to overwrite $skey since we're not using C pointers like BearSSL did |
|
300 for ($j = 0; $j < 8; ++$j) { |
|
301 $skey[($i << 1) + $j] = $q[$j]; |
|
302 } |
|
303 } |
|
304 for ($i = 0, $j = 0; $i < $nkf; ++$i, $j += 2) { |
|
305 $comp_skey[$i] = ($skey[$j] & 0x55555555) |
|
306 | ($skey[$j + 1] & 0xAAAAAAAA); |
|
307 } |
|
308 return new ParagonIE_Sodium_Core_AES_KeySchedule($comp_skey, $num_rounds); |
|
309 } |
|
310 |
|
311 /** |
|
312 * Mutates $q |
|
313 * |
|
314 * @param ParagonIE_Sodium_Core_AES_KeySchedule $skey |
|
315 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
316 * @param int $offset |
|
317 * @return void |
|
318 */ |
|
319 public static function addRoundKey( |
|
320 ParagonIE_Sodium_Core_AES_Block $q, |
|
321 ParagonIE_Sodium_Core_AES_KeySchedule $skey, |
|
322 $offset = 0 |
|
323 ) { |
|
324 $block = $skey->getRoundKey($offset); |
|
325 for ($j = 0; $j < 8; ++$j) { |
|
326 $q[$j] = ($q[$j] ^ $block[$j]) & ParagonIE_Sodium_Core_Util::U32_MAX; |
|
327 } |
|
328 } |
|
329 |
|
330 /** |
|
331 * This mainly exists for testing, as we need the round key features for AEGIS. |
|
332 * |
|
333 * @param string $message |
|
334 * @param string $key |
|
335 * @return string |
|
336 * @throws SodiumException |
|
337 */ |
|
338 public static function decryptBlockECB($message, $key) |
|
339 { |
|
340 if (self::strlen($message) !== 16) { |
|
341 throw new SodiumException('decryptBlockECB() expects a 16 byte message'); |
|
342 } |
|
343 $skey = self::keySchedule($key)->expand(); |
|
344 $q = ParagonIE_Sodium_Core_AES_Block::init(); |
|
345 $q[0] = self::load_4(self::substr($message, 0, 4)); |
|
346 $q[2] = self::load_4(self::substr($message, 4, 4)); |
|
347 $q[4] = self::load_4(self::substr($message, 8, 4)); |
|
348 $q[6] = self::load_4(self::substr($message, 12, 4)); |
|
349 |
|
350 $q->orthogonalize(); |
|
351 self::bitsliceDecryptBlock($skey, $q); |
|
352 $q->orthogonalize(); |
|
353 |
|
354 return self::store32_le($q[0]) . |
|
355 self::store32_le($q[2]) . |
|
356 self::store32_le($q[4]) . |
|
357 self::store32_le($q[6]); |
|
358 } |
|
359 |
|
360 /** |
|
361 * This mainly exists for testing, as we need the round key features for AEGIS. |
|
362 * |
|
363 * @param string $message |
|
364 * @param string $key |
|
365 * @return string |
|
366 * @throws SodiumException |
|
367 */ |
|
368 public static function encryptBlockECB($message, $key) |
|
369 { |
|
370 if (self::strlen($message) !== 16) { |
|
371 throw new SodiumException('encryptBlockECB() expects a 16 byte message'); |
|
372 } |
|
373 $comp_skey = self::keySchedule($key); |
|
374 $skey = $comp_skey->expand(); |
|
375 $q = ParagonIE_Sodium_Core_AES_Block::init(); |
|
376 $q[0] = self::load_4(self::substr($message, 0, 4)); |
|
377 $q[2] = self::load_4(self::substr($message, 4, 4)); |
|
378 $q[4] = self::load_4(self::substr($message, 8, 4)); |
|
379 $q[6] = self::load_4(self::substr($message, 12, 4)); |
|
380 |
|
381 $q->orthogonalize(); |
|
382 self::bitsliceEncryptBlock($skey, $q); |
|
383 $q->orthogonalize(); |
|
384 |
|
385 return self::store32_le($q[0]) . |
|
386 self::store32_le($q[2]) . |
|
387 self::store32_le($q[4]) . |
|
388 self::store32_le($q[6]); |
|
389 } |
|
390 |
|
391 /** |
|
392 * Mutates $q |
|
393 * |
|
394 * @param ParagonIE_Sodium_Core_AES_Expanded $skey |
|
395 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
396 * @return void |
|
397 */ |
|
398 public static function bitsliceEncryptBlock( |
|
399 ParagonIE_Sodium_Core_AES_Expanded $skey, |
|
400 ParagonIE_Sodium_Core_AES_Block $q |
|
401 ) { |
|
402 self::addRoundKey($q, $skey); |
|
403 for ($u = 1; $u < $skey->getNumRounds(); ++$u) { |
|
404 self::sbox($q); |
|
405 $q->shiftRows(); |
|
406 $q->mixColumns(); |
|
407 self::addRoundKey($q, $skey, ($u << 3)); |
|
408 } |
|
409 self::sbox($q); |
|
410 $q->shiftRows(); |
|
411 self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3)); |
|
412 } |
|
413 |
|
414 /** |
|
415 * @param string $x |
|
416 * @param string $y |
|
417 * @return string |
|
418 */ |
|
419 public static function aesRound($x, $y) |
|
420 { |
|
421 $q = ParagonIE_Sodium_Core_AES_Block::init(); |
|
422 $q[0] = self::load_4(self::substr($x, 0, 4)); |
|
423 $q[2] = self::load_4(self::substr($x, 4, 4)); |
|
424 $q[4] = self::load_4(self::substr($x, 8, 4)); |
|
425 $q[6] = self::load_4(self::substr($x, 12, 4)); |
|
426 |
|
427 $rk = ParagonIE_Sodium_Core_AES_Block::init(); |
|
428 $rk[0] = $rk[1] = self::load_4(self::substr($y, 0, 4)); |
|
429 $rk[2] = $rk[3] = self::load_4(self::substr($y, 4, 4)); |
|
430 $rk[4] = $rk[5] = self::load_4(self::substr($y, 8, 4)); |
|
431 $rk[6] = $rk[7] = self::load_4(self::substr($y, 12, 4)); |
|
432 |
|
433 $q->orthogonalize(); |
|
434 self::sbox($q); |
|
435 $q->shiftRows(); |
|
436 $q->mixColumns(); |
|
437 $q->orthogonalize(); |
|
438 // add round key without key schedule: |
|
439 for ($i = 0; $i < 8; ++$i) { |
|
440 $q[$i] ^= $rk[$i]; |
|
441 } |
|
442 return self::store32_le($q[0]) . |
|
443 self::store32_le($q[2]) . |
|
444 self::store32_le($q[4]) . |
|
445 self::store32_le($q[6]); |
|
446 } |
|
447 |
|
448 /** |
|
449 * Process two AES blocks in one shot. |
|
450 * |
|
451 * @param string $b0 First AES block |
|
452 * @param string $rk0 First round key |
|
453 * @param string $b1 Second AES block |
|
454 * @param string $rk1 Second round key |
|
455 * @return string[] |
|
456 */ |
|
457 public static function doubleRound($b0, $rk0, $b1, $rk1) |
|
458 { |
|
459 $q = ParagonIE_Sodium_Core_AES_Block::init(); |
|
460 // First block |
|
461 $q[0] = self::load_4(self::substr($b0, 0, 4)); |
|
462 $q[2] = self::load_4(self::substr($b0, 4, 4)); |
|
463 $q[4] = self::load_4(self::substr($b0, 8, 4)); |
|
464 $q[6] = self::load_4(self::substr($b0, 12, 4)); |
|
465 // Second block |
|
466 $q[1] = self::load_4(self::substr($b1, 0, 4)); |
|
467 $q[3] = self::load_4(self::substr($b1, 4, 4)); |
|
468 $q[5] = self::load_4(self::substr($b1, 8, 4)); |
|
469 $q[7] = self::load_4(self::substr($b1, 12, 4));; |
|
470 |
|
471 $rk = ParagonIE_Sodium_Core_AES_Block::init(); |
|
472 // First round key |
|
473 $rk[0] = self::load_4(self::substr($rk0, 0, 4)); |
|
474 $rk[2] = self::load_4(self::substr($rk0, 4, 4)); |
|
475 $rk[4] = self::load_4(self::substr($rk0, 8, 4)); |
|
476 $rk[6] = self::load_4(self::substr($rk0, 12, 4)); |
|
477 // Second round key |
|
478 $rk[1] = self::load_4(self::substr($rk1, 0, 4)); |
|
479 $rk[3] = self::load_4(self::substr($rk1, 4, 4)); |
|
480 $rk[5] = self::load_4(self::substr($rk1, 8, 4)); |
|
481 $rk[7] = self::load_4(self::substr($rk1, 12, 4)); |
|
482 |
|
483 $q->orthogonalize(); |
|
484 self::sbox($q); |
|
485 $q->shiftRows(); |
|
486 $q->mixColumns(); |
|
487 $q->orthogonalize(); |
|
488 // add round key without key schedule: |
|
489 for ($i = 0; $i < 8; ++$i) { |
|
490 $q[$i] ^= $rk[$i]; |
|
491 } |
|
492 return array( |
|
493 self::store32_le($q[0]) . self::store32_le($q[2]) . self::store32_le($q[4]) . self::store32_le($q[6]), |
|
494 self::store32_le($q[1]) . self::store32_le($q[3]) . self::store32_le($q[5]) . self::store32_le($q[7]), |
|
495 ); |
|
496 } |
|
497 |
|
498 /** |
|
499 * @param ParagonIE_Sodium_Core_AES_Expanded $skey |
|
500 * @param ParagonIE_Sodium_Core_AES_Block $q |
|
501 * @return void |
|
502 */ |
|
503 public static function bitsliceDecryptBlock( |
|
504 ParagonIE_Sodium_Core_AES_Expanded $skey, |
|
505 ParagonIE_Sodium_Core_AES_Block $q |
|
506 ) { |
|
507 self::addRoundKey($q, $skey, ($skey->getNumRounds() << 3)); |
|
508 for ($u = $skey->getNumRounds() - 1; $u > 0; --$u) { |
|
509 $q->inverseShiftRows(); |
|
510 self::invSbox($q); |
|
511 self::addRoundKey($q, $skey, ($u << 3)); |
|
512 $q->inverseMixColumns(); |
|
513 } |
|
514 $q->inverseShiftRows(); |
|
515 self::invSbox($q); |
|
516 self::addRoundKey($q, $skey, ($u << 3)); |
|
517 } |
|
518 } |