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

ParagonIE_Sodium_Crypto32   D

Complexity

Total Complexity 64

Size/Duplication

Total Lines 1160
Duplicated Lines 57.16 %

Coupling/Cohesion

Components 1
Dependencies 11

Importance

Changes 0
Metric Value
dl 663
loc 1160
rs 4.8327
c 0
b 0
f 0
wmc 64
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_Crypto32 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_Crypto32, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
if (class_exists('ParagonIE_Sodium_Crypto32', 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_Crypto32
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_Core32_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_Core32_Util::strlen($ad);
84
85
        /** @var string $mac - Message authentication code */
86
        $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_ChaCha20::stream(
97
            32,
98
            $nonce,
99
            $key
100
        );
101
102
        /* Recalculate the Poly1305 authentication tag (MAC): */
103
        $state = new ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
111
        $state->update($ciphertext);
112
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
113
        $computed_mac = $state->finish();
114
115
        /* Compare the given MAC with the recalculated MAC: */
116
        if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
122
            $ciphertext,
123
            $nonce,
124
            $key,
125
            ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
148
149
        /** @var int $adlen - Length of the associated data */
150
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
151
152
        /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
153
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
154
            32,
155
            $nonce,
156
            $key
157
        );
158
        $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::streamXorIc(
167
            $message,
168
            $nonce,
169
            $key,
170
            ParagonIE_Sodium_Core32_Util::store64_le(1)
171
        );
172
173
        $state->update($ad);
174
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($adlen));
175
        $state->update($ciphertext);
176
        $state->update(ParagonIE_Sodium_Core32_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_Core32_Util::strlen($ad);
200
201
        /** @var int $len - Length of message (ciphertext + MAC) */
202
        $len = ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStream(
209
            32,
210
            $nonce,
211
            $key
212
        );
213
214
        /** @var string $mac - Message authentication code */
215
        $mac = ParagonIE_Sodium_Core32_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_Core32_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_Core32_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_Core32_Util::store64_le($adlen));
240
        $state->update(ParagonIE_Sodium_Core32_Util::store64_le($clen));
241
        $computed_mac = $state->finish();
242
243
        /* Compare the given MAC with the recalculated MAC: */
244
        if (!ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
250
            $ciphertext,
251
            $nonce,
252
            $key,
253
            ParagonIE_Sodium_Core32_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_Core32_Util::strlen($message);
276
277
        /** @var int $adlen - Length of the associated data */
278
        $adlen = ParagonIE_Sodium_Core32_Util::strlen($ad);
279
280
        /** @var string The first block of the chacha20 keystream, used as a poly1305 key */
281
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::ietfStream(
282
            32,
283
            $nonce,
284
            $key
285
        );
286
        $state = new ParagonIE_Sodium_Core32_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_Core32_ChaCha20::ietfStreamXorIc(
295
            $message,
296
            $nonce,
297
            $key,
298
            ParagonIE_Sodium_Core32_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_Core32_Util::store64_le($adlen));
306
        $state->update(ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
329
            ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
330
            $key
331
        );
332
        $nonceLast = "\x00\x00\x00\x00" .
333
            ParagonIE_Sodium_Core32_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_Core32_HChaCha20::hChaCha20(
356
            ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
357
            $key
358
        );
359
        $nonceLast = "\x00\x00\x00\x00" .
360
            ParagonIE_Sodium_Core32_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_Core32_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_Core32_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_Core32_Util::substr($message, 0, 32);
481
482
        /** @var string $ciphertext (ciphertext + MAC) */
483
        $ciphertext = ParagonIE_Sodium_Core32_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_Core32_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
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
546
     *
547
     * @param string $sKey
548
     * @param string $pKey
549
     * @return string
550
     */
551
    public static function box_keypair_from_secretkey_and_publickey($sKey, $pKey)
552
    {
553
        return ParagonIE_Sodium_Core32_Util::substr($sKey, 0, 32) .
554
            ParagonIE_Sodium_Core32_Util::substr($pKey, 0, 32);
555
    }
556
557
    /**
558
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
559
     *
560
     * @param string $keypair
561
     * @return string
562
     * @throws RangeException
563
     */
564
    public static function box_secretkey($keypair)
565
    {
566
        if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== 64) {
567
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
568
        }
569
        return ParagonIE_Sodium_Core32_Util::substr($keypair, 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_publickey($keypair)
580
    {
581
        if (ParagonIE_Sodium_Core32_Util::strlen($keypair) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES) {
582
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_KEYPAIRBYTES bytes long.');
583
        }
584
        return ParagonIE_Sodium_Core32_Util::substr($keypair, 32, 32);
585
    }
586
587
    /**
588
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
589
     *
590
     * @param string $sKey
591
     * @return string
592
     * @throws RangeException
593
     */
594
    public static function box_publickey_from_secretkey($sKey)
595
    {
596
        if (ParagonIE_Sodium_Core32_Util::strlen($sKey) !== ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES) {
597
            throw new RangeException('Must be ParagonIE_Sodium_Compat::CRYPTO_BOX_SECRETKEYBYTES bytes long.');
598
        }
599
        return self::scalarmult_base($sKey);
600
    }
601
602
    /**
603
     * Decrypt a message encrypted with box().
604
     *
605
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
606
     *
607
     * @param string $ciphertext
608
     * @param string $nonce
609
     * @param string $nonce
610
     * @param string $keypair
611
     * @return string
612
     */
613
    public static function box_open($ciphertext, $nonce, $keypair)
614
    {
615
        return self::secretbox_open(
616
            $ciphertext,
617
            $nonce,
618
            self::box_beforenm(
619
                self::box_secretkey($keypair),
620
                self::box_publickey($keypair)
621
            )
622
        );
623
    }
624
625
    /**
626
     * Calculate a BLAKE2b hash.
627
     *
628
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
629
     *
630
     * @param string $message
631
     * @param string|null $key
632
     * @param int $outlen
633
     * @return string
634
     * @throws RangeException
635
     */
636
    public static function generichash($message, $key = '', $outlen = 32)
637
    {
638
        // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
639
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
640
641
        $k = null;
642
        if (!empty($key)) {
643
            /** @var SplFixedArray $k */
644
            $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
645
            if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
646
                throw new RangeException('Invalid key size');
647
            }
648
        }
649
650
        /** @var SplFixedArray $in */
651
        $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
652
653
        /** @var SplFixedArray $ctx */
654
        $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outlen);
655
        ParagonIE_Sodium_Core32_BLAKE2b::update($ctx, $in, $in->count());
656
657
        /** @var SplFixedArray $out */
658
        $out = new SplFixedArray($outlen);
659
        $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($ctx, $out);
660
661
        /** @var array<int, int> */
662
        $outArray = $out->toArray();
663
        return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
664
    }
665
666
    /**
667
     * Finalize a BLAKE2b hashing context, returning the hash.
668
     *
669
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
670
     *
671
     * @param string $ctx
672
     * @param int $outlen
673
     * @return string
674
     * @throws TypeError
675
     */
676
    public static function generichash_final($ctx, $outlen = 32)
677
    {
678
        if (!is_string($ctx)) {
679
            throw new TypeError('Context must be a string');
680
        }
681
        $out = new SplFixedArray($outlen);
682
683
        /** @var SplFixedArray $context */
684
        $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
685
686
        /** @var SplFixedArray $out */
687
        $out = ParagonIE_Sodium_Core32_BLAKE2b::finish($context, $out);
688
689
        /** @var array<int, int> */
690
        $outArray = $out->toArray();
691
        return ParagonIE_Sodium_Core32_Util::intArrayToString($outArray);
692
    }
693
694
    /**
695
     * Initialize a hashing context for BLAKE2b.
696
     *
697
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
698
     *
699
     * @param string $key
700
     * @param int $outputLength
701
     * @return string
702
     * @throws RangeException
703
     */
704
    public static function generichash_init($key = '', $outputLength = 32)
705
    {
706
        // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
707
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
708
709
        $k = null;
710
        if (!empty($key)) {
711
            $k = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($key);
712
            if ($k->count() > ParagonIE_Sodium_Core32_BLAKE2b::KEYBYTES) {
713
                throw new RangeException('Invalid key size');
714
            }
715
        }
716
717
        /** @var SplFixedArray $ctx */
718
        $ctx = ParagonIE_Sodium_Core32_BLAKE2b::init($k, $outputLength);
719
720
        return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($ctx);
721
    }
722
723
    /**
724
     * Update a hashing context for BLAKE2b with $message
725
     *
726
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
727
     *
728
     * @param string $ctx
729
     * @param string $message
730
     * @return string
731
     */
732
    public static function generichash_update($ctx, $message)
733
    {
734
        // This ensures that ParagonIE_Sodium_Core32_BLAKE2b::$iv is initialized
735
        ParagonIE_Sodium_Core32_BLAKE2b::pseudoConstructor();
736
737
        /** @var SplFixedArray $context */
738
        $context = ParagonIE_Sodium_Core32_BLAKE2b::stringToContext($ctx);
739
740
        /** @var SplFixedArray $in */
741
        $in = ParagonIE_Sodium_Core32_BLAKE2b::stringToSplFixedArray($message);
742
743
        ParagonIE_Sodium_Core32_BLAKE2b::update($context, $in, $in->count());
744
745
        return ParagonIE_Sodium_Core32_BLAKE2b::contextToString($context);
746
    }
747
748
    /**
749
     * Libsodium's crypto_kx().
750
     *
751
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
752
     *
753
     * @param string $my_sk
754
     * @param string $their_pk
755
     * @param string $client_pk
756
     * @param string $server_pk
757
     * @return string
758
     */
759
    public static function keyExchange($my_sk, $their_pk, $client_pk, $server_pk)
760
    {
761
        return self::generichash(
762
            self::scalarmult($my_sk, $their_pk) .
763
            $client_pk .
764
            $server_pk
765
        );
766
    }
767
768
    /**
769
     * ECDH over Curve25519
770
     *
771
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
772
     *
773
     * @param string $sKey
774
     * @param string $pKey
775
     * @return string
776
     *
777
     * @throws Error
778
     */
779
    public static function scalarmult($sKey, $pKey)
780
    {
781
        $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10($sKey, $pKey);
782
        self::scalarmult_throw_if_zero($q);
783
        return $q;
784
    }
785
786
    /**
787
     * ECDH over Curve25519, using the basepoint.
788
     * Used to get a secret key from a public key.
789
     *
790
     * @param string $secret
791
     * @return string
792
     *
793
     * @throws Error
794
     */
795
    public static function scalarmult_base($secret)
796
    {
797
        $q = ParagonIE_Sodium_Core32_X25519::crypto_scalarmult_curve25519_ref10_base($secret);
798
        self::scalarmult_throw_if_zero($q);
799
        return $q;
800
    }
801
802
    /**
803
     * This throws an Error if a zero public key was passed to the function.
804
     *
805
     * @param string $q
806
     * @return void
807
     * @throws Error
808
     */
809
    protected static function scalarmult_throw_if_zero($q)
810
    {
811
        $d = 0;
812
        for ($i = 0; $i < self::box_curve25519xsalsa20poly1305_SECRETKEYBYTES; ++$i) {
813
            $d |= ParagonIE_Sodium_Core32_Util::chrToInt($q[$i]);
814
        }
815
816
        /* branch-free variant of === 0 */
817
        if (-(1 & (($d - 1) >> 8))) {
818
            throw new Error('Zero public key is not allowed');
819
        }
820
    }
821
822
    /**
823
     * XSalsa20-Poly1305 authenticated symmetric-key encryption.
824
     *
825
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
826
     *
827
     * @param string $plaintext
828
     * @param string $nonce
829
     * @param string $key
830
     * @return string
831
     */
832
    public static function secretbox($plaintext, $nonce, $key)
833
    {
834
        /** @var string $subkey */
835
        $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
836
837
        /** @var string $block0 */
838
        $block0 = str_repeat("\x00", 32);
839
840
        /** @var int $mlen - Length of the plaintext message */
841
        $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
842
        $mlen0 = $mlen;
843
        if ($mlen0 > 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES) {
844
            $mlen0 = 64 - self::secretbox_xsalsa20poly1305_ZEROBYTES;
845
        }
846
        $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
847
848
        /** @var string $block0 */
849
        $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20_xor(
850
            $block0,
851
            ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
852
            $subkey
853
        );
854
855
        /** @var string $c */
856
        $c = ParagonIE_Sodium_Core32_Util::substr(
857
            $block0,
858
            self::secretbox_xsalsa20poly1305_ZEROBYTES
859
        );
860
        if ($mlen > $mlen0) {
861
            $c .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
862
                ParagonIE_Sodium_Core32_Util::substr(
863
                    $plaintext,
864
                    self::secretbox_xsalsa20poly1305_ZEROBYTES
865
                ),
866
                ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
867
                1,
868
                $subkey
869
            );
870
        }
871
        $state = new ParagonIE_Sodium_Core32_Poly1305_State(
872
            ParagonIE_Sodium_Core32_Util::substr(
873
                $block0,
874
                0,
875
                self::onetimeauth_poly1305_KEYBYTES
876
            )
877
        );
878
        try {
879
            ParagonIE_Sodium_Compat::memzero($block0);
880
            ParagonIE_Sodium_Compat::memzero($subkey);
881
        } catch (Error $ex) {
882
            $block0 = null;
883
            $subkey = null;
884
        }
885
886
        $state->update($c);
887
888
        /** @var string $c - MAC || ciphertext */
889
        $c = $state->finish() . $c;
890
        unset($state);
891
892
        return $c;
893
    }
894
895
    /**
896
     * Decrypt a ciphertext generated via secretbox().
897
     *
898
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
899
     *
900
     * @param string $ciphertext
901
     * @param string $nonce
902
     * @param string $key
903
     * @return string
904
     * @throws Error
905
     */
906
    public static function secretbox_open($ciphertext, $nonce, $key)
907
    {
908
        /** @var string $mac */
909
        $mac = ParagonIE_Sodium_Core32_Util::substr(
910
            $ciphertext,
911
            0,
912
            self::secretbox_xsalsa20poly1305_MACBYTES
913
        );
914
915
        /** @var string $c */
916
        $c = ParagonIE_Sodium_Core32_Util::substr(
917
            $ciphertext,
918
            self::secretbox_xsalsa20poly1305_MACBYTES
919
        );
920
921
        /** @var int $clen */
922
        $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
923
924
        /** @var string $subkey */
925
        $subkey = ParagonIE_Sodium_Core32_HSalsa20::hsalsa20($nonce, $key);
926
927
        /** @var string $block0 */
928
        $block0 = ParagonIE_Sodium_Core32_Salsa20::salsa20(
929
            64,
930
            ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
931
            $subkey
932
        );
933
        $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
934
            $mac,
935
            $c,
936
            ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
937
        );
938
        if (!$verified) {
939
            try {
940
                ParagonIE_Sodium_Compat::memzero($subkey);
941
            } catch (Error $ex) {
942
                $subkey = null;
943
            }
944
            throw new Error('Invalid MAC');
945
        }
946
947
        /** @var string $m - Decrypted message */
948
        $m = ParagonIE_Sodium_Core32_Util::xorStrings(
949
            ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xsalsa20poly1305_ZEROBYTES),
950
            ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xsalsa20poly1305_ZEROBYTES)
951
        );
952
        if ($clen > self::secretbox_xsalsa20poly1305_ZEROBYTES) {
953
            // We had more than 1 block, so let's continue to decrypt the rest.
954
            $m .= ParagonIE_Sodium_Core32_Salsa20::salsa20_xor_ic(
955
                ParagonIE_Sodium_Core32_Util::substr(
956
                    $c,
957
                    self::secretbox_xsalsa20poly1305_ZEROBYTES
958
                ),
959
                ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
960
                1,
961
                $subkey
962
            );
963
        }
964
        return $m;
965
    }
966
967
    /**
968
     * XChaCha20-Poly1305 authenticated symmetric-key encryption.
969
     *
970
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
971
     *
972
     * @param string $plaintext
973
     * @param string $nonce
974
     * @param string $key
975
     * @return string
976
     */
977
    public static function secretbox_xchacha20poly1305($plaintext, $nonce, $key)
978
    {
979
        /** @var string $subkey */
980
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hChaCha20(
981
            ParagonIE_Sodium_Core32_Util::substr($nonce, 0, 16),
982
            $key
983
        );
984
        $nonceLast = ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8);
985
986
        /** @var string $block0 */
987
        $block0 = str_repeat("\x00", 32);
988
989
        /** @var int $mlen - Length of the plaintext message */
990
        $mlen = ParagonIE_Sodium_Core32_Util::strlen($plaintext);
991
        $mlen0 = $mlen;
992
        if ($mlen0 > 64 - self::secretbox_xchacha20poly1305_ZEROBYTES) {
993
            $mlen0 = 64 - self::secretbox_xchacha20poly1305_ZEROBYTES;
994
        }
995
        $block0 .= ParagonIE_Sodium_Core32_Util::substr($plaintext, 0, $mlen0);
996
997
        /** @var string $block0 */
998
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
999
            $block0,
1000
            $nonceLast,
1001
            $subkey
1002
        );
1003
1004
        /** @var string $c */
1005
        $c = ParagonIE_Sodium_Core32_Util::substr(
1006
            $block0,
1007
            self::secretbox_xchacha20poly1305_ZEROBYTES
1008
        );
1009
        if ($mlen > $mlen0) {
1010
            $c .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1011
                ParagonIE_Sodium_Core32_Util::substr(
1012
                    $plaintext,
1013
                    self::secretbox_xchacha20poly1305_ZEROBYTES
1014
                ),
1015
                $nonceLast,
1016
                $subkey,
1017
                ParagonIE_Sodium_Core32_Util::store64_le(1)
1018
            );
1019
        }
1020
        $state = new ParagonIE_Sodium_Core32_Poly1305_State(
1021
            ParagonIE_Sodium_Core32_Util::substr(
1022
                $block0,
1023
                0,
1024
                self::onetimeauth_poly1305_KEYBYTES
1025
            )
1026
        );
1027
        try {
1028
            ParagonIE_Sodium_Compat::memzero($block0);
1029
            ParagonIE_Sodium_Compat::memzero($subkey);
1030
        } catch (Error $ex) {
1031
            $block0 = null;
1032
            $subkey = null;
1033
        }
1034
1035
        $state->update($c);
1036
1037
        /** @var string $c - MAC || ciphertext */
1038
        $c = $state->finish() . $c;
1039
        unset($state);
1040
1041
        return $c;
1042
    }
1043
1044
    /**
1045
     * Decrypt a ciphertext generated via secretbox_xchacha20poly1305().
1046
     *
1047
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1048
     *
1049
     * @param string $ciphertext
1050
     * @param string $nonce
1051
     * @param string $key
1052
     * @return string
1053
     * @throws Error
1054
     */
1055
    public static function secretbox_xchacha20poly1305_open($ciphertext, $nonce, $key)
1056
    {
1057
        /** @var string $mac */
1058
        $mac = ParagonIE_Sodium_Core32_Util::substr(
1059
            $ciphertext,
1060
            0,
1061
            self::secretbox_xchacha20poly1305_MACBYTES
1062
        );
1063
1064
        /** @var string $c */
1065
        $c = ParagonIE_Sodium_Core32_Util::substr(
1066
            $ciphertext,
1067
            self::secretbox_xchacha20poly1305_MACBYTES
1068
        );
1069
1070
        /** @var int $clen */
1071
        $clen = ParagonIE_Sodium_Core32_Util::strlen($c);
1072
1073
        /** @var string $subkey */
1074
        $subkey = ParagonIE_Sodium_Core32_HChaCha20::hchacha20($nonce, $key);
1075
1076
        /** @var string $block0 */
1077
        $block0 = ParagonIE_Sodium_Core32_ChaCha20::stream(
1078
            64,
1079
            ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1080
            $subkey
1081
        );
1082
        $verified = ParagonIE_Sodium_Core32_Poly1305::onetimeauth_verify(
1083
            $mac,
1084
            $c,
1085
            ParagonIE_Sodium_Core32_Util::substr($block0, 0, 32)
1086
        );
1087
1088
        if (!$verified) {
1089
            try {
1090
                ParagonIE_Sodium_Compat::memzero($subkey);
1091
            } catch (Error $ex) {
1092
                $subkey = null;
1093
            }
1094
            throw new Error('Invalid MAC');
1095
        }
1096
1097
        /** @var string $m - Decrypted message */
1098
        $m = ParagonIE_Sodium_Core32_Util::xorStrings(
1099
            ParagonIE_Sodium_Core32_Util::substr($block0, self::secretbox_xchacha20poly1305_ZEROBYTES),
1100
            ParagonIE_Sodium_Core32_Util::substr($c, 0, self::secretbox_xchacha20poly1305_ZEROBYTES)
1101
        );
1102
1103
        if ($clen > self::secretbox_xchacha20poly1305_ZEROBYTES) {
1104
            // We had more than 1 block, so let's continue to decrypt the rest.
1105
            $m .= ParagonIE_Sodium_Core32_ChaCha20::streamXorIc(
1106
                ParagonIE_Sodium_Core32_Util::substr(
1107
                    $c,
1108
                    self::secretbox_xchacha20poly1305_ZEROBYTES
1109
                ),
1110
                ParagonIE_Sodium_Core32_Util::substr($nonce, 16, 8),
1111
                $subkey,
1112
                ParagonIE_Sodium_Core32_Util::store64_le(1)
1113
            );
1114
        }
1115
        return $m;
1116
    }
1117
1118
    /**
1119
     * Detached Ed25519 signature.
1120
     *
1121
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1122
     *
1123
     * @param string $message
1124
     * @param string $sk
1125
     * @return string
1126
     */
1127
    public static function sign_detached($message, $sk)
1128
    {
1129
        return ParagonIE_Sodium_Core32_Ed25519::sign_detached($message, $sk);
1130
    }
1131
1132
    /**
1133
     * Attached Ed25519 signature. (Returns a signed message.)
1134
     *
1135
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1136
     *
1137
     * @param string $message
1138
     * @param string $sk
1139
     * @return string
1140
     */
1141
    public static function sign($message, $sk)
1142
    {
1143
        return ParagonIE_Sodium_Core32_Ed25519::sign($message, $sk);
1144
    }
1145
1146
    /**
1147
     * Opens a signed message. If valid, returns the message.
1148
     *
1149
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1150
     *
1151
     * @param string $signedMessage
1152
     * @param string $pk
1153
     * @return string
1154
     */
1155
    public static function sign_open($signedMessage, $pk)
1156
    {
1157
        return ParagonIE_Sodium_Core32_Ed25519::sign_open($signedMessage, $pk);
1158
    }
1159
1160
    /**
1161
     * Verify a detached signature of a given message and public key.
1162
     *
1163
     * @internal Do not use this directly. Use ParagonIE_Sodium_Compat.
1164
     *
1165
     * @param string $signature
1166
     * @param string $message
1167
     * @param string $pk
1168
     * @return bool
1169
     */
1170
    public static function sign_verify_detached($signature, $message, $pk)
1171
    {
1172
        return ParagonIE_Sodium_Core32_Ed25519::verify_detached($signature, $message, $pk);
1173
    }
1174
}
1175