social/lib/Model/LinkedDataSignature.php

358 wiersze
7.1 KiB
PHP
Czysty Zwykły widok Historia

<?php
declare(strict_types=1);
/**
* Nextcloud - Social Support
*
* This file is licensed under the Affero General Public License version 3 or
* later. See the COPYING file.
*
* @author Maxence Lange <maxence@artificial-owl.com>
* @copyright 2018, Maxence Lange <maxence@artificial-owl.com>
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Social\Model;
use daita\MySmallPhpTools\Traits\TArrayTools;
use JsonSerializable;
use OCA\Social\Exceptions\LinkedDataSignatureMissingException;
use OCA\Social\Model\ActivityPub\ACore;
use OCA\Social\Service\SignatureService;
/**
* Class InstancePath
*
* @package OCA\Social\Model
*/
class LinkedDataSignature implements JsonSerializable {
use TArrayTools;
/** @var string */
private $type = '';
/** @var string */
private $creator = '';
/** @var string */
private $created = '';
/** @var string */
private $nonce = '';
/** @var string */
private $signatureValue = '';
/** @var string */
private $privateKey = '';
/** @var string */
private $publicKey = '';
/** @var array */
private $object = [];
/**
* LinkedDataSignature constructor.
*/
public function __construct() {
}
/**
* @return string
*/
public function getType(): string {
return $this->type;
}
/**
* @param string $type
*
* @return LinkedDataSignature
*/
public function setType(string $type): LinkedDataSignature {
$this->type = $type;
return $this;
}
/**
* @return string
*/
public function getCreator(): string {
return $this->creator;
}
/**
* @param string $creator
*
* @return LinkedDataSignature
*/
public function setCreator(string $creator): LinkedDataSignature {
$this->creator = $creator;
return $this;
}
/**
* @return string
*/
public function getNonce(): string {
return $this->nonce;
}
/**
* @param string $nonce
*
* @return LinkedDataSignature
*/
public function setNonce(string $nonce): LinkedDataSignature {
$this->nonce = $nonce;
return $this;
}
/**
* @return string
*/
public function getCreated(): string {
return $this->created;
}
/**
* @param string $created
*
* @return LinkedDataSignature
*/
public function setCreated(string $created): LinkedDataSignature {
$this->created = $created;
return $this;
}
/**
* @return string
*/
public function getSignatureValue(): string {
return $this->signatureValue;
}
/**
* @param string $signatureValue
*
* @return LinkedDataSignature
*/
public function setSignatureValue(string $signatureValue): LinkedDataSignature {
$this->signatureValue = $signatureValue;
return $this;
}
/**
* @return array
*/
public function getObject(): array {
return $this->object;
}
/**
* @param array $object
*
* @return LinkedDataSignature
*/
public function setObject(array $object): LinkedDataSignature {
$this->object = $object;
return $this;
}
/**
* @return string
*/
public function getPrivateKey(): string {
return $this->privateKey;
}
/**
* @param string $privateKey
*
* @return LinkedDataSignature
*/
public function setPrivateKey(string $privateKey): LinkedDataSignature {
$this->privateKey = $privateKey;
return $this;
}
/**
* @return string
*/
public function getPublicKey(): string {
return $this->publicKey;
}
/**
* @param string $publicKey
*
* @return LinkedDataSignature
*/
public function setPublicKey(string $publicKey): LinkedDataSignature {
$this->publicKey = $publicKey;
return $this;
}
/**
* @throws LinkedDataSignatureMissingException
*/
public function sign() {
$header = [
'@context' => 'https://w3id.org/identity/v1',
'creator' => $this->getCreator(),
'created' => $this->getCreated()
];
$hash = $this->hashedCanonicalize($header) . $this->hashedCanonicalize($this->getObject());
$algo = OPENSSL_ALGO_SHA256;
if ($this->getType() === 'RsaSignature2017') {
$algo = OPENSSL_ALGO_SHA256;
}
if (!openssl_sign($hash, $signed, $this->getPrivateKey(), $algo)) {
throw new LinkedDataSignatureMissingException();
}
$this->setSignatureValue(base64_encode($signed));
}
/**
* @return bool
*/
public function verify(): bool {
$header = [
'@context' => 'https://w3id.org/identity/v1',
'nonce' => $this->getNonce(),
'creator' => $this->getCreator(),
'created' => $this->getCreated()
];
$hashHeader = $this->hashedCanonicalize($header, true);
$hashObject = $this->hashedCanonicalize($this->getObject());
$algo = OPENSSL_ALGO_SHA256;
if ($this->getType() === 'RsaSignature2017') {
$algo = OPENSSL_ALGO_SHA256;
}
$signed = base64_decode($this->getSignatureValue());
if ($signed !== false
&& openssl_verify(
$hashHeader . $hashObject, $signed, $this->getPublicKey(), $algo
) === 1) {
return true;
}
return false;
}
/**
* @param array $data
*
* @param bool $removeEmptyValue
*
* @return string
*/
private function hashedCanonicalize(array $data, bool $removeEmptyValue = false): string {
if ($removeEmptyValue) {
$this->cleanArray($data);
}
$object = json_decode(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
$res = jsonld_normalize(
$object,
[
'algorithm' => 'URDNA2015',
'format' => 'application/nquads',
'documentLoader' => [SignatureService::class, 'documentLoader']
]
);
return hash('sha256', $res);
}
/**
* @param array $data
*
* @throws LinkedDataSignatureMissingException
*/
public function import(array $data) {
// if (!in_array(ACore::CONTEXT_SECURITY, $this->getArray('@context', $data, []))) {
// throw new LinkedDataSignatureMissingException('no @context security entry');
// }
$signature = $this->getArray('signature', $data, []);
if ($signature === []) {
throw new LinkedDataSignatureMissingException('missing signature');
}
$this->setType($this->get('type', $signature, ''));
$this->setCreator($this->get('creator', $signature, ''));
$this->setNonce($this->get('nonce', $signature, ''));
$this->setCreated($this->get('created', $signature, ''));
$this->setSignatureValue($this->get('signatureValue', $signature, ''));
unset($data['signature']);
$this->setObject($data);
}
/**
* @return array
*/
public function jsonSerialize(): array {
return [
'type' => $this->getType(),
'creator' => $this->getCreator(),
'created' => $this->getCreated(),
'signatureValue' => $this->getSignatureValue()
];
}
}