2018-12-19 01:13:33 +00:00
|
|
|
<?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;
|
2019-02-23 22:27:29 +00:00
|
|
|
use OCA\Social\Service\SignatureService;
|
2018-12-19 01:13:33 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Class InstancePath
|
|
|
|
*
|
|
|
|
* @package OCA\Social\Model
|
|
|
|
*/
|
|
|
|
class LinkedDataSignature implements JsonSerializable {
|
|
|
|
|
|
|
|
|
|
|
|
use TArrayTools;
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $type = '';
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $creator = '';
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $created = '';
|
|
|
|
|
2019-01-04 10:32:50 +00:00
|
|
|
/** @var string */
|
|
|
|
private $nonce = '';
|
|
|
|
|
2018-12-19 01:13:33 +00:00
|
|
|
/** @var string */
|
|
|
|
private $signatureValue = '';
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $privateKey = '';
|
|
|
|
|
|
|
|
/** @var string */
|
|
|
|
private $publicKey = '';
|
|
|
|
|
|
|
|
/** @var array */
|
|
|
|
private $object = [];
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LinkedDataSignature constructor.
|
|
|
|
*/
|
|
|
|
public function __construct() {
|
|
|
|
}
|
|
|
|
|
2019-02-23 22:27:29 +00:00
|
|
|
|
2018-12-19 01:13:33 +00:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2019-02-23 22:27:29 +00:00
|
|
|
|
2018-12-19 01:13:33 +00:00
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
2019-01-04 10:32:50 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-12-19 01:13:33 +00:00
|
|
|
/**
|
|
|
|
* @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',
|
2019-01-04 10:34:57 +00:00
|
|
|
'nonce' => $this->getNonce(),
|
2018-12-19 01:13:33 +00:00
|
|
|
'creator' => $this->getCreator(),
|
|
|
|
'created' => $this->getCreated()
|
|
|
|
];
|
|
|
|
|
2019-01-04 10:34:57 +00:00
|
|
|
$hashHeader = $this->hashedCanonicalize($header, true);
|
|
|
|
$hashObject = $this->hashedCanonicalize($this->getObject());
|
2018-12-19 01:13:33 +00:00
|
|
|
|
|
|
|
$algo = OPENSSL_ALGO_SHA256;
|
|
|
|
if ($this->getType() === 'RsaSignature2017') {
|
|
|
|
$algo = OPENSSL_ALGO_SHA256;
|
|
|
|
}
|
|
|
|
|
2019-01-04 11:14:30 +00:00
|
|
|
$signed = base64_decode($this->getSignatureValue());
|
|
|
|
if ($signed !== false
|
|
|
|
&& openssl_verify(
|
|
|
|
$hashHeader . $hashObject, $signed, $this->getPublicKey(), $algo
|
|
|
|
) === 1) {
|
2018-12-19 01:13:33 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $data
|
|
|
|
*
|
2019-01-04 10:34:57 +00:00
|
|
|
* @param bool $removeEmptyValue
|
|
|
|
*
|
2018-12-19 01:13:33 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2019-01-04 10:34:57 +00:00
|
|
|
private function hashedCanonicalize(array $data, bool $removeEmptyValue = false): string {
|
|
|
|
if ($removeEmptyValue) {
|
2019-02-23 00:04:00 +00:00
|
|
|
$this->cleanArray($data);
|
2019-01-04 10:34:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$object = json_decode(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE));
|
2018-12-19 01:13:33 +00:00
|
|
|
$res = jsonld_normalize(
|
|
|
|
$object,
|
|
|
|
[
|
2019-02-23 22:27:29 +00:00
|
|
|
'algorithm' => 'URDNA2015',
|
|
|
|
'format' => 'application/nquads',
|
|
|
|
'documentLoader' => [SignatureService::class, 'documentLoader']
|
2018-12-19 01:13:33 +00:00
|
|
|
]
|
|
|
|
);
|
|
|
|
|
|
|
|
return hash('sha256', $res);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param array $data
|
|
|
|
*
|
|
|
|
* @throws LinkedDataSignatureMissingException
|
|
|
|
*/
|
|
|
|
public function import(array $data) {
|
|
|
|
|
2019-06-21 11:21:15 +00:00
|
|
|
// if (!in_array(ACore::CONTEXT_SECURITY, $this->getArray('@context', $data, []))) {
|
|
|
|
// throw new LinkedDataSignatureMissingException('no @context security entry');
|
|
|
|
// }
|
2018-12-19 01:13:33 +00:00
|
|
|
|
|
|
|
$signature = $this->getArray('signature', $data, []);
|
|
|
|
if ($signature === []) {
|
2019-06-21 11:21:15 +00:00
|
|
|
throw new LinkedDataSignatureMissingException('missing signature');
|
2018-12-19 01:13:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$this->setType($this->get('type', $signature, ''));
|
|
|
|
$this->setCreator($this->get('creator', $signature, ''));
|
2019-01-04 10:32:50 +00:00
|
|
|
$this->setNonce($this->get('nonce', $signature, ''));
|
2018-12-19 01:13:33 +00:00
|
|
|
$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()
|
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|