1#![warn(rustdoc::missing_doc_code_examples)]
13#![allow(
14 clippy::missing_errors_doc,
15 clippy::missing_panics_doc,
16 clippy::module_name_repetitions,
17 clippy::must_use_candidate,
18 clippy::cast_possible_truncation,
19 clippy::similar_names,
20 clippy::implicit_hasher,
21 clippy::redundant_else
22)]
23
24use std::collections::HashSet;
25use std::io::{self, Read, Write};
26use std::sync::Once;
27
28use header::DecryptedHeaderPackets;
29use sodiumoxide::crypto::aead::chacha20poly1305_ietf::{self, Key, Nonce};
30
31use crate::error::Crypt4GHError;
32
33pub mod header;
35
36pub mod keys;
38
39pub mod error;
41
42const CHUNK_SIZE: usize = 4096;
43
44pub const SEGMENT_SIZE: usize = 65_536;
46const CIPHER_DIFF: usize = 28;
47const CIPHER_SEGMENT_SIZE: usize = SEGMENT_SIZE + CIPHER_DIFF;
48
49pub struct WriteInfo<'a, W: Write> {
54 offset: usize,
55 limit: Option<usize>,
56 write_buffer: &'a mut W,
57}
58
59impl<'a, W: Write> WriteInfo<'a, W> {
60 pub fn new(offset: usize, limit: Option<usize>, write_buffer: &'a mut W) -> Self {
62 Self {
63 offset,
64 limit,
65 write_buffer,
66 }
67 }
68
69 fn write_all(&mut self, data: &[u8]) -> Result<(), Crypt4GHError> {
70 match &mut self.limit {
71 Some(limit) => {
72 if *limit >= data.len() {
73 self.write_buffer.write_all(data)?;
74 *limit -= data.len();
75 }
76 else {
77 self.write_buffer.write_all(&data[..*limit])?;
78 *limit = 0;
79 }
80 },
81 None => self.write_buffer.write_all(&data[self.offset..])?,
82 }
83 Ok(())
84 }
85}
86
87#[derive(Debug, PartialEq, Eq, Hash, Clone)]
88pub struct Keys {
90 pub method: u8,
93 pub privkey: Vec<u8>,
95 pub recipient_pubkey: Vec<u8>,
97}
98
99pub(crate) static SODIUM_INIT: Once = Once::new();
100
101pub(crate) fn init() {
102 SODIUM_INIT.call_once(|| {
103 sodiumoxide::init().expect("Unable to initialize libsodium");
104 });
105}
106
107pub fn encrypt<R: Read, W: Write>(
113 recipient_keys: &HashSet<Keys>,
114 read_buffer: &mut R,
115 write_buffer: &mut W,
116 range_start: usize,
117 range_span: Option<usize>,
118) -> Result<(), Crypt4GHError> {
119 crate::init();
120 log::debug!("Start: {}, Span: {:?}", range_start, range_span);
121
122 if recipient_keys.is_empty() {
123 return Err(Crypt4GHError::NoRecipients);
124 }
125
126 log::info!("Encrypting the file");
127 log::debug!(" Start Coordinate: {}", range_start);
128
129 if range_start > 0 {
131 log::info!("Forwarding to position: {}", range_start);
132 }
133
134 read_buffer
135 .by_ref()
136 .take(range_start as u64)
137 .read_to_end(&mut Vec::new())
138 .map_err(|e| Crypt4GHError::NotEnoughInput(range_start, e.into()))?;
139
140 log::debug!(" Span: {:?}", range_span);
141
142 log::info!("Creating Crypt4GH header");
143 let mut session_key = [0_u8; 32];
144 sodiumoxide::randombytes::randombytes_into(&mut session_key);
145 let header_bytes = encrypt_header(recipient_keys, &Some(session_key))?;
146
147 log::debug!("header length: {}", header_bytes.len());
148
149 write_buffer.write_all(&header_bytes)?;
150
151 log::info!("Streaming content");
152
153 let mut segment = [0_u8; SEGMENT_SIZE];
154
155 match range_span {
157 None | Some(0) => loop {
158 let segment_len = read_buffer.read(&mut segment)?;
159 if segment_len == 0 {
160 break;
161 }
162 else if segment_len < SEGMENT_SIZE {
163 let (data, _) = segment.split_at(segment_len);
164 let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12))
165 .ok_or(Crypt4GHError::NoRandomNonce)?;
166 let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?;
167 let encrypted_data = encrypt_segment(data, nonce, &key);
168 write_buffer.write_all(&encrypted_data)?;
169 break;
170 }
171 else {
172 let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12))
173 .ok_or(Crypt4GHError::NoRandomNonce)?;
174 let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?;
175 let encrypted_data = encrypt_segment(&segment, nonce, &key);
176 write_buffer.write_all(&encrypted_data)?;
177 }
178 },
179 Some(mut remaining_length) => {
180 while remaining_length > 0 {
181 let segment_len = read_buffer.read(&mut segment)?;
182
183 if segment_len >= remaining_length {
185 let (data, _) = segment.split_at(remaining_length);
186 let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12))
187 .ok_or(Crypt4GHError::NoRandomNonce)?;
188 let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?;
189 let encrypted_data = encrypt_segment(data, nonce, &key);
190 write_buffer.write_all(&encrypted_data)?;
191 break;
192 }
193
194 if segment_len < SEGMENT_SIZE {
196 let (data, _) = segment.split_at(segment_len);
197 let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12))
198 .ok_or(Crypt4GHError::NoRandomNonce)?;
199 let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?;
200 let encrypted_data = encrypt_segment(data, nonce, &key);
201 write_buffer.write_all(&encrypted_data)?;
202 break;
203 }
204
205 let nonce = Nonce::from_slice(&sodiumoxide::randombytes::randombytes(12))
206 .ok_or(Crypt4GHError::NoRandomNonce)?;
207 let key = Key::from_slice(&session_key).ok_or(Crypt4GHError::NoKey)?;
208 let encrypted_data = encrypt_segment(&segment, nonce, &key);
209 write_buffer.write_all(&encrypted_data)?;
210
211 remaining_length -= segment_len;
212 }
213 },
214 }
215
216 log::info!("Encryption Successful");
217 Ok(())
218}
219
220pub fn encrypt_header(
224 recipient_keys: &HashSet<Keys>,
225 session_key: &Option<[u8; 32]>,
226) -> Result<Vec<u8>, Crypt4GHError> {
227 let encryption_method = 0;
228 let session_key_or_new = session_key.unwrap_or_else(|| {
229 crate::init();
230 let mut session_key = [0_u8; 32];
231 sodiumoxide::randombytes::randombytes_into(&mut session_key);
232 session_key
233 });
234 let header_content = header::make_packet_data_enc(encryption_method, &session_key_or_new);
235 let header_packets = header::encrypt(&header_content, recipient_keys)?;
236 let header_bytes = header::serialize(header_packets);
237 Ok(header_bytes)
238}
239
240pub fn encrypt_segment(data: &[u8], nonce: Nonce, key: &Key) -> Vec<u8> {
244 vec![nonce.0.to_vec(), chacha20poly1305_ietf::seal(data, None, &nonce, key)].concat()
245}
246
247pub fn decrypt<R: Read, W: Write>(
255 keys: &[Keys],
256 read_buffer: &mut R,
257 write_buffer: &mut W,
258 range_start: usize,
259 range_span: Option<usize>,
260 sender_pubkey: &Option<Vec<u8>>,
261) -> Result<(), Crypt4GHError> {
262 range_span.map_or_else(
263 || {
264 log::info!("Decrypting file | Range: [{}, EOF)", range_start);
265 },
266 |span| {
267 log::info!("Decrypting file | Range: [{}, {})", range_start, range_start + span + 1);
268 },
269 );
270
271 let mut temp_buf = [0_u8; 16]; read_buffer
274 .read_exact(&mut temp_buf)
275 .map_err(|e| Crypt4GHError::ReadHeaderError(e.into()))?;
276 let header_info: header::HeaderInfo = header::deconstruct_header_info(&temp_buf)?;
277
278 let encrypted_packets = (0..header_info.packets_count)
280 .map(|_| {
281 let mut length_buffer = [0_u8; 4];
283 read_buffer
284 .read_exact(&mut length_buffer)
285 .map_err(|e| Crypt4GHError::ReadHeaderPacketLengthError(e.into()))?;
286 let length = bincode::deserialize::<u32>(&length_buffer)
287 .map_err(|e| Crypt4GHError::ParseHeaderPacketLengthError(e))?;
288 let length = length - 4;
289
290 let mut encrypted_data = vec![0_u8; length as usize];
292 read_buffer
293 .read_exact(&mut encrypted_data)
294 .map_err(|e| Crypt4GHError::ReadHeaderPacketDataError(e.into()))?;
295 Ok(encrypted_data)
296 })
297 .collect::<Result<Vec<Vec<u8>>, Crypt4GHError>>()?;
298
299 let DecryptedHeaderPackets {
300 data_enc_packets: session_keys,
301 edit_list_packet: edit_list,
302 } = header::deconstruct_header_body(encrypted_packets, keys, sender_pubkey)?;
303
304 range_span.map_or_else(
305 || {
306 log::info!("Slicing from {} | Keeping all bytes", range_start);
307 },
308 |span| {
309 log::info!("Slicing from {} | Keeping {} bytes", range_start, span);
310 },
311 );
312
313 if range_span.is_some() && range_span.unwrap() == 0 {
314 return Err(Crypt4GHError::InvalidRangeSpan(range_span));
315 }
316
317 let mut write_info = WriteInfo::new(range_start, range_span, write_buffer);
318
319 match edit_list {
320 None => body_decrypt(read_buffer, &session_keys, &mut write_info, range_start)?,
321 Some(edit_list_content) => body_decrypt_parts(read_buffer, session_keys, write_info, edit_list_content)?,
322 }
323
324 log::info!("Decryption Over");
325 Ok(())
326}
327
328struct DecryptedBuffer<'a, W: Write> {
329 read_buffer: &'a mut dyn Read,
330 session_keys: Vec<Vec<u8>>,
331 buf: Vec<u8>,
332 is_decrypted: bool,
333 block: u64,
334 output: WriteInfo<'a, W>,
335 index: usize,
336}
337
338impl<'a, W: Write> DecryptedBuffer<'a, W> {
339 fn new(read_buffer: &'a mut impl Read, session_keys: Vec<Vec<u8>>, output: WriteInfo<'a, W>) -> Self {
340 let mut decryptor = Self {
341 read_buffer,
342 session_keys,
343 buf: Vec::with_capacity(CIPHER_SEGMENT_SIZE),
344 is_decrypted: false,
345 block: 0,
346 output,
347 index: 0,
348 };
349
350 decryptor.fetch();
351 decryptor.decrypt();
352 log::debug!("Index = {}", decryptor.index);
353 log::debug!("");
354 decryptor
355 }
356
357 fn fetch(&mut self) {
358 log::debug!("Fetching block {}", self.block);
359 self.block += 1;
360
361 self.buf.clear();
363 self.read_buffer
364 .take(CIPHER_SEGMENT_SIZE as u64)
365 .read_to_end(&mut self.buf)
366 .unwrap();
367
368 self.is_decrypted = false;
369 log::debug!("");
370 }
371
372 fn decrypt(&mut self) {
373 if !self.is_decrypted {
375 log::debug!("Decrypting block");
376 self.buf = decrypt_block(&self.buf, &self.session_keys).unwrap();
377 self.is_decrypted = true;
378 }
379 log::debug!("");
380 }
381
382 fn skip(&mut self, size: usize) {
383 assert!(size > 0, "You shouldn't skip 0 bytes");
384 log::debug!("Skipping {} bytes | Buffer size: {}", size, self.buf.len());
385
386 let mut remaining_size = size;
387
388 while remaining_size > 0 {
390 log::debug!("Left to skip: {} | Buffer size: {}", remaining_size, self.buf.len());
391
392 if remaining_size >= SEGMENT_SIZE {
393 self.fetch();
394 remaining_size -= SEGMENT_SIZE;
395 }
396 else {
397 if (self.index + remaining_size) > SEGMENT_SIZE {
398 self.fetch();
399 }
400 self.index = (self.index + remaining_size) % SEGMENT_SIZE;
401 log::debug!("Index = {}", self.index);
402 remaining_size -= remaining_size;
403 }
404 }
405
406 log::debug!("Finished skipping");
407 log::debug!("");
408
409 self.decrypt();
411 }
412
413 fn read(&mut self, size: usize) -> usize {
414 assert!(size > 0, "You shouldn't read 0 bytes");
415 log::debug!("Reading {} bytes | Buffer size: {}", size, self.buf.len());
416
417 let mut remaining_size = size;
418
419 while remaining_size > 0 {
420 log::debug!("Left to read: {} | Buffer size: {}", remaining_size, self.buf.len());
422 let n_bytes = usize::min(SEGMENT_SIZE - self.index, remaining_size);
423
424 self.decrypt();
426 self.output
427 .write_all(&self.buf[self.index..self.index + n_bytes])
428 .unwrap();
429
430 self.index = (self.index + n_bytes) % self.buf.len();
432 log::debug!("Index = {}", self.index);
433 if self.index == 0 {
434 self.fetch();
435 }
436
437 remaining_size -= n_bytes;
439 }
440
441 log::debug!("Finished reading");
442 log::debug!("");
443
444 size
445 }
446}
447
448pub fn body_decrypt_parts<W: Write>(
454 mut read_buffer: impl Read,
455 session_keys: Vec<Vec<u8>>,
456 output: WriteInfo<W>,
457 edit_list: Vec<u64>,
458) -> Result<(), Crypt4GHError> {
459 log::debug!("Edit List: {:?}", edit_list);
460
461 if edit_list.is_empty() {
462 return Err(Crypt4GHError::EmptyEditList);
463 }
464
465 let mut decrypted = DecryptedBuffer::new(&mut read_buffer, session_keys, output);
466
467 let mut skip = true;
468
469 for edit_length in edit_list {
470 if skip {
471 if edit_length != 0 {
472 decrypted.skip(edit_length as usize);
473 }
474 }
475 else {
476 decrypted.read(edit_length as usize);
477 }
478 skip = !skip;
479 }
480
481 if !skip {
482 loop {
484 let n = decrypted.read(SEGMENT_SIZE);
485 if n == 0 {
486 break;
487 }
488 }
489 }
490
491 Ok(())
492}
493
494pub fn body_decrypt<W: Write>(
500 mut read_buffer: impl Read,
501 session_keys: &[Vec<u8>],
502 output: &mut WriteInfo<W>,
503 range_start: usize,
504) -> Result<(), Crypt4GHError> {
505 if range_start >= SEGMENT_SIZE {
506 let start_segment = range_start / SEGMENT_SIZE;
507 log::info!("Fast-forwarding {} segments", start_segment);
508 let start_ciphersegment = start_segment * CIPHER_SEGMENT_SIZE;
509 read_buffer
510 .read_exact(&mut vec![0_u8; start_ciphersegment])
511 .map_err(|e| Crypt4GHError::BadStartRange(e.into()))?;
512 }
513
514 loop {
515 let mut chunk = Vec::with_capacity(CIPHER_SEGMENT_SIZE);
516 let n = read_buffer
517 .by_ref()
518 .take(CIPHER_SEGMENT_SIZE as u64)
519 .read_to_end(&mut chunk)
520 .map_err(|e| Crypt4GHError::ReadBlockError(e.into()))?;
521
522 if n == 0 {
523 break;
524 }
525
526 let segment = decrypt_block(&chunk, session_keys)?;
527 output
528 .write_all(&segment)
529 .map_err(|e| Crypt4GHError::UnableToWrite(e.into()))?;
530
531 if n < CIPHER_SEGMENT_SIZE {
532 break;
533 }
534 }
535
536 Ok(())
537}
538
539fn decrypt_block(ciphersegment: &[u8], session_keys: &[Vec<u8>]) -> Result<Vec<u8>, Crypt4GHError> {
540 let (nonce_slice, data) = ciphersegment.split_at(12);
541 let nonce = Nonce::from_slice(nonce_slice).ok_or(Crypt4GHError::UnableToWrapNonce)?;
542
543 session_keys
544 .iter()
545 .find_map(|key| Key::from_slice(key).and_then(|key| chacha20poly1305_ietf::open(data, None, &nonce, &key).ok()))
546 .ok_or(Crypt4GHError::UnableToDecryptBlock)
547}
548
549pub fn reencrypt<R: Read, W: Write>(
556 keys: &[Keys],
557 recipient_keys: &HashSet<Keys>,
558 read_buffer: &mut R,
559 write_buffer: &mut W,
560 trim: bool,
561) -> Result<(), Crypt4GHError> {
562 let mut temp_buf = [0_u8; 16]; read_buffer
565 .read_exact(&mut temp_buf)
566 .map_err(|e| Crypt4GHError::ReadHeaderError(e.into()))?;
567 let header_info: header::HeaderInfo = header::deconstruct_header_info(&temp_buf)?;
568
569 let header_packets = (0..header_info.packets_count)
571 .map(|_| {
572 let mut length_buffer = [0_u8; 4];
574 read_buffer
575 .read_exact(&mut length_buffer)
576 .map_err(|e| Crypt4GHError::ReadHeaderPacketLengthError(e.into()))?;
577 let length = bincode::deserialize::<u32>(&length_buffer)
578 .map_err(|e| Crypt4GHError::ParseHeaderPacketLengthError(e))?;
579 let length = length - 4;
580
581 let mut encrypted_data = vec![0_u8; length as usize];
583 read_buffer
584 .read_exact(&mut encrypted_data)
585 .map_err(|e| Crypt4GHError::ReadHeaderPacketDataError(e.into()))?;
586 Ok(encrypted_data)
587 })
588 .collect::<Result<Vec<Vec<u8>>, Crypt4GHError>>()?;
589
590 let packets = header::reencrypt(header_packets, keys, recipient_keys, trim)?;
591 write_buffer.write_all(&header::serialize(packets))?;
592
593 log::info!("Streaming the remainder of the file");
594
595 loop {
596 let mut buf = Vec::with_capacity(CHUNK_SIZE);
597 let data = read_buffer.by_ref().take(CHUNK_SIZE as u64).read_to_end(&mut buf);
598
599 match data {
600 Ok(0) => break,
601 Ok(n) => write_buffer.write_all(&buf[0..n])?,
602 Err(e) if e.kind() == io::ErrorKind::Interrupted => (),
603 Err(e) => return Err(Crypt4GHError::ReadRemainderError(e.into())),
604 }
605 }
606
607 log::info!("Reencryption successful");
608
609 Ok(())
610}
611
612pub fn rearrange<R: Read, W: Write>(
618 keys: Vec<Keys>,
619 read_buffer: &mut R,
620 write_buffer: &mut W,
621 range_start: usize,
622 range_span: Option<usize>,
623) -> Result<(), Crypt4GHError> {
624 let mut temp_buf = [0_u8; 16]; read_buffer
627 .read_exact(&mut temp_buf)
628 .map_err(|e| Crypt4GHError::ReadHeaderError(e.into()))?;
629 let header_info: header::HeaderInfo = header::deconstruct_header_info(&temp_buf)?;
630
631 let header_packets = (0..header_info.packets_count)
633 .map(|_| {
634 let mut length_buffer = [0_u8; 4];
636 read_buffer
637 .read_exact(&mut length_buffer)
638 .map_err(|e| Crypt4GHError::ReadHeaderPacketLengthError(e.into()))?;
639 let length = bincode::deserialize::<u32>(&length_buffer)
640 .map_err(|e| Crypt4GHError::ParseHeaderPacketLengthError(e))?;
641 let length = length - 4;
642
643 let mut encrypted_data = vec![0_u8; length as usize];
645 read_buffer
646 .read_exact(&mut encrypted_data)
647 .map_err(|e| Crypt4GHError::ReadHeaderPacketDataError(e.into()))?;
648 Ok(encrypted_data)
649 })
650 .collect::<Result<Vec<Vec<u8>>, Crypt4GHError>>()?;
651
652 let (packets, mut segment_oracle) = header::rearrange(header_packets, keys, range_start, range_span, &None)?;
653 write_buffer.write_all(&header::serialize(packets))?;
654
655 log::info!("Streaming the remainder of the file");
656
657 loop {
658 let mut buf = Vec::with_capacity(SEGMENT_SIZE + CIPHER_DIFF);
659 let data = read_buffer
660 .by_ref()
661 .take((SEGMENT_SIZE + CIPHER_DIFF) as u64)
662 .read_to_end(&mut buf);
663
664 let keep_segment = segment_oracle.next().unwrap();
665
666 log::debug!("Keep segment: {:?}", keep_segment);
667
668 match data {
669 Ok(0) => break,
670 Ok(n) => {
671 if keep_segment {
672 write_buffer.write_all(&buf[0..n])?;
673 }
674 },
675 Err(e) if e.kind() == io::ErrorKind::Interrupted => (),
676 Err(e) => return Err(Crypt4GHError::ReadRemainderError(e.into())),
677 }
678 }
679
680 log::info!("Rearrangement successful");
681
682 Ok(())
683}