<?php
namespace ParagonIE\Sapient\UnitTests;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use ParagonIE\ConstantTime\Base64UrlSafe;
use ParagonIE\Sapient\Adapter\Guzzle;
use ParagonIE\Sapient\CryptographyKeys\{
SigningPublicKey,
SigningSecretKey
};
use ParagonIE\Sapient\Exception\HeaderMissingException;
use ParagonIE\Sapient\Exception\InvalidMessageException;
use ParagonIE\Sapient\Sapient;
use PHPUnit\Framework\TestCase;
/**
* Class SapientTest
* @package ParagonIE\Sapient\UnitTests
*/
class SapientSignTest extends TestCase
{
/** @var Sapient */
protected $sapient;
/** @var SigningSecretKey */
protected $clientSignSecret;
/** @var SigningPublicKey */
protected $clientSignPublic;
/** @var SigningSecretKey */
protected $serverSignSecret;
/** @var SigningPublicKey */
protected $serverSignPublic;
/**
* Setup the class properties
*/
public function setUp()
{
$this->sapient = new Sapient(new Guzzle());
$this->clientSignSecret = SigningSecretKey::generate();
$this->clientSignPublic = $this->clientSignSecret->getPublickey();
$this->serverSignSecret = SigningSecretKey::generate();
$this->serverSignPublic = $this->serverSignSecret->getPublickey();
}
private function getSampleObjects(): array
{
return [
[],
['test' => 'abcdefg'],
['random' => Base64UrlSafe::encode(
\random_bytes(
\random_int(1, 100)
)
)
],
['structued' => [
'abc' => 'def',
'o' => null,
'ghi' => ['j', 'k', 'l'],
'm' => 1234,
'n' => 56.78,
'p' => ['q' => ['r' => []]]
]]
];
}
/**
* @covers Sapient::createSignedJsonRequest()
* @covers Sapient::verifySignedRequest()
*/
public function testSignedJsonRequest()
{
$sampleObjects = $this->getSampleObjects();
foreach ($sampleObjects as $obj) {
$guzzle = new Guzzle();
$request = $guzzle->createSignedJsonRequest(
'POST',
'/',
$obj,
$this->clientSignSecret
);
$valid = $this->sapient->verifySignedRequest(
$request,
$this->clientSignPublic
);
$this->assertInstanceOf(Request::class, $valid);
$decoded = $this->sapient->decodeSignedJsonRequest($request, $this->clientSignPublic);
$this->assertSame($obj, $decoded);
/* We expect an exception: */
try {
$this->sapient->verifySignedRequest(
$request,
$this->serverSignPublic
);
$this->fail('Bad message signature');
} catch (\Throwable $ex) {
}
$invalid = $request->withBody($this->sapient->stringToStream(('invalid message')));
/* We expect an exception: */
try {
$this->sapient->verifySignedRequest(
$invalid,
$this->clientSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
}
}
/**
* @covers Sapient::createSignedRequest()
* @covers Sapient::verifySignedRequest()
*/
public function testSignedRequest()
{
$randomMessage = Base64UrlSafe::encode(
\random_bytes(
\random_int(101, 200)
)
);
$guzzle = new Guzzle();
$request = $guzzle->createSignedRequest(
'POST',
'/',
$randomMessage,
$this->clientSignSecret
);
$valid = $this->sapient->verifySignedRequest(
$request,
$this->clientSignPublic
);
$this->assertInstanceOf(Request::class, $valid);
$decoded = $this->sapient->verifySignedStringRequest($request, $this->clientSignPublic);
$this->assertSame($randomMessage, $decoded);
/* Test bad public key */
try {
$this->sapient->verifySignedRequest(
$request,
$this->serverSignPublic
);
$this->fail('Bad message signature');
} catch (\Throwable $ex) {
}
$invalid = $request->withBody($this->sapient->stringToStream('invalid message'));
/* Test bad message */
try {
$this->sapient->verifySignedRequest(
$invalid,
$this->clientSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
}
/**
* @covers Sapient::createSignedJsonResponse()
* @covers Sapient::verifySignedResponse()
*/
public function testSignedJsonResponse()
{
$sampleObjects = $this->getSampleObjects();
foreach ($sampleObjects as $obj) {
$guzzle = new Guzzle();
$response = $guzzle->createSignedJsonResponse(
200,
$obj,
$this->serverSignSecret
);
$valid = $this->sapient->verifySignedResponse($response, $this->serverSignPublic);
$this->assertInstanceOf(Response::class, $valid);
$decoded = $this->sapient->decodeSignedJsonResponse($response, $this->serverSignPublic);
$this->assertSame($obj, $decoded);
/* Test bad public key */
try {
$this->sapient->verifySignedResponse(
$valid,
$this->clientSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
$invalid = $response->withBody($this->sapient->stringToStream('invalid message'));
/* Test bad message */
try {
$this->sapient->verifySignedResponse(
$invalid,
$this->serverSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
}
}
/**
* @covers Sapient::createSignedResponse()
* @covers Sapient::verifySignedResponse()
*/
public function testSignedResponse()
{
$randomMessage = Base64UrlSafe::encode(
\random_bytes(
\random_int(101, 200)
)
);
$response = $this->sapient->createSignedResponse(
200,
$randomMessage,
$this->serverSignSecret
);
$valid = $this->sapient->verifySignedResponse($response, $this->serverSignPublic);
$this->assertInstanceOf(Response::class, $valid);
$decoded = $this->sapient->verifySignedStringResponse($response, $this->serverSignPublic);
$this->assertSame($randomMessage, $decoded);
/* Test bad public key */
try {
$this->sapient->verifySignedResponse(
$valid,
$this->clientSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
$invalid = $response->withBody($this->sapient->stringToStream('invalid message'));
/* Test bad message */
try {
$this->sapient->verifySignedResponse(
$invalid,
$this->serverSignPublic
);
$this->fail('Bad message accepted');
} catch (\Throwable $ex) {
}
}
/**
* @covers Sapient::signRequest()
* @covers Sapient::signResponse()
*/
public function testPsr7()
{
$randomMessage = Base64UrlSafe::encode(
\random_bytes(
\random_int(101, 200)
)
);
$request = new Request('POST', '/test', [], $randomMessage);
$signedRequest = $this->sapient->signRequest($request, $this->clientSignSecret);
try {
$verified = $this->sapient->verifySignedRequest(
$signedRequest,
$this->clientSignPublic
);
$this->assertSame(
$randomMessage,
(string) $verified->getBody()
);
$this->assertNotEmpty(
$verified->getHeader(Sapient::HEADER_SIGNATURE_NAME)
);
} catch (HeaderMissingException $exception) {
$this->fail('No header added');
} catch (InvalidMessageException $exception) {
$this->fail('Invalid signature');
}
$response = new Response(200, [], $randomMessage);
$signedResponse = $this->sapient->signResponse($response, $this->serverSignSecret);
try {
$verified = $this->sapient->verifySignedResponse(
$signedResponse,
$this->serverSignPublic
);
$this->assertSame(
$randomMessage,
(string) $verified->getBody()
);
$this->assertNotEmpty(
$verified->getHeader(Sapient::HEADER_SIGNATURE_NAME)
);
} catch (HeaderMissingException $exception) {
$this->fail('No header added');
} catch (InvalidMessageException $exception) {
$this->fail('Invalid signature');
}
}
}
|