From 9d10152ab1557bcac086e04ee871aa8429ad7c83 Mon Sep 17 00:00:00 2001 From: Edouard Paris Date: Tue, 18 Aug 2020 14:16:23 +0200 Subject: [PATCH] feature: service::AuthVerifier --- Cargo.toml | 9 ++++++ src/lib.rs | 3 ++ src/service.rs | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 100 insertions(+) create mode 100644 src/service.rs diff --git a/Cargo.toml b/Cargo.toml index 4334829..859ed72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,15 @@ documentation = "https://docs.rs/lnurl/" [lib] name = "lnurl" +[features] +# Include nothing by default +default = [] +service = ["bitcoin_hashes", "hex", "secp256k1"] + [dependencies] +bitcoin_hashes = { version = "^0.7.6", optional = true } +hex = { version = "0.4.2", optional = true } +secp256k1 = { version = "^0.17.2", optional = true } serde = { version = "^1.0.93", features =["derive"]} serde_json = "^1.0.39" + diff --git a/src/lib.rs b/src/lib.rs index 9d388cd..20a13b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,8 @@ use serde::{Deserialize, Serialize}; +#[cfg(feature = "service")] +pub mod service; + #[derive(Debug, PartialEq, Serialize, Deserialize)] pub enum Event { #[serde(rename = "REGISTERED")] diff --git a/src/service.rs b/src/service.rs new file mode 100644 index 0000000..84c23fc --- /dev/null +++ b/src/service.rs @@ -0,0 +1,88 @@ +extern crate bitcoin_hashes; +extern crate hex; +extern crate secp256k1; + +use bitcoin_hashes::{sha256, Hash}; +use secp256k1::{Error, Message, PublicKey, Secp256k1, Signature, Verification, VerifyOnly}; + +/// VerifierError is the AuthVerifier errors. +pub enum VerifierError { + Secp256k1Error(Error), + HexError(hex::FromHexError), +} + +/// AuthVerifier verifies the secp256k1 signature of a message with a given pubkey. +pub struct AuthVerifier { + secp: Secp256k1, +} + +impl AuthVerifier { + pub fn new() -> Self { + AuthVerifier { + secp: Secp256k1::verification_only(), + } + } + + /// verifies the secp256k1 signature of a message with a given pubkey. + pub fn verify(self, hk1: &str, hsig: &str, hpubkey: &str) -> Result { + let msg = hex::decode(hk1).map_err(|e| VerifierError::HexError(e))?; + let sig = hex::decode(hsig).map_err(|e| VerifierError::HexError(e))?; + let pubkey = hex::decode(hpubkey).map_err(|e| VerifierError::HexError(e))?; + return verify_sig(&self.secp, &msg, &sig, &pubkey) + .map_err(|e| VerifierError::Secp256k1Error(e)); + } +} + +/// verify_sig checks if the signature of a key for a given message is valid. +pub fn verify_sig( + secp: &Secp256k1, + msg: &[u8], + sig: &[u8], + pubkey: &[u8], +) -> Result { + let msg = sha256::Hash::hash(msg); + let msg = Message::from_slice(&msg)?; + let sig = Signature::from_compact(sig)?; + let pubkey = PublicKey::from_slice(pubkey)?; + + Ok(secp.verify(&msg, &sig, &pubkey).is_ok()) +} + +#[cfg(test)] +mod tests { + use super::*; + + extern crate bitcoin_hashes; + use secp256k1::{Error, Message, Secp256k1, SecretKey, Signature, Signing}; + + fn sign( + secp: &Secp256k1, + msg: &[u8], + seckey: [u8; 32], + ) -> Result { + let msg = sha256::Hash::hash(msg); + let msg = Message::from_slice(&msg)?; + let seckey = SecretKey::from_slice(&seckey)?; + Ok(secp.sign(&msg, &seckey)) + } + + #[test] + fn test_verify_sig() { + let secp = Secp256k1::new(); + let seckey = [ + 59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107, 94, 203, 174, + 253, 102, 39, 170, 146, 46, 252, 4, 143, 236, 12, 136, 28, + ]; + let pubkey = [ + 2, 29, 21, 35, 7, 198, 183, 43, 14, 208, 65, 139, 14, 112, 205, 128, 231, 245, 41, 91, + 141, 134, 245, 114, 45, 63, 82, 19, 251, 210, 57, 79, 54, + ]; + let msg = b"This is some message"; + + let signature = sign(&secp, msg, seckey).unwrap(); + + let serialize_sig = signature.serialize_compact(); + + assert!(verify_sig(&secp, msg, &serialize_sig, &pubkey).unwrap()); + } +}