10
10
// This is a port of Andrew Moons poly1305-donna
11
11
// https://github.com/floodyberry/poly1305-donna
12
12
13
- use util:: ser:: { Writeable , Writer } ;
14
- use io:: { self , Write } ;
13
+ use ln:: msgs:: DecodeError ;
14
+ use util:: ser:: { FixedLengthReader , LengthRead , LengthReadableArgs , Readable , Writeable , Writer } ;
15
+ use io:: { self , Read , Write } ;
15
16
16
17
#[ cfg( not( fuzzing) ) ]
17
18
mod real_chachapoly {
@@ -95,22 +96,48 @@ mod real_chachapoly {
95
96
}
96
97
97
98
pub fn decrypt ( & mut self , input : & [ u8 ] , output : & mut [ u8 ] , tag : & [ u8 ] ) -> bool {
98
- assert ! ( input. len( ) == output. len( ) ) ;
99
- assert ! ( self . finished == false ) ;
99
+ if self . decrypt_inner ( input, Some ( output) , Some ( tag) ) {
100
+ self . cipher . process ( input, output) ;
101
+ return true
102
+ }
103
+ false
104
+ }
100
105
101
- self . finished = true ;
106
+ // Decrypt in place, and check the tag if it's provided. If the tag is not provided, then
107
+ // `finish_and_check_tag` may be called to check it later.
108
+ pub fn decrypt_in_place ( & mut self , input : & mut [ u8 ] , tag : Option < & [ u8 ] > ) -> bool {
109
+ if self . decrypt_inner ( input, None , tag) {
110
+ self . cipher . process_in_place ( input) ;
111
+ return true
112
+ }
113
+ false
114
+ }
115
+
116
+ fn decrypt_inner ( & mut self , input : & [ u8 ] , output : Option < & mut [ u8 ] > , tag : Option < & [ u8 ] > ) -> bool {
117
+ if let Some ( output) = output {
118
+ assert ! ( input. len( ) == output. len( ) ) ;
119
+ }
120
+ assert ! ( self . finished == false ) ;
102
121
103
122
self . mac . input ( input) ;
104
123
105
124
self . data_len += input. len ( ) ;
125
+
126
+ if let Some ( tag) = tag {
127
+ return self . finish_and_check_tag ( tag)
128
+ }
129
+ true
130
+ }
131
+
132
+ pub fn finish_and_check_tag ( & mut self , tag : & [ u8 ] ) -> bool {
133
+ self . finished = true ;
106
134
ChaCha20Poly1305RFC :: pad_mac_16 ( & mut self . mac , self . data_len ) ;
107
135
self . mac . input ( & self . aad_len . to_le_bytes ( ) ) ;
108
136
self . mac . input ( & ( self . data_len as u64 ) . to_le_bytes ( ) ) ;
109
137
110
138
let mut calc_tag = [ 0u8 ; 16 ] ;
111
139
self . mac . raw_result ( & mut calc_tag) ;
112
140
if fixed_time_eq ( & calc_tag, tag) {
113
- self . cipher . process ( input, output) ;
114
141
true
115
142
} else {
116
143
false
@@ -121,6 +148,21 @@ mod real_chachapoly {
121
148
#[ cfg( not( fuzzing) ) ]
122
149
pub use self :: real_chachapoly:: ChaCha20Poly1305RFC ;
123
150
151
+ pub ( crate ) struct ChaChaPolyReader < ' a , R : Read > {
152
+ pub chacha : & ' a mut ChaCha20Poly1305RFC ,
153
+ pub read : R ,
154
+ }
155
+
156
+ impl < ' a , R : Read > Read for ChaChaPolyReader < ' a , R > {
157
+ fn read ( & mut self , dest : & mut [ u8 ] ) -> Result < usize , io:: Error > {
158
+ let res = self . read . read ( dest) ?;
159
+ if res > 0 {
160
+ self . chacha . decrypt_in_place ( & mut dest[ 0 ..res] , None ) ;
161
+ }
162
+ Ok ( res)
163
+ }
164
+ }
165
+
124
166
pub ( crate ) struct ChaChaPolyWriter < ' a , W : Writer > {
125
167
pub chacha : & ' a mut ChaCha20Poly1305RFC ,
126
168
pub write : & ' a mut W ,
@@ -164,6 +206,31 @@ impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
164
206
}
165
207
}
166
208
209
+ pub ( crate ) struct ChaChaPolyReadAdapter < R : Readable > {
210
+ #[ allow( unused) ] // This will be used soon for onion messages
211
+ pub readable : R ,
212
+ }
213
+
214
+ impl < T : Readable > LengthReadableArgs < [ u8 ; 32 ] > for ChaChaPolyReadAdapter < T > {
215
+ fn read < R : LengthRead > ( mut r : & mut R , secret : [ u8 ; 32 ] ) -> Result < Self , DecodeError > {
216
+ if r. total_bytes ( ) < 16 { return Err ( DecodeError :: InvalidValue ) }
217
+
218
+ let mut chacha = ChaCha20Poly1305RFC :: new ( & secret, & [ 0 ; 12 ] , & [ ] ) ;
219
+ let decrypted_len = r. total_bytes ( ) - 16 ;
220
+ let s = FixedLengthReader :: new ( & mut r, decrypted_len) ;
221
+ let mut chacha_stream = ChaChaPolyReader { chacha : & mut chacha, read : s } ;
222
+ let readable: T = Readable :: read ( & mut chacha_stream) ?;
223
+
224
+ let mut tag = [ 0 as u8 ; 16 ] ;
225
+ r. read_exact ( & mut tag) ?;
226
+ if !chacha. finish_and_check_tag ( & tag) {
227
+ return Err ( DecodeError :: InvalidValue )
228
+ }
229
+
230
+ Ok ( Self { readable } )
231
+ }
232
+ }
233
+
167
234
#[ cfg( fuzzing) ]
168
235
mod fuzzy_chachapoly {
169
236
#[ derive( Clone , Copy ) ]
@@ -219,7 +286,113 @@ mod fuzzy_chachapoly {
219
286
self . finished = true ;
220
287
true
221
288
}
289
+
290
+ pub fn decrypt_in_place ( & mut self , _input : & mut [ u8 ] , tag : Option < & [ u8 ] > ) -> bool {
291
+ assert ! ( self . finished == false ) ;
292
+ if let Some ( tag) = tag {
293
+ if tag[ ..] != self . tag [ ..] { return false ; }
294
+ }
295
+ self . finished = true ;
296
+ true
297
+ }
298
+
299
+ pub fn finish_and_check_tag ( & mut self , tag : & [ u8 ] ) -> bool {
300
+ if tag[ ..] != self . tag [ ..] { return false ; }
301
+ self . finished = true ;
302
+ true
303
+ }
222
304
}
223
305
}
224
306
#[ cfg( fuzzing) ]
225
307
pub use self :: fuzzy_chachapoly:: ChaCha20Poly1305RFC ;
308
+
309
+ #[ cfg( test) ]
310
+ mod tests {
311
+ use ln:: msgs:: DecodeError ;
312
+ use super :: { ChaChaPolyReadAdapter , ChaChaPolyWriteAdapter } ;
313
+ use util:: ser:: { self , FixedLengthReader , LengthReadableArgs , Writeable } ;
314
+
315
+ // Used for for testing various lengths of serialization.
316
+ #[ derive( Debug , PartialEq ) ]
317
+ struct TestWriteable {
318
+ field1 : Vec < u8 > ,
319
+ field2 : Vec < u8 > ,
320
+ field3 : Vec < u8 > ,
321
+ }
322
+ impl_writeable_tlv_based ! ( TestWriteable , {
323
+ ( 1 , field1, vec_type) ,
324
+ ( 2 , field2, vec_type) ,
325
+ ( 3 , field3, vec_type) ,
326
+ } ) ;
327
+
328
+ #[ test]
329
+ fn test_chacha_stream_adapters ( ) {
330
+ // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
331
+ // encrypted object.
332
+ macro_rules! check_object_read_write {
333
+ ( $obj: expr) => {
334
+ // First, serialize the object, encrypted with ChaCha20Poly1305.
335
+ let rho = [ 42 ; 32 ] ;
336
+ let writeable_len = $obj. serialized_length( ) as u64 + 16 ;
337
+ let write_adapter = ChaChaPolyWriteAdapter :: new( rho, & $obj) ;
338
+ let encrypted_writeable_bytes = write_adapter. encode( ) ;
339
+ let encrypted_writeable = & encrypted_writeable_bytes[ ..] ;
340
+
341
+ // Now deserialize the object back and make sure it matches the original.
342
+ let mut rd = FixedLengthReader :: new( encrypted_writeable, writeable_len) ;
343
+ let read_adapter = <ChaChaPolyReadAdapter <TestWriteable >>:: read( & mut rd, rho) . unwrap( ) ;
344
+ assert_eq!( $obj, read_adapter. readable) ;
345
+ } ;
346
+ }
347
+
348
+ // Try a big object that will require multiple write buffers.
349
+ let big_writeable = TestWriteable {
350
+ field1 : vec ! [ 43 ] ,
351
+ field2 : vec ! [ 44 ; 4192 ] ,
352
+ field3 : vec ! [ 45 ; 4192 + 1 ] ,
353
+ } ;
354
+ check_object_read_write ! ( big_writeable) ;
355
+
356
+ // Try a small object that fits into one write buffer.
357
+ let small_writeable = TestWriteable {
358
+ field1 : vec ! [ 43 ] ,
359
+ field2 : vec ! [ 44 ] ,
360
+ field3 : vec ! [ 45 ] ,
361
+ } ;
362
+ check_object_read_write ! ( small_writeable) ;
363
+ }
364
+
365
+ fn do_chacha_stream_adapters_ser_macros ( ) -> Result < ( ) , DecodeError > {
366
+ // Check that ChaChaPolyReadAdapter and ChaChaPolyWriteAdapter correctly encode and decode an
367
+ // encrypted object.
368
+ let writeable = TestWriteable {
369
+ field1 : vec ! [ 43 ] ,
370
+ field2 : vec ! [ 44 ; 4192 ] ,
371
+ field3 : vec ! [ 45 ; 4192 + 1 ] ,
372
+ } ;
373
+
374
+ // First, serialize the object into a TLV stream, encrypted with ChaCha20Poly1305.
375
+ let rho = [ 42 ; 32 ] ;
376
+ let write_adapter = ChaChaPolyWriteAdapter :: new ( rho, & writeable) ;
377
+ let mut writer = ser:: VecWriter ( Vec :: new ( ) ) ;
378
+ encode_tlv_stream ! ( & mut writer, {
379
+ ( 1 , write_adapter, required) ,
380
+ } ) ;
381
+
382
+ // Now deserialize the object back and make sure it matches the original.
383
+ let mut read_adapter: Option < ChaChaPolyReadAdapter < TestWriteable > > = None ;
384
+ decode_tlv_stream ! ( & writer. 0 [ ..] , {
385
+ ( 1 , read_adapter, ( read_arg, rho) ) ,
386
+ } ) ;
387
+ assert_eq ! ( writeable, read_adapter. unwrap( ) . readable) ;
388
+
389
+ Ok ( ( ) )
390
+ }
391
+
392
+ #[ test]
393
+ fn chacha_stream_adapters_ser_macros ( ) {
394
+ // Test that our stream adapters work as expected with the TLV macros.
395
+ // This also serves to test the `LengthReadableArgs` variant of the `decode_tlv` ser macro.
396
+ do_chacha_stream_adapters_ser_macros ( ) . unwrap ( )
397
+ }
398
+ }
0 commit comments