0
0
Fork 0
mirror of https://github.com/nextcloud/server.git synced 2025-02-24 17:06:50 +00:00
nextcloud_server/lib/private/Security/Signature/Model/OutgoingSignedRequest.php
Maxence Lange 948547bd5d fix(ocm): signatory mapper
Signed-off-by: Maxence Lange <maxence@artificial-owl.com>
2024-12-04 09:30:55 -01:00

229 lines
5.2 KiB
PHP

<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
namespace OC\Security\Signature\Model;
use JsonSerializable;
use NCU\Security\Signature\Enum\DigestAlgorithm;
use NCU\Security\Signature\Enum\SignatureAlgorithm;
use NCU\Security\Signature\Exceptions\SignatoryException;
use NCU\Security\Signature\Exceptions\SignatoryNotFoundException;
use NCU\Security\Signature\IOutgoingSignedRequest;
use NCU\Security\Signature\ISignatoryManager;
use NCU\Security\Signature\ISignatureManager;
use OC\Security\Signature\SignatureManager;
/**
* extends ISignedRequest to add info requested at the generation of the signature
*
* @see ISignatureManager for details on signature
* @since 31.0.0
*/
class OutgoingSignedRequest extends SignedRequest implements
IOutgoingSignedRequest,
JsonSerializable {
private string $host = '';
private array $headers = [];
/** @var list<string> $headerList */
private array $headerList = [];
private SignatureAlgorithm $algorithm;
public function __construct(
string $body,
ISignatoryManager $signatoryManager,
private readonly string $identity,
private readonly string $method,
private readonly string $path,
) {
parent::__construct($body);
$options = $signatoryManager->getOptions();
$this->setHost($identity)
->setAlgorithm($options['algorithm'] ?? SignatureAlgorithm::RSA_SHA256)
->setSignatory($signatoryManager->getLocalSignatory())
->setDigestAlgorithm($options['digestAlgorithm'] ?? DigestAlgorithm::SHA256);
$headers = array_merge([
'(request-target)' => strtolower($method) . ' ' . $path,
'content-length' => strlen($this->getBody()),
'date' => gmdate($options['dateHeader'] ?? SignatureManager::DATE_HEADER),
'digest' => $this->getDigest(),
'host' => $this->getHost()
], $options['extraSignatureHeaders'] ?? []);
$signing = $headerList = [];
foreach ($headers as $element => $value) {
$signing[] = $element . ': ' . $value;
$headerList[] = $element;
if ($element !== '(request-target)') {
$this->addHeader($element, $value);
}
}
$this->setHeaderList($headerList)
->setSignatureData($signing);
}
/**
* @inheritDoc
*
* @param string $host
* @return $this
* @since 31.0.0
*/
public function setHost(string $host): self {
$this->host = $host;
return $this;
}
/**
* @inheritDoc
*
* @return string
* @since 31.0.0
*/
public function getHost(): string {
return $this->host;
}
/**
* @inheritDoc
*
* @param string $key
* @param string|int|float $value
*
* @return self
* @since 31.0.0
*/
public function addHeader(string $key, string|int|float $value): self {
$this->headers[$key] = $value;
return $this;
}
/**
* @inheritDoc
*
* @return array
* @since 31.0.0
*/
public function getHeaders(): array {
return $this->headers;
}
/**
* set the ordered list of used headers in the Signature
*
* @param list<string> $list
*
* @return self
* @since 31.0.0
*/
public function setHeaderList(array $list): self {
$this->headerList = $list;
return $this;
}
/**
* returns ordered list of used headers in the Signature
*
* @return list<string>
* @since 31.0.0
*/
public function getHeaderList(): array {
return $this->headerList;
}
/**
* @inheritDoc
*
* @param SignatureAlgorithm $algorithm
*
* @return self
* @since 31.0.0
*/
public function setAlgorithm(SignatureAlgorithm $algorithm): self {
$this->algorithm = $algorithm;
return $this;
}
/**
* @inheritDoc
*
* @return SignatureAlgorithm
* @since 31.0.0
*/
public function getAlgorithm(): SignatureAlgorithm {
return $this->algorithm;
}
/**
* @inheritDoc
*
* @return self
* @throws SignatoryException
* @throws SignatoryNotFoundException
* @since 31.0.0
*/
public function sign(): self {
$privateKey = $this->getSignatory()->getPrivateKey();
if ($privateKey === '') {
throw new SignatoryException('empty private key');
}
openssl_sign(
implode("\n", $this->getSignatureData()),
$signed,
$privateKey,
$this->getAlgorithm()->value
);
$this->setSignature(base64_encode($signed));
$this->setSigningElements(
[
'keyId="' . $this->getSignatory()->getKeyId() . '"',
'algorithm="' . $this->getAlgorithm()->value . '"',
'headers="' . implode(' ', $this->getHeaderList()) . '"',
'signature="' . $this->getSignature() . '"'
]
);
$this->addHeader('Signature', implode(',', $this->getSigningElements()));
return $this;
}
/**
* @param string $clear
* @param string $privateKey
* @param SignatureAlgorithm $algorithm
*
* @return string
* @throws SignatoryException
*/
private function signString(string $clear, string $privateKey, SignatureAlgorithm $algorithm): string {
if ($privateKey === '') {
throw new SignatoryException('empty private key');
}
openssl_sign($clear, $signed, $privateKey, $algorithm->value);
return base64_encode($signed);
}
public function jsonSerialize(): array {
return array_merge(
parent::jsonSerialize(),
[
'host' => $this->host,
'headers' => $this->headers,
'algorithm' => $this->algorithm->value,
'method' => $this->method,
'identity' => $this->identity,
'path' => $this->path,
]
);
}
}