]> git.mxchange.org Git - friendica.git/blobdiff - src/Util/LDSignature.php
bump version 2023.12
[friendica.git] / src / Util / LDSignature.php
index a52d84e478a36e803ccb8401e3b564d43fa258df..8cfadb16dada19da23ac65ccf997bb610bd448af 100644 (file)
 <?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @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 <https://www.gnu.org/licenses/>.
+ *
+ */
 
 namespace Friendica\Util;
 
-use Friendica\Util\JsonLD;
-use Friendica\Util\DateTimeFormat;
-use Friendica\Protocol\ActivityPub;
+use Friendica\Core\Logger;
+use Friendica\Model\APContact;
 
+/**
+ * Implements JSON-LD signatures
+ *
+ * Ported from Osada: https://framagit.org/macgirvin/osada
+ */
 class LDSignature
 {
-       public static function isSigned($data)
+       /**
+        * Checks if element 'signature' is found and not empty
+        *
+        * @param array $data
+        * @return bool
+        */
+       public static function isSigned(array $data): bool
        {
                return !empty($data['signature']);
        }
 
-       public static function isVerified($data, $pubkey = null)
+       /**
+        * Returns actor (signer) from given data
+        *
+        * @param array $data
+        * @return mixed Returns actor or false on error
+        */
+       public static function getSigner(array $data)
        {
                if (!self::isSigned($data)) {
                        return false;
                }
 
-               if (empty($pubkey)) {
-/*
-                       $creator = $data['signature']['creator'];
-                       $actor = JsonLD::fetchElement($data, 'actor', 'id');
-
-                       $url = (strpos($creator, '#') ? substr($creator, 0, strpos($creator, '#')) : $creator);
-
-                       $profile = ActivityPub::fetchprofile($url);
-                       if (!empty($profile)) {
-                               logger('Taking key from creator ' . $creator, LOGGER_DEBUG);
-                       } elseif ($url != $actor) {
-                               $profile = ActivityPub::fetchprofile($actor);
-                               if (empty($profile)) {
-                                       return false;
-                               }
-                               logger('Taking key from actor ' . $actor, LOGGER_DEBUG);
-                       }
-
-*/
-                       $actor = JsonLD::fetchElement($data, 'actor', 'id');
-                       if (empty($actor)) {
-                               return false;
-                       }
-
-                       $profile = ActivityPub::fetchprofile($actor);
-                       if (empty($profile['pubkey'])) {
-                               return false;
-                       }
-                       $pubkey = $profile['pubkey'];
+               $actor = JsonLD::fetchElement($data, 'actor', 'id');
+               if (empty($actor) || !is_string($actor)) {
+                       return false;
+               }
+
+               $profile = APContact::getByURL($actor);
+               if (empty($profile['pubkey'])) {
+                       return false;
                }
+               $pubkey = $profile['pubkey'];
 
-               $ohash = self::hash(self::signable_options($data['signature']));
-               $dhash = self::hash(self::signable_data($data));
+               $ohash = self::hash(self::signableOptions($data['signature']));
+               $dhash = self::hash(self::signableData($data));
 
                $x = Crypto::rsaVerify($ohash . $dhash, base64_decode($data['signature']['signatureValue']), $pubkey);
-               logger('LD-verify: ' . intval($x));
+               Logger::info('LD-verify', ['verified' => (int)$x, 'actor' => $profile['url']]);
 
-               return $x;
+               if (empty($x)) {
+                       return false;
+               } else {
+                       return $actor;
+               }
        }
 
-       public static function sign($data, $owner)
+       /**
+        * Signs given data by owner's signature
+        *
+        * @param array $data Data to sign
+        * @param array $owner Owner information, like URL
+        * @return array Merged array of $data and signature
+        */
+       public static function sign(array $data, array $owner): array
        {
                $options = [
                        'type' => 'RsaSignature2017',
-                       'nonce' => random_string(64),
+                       'nonce' => Strings::getRandomHex(64),
                        'creator' => $owner['url'] . '#main-key',
-                       'created' => DateTimeFormat::utcNow()
+                       'created' => DateTimeFormat::utcNow(DateTimeFormat::ATOM),
                ];
 
-               $ohash = self::hash(self::signable_options($options));
-               $dhash = self::hash(self::signable_data($data));
+               $ohash = self::hash(self::signableOptions($options));
+               $dhash = self::hash(self::signableData($data));
                $options['signatureValue'] = base64_encode(Crypto::rsaSign($ohash . $dhash, $owner['uprvkey']));
 
                return array_merge($data, ['signature' => $options]);
        }
 
-
-       private static function signable_data($data)
+       /**
+        * Removes element 'signature' from array
+        *
+        * @param array $data
+        * @return array With no element 'signature'
+        */
+       private static function signableData(array $data): array
        {
-               $newdata = [];
-               if (!empty($data)) {
-                       foreach ($data as $k => $v) {
-                               if (!in_array($k, ['signature'])) {
-                                       $newdata[$k] = $v;
-                               }
-                       }
-               }
-               return $newdata;
+               unset($data['signature']);
+               return $data;
        }
 
-
-       private static function signable_options($options)
+       /**
+        * Removes some elements and adds '@context' to it
+        *
+        * @param array $options
+        * @return array With some removed elements and added '@context' element
+        */
+       private static function signableOptions(array $options): array
        {
                $newopts = ['@context' => 'https://w3id.org/identity/v1'];
-               if (!empty($options)) {
-                       foreach ($options as $k => $v) {
-                               if (!in_array($k, ['type','id','signatureValue'])) {
-                                       $newopts[$k] = $v;
-                               }
-                       }
-               }
-               return $newopts;
+
+               unset($options['type']);
+               unset($options['id']);
+               unset($options['signatureValue']);
+
+               return array_merge($newopts, $options);
        }
 
-       private static function hash($obj)
+       /**
+        * Hashes normalized object
+        *
+        * @param ??? $obj
+        * @return string SHA256 hash
+        */
+       private static function hash($obj): string
        {
                return hash('sha256', JsonLD::normalize($obj));
        }