aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php')
-rw-r--r--vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php151
1 files changed, 103 insertions, 48 deletions
diff --git a/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php b/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
index 977bed4cd..31c481f22 100644
--- a/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
+++ b/vendor/macgirvin/http-message-signer/src/HttpMessageSigner.php
@@ -2,6 +2,7 @@
namespace HttpSignature;
+use Bakame\Http\StructuredFields\Dictionary;
use Bakame\Http\StructuredFields\InnerList;
use Bakame\Http\StructuredFields\Item;
use Bakame\Http\StructuredFields\OuterList;
@@ -9,7 +10,7 @@ use Bakame\Http\StructuredFields\Parameters;
use Psr\Http\Message\MessageInterface;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
-use Bakame\Http\StructuredFields\Dictionary;
+use phpseclib\Crypt\RSA;
class HttpMessageSigner
{
@@ -45,6 +46,13 @@ class HttpMessageSigner
/* PSR-7 interface to signing function */
+ /**
+ * @param string $coveredFields
+ * @param MessageInterface $interface
+ * @param RequestInterface|null $originalRequest
+ * @return MessageInterface
+ * @throws UnProcessableSignatureException
+ */
public function signRequest(string $coveredFields, MessageInterface $interface, RequestInterface $originalRequest = null): MessageInterface
{
@@ -66,6 +74,13 @@ class HttpMessageSigner
}
/* PSR-7 verify interface and also check body digest if included */
+ /**
+ * @param MessageInterface $interface
+ * @param RequestInterface|null $originalRequest
+ * @return bool
+ *
+ * @throws UnProcessableSignatureException
+ */
public function verifyRequest(MessageInterface $interface, RequestInterface $originalRequest = null): bool
{
@@ -136,10 +151,10 @@ class HttpMessageSigner
foreach ($indices as $index) {
$member = $dict->getByIndex($index);
if (!$member) {
- throw new \Exception('Index ' . $index . ' not found');
+ throw new UnProcessableSignatureException('Index ' . $index . ' not found');
}
if (in_array($member, $processedComponents, true)) {
- throw new \Exception('Duplicate member found');
+ throw new UnProcessableSignatureException('Duplicate member found');
}
$processedComponents[] = $member;
$signatureComponents[] = $this->canonicalizeComponent($member, $headers, $interface);
@@ -186,10 +201,10 @@ class HttpMessageSigner
foreach ($indices as $index) {
$member = $dict->getByIndex($index);
if (!$member) {
- throw new \Exception('Index ' . $index . ' not found');
+ throw new UnProcessableSignatureException('Index ' . $index . ' not found');
}
if (in_array($member, $processedComponents, true)) {
- throw new \Exception('Duplicate member found');
+ throw new UnProcessableSignatureException('Duplicate member found');
}
$processedComponents[] = $member;
$signatureComponents[] = $this->canonicalizeComponent($member, $headers, $interface);
@@ -239,7 +254,13 @@ class HttpMessageSigner
$signatureComponents[$dictName][] = $this->canonicalizeComponent($member, $headers, $interface);
}
$parameters = $this->extractParameters($members);
-
+ if ($parameters) {
+ foreach ($parameters as $key => $value) {
+ if (!in_array($key, ['created', 'expires', 'nonce', 'alg', 'keyid', 'tag'])) {
+ return false;
+ }
+ }
+ }
if (isset($parameters['expires'])) {
$expires = (int) $parameters['expires'];
if ($expires < time()) {
@@ -257,21 +278,6 @@ class HttpMessageSigner
}
$sigDict = $this->parseStructuredDict($headers['signature']);
- if ($sigDict->isNotEmpty()) {
- $indices = $sigDict->indices();
- foreach ($indices as $index) {
- [$dictName, $members] = $sigDict->getByIndex($index);
- if ($members instanceof Item) {
- $signatures[$dictName] = $members->value();
- }
- if ($members instanceof InnerList) {
- $innerIndices = $members->indices();
- foreach ($innerIndices as $innerIndex) {
- $signatures[$dictName][] = $members->getByIndex($innerIndex);
- }
- }
- }
- }
foreach ($signatureComponents as $dictName => $dictComponents) {
$namedSignatureComponents = $signatureComponents[$dictName];
@@ -283,7 +289,7 @@ class HttpMessageSigner
}
$decodedSig = base64_decode(trim($sigDict[$dictName]->__toString(), ':'));
- return $this->verifySignature($signatureBase, $decodedSig, $params['alg'] ?? $this->algorithm);
+ return $this->verifySignature($signatureBase, $decodedSig, $parameters['alg'] ?? $this->algorithm);
}
return false;
}
@@ -308,12 +314,17 @@ class HttpMessageSigner
$fieldName = $field->value();
$parameters = $this->extractParameters($field);
if (isset($parameters['bs']) && isset($parameters['sf'])) {
- throw new \Exception('Cannot use both bs and sf');
+ throw new UnProcessableSignatureException('Cannot use both bs and sf');
}
$whichRequest = $interface;
- if (isset($parameters['req']) && $interface instanceof ResponseInterface) {
- $whichRequest = $this->getOriginalRequest();
+ if (isset($parameters['req'])) {
+ if ($interface instanceof ResponseInterface) {
+ $whichRequest = $this->getOriginalRequest();
+ }
+ else {
+ throw new UnProcessableSignatureException('missing request for req parameter');
+ }
}
$whichHeaders = $headers;
@@ -353,12 +364,17 @@ class HttpMessageSigner
private function getFieldValue($fieldName, MessageInterface $interface, $headers, $parameters ): array
{
+ // The $interface has no single method to extract this, so build it from
+ // the avilable components.
+ $targetUri = $interface->getUri()->getScheme() . '://' . $interface->getUri()->getAuthority()
+ . $interface->getUri()->getPath() . $interface->getUri()->getQuery();
+
$value = match ($fieldName) {
'@signature-params' => ['', ''],
'@method' => ['"@method"', strtoupper($interface->getMethod())],
'@authority' => ['"@authority"', $interface->getUri()->getAuthority()],
'@scheme' => ['"@scheme"', strtolower($interface->getUri()->getScheme())],
- '@target-uri' => ['"target-uri"', $interface->getUri()->__toString()],
+ '@target-uri' => ['"@target-uri"', $targetUri],
'@request-target' => ['"@request-target"', $interface->getRequestTarget()],
'@path' => ['"@path"', $interface->getUri()->getPath()],
'@query' => ['"@query"', $interface->getUri()->getQuery()],
@@ -412,7 +428,7 @@ class HttpMessageSigner
return ['"_' . $fieldName . '_"', $queryParams[$fieldName] ? '"' . $queryParams[$fieldName] . '"' : ''];
}
}
- throw new \Exception('Query string named parameter not set');
+ throw new UnProcessableSignatureException('Query string named parameter not set');
}
private function applyStructuredField(string $name, string $fieldValue): string
@@ -423,11 +439,11 @@ class HttpMessageSigner
$field = OuterList::fromHttpValue($fieldValue);
break;
case 'innerlist':
- $field = InnerList::fromHttpValue($fieldValue);
- break;
+ $field = InnerList::fromHttpValue($fieldValue);
+ break;
case 'parameters':
- $field = Parameters::fromHttpValue($fieldValue);
- break;
+ $field = Parameters::fromHttpValue($fieldValue);
+ break;
case 'dictionary':
$field = Dictionary::fromHttpValue($fieldValue);
break;
@@ -455,7 +471,7 @@ class HttpMessageSigner
break;
}
if (!$field) {
- return '';
+ throw new UnProcessableSignatureException('Unknown or unregistered structured field type');
}
return $field->toHttpValue();
}
@@ -472,31 +488,29 @@ class HttpMessageSigner
return '';
}
- private function applyByteSequence(string $fieldValue): string
- {
- return $fieldValue;
- }
-
- private function applyTrailer(string $fieldValue): string
- {
- return $fieldValue;
- }
-
private function createSignature(string $data): string
{
return match ($this->algorithm) {
+ 'rsa-v1_5-sha256' => $this->rsaSign($data),
+ 'rsa-v1_5-sha512' => $this->rsa512Sign($data),
'rsa-sha256' => $this->rsaSign($data),
+ 'rsa-pss-sha512' => $this->pssSign($data),
'ed25519' => $this->ed25519Sign($data),
'hmac-sha256' => base64_encode(hash_hmac('sha256', $data, $this->privateKey, true)),
- default => throw new \RuntimeException("Unsupported algorithm: $this->algorithm")
+ default => throw new UnProcessableSignatureException("Unsupported algorithm: $this->algorithm")
};
}
private function verifySignature(string $data, string $signature, string $alg): bool
{
return match ($alg) {
- 'rsa-sha256' => openssl_verify($data, $signature, $this->publicKey,
- OPENSSL_ALGO_SHA256) === 1,
+ 'rsa-v1_5-sha256' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA256) === 1,
+ 'rsa-v1_5-sha512' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA512) === 1,
+ 'rsa-sha256' => openssl_verify($data, $signature, $this->publicKey,
+ OPENSSL_ALGO_SHA256) === 1,
+ 'rsa-pss-sha512' => $this->pssVerify($data, $signature),
'ed25519' => openssl_verify($data, $signature, $this->publicKey, "Ed25519") === 1,
'hmac-sha256' => hash_equals(
base64_encode(hash_hmac('sha256', $data, $this->privateKey, true)),
@@ -511,19 +525,60 @@ class HttpMessageSigner
private function rsaSign(string $data): string
{
if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA256)) {
- throw new \RuntimeException("RSA signing failed");
+ throw new UnProcessableSignatureException("RSA signing failed");
}
return base64_encode($signature);
}
+ private function rsa512Sign(string $data): string
+ {
+ if (!openssl_sign($data, $signature, $this->privateKey, OPENSSL_ALGO_SHA512)) {
+ throw new UnProcessableSignatureException("RSA signing failed");
+ }
+ return base64_encode($signature);
+ }
+
+ private function pssSign(string $data): string
+ {
+ $rsa = new RSA();
+ if ($rsa->loadKey($this->privateKey) !== true) {
+ throw new UnprocessableSignatureException("PSS loadkey failure");
+ };
+ $rsa->setHash('sha512');
+ $rsa->setMGFHash('sha512');
+ $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+ try {
+ $signatureBytes = $rsa->sign($data);
+ } catch (\Exception $exception) {
+ throw new UnprocessableSignatureException($exception->getMessage());
+ }
+ return base64_encode($signatureBytes);
+ }
private function ed25519Sign(string $data): string
{
if (!openssl_sign($data, $signature, $this->privateKey, "Ed25519")) {
- throw new \RuntimeException("Ed25519 signing failed");
+ throw new UnProcessableSignatureException("Ed25519 signing failed");
}
return base64_encode($signature);
}
+ private function pssVerify(string $data, $signature): bool
+ {
+ $rsa = new RSA();
+ if (!$rsa->loadKey($this->publicKey)) {
+ throw new UnprocessableSignatureException("PSS loadkey failure");
+ };
+ $rsa->setHash('sha512');
+ $rsa->setMGFHash('sha512');
+ $rsa->setSignatureMode(RSA::SIGNATURE_PSS);
+ try {
+ $verified = $rsa->verify($data, $signature);
+ } catch (\Exception $exception) {
+ $verified = false;
+ }
+ return $verified;
+ }
+
/* parse a structed dict */
private function parseStructuredDict(string $headerValue)
@@ -567,7 +622,7 @@ class HttpMessageSigner
}
}
if (!isset($algorithmHeaderString)) {
- throw new \RuntimeException("Unsupported algorithm: $algorithm");
+ throw new UnProcessableSignatureException("Unsupported digest algorithm: $algorithm");
}
$digest = hash($algorithm, $body, true);
/**