1
+ use ln:: peers:: { chacha, hkdf} ;
2
+
3
+ /// Returned after a successful handshake to encrypt and decrypt communication with peer nodes
4
+ pub struct Conduit {
5
+ pub ( crate ) sending_key : [ u8 ; 32 ] ,
6
+ pub ( crate ) receiving_key : [ u8 ; 32 ] ,
7
+
8
+ pub ( crate ) sending_chaining_key : [ u8 ; 32 ] ,
9
+ pub ( crate ) receiving_chaining_key : [ u8 ; 32 ] ,
10
+
11
+ pub ( crate ) receiving_nonce : u32 ,
12
+ pub ( crate ) sending_nonce : u32 ,
13
+
14
+ pub ( super ) read_buffer : Option < Vec < u8 > > ,
15
+ }
16
+
17
+ impl Conduit {
18
+ pub fn encrypt ( & mut self , buffer : & [ u8 ] ) -> Vec < u8 > {
19
+ let length = buffer. len ( ) as u16 ;
20
+ let length_bytes = length. to_be_bytes ( ) ;
21
+
22
+ let encrypted_length = chacha:: encrypt ( & self . sending_key , self . sending_nonce as u64 , & [ 0 ; 0 ] , & length_bytes) ;
23
+ self . increment_sending_nonce ( ) ;
24
+
25
+ let encrypted_message = chacha:: encrypt ( & self . sending_key , self . sending_nonce as u64 , & [ 0 ; 0 ] , buffer) ;
26
+ self . increment_sending_nonce ( ) ;
27
+
28
+ let mut ciphertext = encrypted_length;
29
+ ciphertext. extend_from_slice ( & encrypted_message) ;
30
+ ciphertext
31
+ }
32
+
33
+ pub ( super ) fn read ( & mut self , data : & [ u8 ] ) {
34
+ let mut read_buffer = if let Some ( buffer) = self . read_buffer . take ( ) {
35
+ buffer
36
+ } else {
37
+ Vec :: new ( )
38
+ } ;
39
+
40
+ read_buffer. extend_from_slice ( data) ;
41
+ self . read_buffer = Some ( read_buffer) ;
42
+ }
43
+
44
+ /// Add newly received data from the peer node to the buffer and decrypt all possible messages
45
+ pub fn decrypt_message_stream ( & mut self , new_data : Option < & [ u8 ] > ) -> Vec < Vec < u8 > > {
46
+ let mut read_buffer = if let Some ( buffer) = self . read_buffer . take ( ) {
47
+ buffer
48
+ } else {
49
+ Vec :: new ( )
50
+ } ;
51
+
52
+ if let Some ( data) = new_data {
53
+ read_buffer. extend_from_slice ( data) ;
54
+ }
55
+
56
+ let mut messages = Vec :: new ( ) ;
57
+
58
+ loop {
59
+ // todo: find way that won't require cloning the entire buffer
60
+ let ( current_message, offset) = self . decrypt ( & read_buffer[ ..] ) ;
61
+ if offset == 0 {
62
+ break ;
63
+ }
64
+
65
+ read_buffer. drain ( 0 ..offset) ;
66
+
67
+ if let Some ( message) = current_message {
68
+ messages. push ( message) ;
69
+ } else {
70
+ break ;
71
+ }
72
+ }
73
+
74
+ self . read_buffer = Some ( read_buffer) ;
75
+
76
+ messages
77
+ }
78
+
79
+ /// Decrypt a single message. Buffer is an undelimited amount of bytes
80
+ fn decrypt ( & mut self , buffer : & [ u8 ] ) -> ( Option < Vec < u8 > > , usize ) { // the response slice should have the same lifetime as the argument. It's the slice data is read from
81
+ if buffer. len ( ) < 18 {
82
+ return ( None , 0 ) ;
83
+ }
84
+
85
+ let encrypted_length = & buffer[ 0 ..18 ] ; // todo: abort if too short
86
+ let length_vec = chacha:: decrypt ( & self . receiving_key , self . receiving_nonce as u64 , & [ 0 ; 0 ] , encrypted_length) . unwrap ( ) ;
87
+ let mut length_bytes = [ 0u8 ; 2 ] ;
88
+ length_bytes. copy_from_slice ( length_vec. as_slice ( ) ) ;
89
+ let message_length = u16:: from_be_bytes ( length_bytes) as usize ;
90
+
91
+ let message_end_index = message_length + 18 ; // todo: abort if too short
92
+ if buffer. len ( ) < message_end_index {
93
+ return ( None , 0 ) ;
94
+ }
95
+
96
+ let encrypted_message = & buffer[ 18 ..message_end_index] ;
97
+
98
+ self . increment_receiving_nonce ( ) ;
99
+
100
+ let message = chacha:: decrypt ( & self . receiving_key , self . receiving_nonce as u64 , & [ 0 ; 0 ] , encrypted_message) . unwrap ( ) ;
101
+
102
+ self . increment_receiving_nonce ( ) ;
103
+
104
+ ( Some ( message) , message_end_index)
105
+ }
106
+
107
+ fn increment_sending_nonce ( & mut self ) {
108
+ Self :: increment_nonce ( & mut self . sending_nonce , & mut self . sending_chaining_key , & mut self . sending_key ) ;
109
+ }
110
+
111
+ fn increment_receiving_nonce ( & mut self ) {
112
+ Self :: increment_nonce ( & mut self . receiving_nonce , & mut self . receiving_chaining_key , & mut self . receiving_key ) ;
113
+ }
114
+
115
+ fn increment_nonce ( nonce : & mut u32 , chaining_key : & mut [ u8 ; 32 ] , key : & mut [ u8 ; 32 ] ) {
116
+ * nonce += 1 ;
117
+ if * nonce == 1000 {
118
+ Self :: rotate_key ( chaining_key, key) ;
119
+ * nonce = 0 ;
120
+ }
121
+ }
122
+
123
+ fn rotate_key ( chaining_key : & mut [ u8 ; 32 ] , key : & mut [ u8 ; 32 ] ) {
124
+ let ( new_chaining_key, new_key) = hkdf:: derive ( chaining_key, key) ;
125
+ chaining_key. copy_from_slice ( & new_chaining_key) ;
126
+ key. copy_from_slice ( & new_key) ;
127
+ }
128
+ }
129
+
130
+ #[ cfg( test) ]
131
+ mod tests {
132
+ use ln:: peers:: conduit:: Conduit ;
133
+
134
+ #[ test]
135
+ fn test_chaining ( ) {
136
+ let chaining_key_vec = hex:: decode ( "919219dbb2920afa8db80f9a51787a840bcf111ed8d588caf9ab4be716e42b01" ) . unwrap ( ) ;
137
+ let mut chaining_key = [ 0u8 ; 32 ] ;
138
+ chaining_key. copy_from_slice ( & chaining_key_vec) ;
139
+
140
+ let sending_key_vec = hex:: decode ( "969ab31b4d288cedf6218839b27a3e2140827047f2c0f01bf5c04435d43511a9" ) . unwrap ( ) ;
141
+ let mut sending_key = [ 0u8 ; 32 ] ;
142
+ sending_key. copy_from_slice ( & sending_key_vec) ;
143
+
144
+ let receiving_key_vec = hex:: decode ( "bb9020b8965f4df047e07f955f3c4b88418984aadc5cdb35096b9ea8fa5c3442" ) . unwrap ( ) ;
145
+ let mut receiving_key = [ 0u8 ; 32 ] ;
146
+ receiving_key. copy_from_slice ( & receiving_key_vec) ;
147
+
148
+ let mut connected_peer = Conduit {
149
+ sending_key,
150
+ receiving_key,
151
+ sending_chaining_key : chaining_key,
152
+ receiving_chaining_key : chaining_key,
153
+ sending_nonce : 0 ,
154
+ receiving_nonce : 0 ,
155
+ read_buffer : None ,
156
+ } ;
157
+
158
+ let message = hex:: decode ( "68656c6c6f" ) . unwrap ( ) ;
159
+ let mut encrypted_messages: Vec < Vec < u8 > > = Vec :: new ( ) ;
160
+
161
+ for _ in 0 ..1002 {
162
+ let encrypted_message = connected_peer. encrypt ( & message) ;
163
+ encrypted_messages. push ( encrypted_message) ;
164
+ }
165
+
166
+ assert_eq ! ( encrypted_messages[ 0 ] , hex:: decode( "cf2b30ddf0cf3f80e7c35a6e6730b59fe802473180f396d88a8fb0db8cbcf25d2f214cf9ea1d95" ) . unwrap( ) ) ;
167
+ assert_eq ! ( encrypted_messages[ 1 ] , hex:: decode( "72887022101f0b6753e0c7de21657d35a4cb2a1f5cde2650528bbc8f837d0f0d7ad833b1a256a1" ) . unwrap( ) ) ;
168
+ assert_eq ! ( encrypted_messages[ 500 ] , hex:: decode( "178cb9d7387190fa34db9c2d50027d21793c9bc2d40b1e14dcf30ebeeeb220f48364f7a4c68bf8" ) . unwrap( ) ) ;
169
+ assert_eq ! ( encrypted_messages[ 501 ] , hex:: decode( "1b186c57d44eb6de4c057c49940d79bb838a145cb528d6e8fd26dbe50a60ca2c104b56b60e45bd" ) . unwrap( ) ) ;
170
+ assert_eq ! ( encrypted_messages[ 1000 ] , hex:: decode( "4a2f3cc3b5e78ddb83dcb426d9863d9d9a723b0337c89dd0b005d89f8d3c05c52b76b29b740f09" ) . unwrap( ) ) ;
171
+ assert_eq ! ( encrypted_messages[ 1001 ] , hex:: decode( "2ecd8c8a5629d0d02ab457a0fdd0f7b90a192cd46be5ecb6ca570bfc5e268338b1a16cf4ef2d36" ) . unwrap( ) ) ;
172
+ }
173
+ }
0 commit comments