<?php
namespace ParagonIE\Paseto\Tests;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\ConstantTime\Binary;
use ParagonIE\Paseto\Exception\InvalidVersionException;
use ParagonIE\Paseto\Exception\PasetoException;
use ParagonIE\Paseto\Keys\Version2\AsymmetricPublicKey;
use ParagonIE\Paseto\Keys\Version2\AsymmetricSecretKey;
use ParagonIE\Paseto\Keys\Version2\SymmetricKey;
use ParagonIE\Paseto\Protocol\Version1;
use ParagonIE\Paseto\Protocol\Version2;
use PHPUnit\Framework\TestCase;
class Version2Test extends TestCase
{
/**
* @throws \Exception
* @throws \TypeError
*/
public function testKeyGen()
{
$symmetric = Version2::generateSymmetricKey();
$secret = Version2::generateAsymmetricSecretKey();
$this->assertInstanceOf('ParagonIE\Paseto\Keys\SymmetricKey', $symmetric);
$this->assertInstanceOf('ParagonIE\Paseto\Keys\AsymmetricSecretKey', $secret);
$this->assertSame(Version2::getSymmetricKeyByteLength(), Binary::safeStrlen($symmetric->raw()));
$this->assertSame(64, Binary::safeStrlen($secret->raw()));
}
/**
* @covers Version2::decrypt()
* @covers Version2::encrypt()
*
* @throws \Error
* @throws \Exception
* @throws \SodiumException
* @throws \TypeError
*/
public function testEncrypt()
{
$key = new SymmetricKey(random_bytes(32));
$year = (int) (\date('Y')) + 1;
$messages = [
'test',
\json_encode(['data' => 'this is a signed message', 'exp' => $year . '-01-01T00:00:00+00:00'])
];
foreach ($messages as $message) {
$encrypted = Version2::encrypt($message, $key);
$this->assertInternalType('string', $encrypted);
$this->assertSame('v2.local.', Binary::safeSubstr($encrypted, 0, 9));
$decode = Version2::decrypt($encrypted, $key);
$this->assertInternalType('string', $decode);
$this->assertSame($message, $decode);
// Now with a footer
try {
Version2::decrypt($message, $key);
$this->fail('Not a token');
} catch (PasetoException $ex) {
}
try {
Version2::decrypt($encrypted, $key, 'footer');
$this->fail('Footer did not cause expected MAC failure.');
} catch (PasetoException $ex) {
}
$encrypted = Version2::encrypt($message, $key, 'footer');
$this->assertInternalType('string', $encrypted);
$this->assertSame('v2.local.', Binary::safeSubstr($encrypted, 0, 9));
$decode = Version2::decrypt($encrypted, $key, 'footer');
$this->assertInternalType('string', $decode);
$this->assertSame($message, $decode);
$decode = Version2::decrypt($encrypted, $key);
$this->assertInternalType('string', $decode);
$this->assertSame($message, $decode);
try {
Version2::decrypt($encrypted, $key, '');
$this->fail('Missing footer');
} catch (PasetoException $ex) {
}
}
try {
Version1::encrypt('test', $key);
$this->fail('Invalid version accepted');
} catch (InvalidVersionException $ex) {
}
$encrypted = Version2::encrypt('test', $key);
try {
Version1::decrypt($encrypted, $key);
$this->fail('Invalid version accepted');
} catch (InvalidVersionException $ex) {
}
}
/**
* @covers Version2::sign()
* @covers Version2::verify()
*
* @throws InvalidVersionException
* @throws \Error
* @throws \Exception
* @throws \TypeError
*/
public function testSign()
{
$keypair = sodium_crypto_sign_keypair();
$privateKey = new AsymmetricSecretKey(sodium_crypto_sign_secretkey($keypair));
$publicKey = new AsymmetricPublicKey(sodium_crypto_sign_publickey($keypair));
$year = (int) (\date('Y')) + 1;
$messages = [
'test',
\json_encode(['data' => 'this is a signed message', 'exp' => $year . '-01-01T00:00:00'])
];
foreach ($messages as $message) {
$signed = Version2::sign($message, $privateKey);
$this->assertInternalType('string', $signed);
$this->assertSame('v2.public.', Binary::safeSubstr($signed, 0, 10));
$decode = Version2::verify($signed, $publicKey);
$this->assertInternalType('string', $decode);
$this->assertSame($message, $decode);
// Now with a footer
$signed = Version2::sign($message, $privateKey, 'footer');
$this->assertInternalType('string', $signed);
$this->assertSame('v2.public.', Binary::safeSubstr($signed, 0, 10));
try {
Version2::verify($signed, $publicKey, '');
$this->fail('Missing footer');
} catch (PasetoException $ex) {
}
$decode = Version2::verify($signed, $publicKey, 'footer');
$this->assertInternalType('string', $decode);
$this->assertSame($message, $decode);
}
try {
Version1::sign('test', $privateKey);
$this->fail('Invalid version accepted');
} catch (InvalidVersionException $ex) {
}
$signed = Version2::sign('test', $privateKey);
try {
Version1::verify($signed, $publicKey);
$this->fail('Invalid version accepted');
} catch (InvalidVersionException $ex) {
}
}
/**
* @covers AsymmetricSecretKey for version 2
*
* @throws \Error
* @throws \Exception
* @throws \TypeError
*/
public function testWeirdKeypairs()
{
$keypair = sodium_crypto_sign_keypair();
$privateKey = new AsymmetricSecretKey(sodium_crypto_sign_secretkey($keypair));
$publicKey = new AsymmetricPublicKey(sodium_crypto_sign_publickey($keypair));
$seed = Binary::safeSubstr($keypair, 0, 32);
$privateAlt = new AsymmetricSecretKey($seed);
$publicKeyAlt = $privateAlt->getPublicKey();
$this->assertSame(
Base64UrlSafe::encode($privateAlt->raw()),
Base64UrlSafe::encode($privateKey->raw())
);
$this->assertSame(
Base64UrlSafe::encode($publicKeyAlt->raw()),
Base64UrlSafe::encode($publicKey->raw())
);
}
}
|