@@ -9,7 +9,7 @@ use chrono::{DateTime, Utc};
99
1010pub const SIGNATURE_HEADER : & str = "lightspark-signature" ;
1111
12- #[ derive( Clone , Serialize , Deserialize ) ]
12+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
1313pub struct WebhookEvent {
1414 pub event_type : WebhookEventType ,
1515 pub event_id : String ,
@@ -24,46 +24,37 @@ pub struct WebhookEvent {
2424impl WebhookEvent {
2525 pub fn verify_and_parse (
2626 data : & [ u8 ] ,
27- hexdigest : & str ,
27+ hex_digest : & str ,
2828 webhook_secret : & str ,
2929 ) -> Result < WebhookEvent , Error > {
30- let mut hmac: Hmac < Sha256 > =
31- Hmac :: new_from_slice ( webhook_secret. as_bytes ( ) ) . expect ( "HMAC can take key of any size" ) ;
32- hmac. update ( data) ;
33- let result = hmac. finalize ( ) ;
34- let code_bytes = result. into_bytes ( ) ;
35- let hex_string = hex:: encode ( code_bytes) ;
36- if !hex_string
37- . to_ascii_lowercase ( )
38- . eq ( & hexdigest. to_ascii_lowercase ( ) )
39- {
40- return Err ( Error :: WebhookSignatureError ) ;
41- }
42- Self :: parse ( data)
43- }
30+ if let Ok ( digest_bytes) = hex:: decode ( hex_digest) {
31+ let hmac: Hmac < Sha256 > = Hmac :: new_from_slice ( webhook_secret. as_bytes ( ) )
32+ . expect ( "HMAC can take key of any size" )
33+ . chain_update ( data) ;
4434
45- fn parse ( data : & [ u8 ] ) -> Result < WebhookEvent , Error > {
46- let event: WebhookEvent = serde_json:: from_slice ( data) . map_err ( Error :: JsonError ) ?;
47- Ok ( event)
35+ if hmac. verify_slice ( digest_bytes. as_slice ( ) ) . is_ok ( ) {
36+ return serde_json:: from_slice ( data) . map_err ( Error :: JsonError ) ;
37+ }
38+ }
39+ Err ( Error :: WebhookSignatureError )
4840 }
4941}
5042
5143#[ cfg( test) ]
5244mod tests {
45+ use crate :: error:: Error ;
46+
5347 #[ test]
5448 fn test_verify_and_parse ( ) {
5549 let data = "{\" event_type\" : \" NODE_STATUS\" , \" event_id\" : \" 1615c8be5aa44e429eba700db2ed8ca5\" , \" timestamp\" : \" 2023-05-17T23:56:47.874449+00:00\" , \" entity_id\" : \" lightning_node:01882c25-157a-f96b-0000-362d42b64397\" }" ;
56- let hexdigest = "62a8829aeb48b4142533520b1f7f86cdb1ee7d718bf3ea15bc1c662d4c453b74" ;
50+ let hex_digest = "62a8829aeb48b4142533520b1f7f86cdb1ee7d718bf3ea15bc1c662d4c453b74" ;
5751 let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
5852
5953 let result =
60- super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hexdigest , webhook_secret)
54+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest , webhook_secret)
6155 . expect ( "Success case" ) ;
6256
63- assert_eq ! (
64- result. event_type. to_string( ) ,
65- super :: WebhookEventType :: NodeStatus . to_string( )
66- ) ;
57+ assert_eq ! ( result. event_type, super :: WebhookEventType :: NodeStatus ) ;
6758 assert_eq ! ( result. event_id, "1615c8be5aa44e429eba700db2ed8ca5" ) ;
6859 assert_eq ! (
6960 result. entity_id,
@@ -75,6 +66,30 @@ mod tests {
7566 ) ;
7667 }
7768
69+ #[ test]
70+ fn test_invalid_signature ( ) {
71+ let data = "{\" event_type\" : \" NODE_STATUS\" , \" event_id\" : \" 1615c8be5aa44e429eba700db2ed8ca5\" , \" timestamp\" : \" 2023-05-17T23:56:47.874449+00:00\" , \" entity_id\" : \" lightning_node:01882c25-157a-f96b-0000-362d42b64397\" }" ;
72+ let hex_digest = "deadbeef" ;
73+ let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
74+
75+ let result =
76+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest, webhook_secret) ;
77+
78+ result. expect_err ( Error :: WebhookSignatureError . to_string ( ) . as_str ( ) ) ;
79+ }
80+
81+ #[ test]
82+ fn test_invalid_digest_bytes ( ) {
83+ let data = "{\" event_type\" : \" NODE_STATUS\" , \" event_id\" : \" 1615c8be5aa44e429eba700db2ed8ca5\" , \" timestamp\" : \" 2023-05-17T23:56:47.874449+00:00\" , \" entity_id\" : \" lightning_node:01882c25-157a-f96b-0000-362d42b64397\" }" ;
84+ let hex_digest = "NotAHexDigest" ;
85+ let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
86+
87+ let result =
88+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest, webhook_secret) ;
89+
90+ result. expect_err ( Error :: WebhookSignatureError . to_string ( ) . as_str ( ) ) ;
91+ }
92+
7893 #[ test]
7994 fn test_remote_signing_webhook ( ) {
8095 let data = "{\" event_type\" : \" REMOTE_SIGNING\" , \" event_id\" : \" 1615c8be5aa44e429eba700db2ed8ca5\" , \" timestamp\" : \" 2023-05-17T23:56:47.874449+00:00\" , \" entity_id\" : \" lightning_node:01882c25-157a-f96b-0000-362d42b64397\" , \" data\" : {\" sub_event_type\" : \" ECDH\" , \" public_key\" : \" 027c4b09ffb985c298afe7e5813266cbfcb7780b480ac294b0b43dc21f2be3d13c\" }}" ;
@@ -85,10 +100,7 @@ mod tests {
85100 super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hexdigest, webhook_secret)
86101 . expect ( "Success case" ) ;
87102
88- assert_eq ! (
89- result. event_type. to_string( ) ,
90- super :: WebhookEventType :: RemoteSigning . to_string( )
91- ) ;
103+ assert_eq ! ( result. event_type, super :: WebhookEventType :: RemoteSigning ) ;
92104
93105 let data = result. data . expect ( "Data is missing" ) ;
94106 let sub_event_type = data[ "sub_event_type" ]
0 commit comments