3 * @copyright Copyright (C) 2020, Friendica
5 * @license GNU AGPL version 3 or any later version
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as
9 * published by the Free Software Foundation, either version 3 of the
10 * License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
22 namespace Friendica\Util;
24 use Friendica\Core\Logger;
25 use Friendica\Model\APContact;
28 * Implements JSON-LD signatures
30 * Ported from Osada: https://framagit.org/macgirvin/osada
34 public static function isSigned($data)
36 return !empty($data['signature']);
39 public static function getSigner($data)
41 if (!self::isSigned($data)) {
45 $actor = JsonLD::fetchElement($data, 'actor', 'id');
46 if (empty($actor) || !is_string($actor)) {
50 $profile = APContact::getByURL($actor);
51 if (empty($profile['pubkey'])) {
54 $pubkey = $profile['pubkey'];
56 $ohash = self::hash(self::signableOptions($data['signature']));
57 $dhash = self::hash(self::signableData($data));
59 $x = Crypto::rsaVerify($ohash . $dhash, base64_decode($data['signature']['signatureValue']), $pubkey);
60 Logger::log('LD-verify: ' . intval($x));
69 public static function sign($data, $owner)
72 'type' => 'RsaSignature2017',
73 'nonce' => Strings::getRandomHex(64),
74 'creator' => $owner['url'] . '#main-key',
75 'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM)
78 $ohash = self::hash(self::signableOptions($options));
79 $dhash = self::hash(self::signableData($data));
80 $options['signatureValue'] = base64_encode(Crypto::rsaSign($ohash . $dhash, $owner['uprvkey']));
82 return array_merge($data, ['signature' => $options]);
85 private static function signableData($data)
87 unset($data['signature']);
91 private static function signableOptions($options)
93 $newopts = ['@context' => 'https://w3id.org/identity/v1'];
95 unset($options['type']);
96 unset($options['id']);
97 unset($options['signatureValue']);
99 return array_merge($newopts, $options);
102 private static function hash($obj)
104 return hash('sha256', JsonLD::normalize($obj));