Remove LD-signatures with extreme bias.

This commit is contained in:
Mike Macgirvin 2024-01-03 11:17:40 +11:00
parent fe03d1c51a
commit 1ed3fcf68e
22 changed files with 241 additions and 345 deletions

3
.gitignore vendored
View file

@ -69,6 +69,9 @@ nbproject/
*.kdev4
# PHPStorm
.idea/
.phpunit.result.cache
tests/.phpunit.result.cache
tests/.phpunit.cache
## composer

View file

@ -4,13 +4,13 @@ namespace Code\Daemon;
use Code\Lib\Config;
use Code\Lib\IConfig;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Libzot;
use Code\Lib\ObjCache;
use Code\Lib\Queue;
use Code\Lib\Activity;
use Code\Lib\ActivityStreams;
use Code\Lib\ActivityPub;
use Code\Lib\LDSignatures;
use Code\Lib\Channel;
use Code\Extend\Hook;
@ -377,7 +377,7 @@ class Notifier implements DaemonInterface
} else {
self::$encoded_item = array_merge(Activity::ap_context(), Activity::encode_activity($target_item, true));
self::$encoded_item['signature'] = LDSignatures::sign(self::$encoded_item, self::$channel);
self::$encoded_item['proof'] = (new JcsEddsa2022)->sign(self::$encoded_item, self::$channel);
}
logger('target_item: ' . print_r($target_item, true), LOGGER_DEBUG);
logger('encoded: ' . print_r(self::$encoded_item, true), LOGGER_DEBUG);

View file

@ -71,7 +71,7 @@ class ActivityPub
$msg = array_merge(Activity::ap_context(), $ti);
$msg['signature'] = LDSignatures::sign($msg, $arr['channel']);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $arr['channel']);
logger('ActivityPub_encoded (purge_all): ' . json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
@ -106,7 +106,7 @@ class ActivityPub
$msg = array_merge(Activity::ap_context(), $ti);
$msg['signature'] = LDSignatures::sign($msg, $arr['channel']);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $arr['channel']);
logger('ActivityPub_encoded: ' . json_encode($msg, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
@ -278,11 +278,11 @@ class ActivityPub
if (intval($x['recipient']['xchan_type']) === XCHAN_TYPE_GROUP) {
$join_msg = $msg;
$join_msg['type'] = 'Join';
$join_msg['signature'] = LDSignatures::sign($join_msg, $x['sender']);
$join_msg['proof'] = (new JcsEddsa2022)->sign($join_msg, $x['sender']);
$jmsg2 = json_encode($join_msg, JSON_UNESCAPED_SLASHES);
}
$msg['signature'] = LDSignatures::sign($msg, $x['sender']);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $x['sender']);
$jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES);
$h = q(
@ -346,7 +346,7 @@ class ActivityPub
]
);
$msg['signature'] = LDSignatures::sign($msg, $x['sender']);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $x['sender']);
$jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES);
@ -386,7 +386,7 @@ class ActivityPub
]
);
$msg['signature'] = LDSignatures::sign($msg, $x['sender']);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $x['sender']);
$jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES);
$r = q("select * from abook left join hubloc on abook_xchan = hubloc_hash
@ -473,7 +473,7 @@ class ActivityPub
);
}
$msg['signature'] = LDSignatures::sign($msg, $channel);
$msg['proof'] = (new JcsEddsa2022)->sign($msg, $channel);
$jmsg = json_encode($msg, JSON_UNESCAPED_SLASHES);

View file

@ -27,7 +27,7 @@ class ActivityStreams
public $origin = null;
public $owner = null;
public $signer = null;
public $ldsig = null;
public $edsig = null;
public $sigok = false;
public $recips = null;
public $raw_recips = null;
@ -114,15 +114,9 @@ class ActivityStreams
$this->replyto = $this->get_property_obj('replyTo');
}
$this->ldsig = $this->get_compound_property('signature');
if ($this->ldsig) {
$this->signer = $this->get_compound_property('creator', $this->ldsig);
if (
$this->signer && is_array($this->signer) && array_key_exists('publicKey', $this->signer)
&& is_array($this->signer['publicKey']) && $this->signer['publicKey']['publicKeyPem']
) {
$this->sigok = LDSignatures::verify($this->data, $this->signer['publicKey']['publicKeyPem']);
}
$this->edsig = $this->get_compound_property('proof');
if ($this->edsig) {
$this->checkEddsaSignature();
}
// Implied create activity required by C2S specification if no object is present
@ -509,6 +503,41 @@ class ActivityStreams
return $x;
}
public function checkEddsaSignature()
{
$signer = $this->get_property_obj('verificationMethod', $this->edsig);
$parseUrl = parse_url($signer);
if (!empty($parseUrl['fragment']) && str_starts_with($parseUrl['fragment'],'z6Mk')) {
$publicKey = $parseUrl['fragment'];
unset($parseUrl['fragment']);
unset($parseUrl['query']);
}
$url = unparse_url($parseUrl);
$this->signer = [ 'id' => $url ];
$hublocs = Activity::get_actor_hublocs($url);
$hasStoredKey = false;
if ($hublocs) {
foreach ($hublocs as $hubloc) {
if ($publicKey && $hubloc['xchan_epubkey'] === $publicKey) {
$hasStoredKey = true;
break;
}
}
}
if (! $hasStoredKey) {
$this->signer = Activity::fetch($url);
if ($this->signer
&& !empty($this->signer['assertionMethod'])
&& !empty($this->signer['assertionMethod']['publicKeyMultibase'])) {
$publicKey = $this->signer['assertionMethod']['publicKeyMultibase'];
}
}
if ($publicKey) {
$this->sigok = (new JcsEddsa2022)->verify($this->data, $publicKey);
}
}
public function debug() : null | string
{
return var_export($this, true);

View file

@ -53,6 +53,8 @@ class JcsEddsa2022
logger('verify exception:' . $e->getMessage());
}
logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false'));
return $result;
}

View file

@ -1,98 +0,0 @@
<?php
namespace Code\Lib;
use Exception;
require_once('library/jsonld/jsonld.php');
class LDSignatures
{
public static function verify($data, $pubkey): bool
{
$ohash = self::hash(self::signable_options($data['signature']));
$dhash = self::hash(self::signable_data($data));
$result = false;
if (!empty($data['signature']['signatureValue'])) {
$result = Crypto::verify($ohash . $dhash, base64_decode($data['signature']['signatureValue']), $pubkey);
logger('LD-verify: ' . ((intval($result)) ? 'true' : 'false'));
}
return $result;
}
public static function sign($data, $channel): array
{
$options = [
'type' => 'RsaSignature2017',
'nonce' => random_string(),
'creator' => Channel::url($channel),
'created' => datetime_convert('UTC', 'UTC', 'now', 'Y-m-d\TH:i:s\Z')
];
$ohash = self::hash(self::signable_options($options));
$dhash = self::hash(self::signable_data($data));
$options['signatureValue'] = base64_encode(Crypto::sign($ohash . $dhash, $channel['channel_prvkey']));
return $options;
}
public static function signable_data($data): bool|string
{
$newdata = [];
if ($data) {
foreach ($data as $k => $v) {
if ($k != 'signature') {
$newdata[$k] = $v;
}
}
}
return json_encode($newdata, JSON_UNESCAPED_SLASHES);
}
public static function signable_options($options): bool|string
{
$newopts = ['@context' => 'https://w3id.org/identity/v1'];
if ($options) {
foreach ($options as $k => $v) {
if (!in_array($k, ['type', 'id', 'signatureValue'])) {
$newopts[$k] = $v;
}
}
}
return json_encode($newopts, JSON_UNESCAPED_SLASHES);
}
public static function hash($obj): string
{
return hash('sha256', self::normalise($obj));
}
public static function normalise($data)
{
if (is_string($data)) {
$data = json_decode($data);
}
if (!is_object($data)) {
return '';
}
$d = '';
jsonld_set_document_loader('jsonld_document_loader');
try {
$d = jsonld_normalize($data, ['algorithm' => 'URDNA2015', 'format' => 'application/nquads']);
} catch (Exception $e) {
logger('normalise error: ' . $e->getMessage());
logger('normalise error: ' . print_r($data, true), LOGGER_DATA);
}
return $d;
}
}

View file

@ -9,7 +9,7 @@ use Code\Lib\ActivityStreams;
use Code\Lib\Activity as ZlibActivity;
use Code\Lib\Libzot;
use Code\Web\HTTPSig;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\ThreadListener;
use Code\Lib\Channel;
use App;
@ -296,7 +296,7 @@ class Activity extends Controller
$headers = [];
$headers['Content-Type'] = 'application/x-nomad+json';
$x['signature'] = LDSignatures::sign($x, $chan);
$x['signature'] = (new JcsEddsa2022)->sign($x, $chan);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];

View file

@ -8,7 +8,7 @@ use Code\Lib\Libzot;
use Code\Lib\Activity;
use Code\Lib\Libprofile;
use Code\Lib\ActivityStreams;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Crypto;
use Code\Lib\PConfig;
use Code\Lib as Zlib;
@ -153,7 +153,7 @@ class Channel extends Controller
http_status_exit(403, 'Permission denied');
}
as_return_and_die(Activity::encode_person($channel, true, true), $channel, '',false);
as_return_and_die(Activity::encode_person($channel, true, true), $channel);
}
// handle zot6 channel discovery

View file

@ -43,7 +43,7 @@ class Ap_probe extends Controller
if (isset($j['type'])) {
$AS = new ActivityStreams($j, null, true);
if ($AS->is_valid() && isset($AS->data['type'])) {
$html .= EOL . t('LD-Signature: ') . (($AS->sigok) ? 'true' : 'false') . EOL. EOL;
$html .= EOL . t('Eddsa-Signature: ') . (($AS->sigok) ? 'true' : 'false') . EOL. EOL;
if (is_array($AS->obj)
&& isset($AS->obj['type'])
&& !str_contains($AS->obj['type'], 'Collection')

View file

@ -13,7 +13,7 @@ use Code\Lib\Channel;
use Code\Lib\Navbar;
use Code\Lib\Socgraph;
use Code\Lib\XConfig;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Web\HTTPSig;
use Code\Extend\Hook;
use Code\Render\Theme;
@ -305,7 +305,7 @@ class Directory extends Controller
$headers = [];
$headers['Content-Type'] = 'application/x-nomad+json';
$x['signature'] = LDSignatures::sign($x, $chan);
$x['signature'] = (new JcsEddsa2022)->sign($x, $chan);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];

View file

@ -5,7 +5,7 @@ namespace Code\Module;
use Code\Web\Controller;
use Code\Lib\ActivityStreams;
use Code\Lib\Activity;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Channel;
use Code\Web\HTTPSig;

View file

@ -4,7 +4,7 @@ namespace Code\Module;
use App;
use Code\Lib\ActivityStreams;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Activity;
use Code\Web\HTTPSig;
use Code\Web\Controller;

View file

@ -4,7 +4,7 @@ namespace Code\Module;
use App;
use Code\Lib\ActivityStreams;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Activity;
use Code\Web\HTTPSig;
use Code\Web\Controller;

View file

@ -6,7 +6,7 @@ use App;
use Code\Lib\Libzot;
use Code\Lib\ActivityStreams;
use Code\Lib\Activity;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Crypto;
use Code\Web\HTTPSig;
use Code\Web\Controller;
@ -31,7 +31,7 @@ class Home extends Controller
$headers = [];
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"';
$x['signature'] = LDSignatures::sign($x, ['channel_address' => z_root(), 'channel_prvkey' => get_config('system', 'prvkey')]);
$x['signature'] = (new JcsEddsa2022)->sign($x, ['channel_address' => z_root(), 'channel_prvkey' => get_config('system', 'prvkey')]);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
logger('data: ' . jindent($ret), LOGGER_DATA);
$headers['Date'] = datetime_convert('UTC', 'UTC', 'now', 'D, d M Y H:i:s \\G\\M\\T');

View file

@ -138,9 +138,7 @@ class Inbox extends Controller
$v = Activity::get_actor_hublocs($AS->actor['id']);
if ($v && $v[0]['hubloc_hash'] !== $hsig['portable_id']) {
// The sender is not actually the activity actor, so verify the LD signature.
// litepub activities (with no LD signature) will always have a matching actor and sender
// The sender is not actually the activity actor, so verify the object signature.
if ($AS->signer && is_array($AS->signer) && $AS->signer['id'] !== $AS->actor['id']) {
// the activity wasn't signed by the activity actor
return;

View file

@ -22,7 +22,7 @@ namespace Code\Module;
use Code\Lib\Libsync;
use Code\Lib\Activity;
use Code\Lib\ActivityStreams;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Web\HTTPSig;
use Code\Web\Controller;
use Code\Lib\Libzot;
@ -310,7 +310,7 @@ class Item extends Controller
$headers = [];
$headers['Content-Type'] = 'application/x-nomad+json';
$x['signature'] = LDSignatures::sign($x, $chan);
$x['signature'] = (new JcsEddsa2022)->sign($x, $chan);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];

View file

@ -8,7 +8,7 @@ use Code\Lib\Activity;
use Code\Lib\ActivityStreams;
use Code\Lib\ASCollection;
use Code\Lib\Channel;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Libprofile;
use Code\Lib\Libzot;
use Code\Lib\Navbar;
@ -369,7 +369,7 @@ class Search extends Controller
$headers = [];
$headers['Content-Type'] = 'application/x-nomad+json';
$x['signature'] = LDSignatures::sign($x, $chan);
$x['signature'] = (new JcsEddsa2022)->sign($x, $chan);
$ret = json_encode($x, JSON_UNESCAPED_SLASHES);
$headers['Digest'] = HTTPSig::generate_digest_header($ret);
$headers['(request-target)'] = strtolower($_SERVER['REQUEST_METHOD']) . ' ' . $_SERVER['REQUEST_URI'];

View file

@ -9,7 +9,7 @@ use Code\Lib\Activity;
use Code\Lib\ActivityPub;
use Code\Lib\Queue;
use Code\Lib\System;
use Code\Lib\LDSignatures;
use Code\Lib\JcsEddsa2022;
use Code\Lib\Addon;
use Code\Lib\Url;
use Code\Web\HTTPSig;
@ -21,15 +21,11 @@ use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
use PHPMailer\PHPMailer\Exception;
require_once('library/jsonld/jsonld.php');
/**
* @file include/network.php
* @brief Network related functions.
*/
function json_return_and_die($x, $content_type = 'application/json', $debug = false)
{
header("Content-type: $content_type");
@ -40,7 +36,7 @@ function json_return_and_die($x, $content_type = 'application/json', $debug = fa
killme();
}
function as_return_and_die($obj, $channel, $contextType = null, $ldsign = true)
function as_return_and_die($obj, $channel, $contextType = null, $signObject = true)
{
if (! is_array($obj)) {
@ -52,8 +48,8 @@ function as_return_and_die($obj, $channel, $contextType = null, $ldsign = true)
$headers = [];
$headers['Content-Type'] = 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"' ;
if ($ldsign) {
$data['signature'] = LDSignatures::sign($data, $channel);
if ($signObject) {
$data['proof'] = (new JcsEddsa2022)->sign($data, $channel);
}
$json = json_encode($data, JSON_UNESCAPED_SLASHES);
logger('data: ' . jindent($json), LOGGER_DATA);
@ -1436,156 +1432,6 @@ function getBestSupportedMimeType($mimeTypes = null, $acceptedTypes = false)
return null;
}
/**
* @brief Perform caching for jsonld normaliser.
*
* @param string $url
* @return mixed|bool|array
*/
function jsonld_document_loader($url)
{
$doc = (object) [ 'contextUrl' => null, 'document' => null, 'documentUrl' => $url];
$recursion = 0;
$builtins = [
'https://www.w3.org/ns/activitystreams' => 'library/w3org/activitystreams.jsonld',
'https://w3id.org/identity/v1' => 'library/w3org/identity-v1.jsonld',
'https://w3id.org/security/v1' => 'library/w3org/security-v1.jsonld',
];
$x = debug_backtrace();
if ($x) {
foreach ($x as $n) {
if ($n['function'] === __FUNCTION__) {
$recursion++;
}
}
}
if ($recursion > 5) {
logger('jsonld bomb detected at: ' . $url);
killme();
}
$cachepath = 'cache/ldcache';
if (! is_dir($cachepath)) {
Stdio::mkdir($cachepath, STORAGE_DEFAULT_PERMISSIONS, true);
}
$filename = '';
foreach ($builtins as $key => $value) {
if ($url === $key) {
$doc->document = file_get_contents($value);
return $doc;
}
}
if (! $filename) {
$filename = $cachepath . '/' . urlencode($url);
}
if (file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) {
logger('loading ' . $filename . ' from recent cache');
return file_get_contents($filename);
}
$r = jsonld_default_document_loader($url);
if ($r) {
if (!in_array($url, $builtins)) {
file_put_contents($filename, json_encode($r));
}
return $r;
}
if (file_exists($filename)) {
logger('loading ' . $filename . ' from longterm cache');
return file_get_contents($filename);
}
else {
logger($filename . ' does not exist and cannot be loaded');
}
return $doc;
}
/**
* @brief Perform caching for jsonld normaliser.
*
* @param string $url
* @return mixed|bool|array
*/
function hz_jsonld_document_loader($url) {
$doc = (object) [
'contextUrl' => null,
'document' => null,
'documentUrl' => $url
];
$recursion = 0;
$builtins = [
'https://www.w3.org/ns/activitystreams' => 'library/w3org/activitystreams.jsonld',
'https://w3id.org/identity/v1' => 'library/w3org/identity-v1.jsonld',
'https://w3id.org/security/v1' => 'library/w3org/security-v1.jsonld',
];
$x = debug_backtrace();
if ($x) {
foreach ($x as $n) {
if ($n['function'] === __FUNCTION__) {
$recursion++;
}
}
}
if ($recursion > 5) {
logger('jsonld bomb detected at: ' . $url);
killme();
}
foreach ($builtins as $key => $value) {
if ($url === $key) {
$doc->document = file_get_contents($value);
return $doc;
}
}
$cachepath = 'store/[data]/[jsonld]';
if(!is_dir($cachepath)) {
os_mkdir($cachepath, STORAGE_DEFAULT_PERMISSIONS, true);
}
$filename = $cachepath . '/' . urlencode($url);
if (file_exists($filename) && filemtime($filename) > time() - (12 * 60 * 60)) {
logger('loading ' . $filename . ' from recent cache');
return json_decode(file_get_contents($filename));
}
$r = jsonld_default_document_loader($url);
if ($r) {
if (!in_array($url, $builtins)) {
$cache_obj = $r;
// To prevent double encoding we need to decode $cache_obj->document
// before encoding the whole object for storage.
$cache_obj->document = json_decode($cache_obj->document);
file_put_contents($filename, json_encode($cache_obj));
}
return $r;
}
if (file_exists($filename)) {
logger('loading ' . $filename . ' from longterm cache');
return json_decode(file_get_contents($filename));
}
else {
logger($filename . ' does not exist and cannot be loaded');
}
return $doc;
}
function is_https_request()
{

9
tests/bootstrap.php Normal file
View file

@ -0,0 +1,9 @@
<?php
set_include_path(
'../include' . PATH_SEPARATOR
. '../library' . PATH_SEPARATOR
. '../'
);
require_once('../boot.php');

27
tests/phpunit.xml Normal file
View file

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.6/phpunit.xsd"
bootstrap="bootstrap.php"
cacheResultFile=".phpunit.cache/test-results"
executionOrder="depends,defects"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
convertDeprecationsToExceptions="true"
failOnRisky="true"
failOnWarning="true"
verbose="true">
<testsuites>
<testsuite name="default">
<directory>../tests</directory>
</testsuite>
</testsuites>
<coverage cacheDirectory=".phpunit.cache/code-coverage"
processUncoveredFiles="true">
<include>
<directory suffix=".php">.</directory>
</include>
</coverage>
</phpunit>

View file

@ -1,49 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/6.0/phpunit.xsd"
bootstrap="../boot.php"
forceCoversAnnotation="false"
beStrictAboutCoversAnnotation="true"
beStrictAboutOutputDuringTests="true"
beStrictAboutTodoAnnotatedTests="true"
verbose="true">
<testsuite name="Default Test Suite">
<directory suffix="Test.php">./unit/</directory>
</testsuite>
<testsuite name="API Test Suite">
<directory suffix="Test.php" prefix="API">./unit/</directory>
</testsuite>
<testsuite name="Ex-/Import Test Suite">
<!--<directory suffix="Test.php">./unit/eximport/</directory>-->
</testsuite>
<groups>
<exclude>
<group>postgresql</group>
</exclude>
</groups>
<!--cover reporting-->
<filter>
<whitelist processUncoveredFilesFromWhitelist="true">
<directory suffix=".php">../Code/</directory>
<directory suffix=".php">../include/</directory>
</whitelist>
</filter>
<logging>
<log type="junit" target="./results/junit.xml" logIncompleteSkipped="false"/>
<log type="coverage-clover" target="./results/coverage-clover.xml"/>
<log type="coverage-html" target="./results/coverage-report/" lowUpperBound="35"
highLowerBound="70"/>
<log type="testdox-text" target="./results/testdox.txt"/>
</logging>
<php>
<!-- Default test database config, only used if no environment variables
with same names are set.
!!! Never run against a real database, it will truncate all tables -->
<env name="hz_db_server" value="127.0.0.1"/>
<env name="hz_db_scheme" value="mysql"/>
<env name="hz_db_port" value="3306"/>
<env name="hz_db_user" value="travis_hz"/>
<env name="hz_db_pass" value="hubzilla"/>
<env name="hz_db_database" value="travis_hubzilla"/>
</php>
</phpunit>

View file

@ -3,9 +3,10 @@
namespace Code\Tests\Unit\Lib;
use Code\Lib\JcsEddsa2022;
use Code\Tests\Unit\UnitTestCase;
class JcsEddsa2022Test extends \Code\Tests\Unit\UnitTestCase
class JcsEddsa2022Test extends UnitTestCase
{
public function testVerifyFromSpec()
@ -46,4 +47,132 @@ class JcsEddsa2022Test extends \Code\Tests\Unit\UnitTestCase
}
}
public function testSignAndVerify()
{
$publicKey = 'z6MkfpucGTDbMZADwM6vEa8pS3s8Z9xqSEn6HihijZ4fVs9d';
$channel = [
'channel_url' => 'https://example.com/channel/klingon',
'channel_epubkey' => 'FGdbYgr526Swuyya3e8epCBdHahlWNg9I0sBhMKCzpw',
'channel_eprvkey' => 'StLRo8xb7VJ5XdR10OUYQM/uooP7D7fMlgvQFa1wrZIUZ1tiCvnbpLC7LJrd7x6kIF0dqGVY2D0jSwGEwoLOnA',
'channel_address' => 'klingon@example.com',
'channel_system' => false,
];
$document = '{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1",
{
"nomad": "https://example.com/apschema#",
"toot": "http://joinmastodon.org/ns#",
"litepub": "http://litepub.social/ns#",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"oauthRegistrationEndpoint": "litepub:oauthRegistrationEndpoint",
"sensitive": "as:sensitive",
"movedTo": "as:movedTo",
"discoverable": "toot:discoverable",
"indexable": "toot:indexable",
"capabilities": "litepub:capabilities",
"acceptsJoins": "litepub:acceptsJoins",
"Hashtag": "as:Hashtag",
"canReply": "toot:canReply",
"canSearch": "nomad:canSearch",
"approval": "toot:approval",
"expires": "nomad:expires",
"directMessage": "nomad:directMessage",
"Category": "nomad:Category",
"copiedTo": "nomad:copiedTo",
"searchContent": "nomad:searchContent",
"searchTags": "nomad:searchTags"
}
],
"type": "Person",
"id": "https://example.com/channel/klingon",
"preferredUsername": "klingon",
"name": "klingon",
"created": "2023-07-13T20:23:32Z",
"updated": "2023-07-13T20:23:32Z",
"icon": {
"type": "Image",
"mediaType": "image/png",
"updated": "2023-07-13T20:23:32Z",
"url": "https://example.com/photo/profile/l/2",
"height": 300,
"width": 300
},
"url": "https://example.com/channel/klingon",
"tag": [
{
"type": "Note",
"name": "Protocol",
"content": "zot6"
},
{
"type": "Note",
"name": "Protocol",
"content": "nomad"
},
{
"type": "Note",
"name": "Protocol",
"content": "activitypub"
}
],
"inbox": "https://example.com/inbox/klingon",
"outbox": "https://example.com/outbox/klingon",
"followers": "https://example.com/followers/klingon",
"following": "https://example.com/following/klingon",
"wall": "https://example.com/outbox/klingon",
"endpoints": {
"sharedInbox": "https://example.com/inbox",
"oauthRegistrationEndpoint": "https://example.com/api/client/register",
"oauthAuthorizationEndpoint": "https://example.com/authorize",
"oauthTokenEndpoint": "https://example.com/token",
"searchContent": "https://example.com/search/klingon?search={}",
"searchTags": "https://example.com/search/klingon?tag={}"
},
"discoverable": true,
"canSearch": [],
"indexable": false,
"publicKey": {
"id": "https://example.com/channel/klingon?operation=rsakey",
"owner": "https://example.com/channel/klingon",
"signatureAlgorithm": "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA+LXyOD/bzzVgM/nUOJ5m
c4WrQPMlhKqWJvKrumdQw9JJYcyaZp/jmMxDx/w/EwVw+wnV5wZcD0yBVhC7NPRa
nYc5OfNhS4MO74xgZrj+VWSTzNo3YooS/dEIIvsu6bhxfooHj17SA6pMRnZkkVpk
ykpPRYwJw+NvKcRwzpF06rxMqjZ+Bp0ea/X37j4cHaosRoQTJiHmMKKnpByKdImF
TR1juJ69ASh6nh8YVGcz6fz1jBQZPMx05tfNdyN5oZRTr8Nug2CiF3V7yKKS14HD
kE9eeFeTMt58Qi+8kprATYxKrlIuTZmI4YdIRgtM+tPQsosKTFmjzbef4dYooutv
T7XfrE+wYVZlx2pkaeFiKrJVacpmmFJe8zCIFXrofq1aOagU1kpwnXgjneCttA+M
OJ3Y+cPamdfRQDtsBcokJUD40RTwux6OGW9zqkJIpniVB+CZu4nTOHCzMJwbxF0p
JmGZd9kc3PR6Uf/IHAb1xeyTi4FyyYTbRDYuJyqRKbe880QUwgCBcogIbNy4xxsH
UTMy0ucWaDSBRahKUIHl3FRglvnI754NJSXBDIQOwC9oRRH27Vmm1Jy8sltmFLFr
ENJCGgOH8Bhpk+y1jtw1jpTig76wIvw+6zQtgNSfPnrNGIHt5mcoy4pFFXLv2lK2
/u26hUGQAq71Ra0DwgXIWFECAwEAAQ==
-----END PUBLIC KEY-----
"
},
"assertionMethod": [
{
"id": "https://example.com/channel/klingon#z6MkfpucGTDbMZADwM6vEa8pS3s8Z9xqSEn6HihijZ4fVs9d",
"type": "Multikey",
"controller": "https://example.com/channel/klingon",
"publicKeyMultibase": "z6MkfpucGTDbMZADwM6vEa8pS3s8Z9xqSEn6HihijZ4fVs9d"
}
],
"manuallyApprovesFollowers": true
}';
$algorithm = new JcsEddsa2022();
$documentArray = json_decode($document,true);
$documentArray['proof'] = $algorithm->sign($documentArray, $channel);
$verified = (new JcsEddsa2022())->verify($documentArray, $publicKey);
$this->assertTrue($verified, 'Verify encode and decode eddsa-jcs-2022');
}
}