From 8013667b58f6a2c142757212735d5710a786ac6b Mon Sep 17 00:00:00 2001 From: Sergi Delgado Segura Date: Tue, 16 Mar 2021 15:55:44 +0100 Subject: [PATCH] Adds lighting message signing/verification/pk_recovery --- lightning/src/lib.rs | 2 + lightning/src/util/message_signing.rs | 90 +++++++++++++++++++++++++++ lightning/src/util/mod.rs | 1 + 3 files changed, 93 insertions(+) create mode 100644 lightning/src/util/message_signing.rs diff --git a/lightning/src/lib.rs b/lightning/src/lib.rs index 567a7e186..59a714295 100644 --- a/lightning/src/lib.rs +++ b/lightning/src/lib.rs @@ -31,6 +31,8 @@ #[cfg(all(test, feature = "unstable"))] extern crate test; extern crate bitcoin; + + #[cfg(any(test, feature = "_test_utils"))] extern crate hex; #[cfg(any(test, feature = "fuzztarget", feature = "_test_utils"))] extern crate regex; diff --git a/lightning/src/util/message_signing.rs b/lightning/src/util/message_signing.rs new file mode 100644 index 000000000..528855835 --- /dev/null +++ b/lightning/src/util/message_signing.rs @@ -0,0 +1,90 @@ +use crate::util::zbase32; +use bitcoin::hashes::{sha256d, Hash}; +use bitcoin::secp256k1::recovery::{RecoverableSignature, RecoveryId}; +use bitcoin::secp256k1::{Error, Message, PublicKey, Secp256k1, SecretKey}; + +static LN_MESSAGE_PREFIX: &[u8] = "Lightning Signed Message:".as_bytes(); + + +fn sigrec_encode(sig_rec: RecoverableSignature) -> Vec { + let (rid, rsig) = sig_rec.serialize_compact(); + let prefix = rid.to_i32() as u8 + 31; + + [&[prefix], &rsig[..]].concat() +} + + +fn sigrec_decode(sig_rec: Vec) -> Result { + let rsig = &sig_rec[1..]; + let rid = sig_rec[0] as i32 - 31; + + match RecoveryId::from_i32(rid) { + Ok(x) => RecoverableSignature::from_compact(rsig, x), + Err(e) => Err(e) + } +} + + +pub fn sign(msg: &[u8], sk: SecretKey) -> Result { + let secp_ctx = Secp256k1::signing_only(); + let msg_hash = sha256d::Hash::hash(&[LN_MESSAGE_PREFIX, msg].concat()); + + let sig = secp_ctx.sign_recoverable(&Message::from_slice(&msg_hash)?, &sk); + Ok(zbase32::encode(&sigrec_encode(sig))) +} + +pub fn recover_pk(msg: &[u8], sig: String) -> Result{ + let secp_ctx = Secp256k1::verification_only(); + let msg_hash = sha256d::Hash::hash(&[LN_MESSAGE_PREFIX, msg].concat()); + + match zbase32::decode(&sig) { + Ok(sig_rec) => { + match sigrec_decode(sig_rec) { + Ok(sig) => secp_ctx.recover(&Message::from_slice(&msg_hash)?, &sig), + Err(e) => Err(e) + } + }, + Err(_) => Err(Error::InvalidSignature) + } + +} + +pub fn verify(msg: &[u8], sig: String, pk: PublicKey) -> bool { + match recover_pk(msg, sig) { + Ok(x) => x == pk, + Err(_) => false + } +} + +#[cfg(test)] +mod test { + use util::message_signing::{sign, recover_pk, verify}; + use bitcoin::secp256k1::key::ONE_KEY; + use bitcoin::secp256k1::{PublicKey, Secp256k1}; + + #[test] + fn test_sign() { + let message = "test message"; + let zbase32_sig = sign(message.as_bytes(), ONE_KEY); + + assert_eq!(zbase32_sig.unwrap(), "d9tibmnic9t5y41hg7hkakdcra94akas9ku3rmmj4ag9mritc8ok4p5qzefs78c9pqfhpuftqqzhydbdwfg7u6w6wdxcqpqn4sj4e73e") + } + + #[test] + fn test_recover_pk() { + let message = "test message"; + let sig = "d9tibmnic9t5y41hg7hkakdcra94akas9ku3rmmj4ag9mritc8ok4p5qzefs78c9pqfhpuftqqzhydbdwfg7u6w6wdxcqpqn4sj4e73e"; + let pk = recover_pk(message.as_bytes(), String::from(sig)); + + assert_eq!(pk.unwrap(), PublicKey::from_secret_key(&Secp256k1::signing_only(), &ONE_KEY)) + } + + #[test] + fn test_verify() { + let message = "another message"; + let sig = sign(message.as_bytes(), ONE_KEY).unwrap(); + let pk = PublicKey::from_secret_key(&Secp256k1::signing_only(), &ONE_KEY); + + assert!(verify(message.as_bytes(), String::from(sig), pk)) + } +} diff --git a/lightning/src/util/mod.rs b/lightning/src/util/mod.rs index 59b46a4f1..761d1fd02 100644 --- a/lightning/src/util/mod.rs +++ b/lightning/src/util/mod.rs @@ -16,6 +16,7 @@ pub mod events; pub mod errors; pub mod ser; pub mod zbase32; +pub mod message_signing; pub(crate) mod byte_utils; pub(crate) mod chacha20; -- 2.39.5