|
1 <?php |
|
2 |
|
3 if (class_exists('ParagonIE_Sodium_Crypto', false)) { |
|
4 return; |
|
5 } |
|
6 |
|
7 /** |
|
8 * Class ParagonIE_Sodium_Crypto |
|
9 * |
|
10 * ATTENTION! |
|
11 * |
|
12 * If you are using this library, you should be using |
|
13 * ParagonIE_Sodium_Compat in your code, not this class. |
|
14 */ |
|
15 abstract class ParagonIE_Sodium_Crypto |
|
16 { |
|
17 const aead_chacha20poly1305_KEYBYTES = 32; |
|
18 const aead_chacha20poly1305_NSECBYTES = 0; |
|
19 const aead_chacha20poly1305_NPUBBYTES = 8; |
|
20 const aead_chacha20poly1305_ABYTES = 16; |
|
21 |
|
22 const aead_chacha20poly1305_IETF_KEYBYTES = 32; |
|
23 const aead_chacha20poly1305_IETF_NSECBYTES = 0; |
|
24 const aead_chacha20poly1305_IETF_NPUBBYTES = 12; |
|
25 const aead_chacha20poly1305_IETF_ABYTES = 16; |
|
26 |
|
27 const aead_xchacha20poly1305_IETF_KEYBYTES = 32; |
|
28 const aead_xchacha20poly1305_IETF_NSECBYTES = 0; |
|
29 const aead_xchacha20poly1305_IETF_NPUBBYTES = 24; |
|
30 const aead_xchacha20poly1305_IETF_ABYTES = 16; |
|
31 |
|
32 const box_curve25519xsalsa20poly1305_SEEDBYTES = 32; |
|
33 const box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32; |
|
34 const box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32; |
|
35 const box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32; |
|
36 const box_curve25519xsalsa20poly1305_NONCEBYTES = 24; |
|
37 const box_curve25519xsalsa20poly1305_MACBYTES = 16; |
|
38 const box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16; |
|
39 const box_curve25519xsalsa20poly1305_ZEROBYTES = 32; |
|
40 |
|
41 const onetimeauth_poly1305_BYTES = 16; |
|
42 const onetimeauth_poly1305_KEYBYTES = 32; |
|
43 |
|
44 const secretbox_xsalsa20poly1305_KEYBYTES = 32; |
|
45 const secretbox_xsalsa20poly1305_NONCEBYTES = 24; |
|
46 const secretbox_xsalsa20poly1305_MACBYTES = 16; |
|
47 const secretbox_xsalsa20poly1305_BOXZEROBYTES = 16; |
|
48 const secretbox_xsalsa20poly1305_ZEROBYTES = 32; |
|
49 |
|
50 const secretbox_xchacha20poly1305_KEYBYTES = 32; |
|
51 const secretbox_xchacha20poly1305_NONCEBYTES = 24; |
|
52 const secretbox_xchacha20poly1305_MACBYTES = 16; |
|
53 const secretbox_xchacha20poly1305_BOXZEROBYTES = 16; |
|
54 const secretbox_xchacha20poly1305_ZEROBYTES = 32; |
|
55 |
|
56 const stream_salsa20_KEYBYTES = 32; |
|
57 |
|
58 /** |
|
59 * AEAD Decryption with ChaCha20-Poly1305 |
|
60 * |
|
61 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
62 * |
|
63 * @param string $message |
|
64 * @param string $ad |
|
65 * @param string $nonce |
|
66 * @param string $key |
|
67 * @return string |
|
68 * @throws SodiumException |
|
69 * @throws TypeError |
|
70 */ |
|
71 public static function aead_chacha20poly1305_decrypt( |
|
72 $message = '', |
|
73 $ad = '', |
|
74 $nonce = '', |
|
75 $key = '' |
|
76 ) { |
|
77 /** @var int $len - Length of message (ciphertext + MAC) */ |
|
78 $len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
79 |
|
80 /** @var int $clen - Length of ciphertext */ |
|
81 $clen = $len - self::aead_chacha20poly1305_ABYTES; |
|
82 |
|
83 /** @var int $adlen - Length of associated data */ |
|
84 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
85 |
|
86 /** @var string $mac - Message authentication code */ |
|
87 $mac = ParagonIE_Sodium_Core_Util::substr( |
|
88 $message, |
|
89 $clen, |
|
90 self::aead_chacha20poly1305_ABYTES |
|
91 ); |
|
92 |
|
93 /** @var string $ciphertext - The encrypted message (sans MAC) */ |
|
94 $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 0, $clen); |
|
95 |
|
96 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
97 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( |
|
98 32, |
|
99 $nonce, |
|
100 $key |
|
101 ); |
|
102 |
|
103 /* Recalculate the Poly1305 authentication tag (MAC): */ |
|
104 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); |
|
105 try { |
|
106 ParagonIE_Sodium_Compat::memzero($block0); |
|
107 } catch (SodiumException $ex) { |
|
108 $block0 = null; |
|
109 } |
|
110 $state->update($ad); |
|
111 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); |
|
112 $state->update($ciphertext); |
|
113 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); |
|
114 $computed_mac = $state->finish(); |
|
115 |
|
116 /* Compare the given MAC with the recalculated MAC: */ |
|
117 if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { |
|
118 throw new SodiumException('Invalid MAC'); |
|
119 } |
|
120 |
|
121 // Here, we know that the MAC is valid, so we decrypt and return the plaintext |
|
122 return ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
123 $ciphertext, |
|
124 $nonce, |
|
125 $key, |
|
126 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
127 ); |
|
128 } |
|
129 |
|
130 /** |
|
131 * AEAD Encryption with ChaCha20-Poly1305 |
|
132 * |
|
133 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
134 * |
|
135 * @param string $message |
|
136 * @param string $ad |
|
137 * @param string $nonce |
|
138 * @param string $key |
|
139 * @return string |
|
140 * @throws SodiumException |
|
141 * @throws TypeError |
|
142 */ |
|
143 public static function aead_chacha20poly1305_encrypt( |
|
144 $message = '', |
|
145 $ad = '', |
|
146 $nonce = '', |
|
147 $key = '' |
|
148 ) { |
|
149 /** @var int $len - Length of the plaintext message */ |
|
150 $len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
151 |
|
152 /** @var int $adlen - Length of the associated data */ |
|
153 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
154 |
|
155 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
156 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( |
|
157 32, |
|
158 $nonce, |
|
159 $key |
|
160 ); |
|
161 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); |
|
162 try { |
|
163 ParagonIE_Sodium_Compat::memzero($block0); |
|
164 } catch (SodiumException $ex) { |
|
165 $block0 = null; |
|
166 } |
|
167 |
|
168 /** @var string $ciphertext - Raw encrypted data */ |
|
169 $ciphertext = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
170 $message, |
|
171 $nonce, |
|
172 $key, |
|
173 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
174 ); |
|
175 |
|
176 $state->update($ad); |
|
177 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); |
|
178 $state->update($ciphertext); |
|
179 $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); |
|
180 return $ciphertext . $state->finish(); |
|
181 } |
|
182 |
|
183 /** |
|
184 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
185 * |
|
186 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
187 * |
|
188 * @param string $message |
|
189 * @param string $ad |
|
190 * @param string $nonce |
|
191 * @param string $key |
|
192 * @return string |
|
193 * @throws SodiumException |
|
194 * @throws TypeError |
|
195 */ |
|
196 public static function aead_chacha20poly1305_ietf_decrypt( |
|
197 $message = '', |
|
198 $ad = '', |
|
199 $nonce = '', |
|
200 $key = '' |
|
201 ) { |
|
202 /** @var int $adlen - Length of associated data */ |
|
203 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
204 |
|
205 /** @var int $len - Length of message (ciphertext + MAC) */ |
|
206 $len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
207 |
|
208 /** @var int $clen - Length of ciphertext */ |
|
209 $clen = $len - self::aead_chacha20poly1305_IETF_ABYTES; |
|
210 |
|
211 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
212 $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( |
|
213 32, |
|
214 $nonce, |
|
215 $key |
|
216 ); |
|
217 |
|
218 /** @var string $mac - Message authentication code */ |
|
219 $mac = ParagonIE_Sodium_Core_Util::substr( |
|
220 $message, |
|
221 $len - self::aead_chacha20poly1305_IETF_ABYTES, |
|
222 self::aead_chacha20poly1305_IETF_ABYTES |
|
223 ); |
|
224 |
|
225 /** @var string $ciphertext - The encrypted message (sans MAC) */ |
|
226 $ciphertext = ParagonIE_Sodium_Core_Util::substr( |
|
227 $message, |
|
228 0, |
|
229 $len - self::aead_chacha20poly1305_IETF_ABYTES |
|
230 ); |
|
231 |
|
232 /* Recalculate the Poly1305 authentication tag (MAC): */ |
|
233 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); |
|
234 try { |
|
235 ParagonIE_Sodium_Compat::memzero($block0); |
|
236 } catch (SodiumException $ex) { |
|
237 $block0 = null; |
|
238 } |
|
239 $state->update($ad); |
|
240 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); |
|
241 $state->update($ciphertext); |
|
242 $state->update(str_repeat("\x00", (0x10 - $clen) & 0xf)); |
|
243 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); |
|
244 $state->update(ParagonIE_Sodium_Core_Util::store64_le($clen)); |
|
245 $computed_mac = $state->finish(); |
|
246 |
|
247 /* Compare the given MAC with the recalculated MAC: */ |
|
248 if (!ParagonIE_Sodium_Core_Util::verify_16($computed_mac, $mac)) { |
|
249 throw new SodiumException('Invalid MAC'); |
|
250 } |
|
251 |
|
252 // Here, we know that the MAC is valid, so we decrypt and return the plaintext |
|
253 return ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
254 $ciphertext, |
|
255 $nonce, |
|
256 $key, |
|
257 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
258 ); |
|
259 } |
|
260 |
|
261 /** |
|
262 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
263 * |
|
264 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
265 * |
|
266 * @param string $message |
|
267 * @param string $ad |
|
268 * @param string $nonce |
|
269 * @param string $key |
|
270 * @return string |
|
271 * @throws SodiumException |
|
272 * @throws TypeError |
|
273 */ |
|
274 public static function aead_chacha20poly1305_ietf_encrypt( |
|
275 $message = '', |
|
276 $ad = '', |
|
277 $nonce = '', |
|
278 $key = '' |
|
279 ) { |
|
280 /** @var int $len - Length of the plaintext message */ |
|
281 $len = ParagonIE_Sodium_Core_Util::strlen($message); |
|
282 |
|
283 /** @var int $adlen - Length of the associated data */ |
|
284 $adlen = ParagonIE_Sodium_Core_Util::strlen($ad); |
|
285 |
|
286 /** @var string The first block of the chacha20 keystream, used as a poly1305 key */ |
|
287 $block0 = ParagonIE_Sodium_Core_ChaCha20::ietfStream( |
|
288 32, |
|
289 $nonce, |
|
290 $key |
|
291 ); |
|
292 $state = new ParagonIE_Sodium_Core_Poly1305_State($block0); |
|
293 try { |
|
294 ParagonIE_Sodium_Compat::memzero($block0); |
|
295 } catch (SodiumException $ex) { |
|
296 $block0 = null; |
|
297 } |
|
298 |
|
299 /** @var string $ciphertext - Raw encrypted data */ |
|
300 $ciphertext = ParagonIE_Sodium_Core_ChaCha20::ietfStreamXorIc( |
|
301 $message, |
|
302 $nonce, |
|
303 $key, |
|
304 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
305 ); |
|
306 |
|
307 $state->update($ad); |
|
308 $state->update(str_repeat("\x00", ((0x10 - $adlen) & 0xf))); |
|
309 $state->update($ciphertext); |
|
310 $state->update(str_repeat("\x00", ((0x10 - $len) & 0xf))); |
|
311 $state->update(ParagonIE_Sodium_Core_Util::store64_le($adlen)); |
|
312 $state->update(ParagonIE_Sodium_Core_Util::store64_le($len)); |
|
313 return $ciphertext . $state->finish(); |
|
314 } |
|
315 |
|
316 /** |
|
317 * AEAD Decryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
318 * |
|
319 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
320 * |
|
321 * @param string $message |
|
322 * @param string $ad |
|
323 * @param string $nonce |
|
324 * @param string $key |
|
325 * @return string |
|
326 * @throws SodiumException |
|
327 * @throws TypeError |
|
328 */ |
|
329 public static function aead_xchacha20poly1305_ietf_decrypt( |
|
330 $message = '', |
|
331 $ad = '', |
|
332 $nonce = '', |
|
333 $key = '' |
|
334 ) { |
|
335 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( |
|
336 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), |
|
337 $key |
|
338 ); |
|
339 $nonceLast = "\x00\x00\x00\x00" . |
|
340 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); |
|
341 |
|
342 return self::aead_chacha20poly1305_ietf_decrypt($message, $ad, $nonceLast, $subkey); |
|
343 } |
|
344 |
|
345 /** |
|
346 * AEAD Encryption with ChaCha20-Poly1305, IETF mode (96-bit nonce) |
|
347 * |
|
348 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
349 * |
|
350 * @param string $message |
|
351 * @param string $ad |
|
352 * @param string $nonce |
|
353 * @param string $key |
|
354 * @return string |
|
355 * @throws SodiumException |
|
356 * @throws TypeError |
|
357 */ |
|
358 public static function aead_xchacha20poly1305_ietf_encrypt( |
|
359 $message = '', |
|
360 $ad = '', |
|
361 $nonce = '', |
|
362 $key = '' |
|
363 ) { |
|
364 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( |
|
365 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), |
|
366 $key |
|
367 ); |
|
368 $nonceLast = "\x00\x00\x00\x00" . |
|
369 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); |
|
370 |
|
371 return self::aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonceLast, $subkey); |
|
372 } |
|
373 |
|
374 /** |
|
375 * HMAC-SHA-512-256 (a.k.a. the leftmost 256 bits of HMAC-SHA-512) |
|
376 * |
|
377 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
378 * |
|
379 * @param string $message |
|
380 * @param string $key |
|
381 * @return string |
|
382 * @throws TypeError |
|
383 */ |
|
384 public static function auth($message, $key) |
|
385 { |
|
386 return ParagonIE_Sodium_Core_Util::substr( |
|
387 hash_hmac('sha512', $message, $key, true), |
|
388 0, |
|
389 32 |
|
390 ); |
|
391 } |
|
392 |
|
393 /** |
|
394 * HMAC-SHA-512-256 validation. Constant-time via hash_equals(). |
|
395 * |
|
396 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
397 * |
|
398 * @param string $mac |
|
399 * @param string $message |
|
400 * @param string $key |
|
401 * @return bool |
|
402 * @throws SodiumException |
|
403 * @throws TypeError |
|
404 */ |
|
405 public static function auth_verify($mac, $message, $key) |
|
406 { |
|
407 return ParagonIE_Sodium_Core_Util::hashEquals( |
|
408 $mac, |
|
409 self::auth($message, $key) |
|
410 ); |
|
411 } |
|
412 |
|
413 /** |
|
414 * X25519 key exchange followed by XSalsa20Poly1305 symmetric encryption |
|
415 * |
|
416 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
417 * |
|
418 * @param string $plaintext |
|
419 * @param string $nonce |
|
420 * @param string $keypair |
|
421 * @return string |
|
422 * @throws SodiumException |
|
423 * @throws TypeError |
|
424 */ |
|
425 public static function box($plaintext, $nonce, $keypair) |
|
426 { |
|
427 $c = self::secretbox( |
|
428 $plaintext, |
|
429 $nonce, |
|
430 self::box_beforenm( |
|
431 self::box_secretkey($keypair), |
|
432 self::box_publickey($keypair) |
|
433 ) |
|
434 ); |
|
435 return $c; |
|
436 } |
|
437 |
|
438 /** |
|
439 * X25519-XSalsa20-Poly1305 with one ephemeral X25519 keypair. |
|
440 * |
|
441 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
442 * |
|
443 * @param string $message |
|
444 * @param string $publicKey |
|
445 * @return string |
|
446 * @throws SodiumException |
|
447 * @throws TypeError |
|
448 */ |
|
449 public static function box_seal($message, $publicKey) |
|
450 { |
|
451 /** @var string $ephemeralKeypair */ |
|
452 $ephemeralKeypair = self::box_keypair(); |
|
453 |
|
454 /** @var string $ephemeralSK */ |
|
455 $ephemeralSK = self::box_secretkey($ephemeralKeypair); |
|
456 |
|
457 /** @var string $ephemeralPK */ |
|
458 $ephemeralPK = self::box_publickey($ephemeralKeypair); |
|
459 |
|
460 /** @var string $nonce */ |
|
461 $nonce = self::generichash( |
|
462 $ephemeralPK . $publicKey, |
|
463 '', |
|
464 24 |
|
465 ); |
|
466 |
|
467 /** @var string $keypair - The combined keypair used in crypto_box() */ |
|
468 $keypair = self::box_keypair_from_secretkey_and_publickey($ephemeralSK, $publicKey); |
|
469 |
|
470 /** @var string $ciphertext Ciphertext + MAC from crypto_box */ |
|
471 $ciphertext = self::box($message, $nonce, $keypair); |
|
472 try { |
|
473 ParagonIE_Sodium_Compat::memzero($ephemeralKeypair); |
|
474 ParagonIE_Sodium_Compat::memzero($ephemeralSK); |
|
475 ParagonIE_Sodium_Compat::memzero($nonce); |
|
476 } catch (SodiumException $ex) { |
|
477 $ephemeralKeypair = null; |
|
478 $ephemeralSK = null; |
|
479 $nonce = null; |
|
480 } |
|
481 return $ephemeralPK . $ciphertext; |
|
482 } |
|
483 |
|
484 /** |
|
485 * Opens a message encrypted via box_seal(). |
|
486 * |
|
487 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
488 * |
|
489 * @param string $message |
|
490 * @param string $keypair |
|
491 * @return string |
|
492 * @throws SodiumException |
|
493 * @throws TypeError |
|
494 */ |
|
495 public static function box_seal_open($message, $keypair) |
|
496 { |
|
497 /** @var string $ephemeralPK */ |
|
498 $ephemeralPK = ParagonIE_Sodium_Core_Util::substr($message, 0, 32); |
|
499 |
|
500 /** @var string $ciphertext (ciphertext + MAC) */ |
|
501 $ciphertext = ParagonIE_Sodium_Core_Util::substr($message, 32); |
|
502 |
|
503 /** @var string $secretKey */ |
|
504 $secretKey = self::box_secretkey($keypair); |
|
505 |
|
506 /** @var string $publicKey */ |
|
507 $publicKey = self::box_publickey($keypair); |
|
508 |
|
509 /** @var string $nonce */ |
|
510 $nonce = self::generichash( |
|
511 $ephemeralPK . $publicKey, |
|
512 '', |
|
513 24 |
|
514 ); |
|
515 |
|
516 /** @var string $keypair */ |
|
517 $keypair = self::box_keypair_from_secretkey_and_publickey($secretKey, $ephemeralPK); |
|
518 |
|
519 /** @var string $m */ |
|
520 $m = self::box_open($ciphertext, $nonce, $keypair); |
|
521 try { |
|
522 ParagonIE_Sodium_Compat::memzero($secretKey); |
|
523 ParagonIE_Sodium_Compat::memzero($ephemeralPK); |
|
524 ParagonIE_Sodium_Compat::memzero($nonce); |
|
525 } catch (SodiumException $ex) { |
|
526 $secretKey = null; |
|
527 $ephemeralPK = null; |
|
528 $nonce = null; |
|
529 } |
|
530 return $m; |
|
531 } |
|
532 |
|
533 /** |
|
534 * Used by crypto_box() to get the crypto_secretbox() key. |
|
535 * |
|
536 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
537 * |
|
538 * @param string $sk |
|
539 * @param string $pk |
|
540 * @return string |
|
541 * @throws SodiumException |
|
542 * @throws TypeError |
|
543 */ |
|
544 public static function box_beforenm($sk, $pk) |
|
545 { |
|
546 return ParagonIE_Sodium_Core_HSalsa20::hsalsa20( |
|
547 str_repeat("\x00", 16), |
|
548 self::scalarmult($sk, $pk) |
|
549 ); |
|
550 } |
|
551 |
|
552 /** |
|
553 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
554 * |
|
555 * @return string |
|
556 * @throws Exception |
|
557 * @throws SodiumException |
|
558 * @throws TypeError |
|
559 */ |
|
560 public static function box_keypair() |
|
561 { |
|
562 $sKey = random_bytes(32); |
|
563 $pKey = self::scalarmult_base($sKey); |
|
564 return $sKey . $pKey; |
|
565 } |
|
566 |
|
567 /** |
|
568 * @param string $seed |
|
569 * @return string |
|
570 * @throws SodiumException |
|
571 * @throws TypeError |
|
572 */ |
|
573 public static function box_seed_keypair($seed) |
|
574 { |
|
575 $sKey = ParagonIE_Sodium_Core_Util::substr( |
|
576 hash('sha512', $seed, true), |
|
577 0, |
|
578 32 |
|
579 ); |
|
580 $pKey = self::scalarmult_base($sKey); |
|
581 return $sKey . $pKey; |
|
582 } |
|
583 |
|
584 /** |
|
585 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
586 * |
|
587 * @param string $sKey |
|
588 * @param string $pKey |
|
589 * @return string |
|
590 * @throws TypeError |
|
591 */ |
|
592 public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey) |
|
593 { |
|
594 return ParagonIE_Sodium_Core_Util::substr($sKey, 0, 32) . |
|
595 ParagonIE_Sodium_Core_Util::substr($pKey, 0, 32); |
|
596 } |
|
597 |
|
598 /** |
|
599 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
600 * |
|
601 * @param string $keypair |
|
602 * @return string |
|
603 * @throws RangeException |
|
604 * @throws TypeError |
|
605 */ |
|
606 public static function box_secretkey($keypair) |
|
607 { |
|
608 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== 64) { |
|
609 throw new RangeException( |
|
610 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' |
|
611 ); |
|
612 } |
|
613 return ParagonIE_Sodium_Core_Util::substr($keypair, 0, 32); |
|
614 } |
|
615 |
|
616 /** |
|
617 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
618 * |
|
619 * @param string $keypair |
|
620 * @return string |
|
621 * @throws RangeException |
|
622 * @throws TypeError |
|
623 */ |
|
624 public static function box_publickey($keypair) |
|
625 { |
|
626 if (ParagonIE_Sodium_Core_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) { |
|
627 throw new RangeException( |
|
628 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.' |
|
629 ); |
|
630 } |
|
631 return ParagonIE_Sodium_Core_Util::substr($keypair, 32, 32); |
|
632 } |
|
633 |
|
634 /** |
|
635 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
636 * |
|
637 * @param string $sKey |
|
638 * @return string |
|
639 * @throws RangeException |
|
640 * @throws SodiumException |
|
641 * @throws TypeError |
|
642 */ |
|
643 public static function box_publickey_from_secretkey($sKey) |
|
644 { |
|
645 if (ParagonIE_Sodium_Core_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) { |
|
646 throw new RangeException( |
|
647 'Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.' |
|
648 ); |
|
649 } |
|
650 return self::scalarmult_base($sKey); |
|
651 } |
|
652 |
|
653 /** |
|
654 * Decrypt a message encrypted with box(). |
|
655 * |
|
656 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
657 * |
|
658 * @param string $ciphertext |
|
659 * @param string $nonce |
|
660 * @param string $keypair |
|
661 * @return string |
|
662 * @throws SodiumException |
|
663 * @throws TypeError |
|
664 */ |
|
665 public static function box_open($ciphertext, $nonce, $keypair) |
|
666 { |
|
667 return self::secretbox_open( |
|
668 $ciphertext, |
|
669 $nonce, |
|
670 self::box_beforenm( |
|
671 self::box_secretkey($keypair), |
|
672 self::box_publickey($keypair) |
|
673 ) |
|
674 ); |
|
675 } |
|
676 |
|
677 /** |
|
678 * Calculate a BLAKE2b hash. |
|
679 * |
|
680 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
681 * |
|
682 * @param string $message |
|
683 * @param string|null $key |
|
684 * @param int $outlen |
|
685 * @return string |
|
686 * @throws RangeException |
|
687 * @throws SodiumException |
|
688 * @throws TypeError |
|
689 */ |
|
690 public static function generichash($message, $key = '', $outlen = 32) |
|
691 { |
|
692 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized |
|
693 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); |
|
694 |
|
695 $k = null; |
|
696 if (!empty($key)) { |
|
697 /** @var SplFixedArray $k */ |
|
698 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); |
|
699 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { |
|
700 throw new RangeException('Invalid key size'); |
|
701 } |
|
702 } |
|
703 |
|
704 /** @var SplFixedArray $in */ |
|
705 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); |
|
706 |
|
707 /** @var SplFixedArray $ctx */ |
|
708 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outlen); |
|
709 ParagonIE_Sodium_Core_BLAKE2b::update($ctx, $in, $in->count()); |
|
710 |
|
711 /** @var SplFixedArray $out */ |
|
712 $out = new SplFixedArray($outlen); |
|
713 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($ctx, $out); |
|
714 |
|
715 /** @var array<int, int> */ |
|
716 $outArray = $out->toArray(); |
|
717 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); |
|
718 } |
|
719 |
|
720 /** |
|
721 * Finalize a BLAKE2b hashing context, returning the hash. |
|
722 * |
|
723 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
724 * |
|
725 * @param string $ctx |
|
726 * @param int $outlen |
|
727 * @return string |
|
728 * @throws SodiumException |
|
729 * @throws TypeError |
|
730 */ |
|
731 public static function generichash_final($ctx, $outlen = 32) |
|
732 { |
|
733 if (!is_string($ctx)) { |
|
734 throw new TypeError('Context must be a string'); |
|
735 } |
|
736 $out = new SplFixedArray($outlen); |
|
737 |
|
738 /** @var SplFixedArray $context */ |
|
739 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); |
|
740 |
|
741 /** @var SplFixedArray $out */ |
|
742 $out = ParagonIE_Sodium_Core_BLAKE2b::finish($context, $out); |
|
743 |
|
744 /** @var array<int, int> */ |
|
745 $outArray = $out->toArray(); |
|
746 return ParagonIE_Sodium_Core_Util::intArrayToString($outArray); |
|
747 } |
|
748 |
|
749 /** |
|
750 * Initialize a hashing context for BLAKE2b. |
|
751 * |
|
752 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
753 * |
|
754 * @param string $key |
|
755 * @param int $outputLength |
|
756 * @return string |
|
757 * @throws RangeException |
|
758 * @throws SodiumException |
|
759 * @throws TypeError |
|
760 */ |
|
761 public static function generichash_init($key = '', $outputLength = 32) |
|
762 { |
|
763 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized |
|
764 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); |
|
765 |
|
766 $k = null; |
|
767 if (!empty($key)) { |
|
768 $k = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($key); |
|
769 if ($k->count() > ParagonIE_Sodium_Core_BLAKE2b::KEYBYTES) { |
|
770 throw new RangeException('Invalid key size'); |
|
771 } |
|
772 } |
|
773 |
|
774 /** @var SplFixedArray $ctx */ |
|
775 $ctx = ParagonIE_Sodium_Core_BLAKE2b::init($k, $outputLength); |
|
776 |
|
777 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($ctx); |
|
778 } |
|
779 |
|
780 /** |
|
781 * Update a hashing context for BLAKE2b with $message |
|
782 * |
|
783 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
784 * |
|
785 * @param string $ctx |
|
786 * @param string $message |
|
787 * @return string |
|
788 * @throws SodiumException |
|
789 * @throws TypeError |
|
790 */ |
|
791 public static function generichash_update($ctx, $message) |
|
792 { |
|
793 // This ensures that ParagonIE_Sodium_Core_BLAKE2b::$iv is initialized |
|
794 ParagonIE_Sodium_Core_BLAKE2b::pseudoConstructor(); |
|
795 |
|
796 /** @var SplFixedArray $context */ |
|
797 $context = ParagonIE_Sodium_Core_BLAKE2b::stringToContext($ctx); |
|
798 |
|
799 /** @var SplFixedArray $in */ |
|
800 $in = ParagonIE_Sodium_Core_BLAKE2b::stringToSplFixedArray($message); |
|
801 |
|
802 ParagonIE_Sodium_Core_BLAKE2b::update($context, $in, $in->count()); |
|
803 |
|
804 return ParagonIE_Sodium_Core_BLAKE2b::contextToString($context); |
|
805 } |
|
806 |
|
807 /** |
|
808 * Libsodium's crypto_kx(). |
|
809 * |
|
810 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
811 * |
|
812 * @param string $my_sk |
|
813 * @param string $their_pk |
|
814 * @param string $client_pk |
|
815 * @param string $server_pk |
|
816 * @return string |
|
817 * @throws SodiumException |
|
818 * @throws TypeError |
|
819 */ |
|
820 public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk) |
|
821 { |
|
822 return ParagonIE_Sodium_Compat::crypto_generichash( |
|
823 ParagonIE_Sodium_Compat::crypto_scalarmult($my_sk, $their_pk) . |
|
824 $client_pk . |
|
825 $server_pk |
|
826 ); |
|
827 } |
|
828 |
|
829 /** |
|
830 * ECDH over Curve25519 |
|
831 * |
|
832 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
833 * |
|
834 * @param string $sKey |
|
835 * @param string $pKey |
|
836 * @return string |
|
837 * |
|
838 * @throws SodiumException |
|
839 * @throws TypeError |
|
840 */ |
|
841 public static function scalarmult($sKey, $pKey) |
|
842 { |
|
843 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey); |
|
844 self::scalarmult_throw_if_zero($q); |
|
845 return $q; |
|
846 } |
|
847 |
|
848 /** |
|
849 * ECDH over Curve25519, using the basepoint. |
|
850 * Used to get a secret key from a public key. |
|
851 * |
|
852 * @param string $secret |
|
853 * @return string |
|
854 * |
|
855 * @throws SodiumException |
|
856 * @throws TypeError |
|
857 */ |
|
858 public static function scalarmult_base($secret) |
|
859 { |
|
860 $q = ParagonIE_Sodium_Core_X25519::crypto_scalarmult_curve25519_ref10_base($secret); |
|
861 self::scalarmult_throw_if_zero($q); |
|
862 return $q; |
|
863 } |
|
864 |
|
865 /** |
|
866 * This throws an Error if a zero public key was passed to the function. |
|
867 * |
|
868 * @param string $q |
|
869 * @return void |
|
870 * @throws SodiumException |
|
871 * @throws TypeError |
|
872 */ |
|
873 protected static function scalarmult_throw_if_zero($q) |
|
874 { |
|
875 $d = 0; |
|
876 for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) { |
|
877 $d |= ParagonIE_Sodium_Core_Util::chrToInt($q[$i]); |
|
878 } |
|
879 |
|
880 /* branch-free variant of === 0 */ |
|
881 if (-(1 & (($d - 1) >> 8))) { |
|
882 throw new SodiumException('Zero public key is not allowed'); |
|
883 } |
|
884 } |
|
885 |
|
886 /** |
|
887 * XSalsa20-Poly1305 authenticated symmetric-key encryption. |
|
888 * |
|
889 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
890 * |
|
891 * @param string $plaintext |
|
892 * @param string $nonce |
|
893 * @param string $key |
|
894 * @return string |
|
895 * @throws SodiumException |
|
896 * @throws TypeError |
|
897 */ |
|
898 public static function secretbox($plaintext, $nonce, $key) |
|
899 { |
|
900 /** @var string $subkey */ |
|
901 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); |
|
902 |
|
903 /** @var string $block0 */ |
|
904 $block0 = str_repeat("\x00", 32); |
|
905 |
|
906 /** @var int $mlen - Length of the plaintext message */ |
|
907 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); |
|
908 $mlen0 = $mlen; |
|
909 if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) { |
|
910 $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES; |
|
911 } |
|
912 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); |
|
913 |
|
914 /** @var string $block0 */ |
|
915 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20_xor( |
|
916 $block0, |
|
917 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
918 $subkey |
|
919 ); |
|
920 |
|
921 /** @var string $c */ |
|
922 $c = ParagonIE_Sodium_Core_Util::substr( |
|
923 $block0, |
|
924 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
925 ); |
|
926 if ($mlen > $mlen0) { |
|
927 $c .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( |
|
928 ParagonIE_Sodium_Core_Util::substr( |
|
929 $plaintext, |
|
930 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
931 ), |
|
932 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
933 1, |
|
934 $subkey |
|
935 ); |
|
936 } |
|
937 $state = new ParagonIE_Sodium_Core_Poly1305_State( |
|
938 ParagonIE_Sodium_Core_Util::substr( |
|
939 $block0, |
|
940 0, |
|
941 self::onetimeauth_poly1305_KEYBYTES |
|
942 ) |
|
943 ); |
|
944 try { |
|
945 ParagonIE_Sodium_Compat::memzero($block0); |
|
946 ParagonIE_Sodium_Compat::memzero($subkey); |
|
947 } catch (SodiumException $ex) { |
|
948 $block0 = null; |
|
949 $subkey = null; |
|
950 } |
|
951 |
|
952 $state->update($c); |
|
953 |
|
954 /** @var string $c - MAC || ciphertext */ |
|
955 $c = $state->finish() . $c; |
|
956 unset($state); |
|
957 |
|
958 return $c; |
|
959 } |
|
960 |
|
961 /** |
|
962 * Decrypt a ciphertext generated via secretbox(). |
|
963 * |
|
964 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
965 * |
|
966 * @param string $ciphertext |
|
967 * @param string $nonce |
|
968 * @param string $key |
|
969 * @return string |
|
970 * @throws SodiumException |
|
971 * @throws TypeError |
|
972 */ |
|
973 public static function secretbox_open($ciphertext, $nonce, $key) |
|
974 { |
|
975 /** @var string $mac */ |
|
976 $mac = ParagonIE_Sodium_Core_Util::substr( |
|
977 $ciphertext, |
|
978 0, |
|
979 self::secretbox_xsalsa20poly1305_MACBYTES |
|
980 ); |
|
981 |
|
982 /** @var string $c */ |
|
983 $c = ParagonIE_Sodium_Core_Util::substr( |
|
984 $ciphertext, |
|
985 self::secretbox_xsalsa20poly1305_MACBYTES |
|
986 ); |
|
987 |
|
988 /** @var int $clen */ |
|
989 $clen = ParagonIE_Sodium_Core_Util::strlen($c); |
|
990 |
|
991 /** @var string $subkey */ |
|
992 $subkey = ParagonIE_Sodium_Core_HSalsa20::hsalsa20($nonce, $key); |
|
993 |
|
994 /** @var string $block0 */ |
|
995 $block0 = ParagonIE_Sodium_Core_Salsa20::salsa20( |
|
996 64, |
|
997 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
998 $subkey |
|
999 ); |
|
1000 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( |
|
1001 $mac, |
|
1002 $c, |
|
1003 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) |
|
1004 ); |
|
1005 if (!$verified) { |
|
1006 try { |
|
1007 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1008 } catch (SodiumException $ex) { |
|
1009 $subkey = null; |
|
1010 } |
|
1011 throw new SodiumException('Invalid MAC'); |
|
1012 } |
|
1013 |
|
1014 /** @var string $m - Decrypted message */ |
|
1015 $m = ParagonIE_Sodium_Core_Util::xorStrings( |
|
1016 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES), |
|
1017 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES) |
|
1018 ); |
|
1019 if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) { |
|
1020 // We had more than 1 block, so let's continue to decrypt the rest. |
|
1021 $m .= ParagonIE_Sodium_Core_Salsa20::salsa20_xor_ic( |
|
1022 ParagonIE_Sodium_Core_Util::substr( |
|
1023 $c, |
|
1024 self::secretbox_xsalsa20poly1305_ZEROBYTES |
|
1025 ), |
|
1026 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
1027 1, |
|
1028 (string) $subkey |
|
1029 ); |
|
1030 } |
|
1031 return $m; |
|
1032 } |
|
1033 |
|
1034 /** |
|
1035 * XChaCha20-Poly1305 authenticated symmetric-key encryption. |
|
1036 * |
|
1037 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1038 * |
|
1039 * @param string $plaintext |
|
1040 * @param string $nonce |
|
1041 * @param string $key |
|
1042 * @return string |
|
1043 * @throws SodiumException |
|
1044 * @throws TypeError |
|
1045 */ |
|
1046 public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key) |
|
1047 { |
|
1048 /** @var string $subkey */ |
|
1049 $subkey = ParagonIE_Sodium_Core_HChaCha20::hChaCha20( |
|
1050 ParagonIE_Sodium_Core_Util::substr($nonce, 0, 16), |
|
1051 $key |
|
1052 ); |
|
1053 $nonceLast = ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8); |
|
1054 |
|
1055 /** @var string $block0 */ |
|
1056 $block0 = str_repeat("\x00", 32); |
|
1057 |
|
1058 /** @var int $mlen - Length of the plaintext message */ |
|
1059 $mlen = ParagonIE_Sodium_Core_Util::strlen($plaintext); |
|
1060 $mlen0 = $mlen; |
|
1061 if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) { |
|
1062 $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES; |
|
1063 } |
|
1064 $block0 .= ParagonIE_Sodium_Core_Util::substr($plaintext, 0, $mlen0); |
|
1065 |
|
1066 /** @var string $block0 */ |
|
1067 $block0 = ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
1068 $block0, |
|
1069 $nonceLast, |
|
1070 $subkey |
|
1071 ); |
|
1072 |
|
1073 /** @var string $c */ |
|
1074 $c = ParagonIE_Sodium_Core_Util::substr( |
|
1075 $block0, |
|
1076 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1077 ); |
|
1078 if ($mlen > $mlen0) { |
|
1079 $c .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
1080 ParagonIE_Sodium_Core_Util::substr( |
|
1081 $plaintext, |
|
1082 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1083 ), |
|
1084 $nonceLast, |
|
1085 $subkey, |
|
1086 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
1087 ); |
|
1088 } |
|
1089 $state = new ParagonIE_Sodium_Core_Poly1305_State( |
|
1090 ParagonIE_Sodium_Core_Util::substr( |
|
1091 $block0, |
|
1092 0, |
|
1093 self::onetimeauth_poly1305_KEYBYTES |
|
1094 ) |
|
1095 ); |
|
1096 try { |
|
1097 ParagonIE_Sodium_Compat::memzero($block0); |
|
1098 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1099 } catch (SodiumException $ex) { |
|
1100 $block0 = null; |
|
1101 $subkey = null; |
|
1102 } |
|
1103 |
|
1104 $state->update($c); |
|
1105 |
|
1106 /** @var string $c - MAC || ciphertext */ |
|
1107 $c = $state->finish() . $c; |
|
1108 unset($state); |
|
1109 |
|
1110 return $c; |
|
1111 } |
|
1112 |
|
1113 /** |
|
1114 * Decrypt a ciphertext generated via secretbox_xchacha20poly1305(). |
|
1115 * |
|
1116 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1117 * |
|
1118 * @param string $ciphertext |
|
1119 * @param string $nonce |
|
1120 * @param string $key |
|
1121 * @return string |
|
1122 * @throws SodiumException |
|
1123 * @throws TypeError |
|
1124 */ |
|
1125 public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key) |
|
1126 { |
|
1127 /** @var string $mac */ |
|
1128 $mac = ParagonIE_Sodium_Core_Util::substr( |
|
1129 $ciphertext, |
|
1130 0, |
|
1131 self::secretbox_xchacha20poly1305_MACBYTES |
|
1132 ); |
|
1133 |
|
1134 /** @var string $c */ |
|
1135 $c = ParagonIE_Sodium_Core_Util::substr( |
|
1136 $ciphertext, |
|
1137 self::secretbox_xchacha20poly1305_MACBYTES |
|
1138 ); |
|
1139 |
|
1140 /** @var int $clen */ |
|
1141 $clen = ParagonIE_Sodium_Core_Util::strlen($c); |
|
1142 |
|
1143 /** @var string $subkey */ |
|
1144 $subkey = ParagonIE_Sodium_Core_HChaCha20::hchacha20($nonce, $key); |
|
1145 |
|
1146 /** @var string $block0 */ |
|
1147 $block0 = ParagonIE_Sodium_Core_ChaCha20::stream( |
|
1148 64, |
|
1149 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
1150 $subkey |
|
1151 ); |
|
1152 $verified = ParagonIE_Sodium_Core_Poly1305::onetimeauth_verify( |
|
1153 $mac, |
|
1154 $c, |
|
1155 ParagonIE_Sodium_Core_Util::substr($block0, 0, 32) |
|
1156 ); |
|
1157 |
|
1158 if (!$verified) { |
|
1159 try { |
|
1160 ParagonIE_Sodium_Compat::memzero($subkey); |
|
1161 } catch (SodiumException $ex) { |
|
1162 $subkey = null; |
|
1163 } |
|
1164 throw new SodiumException('Invalid MAC'); |
|
1165 } |
|
1166 |
|
1167 /** @var string $m - Decrypted message */ |
|
1168 $m = ParagonIE_Sodium_Core_Util::xorStrings( |
|
1169 ParagonIE_Sodium_Core_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES), |
|
1170 ParagonIE_Sodium_Core_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES) |
|
1171 ); |
|
1172 |
|
1173 if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) { |
|
1174 // We had more than 1 block, so let's continue to decrypt the rest. |
|
1175 $m .= ParagonIE_Sodium_Core_ChaCha20::streamXorIc( |
|
1176 ParagonIE_Sodium_Core_Util::substr( |
|
1177 $c, |
|
1178 self::secretbox_xchacha20poly1305_ZEROBYTES |
|
1179 ), |
|
1180 ParagonIE_Sodium_Core_Util::substr($nonce, 16, 8), |
|
1181 (string) $subkey, |
|
1182 ParagonIE_Sodium_Core_Util::store64_le(1) |
|
1183 ); |
|
1184 } |
|
1185 return $m; |
|
1186 } |
|
1187 |
|
1188 /** |
|
1189 * Detached Ed25519 signature. |
|
1190 * |
|
1191 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1192 * |
|
1193 * @param string $message |
|
1194 * @param string $sk |
|
1195 * @return string |
|
1196 * @throws SodiumException |
|
1197 * @throws TypeError |
|
1198 */ |
|
1199 public static function sign_detached($message, $sk) |
|
1200 { |
|
1201 return ParagonIE_Sodium_Core_Ed25519::sign_detached($message, $sk); |
|
1202 } |
|
1203 |
|
1204 /** |
|
1205 * Attached Ed25519 signature. (Returns a signed message.) |
|
1206 * |
|
1207 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1208 * |
|
1209 * @param string $message |
|
1210 * @param string $sk |
|
1211 * @return string |
|
1212 * @throws SodiumException |
|
1213 * @throws TypeError |
|
1214 */ |
|
1215 public static function sign($message, $sk) |
|
1216 { |
|
1217 return ParagonIE_Sodium_Core_Ed25519::sign($message, $sk); |
|
1218 } |
|
1219 |
|
1220 /** |
|
1221 * Opens a signed message. If valid, returns the message. |
|
1222 * |
|
1223 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1224 * |
|
1225 * @param string $signedMessage |
|
1226 * @param string $pk |
|
1227 * @return string |
|
1228 * @throws SodiumException |
|
1229 * @throws TypeError |
|
1230 */ |
|
1231 public static function sign_open($signedMessage, $pk) |
|
1232 { |
|
1233 return ParagonIE_Sodium_Core_Ed25519::sign_open($signedMessage, $pk); |
|
1234 } |
|
1235 |
|
1236 /** |
|
1237 * Verify a detached signature of a given message and public key. |
|
1238 * |
|
1239 * @internal Do not use this directly. Use ParagonIE_Sodium_Compat. |
|
1240 * |
|
1241 * @param string $signature |
|
1242 * @param string $message |
|
1243 * @param string $pk |
|
1244 * @return bool |
|
1245 * @throws SodiumException |
|
1246 * @throws TypeError |
|
1247 */ |
|
1248 public static function sign_verify_detached($signature, $message, $pk) |
|
1249 { |
|
1250 return ParagonIE_Sodium_Core_Ed25519::verify_detached($signature, $message, $pk); |
|
1251 } |
|
1252 } |