PHP Classes

File: tests/ParserTest.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   PHP PASeTo   tests/ParserTest.php   Download  
File: tests/ParserTest.php
Role: Class source
Content type: text/plain
Description: Class source
Class: PHP PASeTo
Encrypt and decrypt data with PaSeTO protocol
Author: By
Last change:
Date: 4 years ago
Size: 8,260 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace
ParagonIE\Paseto\Tests;

use
ParagonIE\ConstantTime\Hex;
use
ParagonIE\Paseto\Builder;
use
ParagonIE\Paseto\Exception\InvalidKeyException;
use
ParagonIE\Paseto\Exception\InvalidPurposeException;
use
ParagonIE\Paseto\Exception\InvalidVersionException;
use
ParagonIE\Paseto\Exception\PasetoException;
use
ParagonIE\Paseto\JsonToken;
use
ParagonIE\Paseto\Keys\{
   
AsymmetricPublicKey,
   
AsymmetricSecretKey,
   
SymmetricKey
};
use
ParagonIE\Paseto\Keys\Version2\{
   
AsymmetricSecretKey as V2AsymmetricSecretKey,
   
SymmetricKey as V2SymmetricKey
};
use
ParagonIE\Paseto\Parser;
use
ParagonIE\Paseto\Purpose;
use
ParagonIE\Paseto\Protocol\Version2;
use
ParagonIE\Paseto\ProtocolCollection;
use
ParagonIE\Paseto\Rules\NotExpired;
use
PHPUnit\Framework\TestCase;

/**
 * Class ParserTest
 * @package ParagonIE\Paseto\Tests
 */
class ParserTest extends TestCase
{
   
/**
     * @throws \Exception
     */
   
public function testTypeSafety()
    {
       
$keypair = sodium_crypto_sign_keypair();
       
$publicKey = new AsymmetricPublicKey(sodium_crypto_sign_publickey($keypair));

       
// Let's encrypt a bad oken using the Ed25519 public key
       
$badKey = new SymmetricKey(sodium_crypto_sign_publickey($keypair));
       
$badToken = Version2::encrypt('arbitrary test string', $badKey);

        try {
            new
Parser(
               
ProtocolCollection::v2(),
               
Purpose::local(),
               
$publicKey
           
);
           
$this->fail('Invalid key type was accepted by parser');
        } catch (
PasetoException $ex) {
           
$this->assertInstanceOf(InvalidKeyException::class, $ex);
        }

       
// We have a v1.local parser, the token's version should be invalid:
       
$parser = new Parser(
           
ProtocolCollection::v1(),
           
Purpose::local(),
           
$badKey
       
);
        try {
           
$parser->parse($badToken);
           
$this->fail('Invalid purpose was accepted by parser');
        } catch (
PasetoException $ex) {
           
$this->assertInstanceOf(InvalidVersionException::class, $ex);
        }

       
// We have a v2.public parser, the token's purpose should be invalid:
       
$parser = new Parser(
           
ProtocolCollection::v2(),
           
Purpose::public(),
           
$publicKey
       
);
        try {
           
$parser->parse($badToken);
           
$this->fail('Invalid purpose was accepted by parser');
        } catch (
PasetoException $ex) {
           
$this->assertInstanceOf(InvalidPurposeException::class, $ex);
        }
    }

   
/**
     * @covers Parser::parse()
     * @throws PasetoException
     * @throws \Exception
     * @throws \ParagonIE\Paseto\Exception\RuleViolation
     * @throws \TypeError
     */
   
public function testAuthToken()
    {
       
$key = new SymmetricKey('YELLOW SUBMARINE, BLACK WIZARDRY');
       
$v2key = new V2SymmetricKey('YELLOW SUBMARINE, BLACK WIZARDRY');
       
// $nonce = crypto_generichash('Paragon Initiative Enterprises, LLC', '', 24);
       
$nonce = Hex::decode('45742c976d684ff84ebdc0de59809a97cda2f64c84fda19b');

       
$serialized = 'v2.local.3fNxan9FHjedQRSONRnT7Ce_KhhpB0NrlHwAGsCb54x0FhrjBfeNN4uPHFiO5H0iPCZSjwfEkkfiGeYpE6KAfr1Zm3G-VTe4lcXtgDyKATYULT-zLPfshRqisk4n7EbGufWuqilYvYXMCiYbaA';
       
$parser = (new Parser())
            ->
setPurpose(Purpose::local())
            ->
setKey($key);
       
$v2parser = (new Parser())
            ->
setPurpose(Purpose::local())
            ->
setKey($v2key);

       
$token = $parser->parse($serialized);
       
$v2token = $v2parser->parse($serialized);

       
$builder = (Builder::getLocal($key, new Version2(), $token));
       
NonceFixer::buildSetExplicitNonce()->bindTo($builder, $builder)($nonce);

       
$v2builder = (Builder::getLocal($v2key, new Version2(), $v2token));
       
NonceFixer::buildSetExplicitNonce()->bindTo($v2builder, $v2builder)($nonce);

       
$this->assertSame(
           
'2039-01-01T00:00:00+00:00',
           
$token->getExpiration()->format(\DateTime::ATOM),
           
'Mismatched expiration date/time'
       
);
       
$this->assertSame(
           
'2039-01-01T00:00:00+00:00',
           
$v2token->getExpiration()->format(\DateTime::ATOM),
           
'Mismatched expiration date/time'
       
);
       
$this->assertSame(
           
'this is a signed message',
           
$token->get('data'),
           
'Custom claim not found'
       
);
       
$this->assertSame($serialized, (string) $builder);
       
$this->assertSame($serialized, (string) $v2builder);

       
$this->assertTrue($parser->validate($token));
       
$this->assertTrue($parser->validate($v2token));
       
$parser->addRule(new NotExpired(new \DateTime('2007-01-01T00:00:00')));
       
$v2parser->addRule(new NotExpired(new \DateTime('2007-01-01T00:00:00')));
       
$this->assertTrue($parser->validate($token));
       
$this->assertTrue($v2parser->validate($token));

       
$cloned = clone $parser;
       
$cloned->addRule(new NotExpired(new \DateTime('2050-01-01T23:59:59')));
       
$this->assertFalse($cloned->validate($token));

        try {
           
$cloned->parse($serialized);
           
$this->fail('Validation logic is being ignored.');
        } catch (
PasetoException $ex) {
        }
       
$parser->parse($serialized);

       
// Switch to asymmetric-key crypto:
       
$builder->setPurpose(Purpose::public())
                ->
setKey(new AsymmetricSecretKey('YELLOW SUBMARINE, BLACK WIZARDRY'), true);
       
$v2builder->setPurpose(Purpose::public())
                ->
setKey(new V2AsymmetricSecretKey('YELLOW SUBMARINE, BLACK WIZARDRY'), true);
       
$this->assertSame(
           
'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAzOS0wMS0wMVQwMDowMDowMCswMDowMCJ9BAOu3lUQMVHnBcPSkuORw51yiGGQ3QFUMoJO9U0gRAdAOPQEZFsd0YM_GZuBcmrXEOD1Re-Ila8vfPrfM5S6Ag',
            (string)
$builder,
           
'Switching to signing caused a different signature'
       
);
       
$this->assertSame(
           
'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAzOS0wMS0wMVQwMDowMDowMCswMDowMCJ9BAOu3lUQMVHnBcPSkuORw51yiGGQ3QFUMoJO9U0gRAdAOPQEZFsd0YM_GZuBcmrXEOD1Re-Ila8vfPrfM5S6Ag',
            (string)
$v2builder,
           
'Switching to signing caused a different signature'
       
);
    }

   
/**
     * @throws PasetoException
     * @throws \ParagonIE\Paseto\Exception\InvalidPurposeException
     * @throws \ParagonIE\Paseto\Exception\SecurityException
     * @throws \TypeError
     */
   
public function testExtractFooter()
    {
       
$footers = [
            [
               
'token' => 'v2.local.BEsKs5AolRYDb_O-bO-lwHWUextpShFSvu6cB-KuR4wR9uDMjd45cPiOF0zxb7rrtOB5tRcS7dWsFwY4ONEuL5sWeunqHC9jxU0',
               
'footer' => ''
           
],
            [
               
'token' => 'v2.local.FGVEQLywggpvH0AzKtLXz0QRmGYuC6yvl05z9GIX0cnol6UK94cfV77AXnShlUcNgpDR12FrQiurS8jxBRmvoIKmeMWC5wY9Y6w.Q3VvbiBBbHBpbnVz',
               
'footer' => 'Cuon Alpinus'
           
],
            [
               
'token' => 'v2.local.lClhzVOuseCWYep44qbA8rmXry66lUupyENijX37_I_z34EiOlfyuwqIIhOjF-e9m2J-Qs17Gs-BpjpLlh3zf-J37n7YGHqMBV6G5xD2aeIKpck6rhfwHpGF38L7ryYuzuUeqmPg8XozSfU4PuPp9o8.UGFyYWdvbiBJbml0aWF0aXZlIEVudGVycHJpc2Vz',
               
'footer' => 'Paragon Initiative Enterprises'
           
]
        ];
        foreach (
$footers as $f) {
           
$this->assertSame(
               
$f['footer'],
               
Parser::extractFooter($f['token'])
            );
        }
    }

   
/**
     * @throws PasetoException
     * @throws \Exception
     * @throws \TypeError
     */
   
public function testTokenSignVerify()
    {
       
$secretKey = new AsymmetricSecretKey('YELLOW SUBMARINE, BLACK WIZARDRY');
       
$publicKey = $secretKey->getPublicKey();
       
$parser = new Parser(ProtocolCollection::default(), Purpose::public(), $publicKey);
       
$tainted = 'v2.public.eyJkYXRhIjoidGhpcyBpcyBhIHNpZ25lZCBtZXNzYWdlIiwiZXhwIjoiMjAzOS0wMS0wMVQwMDowMDowMCswMDowMCJ9BAOu3lUQMVHnBcPSkuORw51yiGGQ3QFUMoJO9U0gRAdAOPQEZFsd0YM_GZuBcmrXEOD1Re-Ila8vfPrfM5S6Ag';

       
$token = $parser->parse($tainted);
       
$this->assertInstanceOf(JsonToken::class, $token);
    }
}