diff --git a/sample/sample.config.ron b/sample/sample.config.ron index 68921f0..06ea859 100644 --- a/sample/sample.config.ron +++ b/sample/sample.config.ron @@ -10,5 +10,6 @@ FriendlyConfig( public_key_hex: None, user_hex_keys: [ "ee11a5dff40c19a555f41fe42b48f00e618c91225622ae37b6c2bb67b76c4e49" - ] + ], + verify_events: true, ) \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index bd537f8..b419c09 100644 --- a/src/config.rs +++ b/src/config.rs @@ -14,6 +14,7 @@ pub struct FriendlyConfig { pub description: Option, pub public_key_hex: Option, pub user_hex_keys: Vec, + pub verify_events: bool, } impl Default for FriendlyConfig { @@ -29,6 +30,7 @@ impl Default for FriendlyConfig { description: None, public_key_hex: None, user_hex_keys: vec![], + verify_events: true, } } } @@ -46,6 +48,7 @@ impl FriendlyConfig { description, public_key_hex, user_hex_keys, + verify_events, } = self; let mut public_key: Option = None; @@ -70,6 +73,7 @@ impl FriendlyConfig { public_key, user_keys, user_hex_keys, + verify_events, }) } } @@ -87,4 +91,5 @@ pub struct Config { pub public_key: Option, pub user_keys: Vec, pub user_hex_keys: Vec, + pub verify_events: bool, } diff --git a/src/error.rs b/src/error.rs index bc81892..875b81f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -3,6 +3,10 @@ use thiserror::Error; /// Errors that can occur in the chorus crate #[derive(Error, Debug)] pub enum Error { + // Bad event id + #[error("Bad event id, does not match hash")] + BadEventId, + // Bad hex input #[error("Bad hex input")] BadHexInput, @@ -23,6 +27,10 @@ pub enum Error { #[error("Config: {0}")] Config(#[from] ron::error::SpannedError), + // Crypto + #[error("Crypto: {0}")] + Crypto(#[from] secp256k1::Error), + // Duplicate event #[error("Duplicate")] Duplicate, diff --git a/src/nostr.rs b/src/nostr.rs index e42e782..d0a55b0 100644 --- a/src/nostr.rs +++ b/src/nostr.rs @@ -186,7 +186,10 @@ impl WebSocketService { } async fn validate_event(event: &Event<'_>) -> Result { - // FIXME: check signature + // Verify event is valid + if GLOBALS.config.read().await.verify_events { + event.verify()?; + } // Accept relay lists from anybody if event.kind() == Kind(10002) { diff --git a/src/types/event/mod.rs b/src/types/event/mod.rs index d57a796..bd850c7 100644 --- a/src/types/event/mod.rs +++ b/src/types/event/mod.rs @@ -125,6 +125,35 @@ impl<'a> Event<'a> { output.extend(br#""}"#); Ok(output) } + + pub fn verify(&self) -> Result<(), Error> { + use secp256k1::hashes::{sha256, Hash}; + use secp256k1::schnorr::Signature; + use secp256k1::{Message, XOnlyPublicKey}; + + let signable = format!( + r#"[0,"{}",{},{},{},"{}"]"#, + self.pubkey(), + self.created_at(), + self.kind(), + self.tags()?, + unsafe { std::str::from_utf8_unchecked(self.content()) }, + ); + + let hash = sha256::Hash::hash(signable.as_bytes()); + + let hashref = >::as_ref(&hash); + if hashref != self.id().as_slice() { + return Err(Error::BadEventId); + } + + let pubkey = XOnlyPublicKey::from_slice(self.pubkey().as_slice())?; + let sig = Signature::from_slice(self.sig().as_slice())?; + let message = Message::from_digest_slice(hashref)?; + sig.verify(&message, &pubkey)?; + + Ok(()) + } } impl fmt::Display for Event<'_> {