mirror of
https://github.com/pixelfed/pixelfed.git
synced 2024-11-14 10:34:31 +00:00
Remove deprecated http sig library
This commit is contained in:
parent
6c37335e98
commit
bebe7cca77
24 changed files with 0 additions and 1432 deletions
|
@ -1,94 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\ActivityPub\Concern;
|
|
||||||
|
|
||||||
use Zttp\Zttp;
|
|
||||||
|
|
||||||
class HTTPSignature
|
|
||||||
{
|
|
||||||
protected $localhosts = [
|
|
||||||
'127.0.0.1', 'localhost', '::1',
|
|
||||||
];
|
|
||||||
public $profile;
|
|
||||||
public $is_url;
|
|
||||||
|
|
||||||
public function validateUrl()
|
|
||||||
{
|
|
||||||
// If the profile exists, assume its valid
|
|
||||||
if ($this->is_url === false) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
$url = $this->profile;
|
|
||||||
|
|
||||||
try {
|
|
||||||
$url = filter_var($url, FILTER_VALIDATE_URL);
|
|
||||||
$parsed = parse_url($url, PHP_URL_HOST);
|
|
||||||
if (!$parsed || in_array($parsed, $this->localhosts)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function fetchPublicKey($profile, bool $is_url = true)
|
|
||||||
{
|
|
||||||
$this->profile = $profile;
|
|
||||||
$this->is_url = $is_url;
|
|
||||||
$valid = $this->validateUrl();
|
|
||||||
if (!$valid) {
|
|
||||||
throw new \Exception('Invalid URL provided');
|
|
||||||
}
|
|
||||||
if ($is_url && isset($profile->public_key) && $profile->public_key) {
|
|
||||||
return $profile->public_key;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
$url = $this->profile;
|
|
||||||
$res = Zttp::timeout(30)->withHeaders([
|
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
|
||||||
'User-Agent' => 'PixelFedBot v0.1 - https://pixelfed.org',
|
|
||||||
])->get($url);
|
|
||||||
$actor = json_decode($res->getBody(), true);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
throw new Exception('Unable to fetch public key');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $actor['publicKey']['publicKeyPem'];
|
|
||||||
}
|
|
||||||
|
|
||||||
public function sendSignedObject($senderProfile, $url, $body)
|
|
||||||
{
|
|
||||||
$profile = $senderProfile;
|
|
||||||
$context = new Context([
|
|
||||||
'keys' => [$profile->keyId() => $profile->private_key],
|
|
||||||
'algorithm' => 'rsa-sha256',
|
|
||||||
'headers' => ['(request-target)', 'Date'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$handlerStack = GuzzleHttpSignatures::defaultHandlerFromContext($context);
|
|
||||||
$client = new Client(['handler' => $handlerStack]);
|
|
||||||
|
|
||||||
$headers = [
|
|
||||||
'Accept' => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"',
|
|
||||||
'Date' => date('D, d M Y h:i:s').' GMT',
|
|
||||||
'Content-Type' => 'application/activity+json',
|
|
||||||
'User-Agent' => 'PixelFedBot - https://pixelfed.org',
|
|
||||||
];
|
|
||||||
|
|
||||||
$response = $client->post($url, [
|
|
||||||
'options' => [
|
|
||||||
'allow_redirects' => false,
|
|
||||||
'verify' => true,
|
|
||||||
'timeout' => 30,
|
|
||||||
],
|
|
||||||
'headers' => $headers,
|
|
||||||
'body' => $body,
|
|
||||||
]);
|
|
||||||
|
|
||||||
return $response->getBody();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,34 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
abstract class Algorithm
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return HmacAlgorithm
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public static function create($name)
|
|
||||||
{
|
|
||||||
switch ($name) {
|
|
||||||
case 'hmac-sha1':
|
|
||||||
return new HmacAlgorithm('sha1');
|
|
||||||
break;
|
|
||||||
case 'hmac-sha256':
|
|
||||||
return new HmacAlgorithm('sha256');
|
|
||||||
break;
|
|
||||||
case 'rsa-sha1':
|
|
||||||
return new RsaAlgorithm('sha1');
|
|
||||||
break;
|
|
||||||
case 'rsa-sha256':
|
|
||||||
return new RsaAlgorithm('sha256');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new AlgorithmException("No algorithm named '$name'");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class AlgorithmException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
interface AlgorithmInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function name();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $key
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sign($key, $data);
|
|
||||||
}
|
|
|
@ -1,119 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class Context
|
|
||||||
{
|
|
||||||
/** @var array */
|
|
||||||
private $headers;
|
|
||||||
|
|
||||||
/** @var KeyStoreInterface */
|
|
||||||
private $keyStore;
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
private $keys;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $signingKeyId;
|
|
||||||
|
|
||||||
/** @var AlgorithmInterface */
|
|
||||||
private $algorithm;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $args
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function __construct($args)
|
|
||||||
{
|
|
||||||
if (isset($args['keys']) && isset($args['keyStore'])) {
|
|
||||||
throw new Exception(__CLASS__.' accepts keys or keyStore but not both');
|
|
||||||
} elseif (isset($args['keys'])) {
|
|
||||||
// array of keyId => keySecret
|
|
||||||
$this->keys = $args['keys'];
|
|
||||||
} elseif (isset($args['keyStore'])) {
|
|
||||||
$this->setKeyStore($args['keyStore']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// algorithm for signing; not necessary for verifying.
|
|
||||||
if (isset($args['algorithm'])) {
|
|
||||||
$this->algorithm = Algorithm::create($args['algorithm']);
|
|
||||||
}
|
|
||||||
// headers list for signing; not necessary for verifying.
|
|
||||||
if (isset($args['headers'])) {
|
|
||||||
$this->headers = $args['headers'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// signingKeyId specifies the key used for signing messages.
|
|
||||||
if (isset($args['signingKeyId'])) {
|
|
||||||
$this->signingKeyId = $args['signingKeyId'];
|
|
||||||
} elseif (isset($args['keys']) && 1 === count($args['keys'])) {
|
|
||||||
list($this->signingKeyId) = array_keys($args['keys']); // first key
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Signer
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
public function signer()
|
|
||||||
{
|
|
||||||
return new Signer(
|
|
||||||
$this->signingKey(),
|
|
||||||
$this->algorithm,
|
|
||||||
$this->headerList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Verifier
|
|
||||||
*/
|
|
||||||
public function verifier()
|
|
||||||
{
|
|
||||||
return new Verifier($this->keyStore());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Key
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
* @throws KeyStoreException
|
|
||||||
*/
|
|
||||||
private function signingKey()
|
|
||||||
{
|
|
||||||
if (isset($this->signingKeyId)) {
|
|
||||||
return $this->keyStore()->fetch($this->signingKeyId);
|
|
||||||
} else {
|
|
||||||
throw new Exception('no implicit or specified signing key');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HeaderList
|
|
||||||
*/
|
|
||||||
private function headerList()
|
|
||||||
{
|
|
||||||
return new HeaderList($this->headers);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return KeyStore
|
|
||||||
*/
|
|
||||||
private function keyStore()
|
|
||||||
{
|
|
||||||
if (empty($this->keyStore)) {
|
|
||||||
$this->keyStore = new KeyStore($this->keys);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param KeyStoreInterface $keyStore
|
|
||||||
*/
|
|
||||||
private function setKeyStore(KeyStoreInterface $keyStore)
|
|
||||||
{
|
|
||||||
$this->keyStore = $keyStore;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class Exception extends \Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use GuzzleHttp\HandlerStack;
|
|
||||||
use GuzzleHttp\Psr7\Request;
|
|
||||||
use App\Util\HttpSignatures\Context;
|
|
||||||
|
|
||||||
class GuzzleHttpSignatures
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param Context $context
|
|
||||||
* @return HandlerStack
|
|
||||||
*/
|
|
||||||
public static function defaultHandlerFromContext(Context $context)
|
|
||||||
{
|
|
||||||
$stack = HandlerStack::create();
|
|
||||||
$stack->push(self::middlewareFromContext($context));
|
|
||||||
|
|
||||||
return $stack;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Context $context
|
|
||||||
* @return \Closure
|
|
||||||
*/
|
|
||||||
public static function middlewareFromContext(Context $context)
|
|
||||||
{
|
|
||||||
return function (callable $handler) use ($context)
|
|
||||||
{
|
|
||||||
return function (
|
|
||||||
Request $request,
|
|
||||||
array $options
|
|
||||||
) use ($handler, $context)
|
|
||||||
{
|
|
||||||
$request = $context->signer()->sign($request);
|
|
||||||
return $handler($request, $options);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,48 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class HeaderList
|
|
||||||
{
|
|
||||||
/** @var array */
|
|
||||||
public $names;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $names
|
|
||||||
*/
|
|
||||||
public function __construct(array $names)
|
|
||||||
{
|
|
||||||
$this->names = array_map(
|
|
||||||
[$this, 'normalize'],
|
|
||||||
$names
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $string
|
|
||||||
*
|
|
||||||
* @return HeaderList
|
|
||||||
*/
|
|
||||||
public static function fromString($string)
|
|
||||||
{
|
|
||||||
return new static(explode(' ', $string));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function string()
|
|
||||||
{
|
|
||||||
return implode(' ', $this->names);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function normalize($name)
|
|
||||||
{
|
|
||||||
return strtolower($name);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class HmacAlgorithm implements AlgorithmInterface
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
private $digestName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $digestName
|
|
||||||
*/
|
|
||||||
public function __construct($digestName)
|
|
||||||
{
|
|
||||||
$this->digestName = $digestName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function name()
|
|
||||||
{
|
|
||||||
return sprintf('hmac-%s', $this->digestName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $key
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function sign($secret, $data)
|
|
||||||
{
|
|
||||||
return hash_hmac($this->digestName, $data, $secret, true);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,260 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class Key
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
private $id;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $secret;
|
|
||||||
|
|
||||||
/** @var resource */
|
|
||||||
private $certificate;
|
|
||||||
|
|
||||||
/** @var resource */
|
|
||||||
private $publicKey;
|
|
||||||
|
|
||||||
/** @var resource */
|
|
||||||
private $privateKey;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
private $type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $id
|
|
||||||
* @param string|array $secret
|
|
||||||
*/
|
|
||||||
public function __construct($id, $item)
|
|
||||||
{
|
|
||||||
$this->id = $id;
|
|
||||||
if (Key::hasX509Certificate($item) || Key::hasPublicKey($item)) {
|
|
||||||
$publicKey = Key::getPublicKey($item);
|
|
||||||
} else {
|
|
||||||
$publicKey = null;
|
|
||||||
}
|
|
||||||
if (Key::hasPrivateKey($item)) {
|
|
||||||
$privateKey = Key::getPrivateKey($item);
|
|
||||||
} else {
|
|
||||||
$privateKey = null;
|
|
||||||
}
|
|
||||||
if (($publicKey || $privateKey)) {
|
|
||||||
$this->type = 'asymmetric';
|
|
||||||
if ($publicKey && $privateKey) {
|
|
||||||
$publicKeyPEM = openssl_pkey_get_details($publicKey)['key'];
|
|
||||||
$privateKeyPublicPEM = openssl_pkey_get_details($privateKey)['key'];
|
|
||||||
if ($privateKeyPublicPEM != $publicKeyPEM) {
|
|
||||||
throw new KeyException('Supplied Certificate and Key are not related');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$this->privateKey = $privateKey;
|
|
||||||
$this->publicKey = $publicKey;
|
|
||||||
$this->secret = null;
|
|
||||||
} else {
|
|
||||||
$this->type = 'secret';
|
|
||||||
$this->secret = $item;
|
|
||||||
$this->publicKey = null;
|
|
||||||
$this->privateKey = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves private key resource from a input string or
|
|
||||||
* array of strings.
|
|
||||||
*
|
|
||||||
* @param string|array $object PEM-format Private Key or file path to same
|
|
||||||
*
|
|
||||||
* @return resource|false
|
|
||||||
*/
|
|
||||||
public static function getPrivateKey($object)
|
|
||||||
{
|
|
||||||
if (is_array($object)) {
|
|
||||||
foreach ($object as $candidateKey) {
|
|
||||||
$privateKey = Key::getPrivateKey($candidateKey);
|
|
||||||
if ($privateKey) {
|
|
||||||
return $privateKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// OpenSSL libraries don't have detection methods, so try..catch
|
|
||||||
try {
|
|
||||||
$privateKey = openssl_get_privatekey($object);
|
|
||||||
|
|
||||||
return $privateKey;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves public key resource from a input string or
|
|
||||||
* array of strings.
|
|
||||||
*
|
|
||||||
* @param string|array $object PEM-format Public Key or file path to same
|
|
||||||
*
|
|
||||||
* @return resource|false
|
|
||||||
*/
|
|
||||||
public static function getPublicKey($object)
|
|
||||||
{
|
|
||||||
if (is_array($object)) {
|
|
||||||
// If we implement key rotation in future, this should add to a collection
|
|
||||||
foreach ($object as $candidateKey) {
|
|
||||||
$publicKey = Key::getPublicKey($candidateKey);
|
|
||||||
if ($publicKey) {
|
|
||||||
return $publicKey;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// OpenSSL libraries don't have detection methods, so try..catch
|
|
||||||
try {
|
|
||||||
$publicKey = openssl_get_publickey($object);
|
|
||||||
|
|
||||||
return $publicKey;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Signing HTTP Messages 'keyId' field.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws KeyException
|
|
||||||
*/
|
|
||||||
public function getId()
|
|
||||||
{
|
|
||||||
return $this->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve Verifying Key - Public Key for Asymmetric/PKI, or shared secret for HMAC.
|
|
||||||
*
|
|
||||||
* @return string Shared Secret or PEM-format Public Key
|
|
||||||
*
|
|
||||||
* @throws KeyException
|
|
||||||
*/
|
|
||||||
public function getVerifyingKey()
|
|
||||||
{
|
|
||||||
switch ($this->type) {
|
|
||||||
case 'asymmetric':
|
|
||||||
if ($this->publicKey) {
|
|
||||||
return openssl_pkey_get_details($this->publicKey)['key'];
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'secret':
|
|
||||||
return $this->secret;
|
|
||||||
default:
|
|
||||||
throw new KeyException("Unknown key type $this->type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve Signing Key - Private Key for Asymmetric/PKI, or shared secret for HMAC.
|
|
||||||
*
|
|
||||||
* @return string Shared Secret or PEM-format Private Key
|
|
||||||
*
|
|
||||||
* @throws KeyException
|
|
||||||
*/
|
|
||||||
public function getSigningKey()
|
|
||||||
{
|
|
||||||
switch ($this->type) {
|
|
||||||
case 'asymmetric':
|
|
||||||
if ($this->privateKey) {
|
|
||||||
openssl_pkey_export($this->privateKey, $pem);
|
|
||||||
|
|
||||||
return $pem;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'secret':
|
|
||||||
return $this->secret;
|
|
||||||
default:
|
|
||||||
throw new KeyException("Unknown key type $this->type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string 'secret' for HMAC or 'asymmetric'
|
|
||||||
*/
|
|
||||||
public function getType()
|
|
||||||
{
|
|
||||||
return $this->type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if $object is, points to or contains, X.509 PEM-format certificate.
|
|
||||||
*
|
|
||||||
* @param string|array $object PEM Format X.509 Certificate or file path to one
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function hasX509Certificate($object)
|
|
||||||
{
|
|
||||||
if (is_array($object)) {
|
|
||||||
foreach ($object as $candidateCertificate) {
|
|
||||||
$result = Key::hasX509Certificate($candidateCertificate);
|
|
||||||
if ($result) {
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// OpenSSL libraries don't have detection methods, so try..catch
|
|
||||||
try {
|
|
||||||
openssl_x509_export($object, $null);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (\Exception $e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if $object is, points to or contains, PEM-format Public Key.
|
|
||||||
*
|
|
||||||
* @param string|array $object PEM-format Public Key or file path to one
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function hasPublicKey($object)
|
|
||||||
{
|
|
||||||
if (is_array($object)) {
|
|
||||||
foreach ($object as $candidatePublicKey) {
|
|
||||||
$result = Key::hasPublicKey($candidatePublicKey);
|
|
||||||
if ($result) {
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false == !openssl_pkey_get_public($object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test if $object is, points to or contains, PEM-format Private Key.
|
|
||||||
*
|
|
||||||
* @param string|array $object PEM-format Private Key or file path to one
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function hasPrivateKey($object)
|
|
||||||
{
|
|
||||||
if (is_array($object)) {
|
|
||||||
foreach ($object as $candidatePrivateKey) {
|
|
||||||
$result = Key::hasPrivateKey($candidatePrivateKey);
|
|
||||||
if ($result) {
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return false != openssl_pkey_get_private($object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class KeyException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class KeyStore implements KeyStoreInterface
|
|
||||||
{
|
|
||||||
/** @var Key[] */
|
|
||||||
private $keys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $keys
|
|
||||||
*/
|
|
||||||
public function __construct($keys)
|
|
||||||
{
|
|
||||||
$this->keys = [];
|
|
||||||
foreach ($keys as $id => $key) {
|
|
||||||
$this->keys[$id] = new Key($id, $key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $keyId
|
|
||||||
*
|
|
||||||
* @return Key
|
|
||||||
*
|
|
||||||
* @throws KeyStoreException
|
|
||||||
*/
|
|
||||||
public function fetch($keyId)
|
|
||||||
{
|
|
||||||
if (isset($this->keys[$keyId])) {
|
|
||||||
return $this->keys[$keyId];
|
|
||||||
} else {
|
|
||||||
throw new KeyStoreException("Key '$keyId' not found");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class KeyStoreException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,15 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
interface KeyStoreInterface
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* return the secret for the specified $keyId.
|
|
||||||
*
|
|
||||||
* @param string $keyId
|
|
||||||
*
|
|
||||||
* @return Key
|
|
||||||
*/
|
|
||||||
public function fetch($keyId);
|
|
||||||
}
|
|
|
@ -1,64 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class RsaAlgorithm implements AlgorithmInterface
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
private $digestName;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $digestName
|
|
||||||
*/
|
|
||||||
public function __construct($digestName)
|
|
||||||
{
|
|
||||||
$this->digestName = $digestName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function name()
|
|
||||||
{
|
|
||||||
return sprintf('rsa-%s', $this->digestName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $key
|
|
||||||
* @param string $data
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws \HttpSignatures\AlgorithmException
|
|
||||||
*/
|
|
||||||
public function sign($signingKey, $data)
|
|
||||||
{
|
|
||||||
$algo = $this->getRsaHashAlgo($this->digestName);
|
|
||||||
if (!openssl_get_privatekey($signingKey)) {
|
|
||||||
throw new AlgorithmException("OpenSSL doesn't understand the supplied key (not valid or not found)");
|
|
||||||
}
|
|
||||||
$signature = '';
|
|
||||||
openssl_sign($data, $signature, $signingKey, $algo);
|
|
||||||
|
|
||||||
return $signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function verify($message, $signature, $verifyingKey)
|
|
||||||
{
|
|
||||||
$algo = $this->getRsaHashAlgo($this->digestName);
|
|
||||||
|
|
||||||
return openssl_verify($message, base64_decode($signature), $verifyingKey, $algo);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function getRsaHashAlgo($digestName)
|
|
||||||
{
|
|
||||||
switch ($digestName) {
|
|
||||||
case 'sha256':
|
|
||||||
return OPENSSL_ALGO_SHA256;
|
|
||||||
case 'sha1':
|
|
||||||
return OPENSSL_ALGO_SHA1;
|
|
||||||
default:
|
|
||||||
throw new HttpSignatures\AlgorithmException($digestName.' is not a supported hash format');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,38 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
|
||||||
|
|
||||||
class Signature
|
|
||||||
{
|
|
||||||
/** @var Key */
|
|
||||||
private $key;
|
|
||||||
|
|
||||||
/** @var AlgorithmInterface */
|
|
||||||
private $algorithm;
|
|
||||||
|
|
||||||
/** @var SigningString */
|
|
||||||
private $signingString;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
* @param Key $key
|
|
||||||
* @param AlgorithmInterface $algorithm
|
|
||||||
* @param HeaderList $headerList
|
|
||||||
*/
|
|
||||||
public function __construct($message, Key $key, AlgorithmInterface $algorithm, HeaderList $headerList)
|
|
||||||
{
|
|
||||||
$this->key = $key;
|
|
||||||
$this->algorithm = $algorithm;
|
|
||||||
$this->signingString = new SigningString($headerList, $message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function string()
|
|
||||||
{
|
|
||||||
return $this->algorithm->sign(
|
|
||||||
$this->key->getSigningKey(),
|
|
||||||
$this->signingString->string()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class SignatureParameters
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param Key $key
|
|
||||||
* @param AlgorithmInterface $algorithm
|
|
||||||
* @param HeaderList $headerList
|
|
||||||
* @param Signature $signature
|
|
||||||
*/
|
|
||||||
public function __construct($key, $algorithm, $headerList, $signature)
|
|
||||||
{
|
|
||||||
$this->key = $key;
|
|
||||||
$this->algorithm = $algorithm;
|
|
||||||
$this->headerList = $headerList;
|
|
||||||
$this->signature = $signature;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function string()
|
|
||||||
{
|
|
||||||
return implode(',', $this->parameterComponents());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function parameterComponents()
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
sprintf('keyId="%s"', $this->key->getId()),
|
|
||||||
sprintf('algorithm="%s"', $this->algorithm->name()),
|
|
||||||
sprintf('headers="%s"', $this->headerList->string()),
|
|
||||||
sprintf('signature="%s"', $this->signatureBase64()),
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function signatureBase64()
|
|
||||||
{
|
|
||||||
return base64_encode($this->signature->string());
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,111 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class SignatureParametersParser
|
|
||||||
{
|
|
||||||
/** @var string */
|
|
||||||
private $input;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $input
|
|
||||||
*/
|
|
||||||
public function __construct($input)
|
|
||||||
{
|
|
||||||
$this->input = $input;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function parse()
|
|
||||||
{
|
|
||||||
$result = $this->pairsToAssociative(
|
|
||||||
$this->arrayOfPairs()
|
|
||||||
);
|
|
||||||
$this->validate($result);
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param array $pairs
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function pairsToAssociative($pairs)
|
|
||||||
{
|
|
||||||
$result = [];
|
|
||||||
foreach ($pairs as $pair) {
|
|
||||||
$result[$pair[0]] = $pair[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function arrayOfPairs()
|
|
||||||
{
|
|
||||||
return array_map(
|
|
||||||
[$this, 'pair'],
|
|
||||||
$this->segments()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function segments()
|
|
||||||
{
|
|
||||||
return explode(',', $this->input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $segment
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*
|
|
||||||
* @throws SignatureParseException
|
|
||||||
*/
|
|
||||||
private function pair($segment)
|
|
||||||
{
|
|
||||||
$segmentPattern = '/\A(keyId|algorithm|headers|signature)="(.*)"\z/';
|
|
||||||
$matches = [];
|
|
||||||
$result = preg_match($segmentPattern, $segment, $matches);
|
|
||||||
if (1 !== $result) {
|
|
||||||
throw new SignatureParseException("Signature parameters segment '$segment' invalid");
|
|
||||||
}
|
|
||||||
array_shift($matches);
|
|
||||||
|
|
||||||
return $matches;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $result
|
|
||||||
*
|
|
||||||
* @throws SignatureParseException
|
|
||||||
*/
|
|
||||||
private function validate($result)
|
|
||||||
{
|
|
||||||
$this->validateAllKeysArePresent($result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $result
|
|
||||||
*
|
|
||||||
* @throws SignatureParseException
|
|
||||||
*/
|
|
||||||
private function validateAllKeysArePresent($result)
|
|
||||||
{
|
|
||||||
// Regexp in pair() ensures no unwanted keys exist.
|
|
||||||
// Ensure that all wanted keys exist.
|
|
||||||
$wanted = ['keyId', 'algorithm', 'headers', 'signature'];
|
|
||||||
$missing = array_diff($wanted, array_keys($result));
|
|
||||||
if (!empty($missing)) {
|
|
||||||
$csv = implode(', ', $missing);
|
|
||||||
throw new SignatureParseException("Missing keys $csv");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class SignatureParseException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
class SignedHeaderNotPresentException extends Exception
|
|
||||||
{
|
|
||||||
}
|
|
|
@ -1,104 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
|
||||||
|
|
||||||
class Signer
|
|
||||||
{
|
|
||||||
/** @var Key */
|
|
||||||
private $key;
|
|
||||||
|
|
||||||
/** @var HmacAlgorithm */
|
|
||||||
private $algorithm;
|
|
||||||
|
|
||||||
/** @var HeaderList */
|
|
||||||
private $headerList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param Key $key
|
|
||||||
* @param HmacAlgorithm $algorithm
|
|
||||||
* @param HeaderList $headerList
|
|
||||||
*/
|
|
||||||
public function __construct($key, $algorithm, $headerList)
|
|
||||||
{
|
|
||||||
$this->key = $key;
|
|
||||||
$this->algorithm = $algorithm;
|
|
||||||
$this->headerList = $headerList;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return RequestInterface
|
|
||||||
*/
|
|
||||||
public function sign($message)
|
|
||||||
{
|
|
||||||
$signatureParameters = $this->signatureParameters($message);
|
|
||||||
$message = $message->withAddedHeader('Signature', $signatureParameters->string());
|
|
||||||
$message = $message->withAddedHeader('Authorization', 'Signature '.$signatureParameters->string());
|
|
||||||
|
|
||||||
return $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return RequestInterface
|
|
||||||
*/
|
|
||||||
public function signWithDigest($message)
|
|
||||||
{
|
|
||||||
$message = $this->addDigest($message);
|
|
||||||
|
|
||||||
return $this->sign($message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return RequestInterface
|
|
||||||
*/
|
|
||||||
private function addDigest($message)
|
|
||||||
{
|
|
||||||
if (!array_search('digest', $this->headerList->names)) {
|
|
||||||
$this->headerList->names[] = 'digest';
|
|
||||||
}
|
|
||||||
$message = $message->withoutHeader('Digest')
|
|
||||||
->withHeader(
|
|
||||||
'Digest',
|
|
||||||
'SHA-256='.base64_encode(hash('sha256', $message->getBody(), true))
|
|
||||||
);
|
|
||||||
|
|
||||||
return $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return SignatureParameters
|
|
||||||
*/
|
|
||||||
private function signatureParameters($message)
|
|
||||||
{
|
|
||||||
return new SignatureParameters(
|
|
||||||
$this->key,
|
|
||||||
$this->algorithm,
|
|
||||||
$this->headerList,
|
|
||||||
$this->signature($message)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return Signature
|
|
||||||
*/
|
|
||||||
private function signature($message)
|
|
||||||
{
|
|
||||||
return new Signature(
|
|
||||||
$message,
|
|
||||||
$this->key,
|
|
||||||
$this->algorithm,
|
|
||||||
$this->headerList
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,89 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
|
||||||
|
|
||||||
class SigningString
|
|
||||||
{
|
|
||||||
/** @var HeaderList */
|
|
||||||
private $headerList;
|
|
||||||
|
|
||||||
/** @var RequestInterface */
|
|
||||||
private $message;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HeaderList $headerList
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*/
|
|
||||||
public function __construct(HeaderList $headerList, $message)
|
|
||||||
{
|
|
||||||
$this->headerList = $headerList;
|
|
||||||
$this->message = $message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function string()
|
|
||||||
{
|
|
||||||
return implode("\n", $this->lines());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function lines()
|
|
||||||
{
|
|
||||||
return array_map(
|
|
||||||
[$this, 'line'],
|
|
||||||
$this->headerList->names
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws SignedHeaderNotPresentException
|
|
||||||
*/
|
|
||||||
private function line($name)
|
|
||||||
{
|
|
||||||
if ('(request-target)' == $name) {
|
|
||||||
return $this->requestTargetLine();
|
|
||||||
} else {
|
|
||||||
return sprintf('%s: %s', $name, $this->headerValue($name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws SignedHeaderNotPresentException
|
|
||||||
*/
|
|
||||||
private function headerValue($name)
|
|
||||||
{
|
|
||||||
if ($this->message->hasHeader($name)) {
|
|
||||||
$header = $this->message->getHeader($name);
|
|
||||||
|
|
||||||
return end($header);
|
|
||||||
} else {
|
|
||||||
throw new SignedHeaderNotPresentException("Header '$name' not in message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function requestTargetLine()
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
'(request-target): %s %s',
|
|
||||||
strtolower($this->message->getMethod()),
|
|
||||||
$this->message->getRequestTarget()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,202 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
|
||||||
|
|
||||||
class Verification
|
|
||||||
{
|
|
||||||
/** @var RequestInterface */
|
|
||||||
private $message;
|
|
||||||
|
|
||||||
/** @var KeyStoreInterface */
|
|
||||||
private $keyStore;
|
|
||||||
|
|
||||||
/** @var array */
|
|
||||||
private $_parameters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
* @param KeyStoreInterface $keyStore
|
|
||||||
*/
|
|
||||||
public function __construct($message, KeyStoreInterface $keyStore)
|
|
||||||
{
|
|
||||||
$this->message = $message;
|
|
||||||
$this->keyStore = $keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isValid()
|
|
||||||
{
|
|
||||||
return $this->hasSignatureHeader() && $this->signatureMatches();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function signatureMatches()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
$key = $this->key();
|
|
||||||
switch ($key->getType()) {
|
|
||||||
case 'secret':
|
|
||||||
$random = random_bytes(32);
|
|
||||||
$expectedResult = hash_hmac(
|
|
||||||
'sha256', $this->expectedSignatureBase64(),
|
|
||||||
$random,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
$providedResult = hash_hmac(
|
|
||||||
'sha256', $this->providedSignatureBase64(),
|
|
||||||
$random,
|
|
||||||
true
|
|
||||||
);
|
|
||||||
|
|
||||||
return $expectedResult === $providedResult;
|
|
||||||
case 'asymmetric':
|
|
||||||
$signedString = new SigningString(
|
|
||||||
$this->headerList(),
|
|
||||||
$this->message
|
|
||||||
);
|
|
||||||
$hashAlgo = explode('-', $this->parameter('algorithm'))[1];
|
|
||||||
$algorithm = new RsaAlgorithm($hashAlgo);
|
|
||||||
$result = $algorithm->verify(
|
|
||||||
$signedString->string(),
|
|
||||||
$this->parameter('signature'),
|
|
||||||
$key->getVerifyingKey());
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
default:
|
|
||||||
throw new Exception("Unknown key type '".$key->getType()."', cannot verify");
|
|
||||||
}
|
|
||||||
} catch (SignatureParseException $e) {
|
|
||||||
return false;
|
|
||||||
} catch (KeyStoreException $e) {
|
|
||||||
return false;
|
|
||||||
} catch (SignedHeaderNotPresentException $e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function expectedSignatureBase64()
|
|
||||||
{
|
|
||||||
return base64_encode($this->expectedSignature()->string());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Signature
|
|
||||||
*/
|
|
||||||
private function expectedSignature()
|
|
||||||
{
|
|
||||||
return new Signature(
|
|
||||||
$this->message,
|
|
||||||
$this->key(),
|
|
||||||
$this->algorithm(),
|
|
||||||
$this->headerList()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
private function providedSignatureBase64()
|
|
||||||
{
|
|
||||||
return $this->parameter('signature');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Key
|
|
||||||
*/
|
|
||||||
private function key()
|
|
||||||
{
|
|
||||||
return $this->keyStore->fetch($this->parameter('keyId'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HmacAlgorithm
|
|
||||||
*/
|
|
||||||
private function algorithm()
|
|
||||||
{
|
|
||||||
return Algorithm::create($this->parameter('algorithm'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return HeaderList
|
|
||||||
*/
|
|
||||||
private function headerList()
|
|
||||||
{
|
|
||||||
return HeaderList::fromString($this->parameter('headers'));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param string $name
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function parameter($name)
|
|
||||||
{
|
|
||||||
$parameters = $this->parameters();
|
|
||||||
if (!isset($parameters[$name])) {
|
|
||||||
throw new Exception("Signature parameters does not contain '$name'");
|
|
||||||
}
|
|
||||||
|
|
||||||
return $parameters[$name];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
private function parameters()
|
|
||||||
{
|
|
||||||
if (!isset($this->_parameters)) {
|
|
||||||
$parser = new SignatureParametersParser($this->signatureHeader());
|
|
||||||
$this->_parameters = $parser->parse();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->_parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
private function hasSignatureHeader()
|
|
||||||
{
|
|
||||||
return $this->message->hasHeader('Signature') || $this->message->hasHeader('Authorization');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*
|
|
||||||
* @throws Exception
|
|
||||||
*/
|
|
||||||
private function signatureHeader()
|
|
||||||
{
|
|
||||||
if ($signature = $this->fetchHeader('Signature')) {
|
|
||||||
return $signature;
|
|
||||||
} elseif ($authorization = $this->fetchHeader('Authorization')) {
|
|
||||||
return substr($authorization, strlen('Signature '));
|
|
||||||
} else {
|
|
||||||
throw new Exception('HTTP message has no Signature or Authorization header');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param $name
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
private function fetchHeader($name)
|
|
||||||
{
|
|
||||||
// grab the most recently set header.
|
|
||||||
$header = $this->message->getHeader($name);
|
|
||||||
|
|
||||||
return end($header);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,31 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Util\HttpSignatures;
|
|
||||||
|
|
||||||
use Psr\Http\Message\RequestInterface;
|
|
||||||
|
|
||||||
class Verifier
|
|
||||||
{
|
|
||||||
/** @var KeyStoreInterface */
|
|
||||||
private $keyStore;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param KeyStoreInterface $keyStore
|
|
||||||
*/
|
|
||||||
public function __construct(KeyStoreInterface $keyStore)
|
|
||||||
{
|
|
||||||
$this->keyStore = $keyStore;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param RequestInterface $message
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isValid($message)
|
|
||||||
{
|
|
||||||
$verification = new Verification($message, $this->keyStore);
|
|
||||||
|
|
||||||
return $verification->isValid();
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue