diff options
-rw-r--r-- | Zotlabs/Lib/JcsEddsa2022.php | 91 | ||||
-rw-r--r-- | Zotlabs/Lib/Multibase.php | 34 |
2 files changed, 125 insertions, 0 deletions
diff --git a/Zotlabs/Lib/JcsEddsa2022.php b/Zotlabs/Lib/JcsEddsa2022.php new file mode 100644 index 000000000..cb5462952 --- /dev/null +++ b/Zotlabs/Lib/JcsEddsa2022.php @@ -0,0 +1,91 @@ +<?php + +namespace Zotlabs\Lib; + +use Mmccook\JsonCanonicalizator\JsonCanonicalizatorFactory; +use StephenHill\Base58; + +class JcsEddsa2022 { + + public function __construct() { + return $this; + } + + public function sign($data, $channel): array { + $base58 = new Base58(); + $pubkey = (new Multibase())->publicKey($channel['channel_epubkey']); + $options = [ + 'type' => 'DataIntegrityProof', + 'cryptosuite' => 'eddsa-jcs-2022', + 'created' => datetime_convert(format: ATOM_TIME), + 'verificationMethod' => channel_url($channel) . '#' . $pubkey, + 'proofPurpose' => 'assertionMethod', + ]; + + $optionsHash = $this->hash($this->signableOptions($options), true); + $dataHash = $this->hash($this->signableData($data), true); + + $options['proofValue'] = 'z' . $base58->encode(sodium_crypto_sign_detached($optionsHash . $dataHash, + sodium_base642bin($channel['channel_eprvkey'], SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING))); + + return $options; + } + + public function verify($data, $publicKey) { + $base58 = new Base58(); + $encodedSignature = $data['proof']['proofValue'] ?? ''; + if (!str_starts_with($encodedSignature,'z')) { + return false; + } + $encodedSignature = substr($encodedSignature, 1); + $optionsHash = $this->hash($this->signableOptions($data['proof']), true); + $dataHash = $this->hash($this->signableData($data),true); + + try { + $result = sodium_crypto_sign_verify_detached($base58->decode($encodedSignature), $optionsHash . $dataHash, + (new Multibase())->decode($publicKey, true)); + } + catch (\Exception $e) { + logger('verify exception:' . $e->getMessage()); + } + + logger('SignatureVerify (eddsa-jcs-2022) ' . (($result) ? 'true' : 'false')); + + return $result; + } + + public function signableData($data) { + $signableData = []; + if ($data) { + foreach ($data as $k => $v) { + if ($k != 'proof') { + $signableData[$k] = $v; + } + } + } + return $signableData; + } + + public function signableOptions($options) { + $signableOptions = []; + + if ($options) { + foreach ($options as $k => $v) { + if ($k !== 'proofValue') { + $signableOptions[$k] = $v; + } + } + } + return $signableOptions; + } + + public function hash($obj, $binary = false) { + return hash('sha256', $this->canonicalize($obj), $binary); + } + + public function canonicalize($data) { + $canonicalization = JsonCanonicalizatorFactory::getInstance(); + return $canonicalization->canonicalize($data); + } + +} diff --git a/Zotlabs/Lib/Multibase.php b/Zotlabs/Lib/Multibase.php new file mode 100644 index 000000000..099723630 --- /dev/null +++ b/Zotlabs/Lib/Multibase.php @@ -0,0 +1,34 @@ +<?php +namespace Zotlabs\Lib; + +use StephenHill\Base58; + +class Multibase { + + protected $key = null; + + public function __construct() { + return $this; + } + + public function publicKey($key) { + $base58 = new Base58(); + $raw = hex2bin('ed01') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + return 'z' . $base58->encode($raw); + } + + public function secretKey($key) { + $base58 = new Base58(); + $raw = hex2bin('8026') . sodium_base642bin($key, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + return 'z' . $base58->encode($raw); + } + + public function decode($key, $binary = false) { + $base58 = new Base58(); + $key = substr($key,1); + $raw = $base58->decode($key); + $binaryKey = substr($raw, 2); + return $binary ? $binaryKey : sodium_bin2base64($binaryKey, SODIUM_BASE64_VARIANT_ORIGINAL_NO_PADDING); + } + +} |