@@ -18,10 +18,9 @@ use crate::lsps5::msgs::{
1818use crate :: message_queue:: MessageQueue ;
1919use crate :: prelude:: * ;
2020
21- use bitcoin:: hashes:: { sha256, Hash } ;
22- use bitcoin:: hex:: { DisplayHex , FromHex } ;
23- use bitcoin:: secp256k1:: { PublicKey , Secp256k1 } ;
21+ use bitcoin:: secp256k1:: PublicKey ;
2422use lightning:: ln:: msgs:: { ErrorAction , LightningError } ;
23+ use lightning:: util:: message_signing;
2524
2625use crate :: sync:: { Arc , Mutex , RwLock } ;
2726use core:: ops:: Deref ;
@@ -501,7 +500,7 @@ where
501500 /// * On success: `true` if the signature is valid
502501 /// * On error: LightningError with error description
503502 pub fn verify_notification_signature (
504- & self , counterparty_node_id : PublicKey , timestamp : & str , signature : & str ,
503+ counterparty_node_id : PublicKey , timestamp : & str , signature : & str ,
505504 notification : & WebhookNotification ,
506505 ) -> Result < bool , LightningError > {
507506 // Check timestamp format
@@ -540,91 +539,19 @@ where
540539 } ,
541540 } ;
542541
542+ // Create the message in the same format as used in sign_notification
543543 let message = format ! (
544544 "LSPS5: DO NOT SIGN THIS MESSAGE MANUALLY: LSP: At {} I notify {}" ,
545545 timestamp, notification_json
546546 ) ;
547547
548- // Remove the "lspsig:" prefix if present
549- let signature_data =
550- if signature. starts_with ( "lspsig:" ) { & signature[ 7 ..] } else { signature } ;
551-
552- // Decode the hex signature
553- let signature_bytes: Vec < u8 > = match FromHex :: from_hex ( signature_data) {
554- Ok ( bytes) => bytes,
555- Err ( e) => {
556- return Err ( LightningError {
557- err : format ! ( "Failed to decode signature from hex: {}" , e) ,
558- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
559- } ) ;
560- } ,
561- } ;
562-
563- if signature_bytes. len ( ) != 65 {
564- return Err ( LightningError {
565- err : format ! ( "Invalid signature length: {}, expected 65" , signature_bytes. len( ) ) ,
566- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
567- } ) ;
568- }
569-
570- // Extract recovery ID and signature data
571- let recovery_id =
572- match bitcoin:: secp256k1:: ecdsa:: RecoveryId :: from_i32 ( signature_bytes[ 0 ] as i32 ) {
573- Ok ( id) => id,
574- Err ( e) => {
575- return Err ( LightningError {
576- err : format ! ( "Invalid recovery ID: {}" , e) ,
577- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
578- } ) ;
579- } ,
580- } ;
581-
582- // Create recoverable signature
583- let mut sig_data = [ 0u8 ; 64 ] ;
584- sig_data. copy_from_slice ( & signature_bytes[ 1 ..65 ] ) ;
585-
586- let recoverable_sig = match bitcoin:: secp256k1:: ecdsa:: RecoverableSignature :: from_compact (
587- & sig_data,
588- recovery_id,
589- ) {
590- Ok ( sig) => sig,
591- Err ( e) => {
592- return Err ( LightningError {
593- err : format ! ( "Invalid recoverable signature: {}" , e) ,
594- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
595- } ) ;
596- } ,
597- } ;
598-
599- // Hash the message
600- let message_hash = sha256:: Hash :: hash ( message. as_bytes ( ) ) ;
601- let message_to_verify =
602- match bitcoin:: secp256k1:: Message :: from_digest_slice ( message_hash. as_ref ( ) ) {
603- Ok ( msg) => msg,
604- Err ( e) => {
605- return Err ( LightningError {
606- err : format ! ( "Invalid message hash: {}" , e) ,
607- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
608- } ) ;
609- } ,
610- } ;
611-
612- // Verify signature and recover key
613- let secp = Secp256k1 :: verification_only ( ) ;
614- match secp. recover_ecdsa ( & message_to_verify, & recoverable_sig) {
615- Ok ( recovered_key) => {
616- if recovered_key != counterparty_node_id {
617- return Err ( LightningError {
618- err : "Recovered key does not match LSP key" . to_string ( ) ,
619- action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
620- } ) ;
621- }
622- Ok ( true )
623- } ,
624- Err ( e) => Err ( LightningError {
625- err : format ! ( "Failed to recover key: {}" , e) ,
548+ if message_signing:: verify ( message. as_bytes ( ) , signature, & counterparty_node_id) {
549+ Ok ( true )
550+ } else {
551+ Err ( LightningError {
552+ err : "Invalid signature" . to_string ( ) ,
626553 action : ErrorAction :: IgnoreAndLog ( Level :: Error ) ,
627- } ) ,
554+ } )
628555 }
629556 }
630557
@@ -643,8 +570,7 @@ where
643570 /// * On success: The parsed webhook notification
644571 /// * On error: LightningError with error description
645572 pub fn parse_webhook_notification (
646- & self , counterparty_node_id : PublicKey , timestamp : & str , signature : & str ,
647- notification_json : & str ,
573+ counterparty_node_id : PublicKey , timestamp : & str , signature : & str , notification_json : & str ,
648574 ) -> Result < WebhookNotification , LightningError > {
649575 // Parse the notification JSON
650576 let notification: WebhookNotification = match serde_json:: from_str ( notification_json) {
@@ -657,7 +583,7 @@ where
657583 } ,
658584 } ;
659585 // Verify signature
660- match self . verify_notification_signature (
586+ match Self :: verify_notification_signature (
661587 counterparty_node_id,
662588 timestamp,
663589 signature,
@@ -689,7 +615,7 @@ mod tests {
689615 use crate :: {
690616 lsps0:: ser:: LSPSRequestId , lsps5:: msgs:: SetWebhookResponse , tests:: utils:: TestEntropy ,
691617 } ;
692- use bitcoin:: secp256k1:: SecretKey ;
618+ use bitcoin:: { key :: Secp256k1 , secp256k1:: SecretKey } ;
693619
694620 fn setup_test_client ( ) -> (
695621 LSPS5ClientHandler < Arc < TestEntropy > > ,
0 commit comments