PHP Classes

File: tests/compat/PHP72Test.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   PHP Sodium Compat   tests/compat/PHP72Test.php   Download  
File: tests/compat/PHP72Test.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP Sodium Compat
Cryptographic functions of libsodium in pure PHP
Author: By
Last change: Implement Ristretto255

This is compatible with the new ext/sodium functions landing in PHP 8.1.0 and PECL libsodium 2.0.25.

See: https://github.com/php/php-src/pull/6922
See: https://github.com/jedisct1/libsodium-php/pull/212
Add compatibility test to prevent #125 from regressing
Date: 1 year ago
Size: 41,438 bytes
 

Contents

Class file image Download
<?php /** * Class SodiumCompatTest */ class PHP72Test extends PHPUnit_Framework_TestCase { /** * @before */ public function before() { if (PHP_VERSION_ID < 70200) { $this->markTestSkipped('PHP < 7.2.0; skipping PHP 7.2 compatibility test suite.'); } ParagonIE_Sodium_Compat::$disableFallbackForUnitTests = true; } /** * @throws SodiumException */ public function testAdd() { $a = "\x12\x34\x56\x78"; $b = "\x01\x00\x00\x00"; $c = "\xFF\xFF\xFF\xFF"; $tmp = $a; ParagonIE_Sodium_Compat::add($tmp, $b); $this->assertEquals("\x13\x34\x56\x78", $tmp); ParagonIE_Sodium_Compat::add($tmp, $b); $this->assertEquals("\x14\x34\x56\x78", $tmp); $tmp = $a; ParagonIE_Sodium_Compat::add($tmp, $c); $this->assertEquals("\x11\x34\x56\x78", $tmp); } /** * See Issue #125 * @ref https://github.com/paragonie/sodium_compat/issues/125 * @throws SodiumException */ public function testAeadXChaCha20EmptyAad() { $key = sodium_crypto_aead_xchacha20poly1305_ietf_keygen(); $nonce = random_bytes(24); $message = 'Pi day was a month ago and I suddenly crave pie.'; $c1 = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($message, '', $nonce, $key); $c3 = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt($message, '', $nonce, $key); if (PHP_VERSION_ID < 80100) { $c2 = sodium_crypto_aead_xchacha20poly1305_ietf_encrypt($message, NULL, $nonce, $key); $c4 = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt($message, NULL, $nonce, $key); } $this->assertEquals(sodium_bin2hex($c1), sodium_bin2hex($c3)); if (PHP_VERSION_ID < 80100) { $this->assertEquals(sodium_bin2hex($c1), sodium_bin2hex($c2)); $this->assertEquals(sodium_bin2hex($c1), sodium_bin2hex($c4)); } $p1 = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($c1, '', $nonce, $key); $p3 = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_decrypt($c1, '', $nonce, $key); if (PHP_VERSION_ID < 80100) { $p2 = sodium_crypto_aead_xchacha20poly1305_ietf_decrypt($c1, NULL, $nonce, $key); $p4 = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_decrypt($c1, NULL, $nonce, $key); } $this->assertSame($message, $p1); $this->assertSame($message, $p3); if (PHP_VERSION_ID < 80100) { $this->assertSame($message, $p2); $this->assertSame($message, $p4); } } /** * @covers ParagonIE_Sodium_Core_Util::compare() */ public function testCompare() { $a = pack('H*', '589a84d7ec2db8f982841cedca674ec1'); $b = $a; $b[15] = 'a'; $this->assertSame( sodium_compare($a, $b), ParagonIE_Sodium_Core_Util::compare($a, $b), bin2hex($a) . ' vs ' . bin2hex($b) ); $a = random_bytes(16); $b = $a; $b[15] = 'a'; $this->assertSame( sodium_compare($a, $b), ParagonIE_Sodium_Core_Util::compare($a, $b), bin2hex($a) ); } /** * @covers ParagonIE_Sodium_Core_Util::bin2hex() */ public function testBin2hex() { $str = random_bytes(random_int(1, 63)); $this->assertSame( sodium_bin2hex($str), ParagonIE_Sodium_Core_Util::bin2hex($str) ); } /** * @covers ParagonIE_Sodium_Core_Util::hex2bin() */ public function testHex2bin() { $str = bin2hex(random_bytes(random_int(1, 63))); $this->assertSame( sodium_hex2bin($str), ParagonIE_Sodium_Core_Util::hex2bin($str) ); } /** * */ public function testAeadChapoly() { $message = str_repeat("\x00", 128); $key = str_repeat("\x00", 32); $nonce = str_repeat("\x00", 8); $ad = ''; $pecl = sodium_crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Empty test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($pecl, $ad, $nonce, $key), 'Blank Message decryption' ); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $pecl = sodium_crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Static test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($pecl, $ad, $nonce, $key), 'Static Message decryption' ); $ad = 'test'; $pecl = sodium_crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Static test with AD'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($pecl, $ad, $nonce, $key), 'Static Message decryption (with AD)' ); $key = random_bytes(32); $nonce = random_bytes(8); $ad = ''; $pecl = sodium_crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Random test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($pecl, $ad, $nonce, $key), 'Random Message decryption' ); $ad = 'test'; $pecl = sodium_crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Random test with AD'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_decrypt($pecl, $ad, $nonce, $key), 'Random Message decryption (with AD)' ); } /** * */ public function testAeadChapolyIetf() { $message = str_repeat("\x00", 128); $key = str_repeat("\x00", 32); $nonce = str_repeat("\x00", 12); $ad = ''; $pecl = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Empty test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($pecl, $ad, $nonce, $key), 'Blank Message decryption' ); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $pecl = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Static test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($pecl, $ad, $nonce, $key), 'Static Message decryption' ); $ad = 'test'; $pecl = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Static test with AD'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($pecl, $ad, $nonce, $key), 'Static Message decryption (with AD)' ); $key = random_bytes(32); $nonce = random_bytes(12); $ad = ''; $pecl = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Random test'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($pecl, $ad, $nonce, $key), 'Random Message decryption' ); $ad = 'test'; $pecl = sodium_crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $compat = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt($message, $ad, $nonce, $key); $this->assertSame(bin2hex($pecl), bin2hex($compat), 'Random test with AD'); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_decrypt($pecl, $ad, $nonce, $key), 'Random Message decryption (with AD)' ); } /** * */ public function testCryptoAuth() { $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $key = random_bytes(32); $this->assertSame( bin2hex(sodium_crypto_auth($message, $key)), bin2hex(ParagonIE_Sodium_Compat::crypto_auth($message, $key)) ); $mac = sodium_crypto_auth($message, $key); $this->assertTrue( ParagonIE_Sodium_Compat::crypto_auth_verify($mac, $message, $key) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_box() * @covers ParagonIE_Sodium_Compat::crypto_box_open() */ public function testCryptoBox() { $nonce = str_repeat("\x00", 24); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $alice_box_kp = sodium_crypto_box_keypair(); $alice_box_secretkey = sodium_crypto_box_secretkey($alice_box_kp); $alice_box_publickey = sodium_crypto_box_publickey($alice_box_kp); $bob_box_kp = sodium_crypto_box_keypair(); $bob_box_secretkey = sodium_crypto_box_secretkey($bob_box_kp); $bob_box_publickey = sodium_crypto_box_publickey($bob_box_kp); $alice_to_bob = sodium_crypto_box_keypair_from_secretkey_and_publickey( $alice_box_secretkey, $bob_box_publickey ); $bob_to_alice = sodium_crypto_box_keypair_from_secretkey_and_publickey( $bob_box_secretkey, $alice_box_publickey ); $bob_to_alice2 = ParagonIE_Sodium_Compat::crypto_box_keypair_from_secretkey_and_publickey( $bob_box_secretkey, $alice_box_publickey ); $this->assertSame($bob_to_alice, $bob_to_alice2); $this->assertSame( bin2hex(sodium_crypto_box($message, $nonce, $alice_to_bob)), bin2hex(ParagonIE_Sodium_Compat::crypto_box($message, $nonce, $alice_to_bob)), 'box' ); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_box_open( sodium_crypto_box($message, $nonce, $alice_to_bob), $nonce, $bob_to_alice ) ); $message = str_repeat("Lorem ipsum dolor sit amet, consectetur adipiscing elit. ", 8); $this->assertSame( bin2hex(sodium_crypto_box($message, $nonce, $alice_to_bob)), bin2hex(ParagonIE_Sodium_Compat::crypto_box($message, $nonce, $alice_to_bob)), 'crypto_box is failing with large messages' ); $this->assertSame( bin2hex($message), bin2hex( ParagonIE_Sodium_Compat::crypto_box_open( sodium_crypto_box($message, $nonce, $alice_to_bob), $nonce, $bob_to_alice ) ) ); } public function testCryptoBoxSeal() { $msg = ParagonIE_Sodium_Core_Util::hex2bin( '7375f4094f1151640bd853cb13dbc1a0ee9e13b0287a89d34fa2f6732be9de13f88457553d'. '768347116522d6d32c9cb353ef07aa7c83bd129b2bb5db35b28334c935b24f2639405a0604' ); $kp = ParagonIE_Sodium_Core_Util::hex2bin( '36a6c2b96a650d80bf7e025e0f58f3d636339575defb370801a54213bd54582d'. '5aecbcf7866e7a4d58a6c1317e2b955f54ecbe2fcbbf7d262c10636ed524480c' ); $alice_opened2 = ParagonIE_Sodium_Compat::crypto_box_seal_open($msg, $kp); $this->assertSame( bin2hex('This is for your eyes only'), bin2hex($alice_opened2), 'Decryption failed #2' ); $alice_box_kp = ParagonIE_Sodium_Core_Util::hex2bin( '15b36cb00213373fb3fb03958fb0cc0012ecaca112fd249d3cf0961e311caac9' . 'fb4cb34f74a928b79123333c1e63d991060244cda98affee14c3398c6d315574' ); $alice_box_publickey = ParagonIE_Sodium_Core_Util::hex2bin( 'fb4cb34f74a928b79123333c1e63d991060244cda98affee14c3398c6d315574' ); $anonymous_message_to_alice = sodium_crypto_box_seal( 'Anonymous message', $alice_box_publickey); $decrypted_message = ParagonIE_Sodium_Compat::crypto_box_seal_open( $anonymous_message_to_alice, $alice_box_kp ); $this->assertSame( 'Anonymous message', $decrypted_message ); $messages = array( 'test', 'slightly longer message', str_repeat('a', 29) . ' 32', str_repeat('a', 30) . ' 33', str_repeat('a', 31) . ' 34', "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", ); foreach ($messages as $message) { $sealed_to_alice1 = sodium_crypto_box_seal($message, $alice_box_publickey); $sealed_to_alice2 = ParagonIE_Sodium_Compat::crypto_box_seal( $message, $alice_box_publickey ); $this->assertSame( ParagonIE_Sodium_Core_Util::strlen($sealed_to_alice1), ParagonIE_Sodium_Core_Util::strlen($sealed_to_alice2), 'String length should not differ' ); $alice_opened1 = ParagonIE_Sodium_Compat::crypto_box_seal_open($sealed_to_alice1, $alice_box_kp); $this->assertSame( bin2hex(sodium_crypto_box_seal_open($sealed_to_alice1, $alice_box_kp)), bin2hex($message), 'Decryption failed #1: ' . $message ); $this->assertSame( bin2hex($message), bin2hex($alice_opened1), 'Decryption failed #1: ' . $message ); $this->assertSame( bin2hex($alice_opened1), bin2hex(sodium_crypto_box_seal_open($sealed_to_alice1, $alice_box_kp)), 'Decryption failed #1: ' . $message ); $alice_opened2 = ParagonIE_Sodium_Compat::crypto_box_seal_open( $sealed_to_alice2, $alice_box_kp ); $this->assertSame( $message, $alice_opened2, 'Decryption failed #2: ' . $message ); $this->assertSame( bin2hex(sodium_crypto_box_seal_open($sealed_to_alice2, $alice_box_kp)), bin2hex($message), 'Decryption failed #2: ' . $message ); $this->assertSame( bin2hex(sodium_crypto_box_seal_open($sealed_to_alice2, $alice_box_kp)), bin2hex($alice_opened2), 'Decryption failed #2: ' . $message ); } } /** * @covers ParagonIE_Sodium_Compat::crypto_generichash() */ public function testCryptoGenerichash() { $this->assertSame( bin2hex(sodium_crypto_generichash('apple')), bin2hex(ParagonIE_Sodium_Compat::crypto_generichash('apple')), 'BLAKE2b implementation' ); $this->assertSame( bin2hex(sodium_crypto_generichash('apple', 'catastrophic failure')), bin2hex(ParagonIE_Sodium_Compat::crypto_generichash('apple', 'catastrophic failure')), 'BLAKE2b with a key' ); $this->assertSame( bin2hex(sodium_crypto_generichash('apple', '', 64)), bin2hex(ParagonIE_Sodium_Compat::crypto_generichash('apple', '', 64)), 'BLAKE2b implementation with output length' ); $this->assertSame( bin2hex(sodium_crypto_generichash('apple', 'catastrophic failure', 24)), bin2hex(ParagonIE_Sodium_Compat::crypto_generichash('apple', 'catastrophic failure', 24)), 'BLAKE2b implementation with output length' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_generichash_init() * @covers ParagonIE_Sodium_Compat::crypto_generichash_update() * @covers ParagonIE_Sodium_Compat::crypto_generichash_final() */ public function testCryptoGenerichashStream() { $key = "\x1c" . str_repeat("\x80", 30) . "\xaf"; $ctx = sodium_crypto_generichash_init($key); $nativeCtx = ''; for ($i = 0; $i < ParagonIE_Sodium_Core_Util::strlen($ctx); ++$i) { $nativeCtx .= $ctx[$i]; } sodium_crypto_generichash_update($nativeCtx, 'Paragon Initiative'); ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, 'Paragon Initiative'); sodium_crypto_generichash_update($nativeCtx, ' Enterprises, LLC'); ParagonIE_Sodium_Compat::crypto_generichash_update($ctx, ' Enterprises, LLC'); $this->assertSame( bin2hex(sodium_crypto_generichash_final($nativeCtx, 32)), bin2hex(ParagonIE_Sodium_Compat::crypto_generichash_final($ctx, 32)), 'generichash_final()' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_sign_seed_keypair() */ public function testSignKeypair() { $seed = random_bytes(32); $kp = sodium_crypto_sign_seed_keypair($seed); $this->assertSame( bin2hex($kp), bin2hex( ParagonIE_Sodium_Compat::crypto_sign_seed_keypair($seed) ), 'crypto_sign_seed_keypair() is invalid.' ); $secret = sodium_crypto_sign_secretkey($kp); $public = sodium_crypto_sign_publickey($kp); $pk = ''; $sk = ''; ParagonIE_Sodium_Core_Ed25519::seed_keypair($pk, $sk, $seed); $this->assertSame( bin2hex($secret), bin2hex($sk), 'Seed secret key' ); $this->assertSame( bin2hex($public), bin2hex($pk), 'Seed public key' ); $keypair = ParagonIE_Sodium_Compat::crypto_sign_keypair(); $secret = sodium_crypto_sign_secretkey($keypair); $public = sodium_crypto_sign_publickey($keypair); $this->assertSame( bin2hex($public), bin2hex( sodium_crypto_sign_publickey_from_secretkey($secret) ), 'Conversion from existing secret key is failing. This is a very bad thing!' ); if (is_callable('sodium_crypto_sign_ed25519_sk_to_curve25519')) { $this->assertSame( bin2hex(sodium_crypto_sign_ed25519_sk_to_curve25519($secret)), bin2hex(ParagonIE_Sodium_Compat::crypto_sign_ed25519_sk_to_curve25519($secret)), 'crypto_sign_ed25519_sk_to_curve25519' ); } } public function testSignKeypair2() { $keypair = sodium_crypto_sign_keypair(); $secret = sodium_crypto_sign_secretkey($keypair); $public = sodium_crypto_sign_publickey($keypair); $this->assertSame( bin2hex($public), bin2hex( ParagonIE_Sodium_Compat::crypto_sign_publickey_from_secretkey($secret) ), 'Conversion from existing secret key is failing. This is a very bad thing!' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_sign() * @covers ParagonIE_Sodium_Compat::crypto_sign_open() * @covers ParagonIE_Sodium_Compat::crypto_sign_detached() * @covers ParagonIE_Sodium_Compat::crypto_sign_verify_detached() */ public function testCryptoSign() { $keypair = ParagonIE_Sodium_Core_Util::hex2bin( 'fcdf31aae72e280cc760186d83e41be216fe1f2c7407dd393ad3a45a2fa501a4' . 'ee00f800ae9e986b994ec0af67fe6b017eb78704e81639eee7efa3d3a831d1bc' . 'ee00f800ae9e986b994ec0af67fe6b017eb78704e81639eee7efa3d3a831d1bc' ); $secret = sodium_crypto_sign_secretkey($keypair); $public = sodium_crypto_sign_publickey($keypair); $this->assertSame( $secret, ParagonIE_Sodium_Compat::crypto_sign_secretkey($keypair), 'crypto_sign_secretkey() is broken' ); $this->assertSame( $public, ParagonIE_Sodium_Compat::crypto_sign_publickey($keypair), 'crypto_sign_publickey() is broken' ); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $expected = '36a6d2748f6ab8f76c122a562d55343cb7c6f15c8a45bd55bd8b9e9fadd2363f' . '370cb78fba42c550d487b9bd7413312b6490c8b3ee2cea638997172a9c8c250f'; $this->assertSame( $expected, bin2hex(sodium_crypto_sign_detached($message, $secret)), 'Generated different signatures' ); $this->assertSame( bin2hex(sodium_crypto_sign_detached($message, $secret)), bin2hex(ParagonIE_Sodium_Compat::crypto_sign_detached($message, $secret)), 'Generated different signatures' ); $this->assertSame( $expected, bin2hex(ParagonIE_Sodium_Compat::crypto_sign_detached($message, $secret)), 'Generated different signatures' ); $message = 'Test message: ' . base64_encode(random_bytes(33)); $keypair = sodium_crypto_sign_keypair(); $secret = sodium_crypto_sign_secretkey($keypair); $public = sodium_crypto_sign_publickey($keypair); $public2 = ParagonIE_Sodium_Compat::crypto_sign_publickey($keypair); $this->assertSame($public, $public2); $signature = sodium_crypto_sign_detached($message, $secret); $this->assertSame( bin2hex($signature), bin2hex(ParagonIE_Sodium_Compat::crypto_sign_detached($message, $secret)), 'Generated different signatures' ); $this->assertTrue( ParagonIE_Sodium_Compat::crypto_sign_verify_detached($signature, $message, $public), 'Signature verification failed in compatibility test.' ); // Signed messages (NaCl compatibility): $signed = sodium_crypto_sign($message, $secret); $this->assertSame( bin2hex($signed), bin2hex(ParagonIE_Sodium_Compat::crypto_sign($message, $secret)), 'Basic crypto_sign works' ); $this->assertSame( bin2hex(sodium_crypto_sign_open($signed, $public)), bin2hex(ParagonIE_Sodium_Compat::crypto_sign_open($signed, $public)), 'Basic crypto_sign_open works' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_secretbox() */ public function testCryptoSecretBox() { $key = str_repeat("\x80", 32); $nonce = str_repeat("\x00", 24); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $this->assertSame( ParagonIE_Sodium_Core_Util::substr( bin2hex(sodium_crypto_secretbox($message, $nonce, $key)), 0, 32 ), ParagonIE_Sodium_Core_Util::substr( bin2hex(ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key)), 0, 32 ), 'secretbox - short messages' ); $this->assertSame( $message, ParagonIE_Sodium_Compat::crypto_secretbox_open( sodium_crypto_secretbox($message, $nonce, $key), $nonce, $key ) ); $this->assertSame( $message, sodium_crypto_secretbox_open( ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key), $nonce, $key ) ); $message = str_repeat('a', 97); $this->assertSame( bin2hex(sodium_crypto_secretbox($message, $nonce, $key)), bin2hex(ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key)), 'secretbox - long messages (multiple of 16)' ); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $message = str_repeat($message, 16); $this->assertSame( bin2hex(sodium_crypto_secretbox($message, $nonce, $key)), bin2hex(ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key)), 'secretbox - long messages (multiple of 16)' ); $message .= 'a'; $this->assertSame( bin2hex(sodium_crypto_secretbox($message, $nonce, $key)), bin2hex(ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key)), 'secretbox - long messages (NOT a multiple of 16)' ); $message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."; $this->assertSame( bin2hex(sodium_crypto_secretbox($message, $nonce, $key)), bin2hex(ParagonIE_Sodium_Compat::crypto_secretbox($message, $nonce, $key)), 'secretbox - medium messages' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_scalarmult_base() */ public function testCryptoScalarmultBase() { $keypair = sodium_crypto_box_keypair(); $secret = sodium_crypto_box_secretkey($keypair); $public = sodium_crypto_box_publickey($keypair); $this->assertSame( $public, ParagonIE_Sodium_Compat::crypto_scalarmult_base($secret) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_scalarmult() */ public function testCryptoScalarmult() { $alice_box_kp = sodium_crypto_box_keypair(); $alice_box_secretkey = sodium_crypto_box_secretkey($alice_box_kp); $alice_box_publickey = sodium_crypto_box_publickey($alice_box_kp); $bob_box_kp = sodium_crypto_box_keypair(); $bob_box_secretkey = sodium_crypto_box_secretkey($bob_box_kp); $bob_box_publickey = sodium_crypto_box_publickey($bob_box_kp); $this->assertSame( sodium_crypto_scalarmult($alice_box_secretkey, $bob_box_publickey), ParagonIE_Sodium_Compat::crypto_scalarmult($alice_box_secretkey, $bob_box_publickey) ); $this->assertSame( sodium_crypto_scalarmult($bob_box_secretkey, $alice_box_publickey), ParagonIE_Sodium_Compat::crypto_scalarmult($bob_box_secretkey, $alice_box_publickey) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_box_secretkey() * @covers ParagonIE_Sodium_Compat::crypto_box_publickey() */ public function testCryptoBoxKeypairs() { $keypair = sodium_crypto_box_keypair(); $secret = sodium_crypto_box_secretkey($keypair); $public = sodium_crypto_box_publickey($keypair); $this->assertSame( $secret, ParagonIE_Sodium_Compat::crypto_box_secretkey($keypair) ); $this->assertSame( $public, ParagonIE_Sodium_Compat::crypto_box_publickey($keypair) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_stream() */ public function testCryptoStream() { $key = str_repeat("\x80", 32); $nonce = str_repeat("\x00", 24); $streamed = sodium_crypto_stream(64, $nonce, $key); $this->assertSame( bin2hex($streamed), bin2hex(ParagonIE_Sodium_Compat::crypto_stream(64, $nonce, $key)), 'crypto_stream_xor() is not working' ); $key = random_bytes(32); $nonce = random_bytes(24); $streamed = sodium_crypto_stream(1024, $nonce, $key); $this->assertSame( bin2hex($streamed), bin2hex(ParagonIE_Sodium_Compat::crypto_stream(1024, $nonce, $key)), 'crypto_stream() is not working' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_stream_xor() */ public function testCryptoStreamXor() { $key = str_repeat("\x80", 32); $nonce = str_repeat("\x00", 24); $message = 'Test message'; $streamed = sodium_crypto_stream_xor($message, $nonce, $key); $this->assertSame( bin2hex($streamed), bin2hex(ParagonIE_Sodium_Compat::crypto_stream_xor($message, $nonce, $key)), 'crypto_stream_xor() is not working' ); $key = random_bytes(32); $nonce = random_bytes(24); $message = 'Test message: ' . base64_encode(random_bytes(93)); $streamed = sodium_crypto_stream_xor($message, $nonce, $key); $this->assertSame( bin2hex($streamed), bin2hex(ParagonIE_Sodium_Compat::crypto_stream_xor($message, $nonce, $key)), 'crypto_stream_xor() is not working' ); } /** * @covers ParagonIE_Sodium_Compat::crypto_kx() */ public function testCryptoKx() { if (!is_callable('sodium_crypto_kx')) { $this->markTestSkipped('sodium_crypto_kx not defined'); return; } $alice_box_kp = sodium_crypto_box_keypair(); $alice_box_secretkey = sodium_crypto_box_secretkey($alice_box_kp); $alice_box_publickey = sodium_crypto_box_publickey($alice_box_kp); $bob_box_kp = sodium_crypto_box_keypair(); $bob_box_publickey = sodium_crypto_box_publickey($bob_box_kp); // Let's designate Bob as the server. $this->assertSame( bin2hex( sodium_crypto_kx( $alice_box_secretkey, $bob_box_publickey, $alice_box_publickey, $bob_box_publickey ) ), bin2hex( ParagonIE_Sodium_Compat::crypto_kx( $alice_box_secretkey, $bob_box_publickey, $alice_box_publickey, $bob_box_publickey ) ) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_pwhash() * * @throws SodiumException * @throws TypeError */ public function testCryptoPwhash() { if (!\extension_loaded('sodium')) { $this->markTestSkipped('Libsodium not loaded'); } ParagonIE_Sodium_Compat::$disableFallbackForUnitTests = false; $passwd = 'test password'; $salt = random_bytes(16); $native = \sodium_crypto_pwhash( 16, $passwd, $salt, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE ); $compat = ParagonIE_Sodium_Compat::crypto_pwhash( 16, $passwd, $salt, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE ); ParagonIE_Sodium_Compat::$disableFallbackForUnitTests = true; $this->assertSame( \sodium_bin2hex($native), \sodium_bin2hex($compat) ); } /** * @covers ParagonIE_Sodium_Compat::crypto_kdf_derive_from_key() */ public function testKdf() { $key = ParagonIE_Sodium_Compat::crypto_kdf_keygen(); $subkey_id = random_int(1, PHP_INT_MAX); $context = 'NaClTest'; $a = sodium_crypto_kdf_derive_from_key(32, $subkey_id, $context, $key); $b = ParagonIE_Sodium_Compat::crypto_kdf_derive_from_key(32, $subkey_id, $context, $key); $this->assertEquals( bin2hex($a), bin2hex($b), 'kdf outputs differ' ); } /** * @throws SodiumException */ public function testPwhashNeedsRehash() { if (!\extension_loaded('sodium')) { $this->markTestSkipped('Libsodium not loaded'); } $hash = sodium_crypto_pwhash_str( 'test', SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE ); $this->assertFalse(ParagonIE_Sodium_Compat::crypto_pwhash_str_needs_rehash( $hash, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE )); $this->assertTrue(ParagonIE_Sodium_Compat::crypto_pwhash_str_needs_rehash( $hash, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE << 1 )); $this->assertTrue(ParagonIE_Sodium_Compat::crypto_pwhash_str_needs_rehash( $hash, SODIUM_CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE + 1, SODIUM_CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE )); } /** * @throws SodiumException */ public function testCryptoShorthash() { $message = str_repeat("\x00", 8); $key = str_repeat("\x00", 16); $this->shorthashVerify($message, $key); $key = str_repeat("\xff", 16); $this->shorthashVerify($message, $key); $message = str_repeat("\x01", 8); $this->shorthashVerify($message, $key); $message = str_repeat("\x01", 7) . "\x02"; $this->shorthashVerify($message, $key); $key = str_repeat("\xff", 8) . str_repeat("\x00", 8); $this->shorthashVerify($message, $key); $message = str_repeat("\x00", 8); $key = random_bytes(16); $this->shorthashVerify($message, $key); $message = random_bytes(random_int(1, 100)); $this->shorthashVerify($message, $key); } /** * Verify the _init() functions behave correctly * * @throws SodiumException * @throws Exception */ public function testSecretStreamStates() { $key = str_repeat("A", 32); list($stateA, $header) = sodium_crypto_secretstream_xchacha20poly1305_init_push($key); $stateB = sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $key); $this->assertEquals(bin2hex($stateA), bin2hex($stateB)); $x = sodium_crypto_secretstream_xchacha20poly1305_push($stateA, 'test'); $y = sodium_crypto_secretstream_xchacha20poly1305_push($stateB, 'test'); $this->assertEquals(bin2hex($stateA), bin2hex($stateB), 'state 1'); $this->assertEquals(bin2hex($x), bin2hex($y), 'cipher 1'); $x = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_push($stateA, 'test'); $y = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_push($stateB, 'test'); $this->assertEquals(bin2hex($stateA), bin2hex($stateB), 'state 2'); $this->assertEquals(bin2hex($x), bin2hex($y), 'cipher 2'); // This is where things may get tricky... $x = sodium_crypto_secretstream_xchacha20poly1305_push($stateA, 'test'); $y = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_push($stateB, 'test'); $this->assertEquals(bin2hex($x), bin2hex($y), 'cipher 3'); $this->assertEquals(bin2hex($stateA), bin2hex($stateB), 'state 3'); // var_dump(bin2hex($stateA), bin2hex($header)); list($stateC, $header2) = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_init_push($key); $stateD = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_init_pull($header2, $key); $this->assertEquals(bin2hex($stateC), bin2hex($stateD)); } public function testSecretStream() { $key = str_repeat("A", 32); // list($state, $header) = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_init_push($key); $state = ParagonIE_Sodium_Core_Util::hex2bin( '5160239f7348e57e618b4a88a966ed78cb354a1e93a9bfa091f0469fc3007bf501000000280ade65e20103c20000000000000000' ); $header = ParagonIE_Sodium_Core_Util::hex2bin( '050fbb107d2050e960ed6e9988d7b2bd280ade65e20103c2' ); $state_copy = '' . $state; $inputs = array( "This is just a test message! :)", "Paragon Initiative Enterprises", "sodium_compat improves PHP code and makes PHP 7.2 migrations easy" ); $outputs = array(); $copy2 = $state_copy; foreach ($inputs as $i => $input) { $outputs[$i] = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_push($state, $input); $encrypt = sodium_crypto_secretstream_xchacha20poly1305_push($copy2, $input); $this->assertSame(bin2hex($outputs[$i]), bin2hex($encrypt), 'Ciphertext mismatch at (i = ' . $i . ')'); $this->assertSame(bin2hex($copy2), bin2hex($state), 'state after message i = ' . $i); } $state2 = sodium_crypto_secretstream_xchacha20poly1305_init_pull($header, $key); $this->assertSame(bin2hex($state_copy), bin2hex($state2)); for ($i = 0; $i < count($outputs); ++$i) { list($decrypt, $tag) = ParagonIE_Sodium_Compat::crypto_secretstream_xchacha20poly1305_pull($state_copy, $outputs[$i]); $this->assertEquals($inputs[$i], $decrypt, 'decrypt i = ' . $i); $this->assertEquals(0, $tag); list($decrypt, $tag) = sodium_crypto_secretstream_xchacha20poly1305_pull($state2, $outputs[$i]); $this->assertSame(bin2hex($state_copy), bin2hex($state2), 'state after message i = ' . $i); $this->assertEquals($inputs[$i], $decrypt, 'decrypt i = ' . $i); $this->assertEquals(0, $tag); } $this->assertSame(bin2hex($state), bin2hex($state2)); } /** * @throws SodiumException * @throws Exception */ public function testSodiumPad() { for ($i = 0; $i < 100; ++$i) { $block = random_int(16, 256); if (($i & 1) === 0) { $original = str_repeat("A", random_int(1, 1024)); } else { $original = random_bytes(random_int(1, 1024)); } $paddedA = sodium_pad($original, $block); $unpaddedA = sodium_unpad($paddedA, $block); $this->assertEquals($unpaddedA, $original); $this->assertNotEquals($paddedA, $original); $paddedB = ParagonIE_Sodium_Compat::pad($original, $block); $this->assertEquals(bin2hex($paddedA), bin2hex($paddedB), 'i = ' . $i); $unpaddedB = ParagonIE_Sodium_Compat::unpad($paddedB, $block); $this->assertEquals($unpaddedB, $original); } } /** * @throws SodiumException */ public function testKeyExchange() { $alice = ParagonIE_Sodium_Compat::crypto_kx_keypair(); $alice_pk = ParagonIE_Sodium_Compat::crypto_kx_publickey($alice); $bob = ParagonIE_Sodium_Compat::crypto_kx_keypair(); $bob_pk = ParagonIE_Sodium_Compat::crypto_kx_publickey($bob); $alice_to_bob = ParagonIE_Sodium_Compat::crypto_kx_client_session_keys($alice, $bob_pk); $bob_to_alice = ParagonIE_Sodium_Compat::crypto_kx_server_session_keys($bob, $alice_pk); $this->assertEquals($alice_to_bob[0], $bob_to_alice[1]); $this->assertEquals($alice_to_bob[1], $bob_to_alice[0]); $alice_to_bob2 = sodium_crypto_kx_client_session_keys($alice, $bob_pk); $bob_to_alice2 = sodium_crypto_kx_server_session_keys($bob, $alice_pk); $this->assertEquals($alice_to_bob[0], $alice_to_bob2[0]); $this->assertEquals($alice_to_bob[1], $alice_to_bob2[1]); $this->assertEquals($bob_to_alice[0], $bob_to_alice2[0]); $this->assertEquals($bob_to_alice[1], $bob_to_alice2[1]); } /** * @param string $m * @param string $k * @throws SodiumException */ protected function shorthashVerify($m, $k) { $this->assertSame( bin2hex(sodium_crypto_shorthash($m, $k)), bin2hex(ParagonIE_Sodium_Compat::crypto_shorthash($m, $k)) ); } }