Test Failed
Push — develop ( 266ee2...ce70fb )
by J.D.
05:51
created

ParagonIE_Sodium_Crypto   D

Complexity

Total Complexity 65

Size/Duplication

Total Lines 1175
Duplicated Lines 55.74 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 655
loc 1175
rs 4.8421
c 0
b 0
f 0
wmc 65
lcom 1
cbo 11

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ParagonIE_Sodium_Crypto often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ParagonIE_Sodium_Crypto, and based on these observations, apply Extract Interface, too.

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