@@ -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,36 @@ 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+
5346 #[ test]
5447 fn test_verify_and_parse ( ) {
5548 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" ;
49+ let hex_digest = "62a8829aeb48b4142533520b1f7f86cdb1ee7d718bf3ea15bc1c662d4c453b74" ;
5750 let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
5851
5952 let result =
60- super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hexdigest , webhook_secret)
53+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest , webhook_secret)
6154 . expect ( "Success case" ) ;
6255
63- assert_eq ! (
64- result. event_type. to_string( ) ,
65- super :: WebhookEventType :: NodeStatus . to_string( )
66- ) ;
56+ assert_eq ! ( result. event_type, super :: WebhookEventType :: NodeStatus ) ;
6757 assert_eq ! ( result. event_id, "1615c8be5aa44e429eba700db2ed8ca5" ) ;
6858 assert_eq ! (
6959 result. entity_id,
@@ -75,20 +65,41 @@ mod tests {
7565 ) ;
7666 }
7767
68+ #[ test]
69+ fn test_invalid_signature ( ) {
70+ 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\" }" ;
71+ let hex_digest = "deadbeef" ;
72+ let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
73+
74+ let result =
75+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest, webhook_secret) ;
76+
77+ assert ! ( matches!( result, Err ( super :: Error :: WebhookSignatureError ) ) ) ;
78+ }
79+
80+ #[ test]
81+ fn test_invalid_digest_bytes ( ) {
82+ 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\" }" ;
83+ let hex_digest = "NotAHexDigest" ;
84+ let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
85+
86+ let result =
87+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest, webhook_secret) ;
88+
89+ assert ! ( matches!( result, Err ( super :: Error :: WebhookSignatureError ) ) ) ;
90+ }
91+
7892 #[ test]
7993 fn test_remote_signing_webhook ( ) {
8094 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\" }}" ;
81- let hexdigest = "17db38526ce47682f4052e3182766fe2f23810ac538e32d5f20bbe1deb2e3519" ;
95+ let hex_digest = "17db38526ce47682f4052e3182766fe2f23810ac538e32d5f20bbe1deb2e3519" ;
8296 let webhook_secret = "3gZ5oQQUASYmqQNuEk0KambNMVkOADDItIJjzUlAWjX" ;
8397
8498 let result =
85- super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hexdigest , webhook_secret)
99+ super :: WebhookEvent :: verify_and_parse ( data. as_bytes ( ) , hex_digest , webhook_secret)
86100 . expect ( "Success case" ) ;
87101
88- assert_eq ! (
89- result. event_type. to_string( ) ,
90- super :: WebhookEventType :: RemoteSigning . to_string( )
91- ) ;
102+ assert_eq ! ( result. event_type, super :: WebhookEventType :: RemoteSigning ) ;
92103
93104 let data = result. data . expect ( "Data is missing" ) ;
94105 let sub_event_type = data[ "sub_event_type" ]
0 commit comments