Skip to content

Commit

Permalink
verify method for json strings (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
1ma authored Nov 16, 2023
1 parent bd9f4d8 commit 7ba76da
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
57 changes: 56 additions & 1 deletion src/Event/Event.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace swentel\nostr\Event;

use Mdanter\Ecc\Crypto\Signature\SchnorrSignature;
use swentel\nostr\EventInterface;

class Event implements EventInterface
Expand Down Expand Up @@ -68,6 +69,60 @@ public function __construct()
$this->setKind($this->kind);
}

/**
* Returns true if $json encodes a valid Nostr event.
*/
public static function verify(string $json): bool
{
try {
$event = json_decode($json, flags: \JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE | \JSON_THROW_ON_ERROR);
} catch (\JsonException) {
return false;
}

if (!$event instanceof \stdClass
|| !property_exists($event, 'id')
|| !property_exists($event, 'pubkey')
|| !property_exists($event, 'created_at')
|| !property_exists($event, 'kind')
|| !property_exists($event, 'tags')
|| !property_exists($event, 'content')
|| !property_exists($event, 'sig')
|| !is_string($event->id)
|| !is_string($event->pubkey)
|| !is_int($event->created_at)
|| !is_int($event->kind)
|| !is_array($event->tags)
|| !is_string($event->content)
|| !is_string($event->sig)
) {
return false;
}

foreach ($event->tags as $tag) {
if (!is_array($tag)) {
return false;
}

foreach ($tag as $value) {
if (!is_string($value)) {
return false;
}
}
}

$computedId = hash('sha256', json_encode(
[0, $event->pubkey, $event->created_at, $event->kind, $event->tags, $event->content],
\JSON_UNESCAPED_SLASHES | \JSON_UNESCAPED_UNICODE
));

if (!hash_equals($computedId, $event->id)) {
return false;
}

return (new SchnorrSignature())->verify($event->pubkey, $event->sig, $event->id);
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -212,4 +267,4 @@ public function toArray(array $ignore_properties = []): array
return $array;
}

}
}
37 changes: 37 additions & 0 deletions tests/VerifyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

use PHPUnit\Framework\TestCase;
use swentel\nostr\Event\Event;

class VerifyTest extends TestCase
{
public function testValidEventValidation()
{
$json = '{"id":"ac21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1,"tags":[["e","5361bb83c899cf75589a25c32cea5b868d4990da41fd4dba84144eede5ad1359"],["p","d4dea80c64ebd3f9bc8271893191dbc851ecd2b7bcb811bb87386b5158ee735d"]],"content":"Gm ☕️⚡️🌅","sig":"46a9d4f4470bdf685d4fc4f664d1d4c8576e501cfe0ccf044ba65d36c3ee66eab3f261d524a4bdbde71bc2d921c13829a5ac596538ded74125f5060d4f3f805d"}';

$this->assertTrue(Event::verify($json));
}

/**
* @dataProvider invalidEventsProvider
*/
public function testInvalidEventsValidation(string $json)
{
$this->assertFalse(Event::verify($json));
}

public static function invalidEventsProvider(): array
{
return [
'not json' => ['Craig Wright is not Satoshi Nakamoto'],
'non object' => ['[1,2,3'],
'empty json' => ['{}'],
'meaningless json' => ['{"foo":"bar"}'],
'missing fields' => ['{"id":"ac21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1}'],
'invalid tag' => ['{"id":"ac21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1,"tags":[1234,["p","d4dea80c64ebd3f9bc8271893191dbc851ecd2b7bcb811bb87386b5158ee735d"]],"content":"Gm ☕️⚡️🌅","sig":"46a9d4f4470bdf685d4fc4f664d1d4c8576e501cfe0ccf044ba65d36c3ee66eab3f261d524a4bdbde71bc2d921c13829a5ac596538ded74125f5060d4f3f805d"}'],
'invalid tag value' => ['{"id":"ac21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1,"tags":[[123,"5361bb83c899cf75589a25c32cea5b868d4990da41fd4dba84144eede5ad1359"],["p","d4dea80c64ebd3f9bc8271893191dbc851ecd2b7bcb811bb87386b5158ee735d"]],"content":"Gm ☕️⚡️🌅","sig":"46a9d4f4470bdf685d4fc4f664d1d4c8576e501cfe0ccf044ba65d36c3ee66eab3f261d524a4bdbde71bc2d921c13829a5ac596538ded74125f5060d4f3f805d"}'],
'invalid id' => ['{"id":"bc21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1,"tags":[["e","5361bb83c899cf75589a25c32cea5b868d4990da41fd4dba84144eede5ad1359"],["p","d4dea80c64ebd3f9bc8271893191dbc851ecd2b7bcb811bb87386b5158ee735d"]],"content":"Gm ☕️⚡️🌅","sig":"46a9d4f4470bdf685d4fc4f664d1d4c8576e501cfe0ccf044ba65d36c3ee66eab3f261d524a4bdbde71bc2d921c13829a5ac596538ded74125f5060d4f3f805d"}'],
'invalid sig' => ['{"id":"ac21a6c4cb128a27c0b9a229bc1fa8e7167660664354d5e5a481825d01188108","pubkey":"7543c184ff776be3c13d2437894494173cfea4e9919d48fb2934216a13a53c58","created_at":1697800339,"kind":1,"tags":[["e","5361bb83c899cf75589a25c32cea5b868d4990da41fd4dba84144eede5ad1359"],["p","d4dea80c64ebd3f9bc8271893191dbc851ecd2b7bcb811bb87386b5158ee735d"]],"content":"Gm ☕️⚡️🌅","sig":"56a9d4f4470bdf685d4fc4f664d1d4c8576e501cfe0ccf044ba65d36c3ee66eab3f261d524a4bdbde71bc2d921c13829a5ac596538ded74125f5060d4f3f805d"}']
];
}
}

0 comments on commit 7ba76da

Please sign in to comment.