From 9a3cf15050927bbd1f4ec184c56fbf47cc45ead6 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Thu, 22 Jun 2017 16:01:59 -0400 Subject: [PATCH 1/4] Proposal: Inject adapters, punt to adapter where appropriate. --- README.md | 12 +-- composer.json | 5 +- src/Adapter/AdapterInterface.php | 11 ++- src/Adapter/Guzzle.php | 20 ++++- src/Sapient.php | 123 ++++++++++++++++++++----------- tests/Adapter/GuzzleTest.php | 23 +++++- tests/KeyExchangeTest.php | 1 - tests/SapientSealTest.php | 79 ++++++++++---------- tests/SapientSignTest.php | 58 ++++++++------- tests/SapientSymmetricTest.php | 23 +++--- tests/SimpleTest.php | 8 +- 11 files changed, 226 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index cd35b76..ed0ff82 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ use ParagonIE\Sapient\Exception\InvalidMessageException; $http = new Client([ 'base_uri' => 'https://your-api.example.com' ]); -$adapter = new GuzzleAdapter($http); +$sapient = new Sapient(new GuzzleAdapter($http)); // Keys $clientSigningKey = new SigningSecretKey( @@ -83,7 +83,7 @@ $myMessage = [ ]; // Create the signed request: -$request = $adapter->createSignedJsonRequest( +$request = $sapient->createSignedJsonRequest( 'POST', '/my/api/endpoint', $myMessage, @@ -93,7 +93,7 @@ $request = $adapter->createSignedJsonRequest( $response = $http->send($request); try { /** @var array $verifiedResponse */ - $verifiedResponse = Sapient::decodeSignedJsonResponse( + $verifiedResponse = $sapient->decodeSignedJsonResponse( $response, $serverPublicKey ); @@ -121,7 +121,7 @@ use ParagonIE\Sapient\Exception\InvalidMessageException; $http = new Client([ 'base_uri' => 'https://your-api.example.com' ]); -$adapter = new GuzzleAdapter($http); +$sapient = new Sapient(new GuzzleAdapter($http)); $clientPublicKey = new SigningPublicKey( Base64UrlSafe::decode( @@ -131,7 +131,7 @@ $clientPublicKey = new SigningPublicKey( $request = ServerRequest::fromGlobals(); try { /** @var array $decodedRequest */ - $decodedRequest = Sapient::decodeSignedJsonRequest( + $decodedRequest = $sapient->decodeSignedJsonRequest( $request, $clientPublicKey ); @@ -157,7 +157,7 @@ $responseMessage = [ ] ]; -$response = $adapter->createSignedJsonResponse( +$response = $sapient->createSignedJsonResponse( 200, $responseMessage, $serverSignSecret diff --git a/composer.json b/composer.json index 2a182fd..3b7ec7d 100644 --- a/composer.json +++ b/composer.json @@ -4,12 +4,13 @@ "type": "library", "require": { "php": "^7", - "guzzlehttp/guzzle": "^6", "guzzlehttp/psr7": "^1", "paragonie/constant_time_encoding": "^2", - "paragonie/sodium_compat": "^1" + "paragonie/sodium_compat": "^1", + "psr/http-message": "^1" }, "require-dev": { + "guzzlehttp/guzzle": "^6", "phpunit/phpunit": "^6", "vimeo/psalm": "dev-master" }, diff --git a/src/Adapter/AdapterInterface.php b/src/Adapter/AdapterInterface.php index f6b9898..ed79e37 100644 --- a/src/Adapter/AdapterInterface.php +++ b/src/Adapter/AdapterInterface.php @@ -13,7 +13,8 @@ }; use Psr\Http\Message\{ RequestInterface, - ResponseInterface + ResponseInterface, + StreamInterface }; @@ -336,4 +337,12 @@ public function createSignedResponse( array $headers = [], string $version = '1.1' ); + + /** + * Adapter-specific way of converting a string into a StreamInterface + * + * @param string $input + * @return StreamInterface + */ + public function stringToStream(string $input): StreamInterface; } diff --git a/src/Adapter/Guzzle.php b/src/Adapter/Guzzle.php index 40f7a1c..2520de6 100644 --- a/src/Adapter/Guzzle.php +++ b/src/Adapter/Guzzle.php @@ -7,6 +7,7 @@ Request, Response }; +use function GuzzleHttp\Psr7\stream_for; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\Sapient\Exception\{ InvalidMessageException @@ -20,8 +21,7 @@ use ParagonIE\Sapient\Sapient; use ParagonIE\Sapient\Simple; use Psr\Http\Message\{ - RequestInterface, - ResponseInterface + RequestInterface, ResponseInterface, StreamInterface }; /** @@ -583,4 +583,20 @@ public function createSignedResponse( $version ); } + + /** + * Adapter-specific way of converting a string into a StreamInterface + * + * @param string $input + * @return StreamInterface + * @throws \TypeError + */ + public function stringToStream(string $input): StreamInterface + { + $stream = stream_for($input); + if (!($stream instanceof StreamInterface)) { + throw new \TypeError('Could not convert string to a stream'); + } + return $stream; + } } diff --git a/src/Sapient.php b/src/Sapient.php index 7d19876..1aa2150 100644 --- a/src/Sapient.php +++ b/src/Sapient.php @@ -2,8 +2,8 @@ declare(strict_types=1); namespace ParagonIE\Sapient; -use function GuzzleHttp\Psr7\stream_for; use ParagonIE\ConstantTime\Base64UrlSafe; +use ParagonIE\Sapient\Adapter\AdapterInterface; use ParagonIE\Sapient\Exception\{ HeaderMissingException, InvalidMessageException @@ -18,18 +18,53 @@ }; use Psr\Http\Message\{ RequestInterface, - ResponseInterface + ResponseInterface, + StreamInterface }; /** * Class Sapient * @package ParagonIE\Sapient + * + * These methods are provided by the adapter: + * @method RequestInterface createSymmetricAuthenticatedJsonRequest(string $method, string $uri, array $arrayToJsonify, SharedAuthenticationKey $key, array $headers = []) + * @method ResponseInterface createSymmetricAuthenticatedJsonResponse(int $status, array $arrayToJsonify, SharedAuthenticationKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSymmetricEncryptedJsonRequest(string $method, string $uri, array $arrayToJsonify, SharedEncryptionKey $key, array $headers = []) + * @method ResponseInterface createSymmetricEncryptedJsonResponse(int $status, array $arrayToJsonify, SharedEncryptionKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSealedJsonRequest(string $method, string $uri, array $arrayToJsonify, SealingPublicKey $key, array $headers = []) + * @method ResponseInterface createSealedJsonResponse(int $status, array $arrayToJsonify, SealingPublicKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSignedJsonRequest(string $method, string $uri, array $arrayToJsonify, SigningSecretKey $key, array $headers = []) + * @method ResponseInterface createSignedJsonResponse(int $status, array $arrayToJsonify, SigningSecretKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSymmetricAuthenticatedRequest(string $method, string $uri, string $body, SharedAuthenticationKey $key, array $headers = []) + * @method ResponseInterface createSymmetricAuthenticatedResponse(int $status, string $body, SharedAuthenticationKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSymmetricEncryptedRequest(string $method, string $uri, string $body, SharedEncryptionKey $key, array $headers = []) + * @method ResponseInterface createSymmetricEncryptedResponse(int $status, string $body, SharedEncryptionKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSealedRequest(string $method, string $uri, string $body, SealingPublicKey $key, array $headers = []) + * @method ResponseInterface createSealedResponse(int $status, string $body, SealingPublicKey $key, array $headers = [], string $version = '1.1') + * @method RequestInterface createSignedRequest(string $method, string $uri, string $body, SigningSecretKey $key, array $headers = []) + * @method ResponseInterface createSignedResponse(int $status, string $body, SigningSecretKey $key, array $headers = [], string $version = '1.1') + * @method StreamInterface stringToStream(string $input) */ class Sapient { const HEADER_AUTH_NAME = 'Body-HMAC-SHA512256'; const HEADER_SIGNATURE_NAME = 'Body-Signature-Ed25519'; + /** + * @var AdapterInterface + */ + protected $adapter; + + /** + * Sapient constructor. + * + * @param AdapterInterface $adapter + */ + public function __construct(AdapterInterface $adapter) + { + $this->adapter = $adapter; + } + /** * Verify the Body-Signature-Ed25519 header, and then decode the HTTP * Request body into an array (assuming the body is a valid JSON string). @@ -38,12 +73,12 @@ class Sapient * @param SigningPublicKey $publicKey * @return array */ - public static function decodeSignedJsonRequest( + public function decodeSignedJsonRequest( RequestInterface $request, SigningPublicKey $publicKey ): array { return \json_decode( - static::decodeSignedRequest($request, $publicKey), + $this->decodeSignedRequest($request, $publicKey), true ); } @@ -56,11 +91,11 @@ public static function decodeSignedJsonRequest( * @param SigningPublicKey $publicKey * @return string */ - public static function decodeSignedRequest( + public function decodeSignedRequest( RequestInterface $request, SigningPublicKey $publicKey ): string { - $verified = static::verifySignedRequest($request, $publicKey); + $verified = $this->verifySignedRequest($request, $publicKey); return (string) $verified->getBody(); } @@ -72,12 +107,12 @@ public static function decodeSignedRequest( * @param SigningPublicKey $publicKey * @return array */ - public static function decodeSignedJsonResponse( + public function decodeSignedJsonResponse( ResponseInterface $response, SigningPublicKey $publicKey ): array { return \json_decode( - static::decodeSignedResponse($response, $publicKey), + $this->decodeSignedResponse($response, $publicKey), true ); } @@ -90,11 +125,11 @@ public static function decodeSignedJsonResponse( * @param SigningPublicKey $publicKey * @return string */ - public static function decodeSignedResponse( + public function decodeSignedResponse( ResponseInterface $response, SigningPublicKey $publicKey ): string { - $verified = static::verifySignedResponse($response, $publicKey); + $verified = $this->verifySignedResponse($response, $publicKey); return (string) $verified->getBody(); } @@ -106,11 +141,11 @@ public static function decodeSignedResponse( * @param SharedEncryptionKey $key * @return array */ - public static function decryptJsonRequestWithSharedKey( + public function decryptJsonRequestWithSharedKey( RequestInterface $request, SharedEncryptionKey $key ): array { - $decrypted = static::decryptRequestWithSharedKey( + $decrypted = $this->decryptRequestWithSharedKey( $request, $key ); @@ -127,11 +162,11 @@ public static function decryptJsonRequestWithSharedKey( * @param SharedEncryptionKey $key * @return array */ - public static function decryptJsonResponseWithSharedKey( + public function decryptJsonResponseWithSharedKey( ResponseInterface $response, SharedEncryptionKey $key ): array { - $decrypted = static::decryptResponseWithSharedKey( + $decrypted = $this->decryptResponseWithSharedKey( $response, $key ); @@ -148,13 +183,13 @@ public static function decryptJsonResponseWithSharedKey( * @param SharedEncryptionKey $key * @return RequestInterface */ - public static function decryptRequestWithSharedKey( + public function decryptRequestWithSharedKey( RequestInterface $request, SharedEncryptionKey $key ): RequestInterface { $encrypted = Base64UrlSafe::decode((string) $request->getBody()); return $request->withBody( - stream_for( + $this->adapter->stringToStream( Simple::decrypt($encrypted, $key) ) ); @@ -167,13 +202,13 @@ public static function decryptRequestWithSharedKey( * @param SharedEncryptionKey $key * @return ResponseInterface */ - public static function decryptResponseWithSharedKey( + public function decryptResponseWithSharedKey( ResponseInterface $response, SharedEncryptionKey $key ): ResponseInterface { $encrypted = Base64UrlSafe::decode((string) $response->getBody()); return $response->withBody( - stream_for( + $this->adapter->stringToStream( Simple::decrypt($encrypted, $key) ) ); @@ -186,7 +221,7 @@ public static function decryptResponseWithSharedKey( * @param SharedEncryptionKey $key * @return RequestInterface */ - public static function encryptRequestWithSharedKey( + public function encryptRequestWithSharedKey( RequestInterface $request, SharedEncryptionKey $key ): RequestInterface { @@ -194,7 +229,7 @@ public static function encryptRequestWithSharedKey( Simple::encrypt((string) $request->getBody(), $key) ); return $request->withBody( - stream_for($encrypted) + $this->adapter->stringToStream($encrypted) ); } @@ -205,7 +240,7 @@ public static function encryptRequestWithSharedKey( * @param SharedEncryptionKey $key * @return ResponseInterface */ - public static function encryptResponseWithSharedKey( + public function encryptResponseWithSharedKey( ResponseInterface $response, SharedEncryptionKey $key ): ResponseInterface { @@ -213,7 +248,7 @@ public static function encryptResponseWithSharedKey( Simple::encrypt((string) $response->getBody(), $key) ); return $response->withBody( - stream_for($encrypted) + $this->adapter->stringToStream($encrypted) ); } @@ -224,7 +259,7 @@ public static function encryptResponseWithSharedKey( * @param SealingPublicKey $publicKey * @return RequestInterface */ - public static function sealRequest( + public function sealRequest( RequestInterface $request, SealingPublicKey $publicKey ): RequestInterface { @@ -233,7 +268,7 @@ public static function sealRequest( $publicKey ); return $request->withBody( - stream_for( + $this->adapter->stringToStream( Base64UrlSafe::encode($sealed) ) ); @@ -246,7 +281,7 @@ public static function sealRequest( * @param SealingPublicKey $publicKey * @return ResponseInterface */ - public static function sealResponse( + public function sealResponse( ResponseInterface $response, SealingPublicKey $publicKey ): ResponseInterface { @@ -255,7 +290,7 @@ public static function sealResponse( $publicKey ); return $response->withBody( - stream_for( + $this->adapter->stringToStream( Base64UrlSafe::encode($sealed) ) ); @@ -268,7 +303,7 @@ public static function sealResponse( * @param SigningSecretKey $secretKey * @return RequestInterface */ - public static function signRequest( + public function signRequest( RequestInterface $request, SigningSecretKey $secretKey ): RequestInterface { @@ -289,7 +324,7 @@ public static function signRequest( * @param SigningSecretKey $secretKey * @return ResponseInterface */ - public static function signResponse( + public function signResponse( ResponseInterface $response, SigningSecretKey $secretKey ): ResponseInterface { @@ -311,12 +346,12 @@ public static function signResponse( * @param SealingSecretKey $secretKey * @return array */ - public static function unsealJsonRequest( + public function unsealJsonRequest( RequestInterface $request, SealingSecretKey $secretKey ): array { return \json_decode( - (string) static::unsealRequest($request, $secretKey)->getBody(), + (string) $this->unsealRequest($request, $secretKey)->getBody(), true ); } @@ -329,12 +364,12 @@ public static function unsealJsonRequest( * @param SealingSecretKey $secretKey * @return array */ - public static function unsealJsonResponse( + public function unsealJsonResponse( ResponseInterface $response, SealingSecretKey $secretKey ): array { return \json_decode( - (string) static::unsealResponse($response, $secretKey)->getBody(), + (string) $this->unsealResponse($response, $secretKey)->getBody(), true ); } @@ -348,7 +383,7 @@ public static function unsealJsonResponse( * @return RequestInterface * @throws InvalidMessageException */ - public static function unsealRequest( + public function unsealRequest( RequestInterface $request, SealingSecretKey $secretKey ): RequestInterface { @@ -357,7 +392,7 @@ public static function unsealRequest( $body, $secretKey ); - return $request->withBody(stream_for($unsealed)); + return $request->withBody($this->adapter->stringToStream($unsealed)); } /** @@ -369,7 +404,7 @@ public static function unsealRequest( * @return ResponseInterface * @throws InvalidMessageException */ - public static function unsealResponse( + public function unsealResponse( ResponseInterface $response, SealingSecretKey $secretKey ): ResponseInterface { @@ -378,7 +413,7 @@ public static function unsealResponse( $body, $secretKey ); - return $response->withBody(stream_for($unsealed)); + return $response->withBody($this->adapter->stringToStream($unsealed)); } /** @@ -394,7 +429,7 @@ public static function unsealResponse( * @throws HeaderMissingException * @throws InvalidMessageException */ - public static function verifySignedRequest( + public function verifySignedRequest( RequestInterface $request, SigningPublicKey $publicKey ): RequestInterface { @@ -433,7 +468,7 @@ public static function verifySignedRequest( * @throws HeaderMissingException * @throws InvalidMessageException */ - public static function verifySignedResponse( + public function verifySignedResponse( ResponseInterface $response, SigningPublicKey $publicKey ): ResponseInterface { @@ -470,7 +505,7 @@ public static function verifySignedResponse( * @throws HeaderMissingException * @throws InvalidMessageException */ - public static function verifySymmetricAuthenticatedRequest( + public function verifySymmetricAuthenticatedRequest( RequestInterface $request, SharedAuthenticationKey $key ): RequestInterface { @@ -508,7 +543,7 @@ public static function verifySymmetricAuthenticatedRequest( * @throws HeaderMissingException * @throws InvalidMessageException */ - public static function verifySymmetricAuthenticatedResponse( + public function verifySymmetricAuthenticatedResponse( ResponseInterface $response, SharedAuthenticationKey $key ): ResponseInterface { @@ -535,7 +570,7 @@ public static function verifySymmetricAuthenticatedResponse( } /** - * Magic method in case this is called in an object context. + * Punt adapter methods to the adapter. * * @param string $name * @param array $arguments @@ -544,9 +579,9 @@ public static function verifySymmetricAuthenticatedResponse( */ public function __call($name, $arguments) { - if (\is_callable([$name, $arguments])) { - throw new \Error('Method not found: ' . $name); + if (!\method_exists($this->adapter, $name)) { + throw new \Error('Could not call method ' . $name); } - return self::$name(...$arguments); + return $this->adapter->$name(...$arguments); } } diff --git a/tests/Adapter/GuzzleTest.php b/tests/Adapter/GuzzleTest.php index 234aedb..08d0d43 100644 --- a/tests/Adapter/GuzzleTest.php +++ b/tests/Adapter/GuzzleTest.php @@ -15,6 +15,7 @@ SigningSecretKey }; use PHPUnit\Framework\TestCase; +use Psr\Http\Message\StreamInterface; class GuzzleTest extends TestCase { @@ -73,7 +74,10 @@ public function setup() } /** - * @covers Guzzle + * @covers Guzzle::createSealedJsonRequest() + * @covers Guzzle::createSignedJsonRequest() + * @covers Guzzle::createSymmetricAuthenticatedJsonRequest() + * @covers Guzzle::createSymmetricEncryptedJsonRequest() */ public function testReturnTypeForCreateRequest() { @@ -97,17 +101,20 @@ public function testReturnTypeForCreateRequest() /** - * @covers Guzzle + * @covers Guzzle::createSealedJsonResponse() + * @covers Guzzle::createSignedJsonResponse() + * @covers Guzzle::createSymmetricAuthenticatedJsonResponse() + * @covers Guzzle::createSymmetricEncryptedJsonResponse() */ public function testReturnTypeForCreateResponse() { $this->assertInstanceOf( Response::class, - $this->adapter->createSignedJsonResponse(200, [], $this->serverSignSecret) + $this->adapter->createSealedJsonResponse(200, [], $this->serverSealPublic) ); $this->assertInstanceOf( Response::class, - $this->adapter->createSealedJsonResponse(200, [], $this->serverSealPublic) + $this->adapter->createSignedJsonResponse(200, [], $this->serverSignSecret) ); $this->assertInstanceOf( Response::class, @@ -118,4 +125,12 @@ public function testReturnTypeForCreateResponse() $this->adapter->createSymmetricAuthenticatedJsonResponse(200, [], $this->sharedAuthenticationKey) ); } + + /** + * @covers Guzzle::stringToStream() + */ + public function testStringToStream() + { + $this->assertInstanceOf(StreamInterface::class, $this->adapter->stringToStream('test')); + } } diff --git a/tests/KeyExchangeTest.php b/tests/KeyExchangeTest.php index 7b3d1d0..bfeac92 100644 --- a/tests/KeyExchangeTest.php +++ b/tests/KeyExchangeTest.php @@ -5,7 +5,6 @@ SealingPublicKey, SealingSecretKey }; -use ParagonIE\Sapient\Sapient; use PHPUnit\Framework\TestCase; /** diff --git a/tests/SapientSealTest.php b/tests/SapientSealTest.php index a70b959..ace387e 100644 --- a/tests/SapientSealTest.php +++ b/tests/SapientSealTest.php @@ -1,10 +1,8 @@ sapient = new Sapient(new Guzzle()); + $this->clientSealSecret = SealingSecretKey::generate(); $this->clientSealPublic = $this->clientSealSecret->getPublickey(); @@ -86,7 +85,7 @@ public function testSignedJsonRequest() $obj, $this->clientSealPublic ); - $decoded = Sapient::unsealJsonRequest( + $decoded = $this->sapient->unsealJsonRequest( $request, $this->clientSealSecret ); @@ -94,7 +93,7 @@ public function testSignedJsonRequest() /* We expect an exception: */ try { - Sapient::unsealJsonRequest( + $this->sapient->unsealJsonRequest( $request, $this->serverSealSecret ); @@ -102,12 +101,14 @@ public function testSignedJsonRequest() } catch (\Throwable $ex) { } - $invalid = $request->withBody(stream_for( - Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') - )); + $invalid = $request->withBody( + $this->sapient->stringToStream( + Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') + ) + ); /* We expect an exception: */ try { - Sapient::unsealJsonRequest( + $this->sapient->unsealJsonRequest( $invalid, $this->clientSealSecret ); @@ -128,14 +129,13 @@ public function testSignedRequest() \random_int(101, 200) ) ); - $guzzle = new Guzzle(); - $request = $guzzle->createSealedRequest( + $request = $this->sapient->createSealedRequest( 'POST', '/', $randomMessage, $this->clientSealPublic ); - $decoded = Sapient::unsealRequest( + $decoded = $this->sapient->unsealRequest( $request, $this->clientSealSecret ); @@ -144,7 +144,7 @@ public function testSignedRequest() /* Test bad public key */ try { - Sapient::unsealRequest( + $this->sapient->unsealRequest( $request, $this->serverSealSecret ); @@ -152,13 +152,15 @@ public function testSignedRequest() } catch (\Throwable $ex) { } - $invalid = $request->withBody(stream_for( - Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') - )); + $invalid = $request->withBody( + $this->sapient->stringToStream( + Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') + ) + ); /* Test bad message */ try { - Sapient::unsealRequest( + $this->sapient->unsealRequest( $invalid, $this->serverSealSecret ); @@ -181,18 +183,18 @@ public function testSignedJsonResponse() $obj, $this->serverSealPublic ); - $responseRaw = Sapient::unsealResponse( + $responseRaw = $this->sapient->unsealResponse( $response, $this->serverSealSecret ); $this->assertInstanceOf(Response::class, $responseRaw); - $decoded = Sapient::unsealJsonResponse($response, $this->serverSealSecret); + $decoded = $this->sapient->unsealJsonResponse($response, $this->serverSealSecret); $this->assertSame($obj, $decoded); /* Test bad public key */ try { - Sapient::unsealResponse( + $this->sapient->unsealResponse( $response, $this->clientSealSecret ); @@ -200,12 +202,14 @@ public function testSignedJsonResponse() } catch (\Throwable $ex) { } - $invalid = $response->withBody(stream_for( - Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') - )); + $invalid = $response->withBody( + $this->sapient->stringToStream( + Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') + ) + ); /* Test bad message */ try { - Sapient::unsealResponse( + $this->sapient->unsealResponse( $invalid, $this->serverSealSecret ); @@ -226,24 +230,23 @@ public function testSealedResponse() \random_int(101, 200) ) ); - $guzzle = new Guzzle(); - $response = $guzzle->createSealedResponse( + $response = $this->sapient->createSealedResponse( 200, $randomMessage, $this->serverSealPublic ); - $responseRaw = Sapient::unsealResponse( + $responseRaw = $this->sapient->unsealResponse( $response, $this->serverSealSecret ); $this->assertInstanceOf(Response::class, $responseRaw); - $decoded = Sapient::unsealResponse($response, $this->serverSealSecret); + $decoded = $this->sapient->unsealResponse($response, $this->serverSealSecret); $this->assertSame($randomMessage, (string) $decoded->getBody()); /* Test bad public key */ try { - Sapient::unsealResponse( + $this->sapient->unsealResponse( $response, $this->clientSealSecret ); @@ -251,12 +254,14 @@ public function testSealedResponse() } catch (\Throwable $ex) { } - $invalid = $response->withBody(stream_for( - Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') - )); + $invalid = $response->withBody( + $this->sapient->stringToStream( + Base64UrlSafe::encode('invalid message goes here for verifying the failure of crypto_box_seal') + ) + ); /* Test bad message */ try { - Sapient::unsealResponse( + $this->sapient->unsealResponse( $invalid, $this->serverSealSecret ); @@ -278,9 +283,9 @@ public function testPsr7() ); $request = new Request('POST', '/test', [], $randomMessage); - $signedRequest = Sapient::sealRequest($request, $this->clientSealPublic); + $signedRequest = $this->sapient->sealRequest($request, $this->clientSealPublic); try { - $unsealed = Sapient::unsealRequest( + $unsealed = $this->sapient->unsealRequest( $signedRequest, $this->clientSealSecret ); @@ -293,9 +298,9 @@ public function testPsr7() } $response = new Response(200, [], $randomMessage); - $signedResponse = Sapient::sealResponse($response, $this->clientSealPublic); + $signedResponse = $this->sapient->sealResponse($response, $this->clientSealPublic); try { - $unsealed = Sapient::unsealResponse( + $unsealed = $this->sapient->unsealResponse( $signedResponse, $this->clientSealSecret ); diff --git a/tests/SapientSignTest.php b/tests/SapientSignTest.php index 4b33352..97e036f 100644 --- a/tests/SapientSignTest.php +++ b/tests/SapientSignTest.php @@ -1,10 +1,8 @@ sapient = new Sapient(new Guzzle()); + $this->clientSignSecret = SigningSecretKey::generate(); $this->clientSignPublic = $this->clientSignSecret->getPublickey(); @@ -84,17 +87,17 @@ public function testSignedJsonRequest() $obj, $this->clientSignSecret ); - $valid = Sapient::verifySignedRequest( + $valid = $this->sapient->verifySignedRequest( $request, $this->clientSignPublic ); $this->assertInstanceOf(Request::class, $valid); - $decoded = Sapient::decodeSignedJsonRequest($request, $this->clientSignPublic); + $decoded = $this->sapient->decodeSignedJsonRequest($request, $this->clientSignPublic); $this->assertSame($obj, $decoded); /* We expect an exception: */ try { - Sapient::verifySignedRequest( + $this->sapient->verifySignedRequest( $request, $this->serverSignPublic ); @@ -102,10 +105,10 @@ public function testSignedJsonRequest() } catch (\Throwable $ex) { } - $invalid = $request->withBody(stream_for('invalid message')); + $invalid = $request->withBody($this->sapient->stringToStream(('invalid message'))); /* We expect an exception: */ try { - Sapient::verifySignedRequest( + $this->sapient->verifySignedRequest( $invalid, $this->clientSignPublic ); @@ -133,18 +136,18 @@ public function testSignedRequest() $randomMessage, $this->clientSignSecret ); - $valid = Sapient::verifySignedRequest( + $valid = $this->sapient->verifySignedRequest( $request, $this->clientSignPublic ); $this->assertInstanceOf(Request::class, $valid); - $decoded = Sapient::decodeSignedRequest($request, $this->clientSignPublic); + $decoded = $this->sapient->decodeSignedRequest($request, $this->clientSignPublic); $this->assertSame($randomMessage, $decoded); /* Test bad public key */ try { - Sapient::verifySignedRequest( + $this->sapient->verifySignedRequest( $request, $this->serverSignPublic ); @@ -152,11 +155,11 @@ public function testSignedRequest() } catch (\Throwable $ex) { } - $invalid = $request->withBody(stream_for('invalid message')); + $invalid = $request->withBody($this->sapient->stringToStream('invalid message')); /* Test bad message */ try { - Sapient::verifySignedRequest( + $this->sapient->verifySignedRequest( $invalid, $this->clientSignPublic ); @@ -180,15 +183,15 @@ public function testSignedJsonResponse() $obj, $this->serverSignSecret ); - $valid = Sapient::verifySignedResponse($response, $this->serverSignPublic); + $valid = $this->sapient->verifySignedResponse($response, $this->serverSignPublic); $this->assertInstanceOf(Response::class, $valid); - $decoded = Sapient::decodeSignedJsonResponse($response, $this->serverSignPublic); + $decoded = $this->sapient->decodeSignedJsonResponse($response, $this->serverSignPublic); $this->assertSame($obj, $decoded); /* Test bad public key */ try { - Sapient::verifySignedResponse( + $this->sapient->verifySignedResponse( $valid, $this->clientSignPublic ); @@ -196,10 +199,10 @@ public function testSignedJsonResponse() } catch (\Throwable $ex) { } - $invalid = $response->withBody(stream_for('invalid message')); + $invalid = $response->withBody($this->sapient->stringToStream('invalid message')); /* Test bad message */ try { - Sapient::verifySignedResponse( + $this->sapient->verifySignedResponse( $invalid, $this->serverSignPublic ); @@ -221,21 +224,20 @@ public function testSignedResponse() ) ); - $guzzle = new Guzzle(); - $response = $guzzle->createSignedResponse( + $response = $this->sapient->createSignedResponse( 200, $randomMessage, $this->serverSignSecret ); - $valid = Sapient::verifySignedResponse($response, $this->serverSignPublic); + $valid = $this->sapient->verifySignedResponse($response, $this->serverSignPublic); $this->assertInstanceOf(Response::class, $valid); - $decoded = Sapient::decodeSignedResponse($response, $this->serverSignPublic); + $decoded = $this->sapient->decodeSignedResponse($response, $this->serverSignPublic); $this->assertSame($randomMessage, $decoded); /* Test bad public key */ try { - Sapient::verifySignedResponse( + $this->sapient->verifySignedResponse( $valid, $this->clientSignPublic ); @@ -243,10 +245,10 @@ public function testSignedResponse() } catch (\Throwable $ex) { } - $invalid = $response->withBody(stream_for('invalid message')); + $invalid = $response->withBody($this->sapient->stringToStream('invalid message')); /* Test bad message */ try { - Sapient::verifySignedResponse( + $this->sapient->verifySignedResponse( $invalid, $this->serverSignPublic ); @@ -268,9 +270,9 @@ public function testPsr7() ); $request = new Request('POST', '/test', [], $randomMessage); - $signedRequest = Sapient::signRequest($request, $this->clientSignSecret); + $signedRequest = $this->sapient->signRequest($request, $this->clientSignSecret); try { - $verified = Sapient::verifySignedRequest( + $verified = $this->sapient->verifySignedRequest( $signedRequest, $this->clientSignPublic ); @@ -288,9 +290,9 @@ public function testPsr7() } $response = new Response(200, [], $randomMessage); - $signedResponse = Sapient::signResponse($response, $this->serverSignSecret); + $signedResponse = $this->sapient->signResponse($response, $this->serverSignSecret); try { - $verified = Sapient::verifySignedResponse( + $verified = $this->sapient->verifySignedResponse( $signedResponse, $this->serverSignPublic ); diff --git a/tests/SapientSymmetricTest.php b/tests/SapientSymmetricTest.php index d2908b1..39c5f27 100644 --- a/tests/SapientSymmetricTest.php +++ b/tests/SapientSymmetricTest.php @@ -18,6 +18,9 @@ */ class SapientSymmetricTest extends TestCase { + /** @var Sapient */ + protected $sapient; + /** @var SharedAuthenticationKey */ protected $sharedAuthenticationKey; @@ -51,6 +54,7 @@ private function getSampleObjects(): array */ public function setUp() { + $this->sapient = new Sapient(new Guzzle()); $this->sharedEncryptionKey = SharedEncryptionKey::generate(); $this->sharedAuthenticationKey = SharedAuthenticationKey::generate(); } @@ -62,21 +66,20 @@ public function setUp() public function testEncryptDecryptJsonRequest() { foreach ($this->getSampleObjects() as $obj) { - $guzzle = new Guzzle(); - $request = $guzzle->createSymmetricEncryptedJsonRequest( + $request = $this->sapient->createSymmetricEncryptedJsonRequest( 'POST', '/test/api-endpoint', $obj, $this->sharedEncryptionKey ); - $decrypted = Sapient::decryptJsonRequestWithSharedKey( + $decrypted = $this->sapient->decryptJsonRequestWithSharedKey( $request, $this->sharedEncryptionKey ); $this->assertSame($obj, $decrypted); try { - Sapient::decryptJsonRequestWithSharedKey( + $this->sapient->decryptJsonRequestWithSharedKey( $request, SharedEncryptionKey::generate() ); @@ -103,7 +106,7 @@ public function testEncryptDecryptRequest() $randomMessage, $this->sharedEncryptionKey ); - $decrypted = Sapient::decryptRequestWithSharedKey( + $decrypted = $this->sapient->decryptRequestWithSharedKey( $request, $this->sharedEncryptionKey ); @@ -113,7 +116,7 @@ public function testEncryptDecryptRequest() $this->assertSame($randomMessage, $decryptedBody); try { - Sapient::decryptRequestWithSharedKey( + $this->sapient->decryptRequestWithSharedKey( $request, SharedEncryptionKey::generate() ); @@ -136,14 +139,14 @@ public function testEncryptDecryptJsonResponse() $obj, $this->sharedEncryptionKey ); - $decrypted = Sapient::decryptJsonResponseWithSharedKey( + $decrypted = $this->sapient->decryptJsonResponseWithSharedKey( $Response, $this->sharedEncryptionKey ); $this->assertSame($obj, $decrypted); try { - Sapient::decryptJsonResponseWithSharedKey( + $this->sapient->decryptJsonResponseWithSharedKey( $Response, SharedEncryptionKey::generate() ); @@ -169,7 +172,7 @@ public function testEncryptDecryptResponse() $randomMessage, $this->sharedEncryptionKey ); - $decrypted = Sapient::decryptResponseWithSharedKey( + $decrypted = $this->sapient->decryptResponseWithSharedKey( $response, $this->sharedEncryptionKey ); @@ -179,7 +182,7 @@ public function testEncryptDecryptResponse() $this->assertSame($randomMessage, $decryptedBody); try { - Sapient::decryptResponseWithSharedKey( + $this->sapient->decryptResponseWithSharedKey( $response, SharedEncryptionKey::generate() ); diff --git a/tests/SimpleTest.php b/tests/SimpleTest.php index 0139574..986c210 100644 --- a/tests/SimpleTest.php +++ b/tests/SimpleTest.php @@ -1,8 +1,11 @@ getPublickey(); $messages = [ From d38782bb701911478a1e60d305251c344ef6a9a3 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Thu, 22 Jun 2017 16:06:15 -0400 Subject: [PATCH 2/4] Skip the Guzzle test if it's not installed. --- tests/Adapter/GuzzleTest.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Adapter/GuzzleTest.php b/tests/Adapter/GuzzleTest.php index 08d0d43..7614b3a 100644 --- a/tests/Adapter/GuzzleTest.php +++ b/tests/Adapter/GuzzleTest.php @@ -56,6 +56,10 @@ class GuzzleTest extends TestCase */ public function setup() { + if (!\class_exists('GuzzleHttp\Client')) { + $this->markTestSkipped('Guzzle not included as a dependency.'); + return; + } $this->adapter = new Guzzle(); $this->clientSignSecret = SigningSecretKey::generate(); $this->clientSignPublic = $this->clientSignSecret->getPublickey(); From 62c9b8742c82c936ce5b8c58d8d49a740e1bd011 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Thu, 22 Jun 2017 16:09:31 -0400 Subject: [PATCH 3/4] Suggest Guzzle. --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index 3b7ec7d..6a1884e 100644 --- a/composer.json +++ b/composer.json @@ -14,6 +14,9 @@ "phpunit/phpunit": "^6", "vimeo/psalm": "dev-master" }, + "suggest": { + "guzzlehttp/guzzle": ">= 6.0 -- Sapient can be used a Guzzle adapter" + }, "autoload": { "psr-4": { "ParagonIE\\Sapient\\": "src" From 922bb964f540b40e0757914bd27badb9d7ca3ae7 Mon Sep 17 00:00:00 2001 From: Paragon Initiative Enterprises Date: Thu, 22 Jun 2017 16:11:24 -0400 Subject: [PATCH 4/4] Proper whitespace. --- src/Adapter/Guzzle.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Adapter/Guzzle.php b/src/Adapter/Guzzle.php index 2520de6..bbb981f 100644 --- a/src/Adapter/Guzzle.php +++ b/src/Adapter/Guzzle.php @@ -21,7 +21,9 @@ use ParagonIE\Sapient\Sapient; use ParagonIE\Sapient\Simple; use Psr\Http\Message\{ - RequestInterface, ResponseInterface, StreamInterface + RequestInterface, + ResponseInterface, + StreamInterface }; /**