PHP Classes

File: src/Adapter/Guzzle.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   sapient   src/Adapter/Guzzle.php   Download  
File: src/Adapter/Guzzle.php
Role: Class source
Content type: text/plain
Description: Class source
Class: sapient
Add a security layer to server to server requests
Author: By
Last change: Move convenience method definitions outside of AdapterInterface.
Date: 6 years ago
Size: 16,902 bytes
 

Contents

Class file image Download
<?php declare(strict_types=1); namespace ParagonIE\Sapient\Adapter; use GuzzleHttp\Client; use GuzzleHttp\Psr7\{ Request, Response }; use function GuzzleHttp\Psr7\stream_for; use ParagonIE\ConstantTime\Base64UrlSafe; use ParagonIE\Sapient\Exception\{ InvalidMessageException }; use ParagonIE\Sapient\CryptographyKeys\{ SealingPublicKey, SharedAuthenticationKey, SharedEncryptionKey, SigningSecretKey }; use ParagonIE\Sapient\Sapient; use ParagonIE\Sapient\Simple; use Psr\Http\Message\{ RequestInterface, ResponseInterface, StreamInterface }; /** * Class Guzzle * @package ParagonIE\Sapient\Adapter */ class Guzzle implements AdapterInterface, ConvenienceInterface { /** * @var Client */ protected $guzzle; /** * Guzzle constructor. * @param Client $guzzleClient */ public function __construct(Client $guzzleClient = null) { if (!$guzzleClient) { $guzzleClient = new Client(); } $this->guzzle = $guzzleClient; } /** * Create an HTTP request object with a JSON body that is authenticated * with a pre-shared key. The authentication tag is stored in a * Body-HMAC-SHA512256 header. * * @param string $method * @param string $uri * @param array $arrayToJsonify * @param SharedAuthenticationKey $key * @param array $headers * @return RequestInterface * @throws InvalidMessageException */ public function createSymmetricAuthenticatedJsonRequest( string $method, string $uri, array $arrayToJsonify, SharedAuthenticationKey $key, array $headers = [] ): RequestInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSymmetricAuthenticatedRequest( $method, $uri, $body, $key, $headers ); } /** * Create an HTTP response object with a JSON body that is authenticated * with a pre-shared key. The authentication tag is stored in a * Body-HMAC-SHA512256 header. * * @param int $status * @param array $arrayToJsonify * @param SharedAuthenticationKey $key * @param array $headers * @param string $version * @return ResponseInterface * @throws InvalidMessageException */ public function createSymmetricAuthenticatedJsonResponse( int $status, array $arrayToJsonify, SharedAuthenticationKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSymmetricAuthenticatedResponse( $status, $body, $key, $headers, $version ); } /** * Create an HTTP request object with a JSON body that is encrypted * with a pre-shared key. * * @param string $method * @param string $uri * @param array $arrayToJsonify * @param SharedEncryptionKey $key * @param array $headers * @return RequestInterface * @throws InvalidMessageException */ public function createSymmetricEncryptedJsonRequest( string $method, string $uri, array $arrayToJsonify, SharedEncryptionKey $key, array $headers = [] ): RequestInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSymmetricEncryptedRequest( $method, $uri, $body, $key, $headers ); } /** * Create an HTTP response object with a JSON body that is encrypted * with a pre-shared key. * * @param int $status * @param array $arrayToJsonify * @param SharedEncryptionKey $key * @param array $headers * @param string $version * @return ResponseInterface * @throws InvalidMessageException */ public function createSymmetricEncryptedJsonResponse( int $status, array $arrayToJsonify, SharedEncryptionKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSymmetricEncryptedResponse( $status, $body, $key, $headers, $version ); } /** * Create an HTTP request object with a JSON body that is encrypted * with the server's public key. * * @param string $method * @param string $uri * @param array $arrayToJsonify * @param SealingPublicKey $key * @param array $headers * @return RequestInterface * @throws InvalidMessageException */ public function createSealedJsonRequest( string $method, string $uri, array $arrayToJsonify, SealingPublicKey $key, array $headers = [] ): RequestInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSealedRequest( $method, $uri, $body, $key, $headers ); } /** * Create an HTTP response object with a JSON body that is encrypted * with the server's public key. * * @param int $status * @param array $arrayToJsonify * @param SealingPublicKey $key * @param array $headers * @param string $version * @return ResponseInterface * @throws InvalidMessageException */ public function createSealedJsonResponse( int $status, array $arrayToJsonify, SealingPublicKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSealedResponse( $status, $body, $key, $headers, $version ); } /** * Creates a JSON-signed API request to be sent to an API. * Enforces hard-coded Ed25519 keys. * * @param string $method * @param string $uri * @param array $arrayToJsonify * @param SigningSecretKey $key * @param array $headers * @return RequestInterface * @throws InvalidMessageException */ public function createSignedJsonRequest( string $method, string $uri, array $arrayToJsonify, SigningSecretKey $key, array $headers = [] ): RequestInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSignedRequest( $method, $uri, $body, $key, $headers ); } /** * Creates a JSON-signed API response to be returned from an API. * Enforces hard-coded Ed25519 keys. * * @param int $status * @param array $arrayToJsonify * @param SigningSecretKey $key * @param array $headers * @param string $version * @return ResponseInterface * @throws InvalidMessageException */ public function createSignedJsonResponse( int $status, array $arrayToJsonify, SigningSecretKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { list ($body, $headers) = $this->makeJSON($arrayToJsonify, $headers); if (!\is_string($body)) { throw new InvalidMessageException('Cannot JSON-encode this message.'); } return $this->createSignedResponse( $status, $body, $key, $headers, $version ); } /** * Authenticate your HTTP request with a pre-shared key. * * @param string $method * @param string $uri * @param string $body * @param SharedAuthenticationKey $key * @param array $headers * @return RequestInterface */ public function createSymmetricAuthenticatedRequest( string $method, string $uri, string $body, SharedAuthenticationKey $key, array $headers = [] ): RequestInterface { $mac = \ParagonIE_Sodium_Compat::crypto_auth($body, $key->getString(true)); if (isset($headers[Sapient::HEADER_SIGNATURE_NAME])) { $headers[Sapient::HEADER_AUTH_NAME][] = Base64UrlSafe::encode($mac); } else { $headers[Sapient::HEADER_AUTH_NAME] = Base64UrlSafe::encode($mac); } return new Request( $method, $uri, $headers, $body ); } /** * Authenticate your HTTP response with a pre-shared key. * * @param int $status * @param string $body * @param SharedAuthenticationKey $key * @param array $headers * @param string $version * @return ResponseInterface */ public function createSymmetricAuthenticatedResponse( int $status, string $body, SharedAuthenticationKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { $mac = \ParagonIE_Sodium_Compat::crypto_auth($body, $key->getString(true)); if (isset($headers[Sapient::HEADER_SIGNATURE_NAME])) { $headers[Sapient::HEADER_AUTH_NAME][] = Base64UrlSafe::encode($mac); } else { $headers[Sapient::HEADER_AUTH_NAME] = Base64UrlSafe::encode($mac); } return new Response( $status, $headers, $body, $version ); } /** * Encrypt your HTTP request with a pre-shared key. * * @param string $method * @param string $uri * @param string $body * @param SharedEncryptionKey $key * @param array $headers * @return RequestInterface */ public function createSymmetricEncryptedRequest( string $method, string $uri, string $body, SharedEncryptionKey $key, array $headers = [] ): RequestInterface { return new Request( $method, $uri, $headers, Base64UrlSafe::encode(Simple::encrypt($body, $key)) ); } /** * Encrypt your HTTP response with a pre-shared key. * * @param int $status * @param string $body * @param SharedEncryptionKey $key * @param array $headers * @param string $version * @return ResponseInterface */ public function createSymmetricEncryptedResponse( int $status, string $body, SharedEncryptionKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { return new Response( $status, $headers, Base64UrlSafe::encode(Simple::encrypt($body, $key)), $version ); } /** * Encrypt your HTTP request with the server's public key, so that only * the server can decrypt the message. * * @param string $method * @param string $uri * @param string $body * @param SealingPublicKey $key * @param array $headers * @return RequestInterface */ public function createSealedRequest( string $method, string $uri, string $body, SealingPublicKey $key, array $headers = [] ): RequestInterface { $sealed = Simple::seal( $body, $key ); return new Request( $method, $uri, $headers, Base64UrlSafe::encode($sealed) ); } /** * Encrypt your HTTP response with the client's public key, so that only * the client can decrypt the message. * * @param int $status * @param string $body * @param SealingPublicKey $key * @param array $headers * @param string $version * @return ResponseInterface */ public function createSealedResponse( int $status, string $body, SealingPublicKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { $sealed = Simple::seal( $body, $key ); return new Response( $status, $headers, Base64UrlSafe::encode($sealed), $version ); } /** * Ed25519-sign a request body. * * This adds an HTTP header (Body-Signature-Ed25519) which is the base64url * encoded Ed25519 signature of the HTTP request body. * * @param string $method * @param string $uri * @param string $body * @param SigningSecretKey $key * @param array $headers * @return RequestInterface */ public function createSignedRequest( string $method, string $uri, string $body, SigningSecretKey $key, array $headers = [] ): RequestInterface { $signature = \ParagonIE_Sodium_Compat::crypto_sign_detached( $body, $key->getString(true) ); if (isset($headers[Sapient::HEADER_SIGNATURE_NAME])) { $headers[Sapient::HEADER_SIGNATURE_NAME][] = Base64UrlSafe::encode($signature); } else { $headers[Sapient::HEADER_SIGNATURE_NAME] = Base64UrlSafe::encode($signature); } return new Request( $method, $uri, $headers, $body ); } /** * Ed25519-sign a response body. * * This adds an HTTP header (Body-Signature-Ed25519) which is the base64url * encoded Ed25519 signature of the HTTP response body. * * @param int $status * @param string $body * @param SigningSecretKey $key * @param array $headers * @param string $version * @return ResponseInterface */ public function createSignedResponse( int $status, string $body, SigningSecretKey $key, array $headers = [], string $version = '1.1' ): ResponseInterface { $signature = \ParagonIE_Sodium_Compat::crypto_sign_detached( $body, $key->getString(true) ); if (isset($headers[Sapient::HEADER_SIGNATURE_NAME])) { $headers[Sapient::HEADER_SIGNATURE_NAME][] = Base64UrlSafe::encode($signature); } else { $headers[Sapient::HEADER_SIGNATURE_NAME] = Base64UrlSafe::encode($signature); } return new Response( $status, $headers, $body, $version ); } /** * This is not part of the AdapterInterface. * * @return Client */ public function getGuzzleClient(): Client { return $this->guzzle; } /** * 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; } /** * JSON encode body, add Content-Type header. * * @param array $arrayToJsonify * @param array $headers * @return array */ protected function makeJSON(array $arrayToJsonify, array $headers): array { if (empty($headers['Content-Type'])) { $headers['Content-Type'] = 'application/json'; } /** @var string $body */ $body = \json_encode($arrayToJsonify, JSON_PRETTY_PRINT); return [$body, $headers]; } }