| 
<?phpuse PHPUnit\Framework\TestCase;
 
 class WycheproofTest extends TestCase
 {
 private $dir;
 
 /**
 * @before
 */
 public function before(): void
 {
 ParagonIE_Sodium_Compat::$disableFallbackForUnitTests = true;
 $this->dir = dirname(__FILE__) . '/wycheproof/';
 }
 
 /**
 * @throws Exception
 */
 public function testChaCha20Poly1305(): void
 {
 if (empty($this->dir)) {
 $this->before();
 }
 $this->mainTestingLoop('chacha20_poly1305_test.json', 'doChaCha20Poly1305Test', false);
 }
 
 /**
 * @throws Exception
 */
 public function testXChaCha20Poly1305(): void
 {
 if (empty($this->dir)) {
 $this->before();
 }
 $this->mainTestingLoop('xchacha20_poly1305_test.json', 'doXChaCha20Poly1305Test', false);
 }
 
 /**
 * @throws Exception
 */
 public function testSipHash24(): void
 {
 if (empty($this->dir)) {
 $this->before();
 }
 $this->mainTestingLoop('siphash_2_4_test.json', 'doSipHash24Test', false);
 }
 
 /**
 * @throws Exception
 */
 public function testX25519(): void
 {
 if (!defined('DO_PEDANTIC_TEST')) {
 $this->markTestSkipped('Skipping Wycheproof Tests. Use DO_PEDANTIC_TEST to enable.');
 }
 if (empty($this->dir)) {
 $this->before();
 }
 $this->mainTestingLoop('x25519_test.json', 'doX25519Test', false);
 }
 
 /**
 * @param $filename
 * @param $method
 *
 * @throws Exception
 */
 public function mainTestingLoop($filename, $method, $progress = false): void
 {
 $total = 0;
 $document = $this->getJson($this->dir . $filename);
 if ($progress) {
 $groupCount = count($document['testGroups']);
 $groupId = 1;
 }
 foreach ($document['testGroups'] as $testGroup) {
 if ($progress) {
 $testCount = count($testGroup['tests']);
 $testId = 1;
 }
 foreach ($testGroup['tests'] as $test) {
 ++$total;
 if ($progress) {
 echo "[Group {$groupId} : Test {$testId}]", PHP_EOL;
 }
 $message = "{$document['algorithm']} :: #{$test['tcId']} - {$test['comment']}";
 try {
 $result = call_user_func_array(array($this, $method), array($test));
 $expected = ($test['result'] === 'valid');
 if ($result !== $expected) {
 call_user_func_array(array($this, $method), array($test, true));
 }
 $this->assertSame($result, $expected, $message);
 } catch (Exception $ex) {
 if ($test['result'] === 'valid') {
 $this->fail("{$message} (" . $ex->getMessage() . ")");
 }
 }
 if ($progress) {
 ++$groupId;
 }
 }
 if ($progress) {
 ++$groupId;
 }
 }
 }
 
 /**
 * @param array $test
 * @param bool $verbose
 * @return bool
 * @throws SodiumException
 */
 public function doChaCha20Poly1305Test(array $test, $verbose = false)
 {
 $key = ParagonIE_Sodium_Compat::hex2bin($test['key']);
 $iv = ParagonIE_Sodium_Compat::hex2bin($test['iv']);
 $aad = ParagonIE_Sodium_Compat::hex2bin($test['aad']);
 $msg = ParagonIE_Sodium_Compat::hex2bin($test['msg']);
 $ct = ParagonIE_Sodium_Compat::hex2bin($test['ct']);
 $tag = ParagonIE_Sodium_Compat::hex2bin($test['tag']);
 
 $encrypted = ParagonIE_Sodium_Compat::crypto_aead_chacha20poly1305_ietf_encrypt(
 $msg,
 $aad,
 $iv,
 $key
 );
 if ($verbose && !ParagonIE_Sodium_Core_Util::hashEquals($ct . $tag, $encrypted)) {
 echo 'Difference in Wycheproof test vectors:', PHP_EOL;
 echo '- ', ParagonIE_Sodium_Core_Util::bin2hex($ct . $tag), PHP_EOL;
 echo '+ ', ParagonIE_Sodium_Core_Util::bin2hex($encrypted), PHP_EOL;
 }
 return ParagonIE_Sodium_Core_Util::hashEquals($ct . $tag, $encrypted);
 }
 
 /**
 * @param array $test
 * @param bool $verbose
 * @return bool
 * @throws SodiumException
 */
 public function doXChaCha20Poly1305Test(array $test, $verbose = false)
 {
 $key = ParagonIE_Sodium_Compat::hex2bin($test['key']);
 $iv = ParagonIE_Sodium_Compat::hex2bin($test['iv']);
 $aad = ParagonIE_Sodium_Compat::hex2bin($test['aad']);
 $msg = ParagonIE_Sodium_Compat::hex2bin($test['msg']);
 $ct = ParagonIE_Sodium_Compat::hex2bin($test['ct']);
 $tag = ParagonIE_Sodium_Compat::hex2bin($test['tag']);
 
 $encrypted = ParagonIE_Sodium_Compat::crypto_aead_xchacha20poly1305_ietf_encrypt(
 $msg,
 $aad,
 $iv,
 $key
 );
 if ($verbose && !ParagonIE_Sodium_Core_Util::hashEquals($ct . $tag, $encrypted)) {
 echo 'Difference in Wycheproof test vectors:', PHP_EOL;
 echo '- ', ParagonIE_Sodium_Core_Util::bin2hex($ct . $tag), PHP_EOL;
 echo '+ ', ParagonIE_Sodium_Core_Util::bin2hex($encrypted), PHP_EOL;
 }
 return ParagonIE_Sodium_Core_Util::hashEquals($ct . $tag, $encrypted);
 }
 
 /**
 * @param array $test
 * @param bool $verbose
 * @return bool
 * @throws SodiumException
 */
 public function doX25519Test(array $test, $verbose = false)
 {
 $private = ParagonIE_Sodium_Compat::hex2bin($test['private']);
 $public = ParagonIE_Sodium_Compat::hex2bin($test['public']);
 $shared = ParagonIE_Sodium_Compat::hex2bin($test['shared']);
 
 $scalarmult = ParagonIE_Sodium_Compat::crypto_scalarmult($private, $public);
 if ($verbose &&!ParagonIE_Sodium_Core_Util::hashEquals($shared, $scalarmult)) {
 echo 'Difference in Wycheproof test vectors:', PHP_EOL;
 echo '- ', ParagonIE_Sodium_Core_Util::bin2hex($shared), PHP_EOL;
 echo '+ ', ParagonIE_Sodium_Core_Util::bin2hex($scalarmult), PHP_EOL;
 }
 return ParagonIE_Sodium_Core_Util::hashEquals($shared, $scalarmult);
 }
 
 /**
 * @param array $test
 * @param bool $verbose
 * @return bool
 * @throws SodiumException
 */
 public function doSipHash24Test(array $test, $verbose = false)
 {
 $key = ParagonIE_Sodium_Compat::hex2bin($test['key']);
 $msg = ParagonIE_Sodium_Compat::hex2bin($test['msg']);
 $tag = ParagonIE_Sodium_Compat::hex2bin($test['tag']);
 $result = ParagonIE_Sodium_Compat::crypto_shorthash($msg, $key);
 if ($verbose && !ParagonIE_Sodium_Core_Util::hashEquals($tag, $result)) {
 echo 'Difference in Wycheproof test vectors:', PHP_EOL;
 echo '- ', ParagonIE_Sodium_Core_Util::bin2hex($tag), PHP_EOL;
 echo '+ ', ParagonIE_Sodium_Core_Util::bin2hex($result), PHP_EOL;
 }
 return ParagonIE_Sodium_Core_Util::hashEquals($tag, $result);
 }
 
 /**
 * @param string $file
 *
 * @return mixed
 * @throws Exception
 */
 public function getJson($file = '')
 {
 if (!is_readable($file)) {
 throw new Exception('Could not read file');
 }
 $contents = file_get_contents($file);
 if (!is_string($contents)) {
 throw new Exception('Could not read file');
 }
 $decoded = json_decode($contents, true);
 if (!is_array($decoded)) {
 throw new Exception('Error decoding JSON blob');
 }
 return $decoded;
 }
 }
 
 |