1 //! ECE AES-GCM 128 encrypter/decrypter pipe implementation for Firefox Send v3.
2
3 use std::cmp::min;
4 use std::io::{self, Read, Write};
5
6 use byteorder::{BigEndian, ByteOrder};
7 use bytes::BytesMut;
8 #[cfg(feature = "crypto-openssl")]
9 use openssl::symm;
10 #[cfg(feature = "crypto-ring")]
11 use ring::aead;
12
13 use super::{Crypt, CryptMode};
14 use crate::config::{self, TAG_LEN};
15 use crate::crypto::{hkdf::hkdf, rand_bytes};
16 use crate::pipe::{prelude::*, DEFAULT_BUF_SIZE};
17
18 /// The default record size in bytes to use for encryption.
19 ///
20 /// This value matches the default configured in the Firefox Send v3 source code.
21 pub const RS: u32 = config::ECE_RECORD_SIZE;
22
23 /// The crypto key length.
24 const KEY_LEN: usize = 16;
25
26 /// The crypto nonce length.
27 const NONCE_LEN: usize = 12;
28
29 /// The length in bytes of the header.
30 pub const HEADER_LEN: u32 = 21;
31
32 /// The length in bytes of the crypto salt.
33 const SALT_LEN: usize = 16;
34
35 /// The length in bytes of the record size, as encoded in the ECE header.
36 const RS_LEN: usize = 4;
37
38 /// The key info text.
39 const KEY_INFO: &str = "Content-Encoding: aes128gcm\0";
40
41 /// The nonce info text.
42 const NONCE_INFO: &str = "Content-Encoding: nonce\0";
43
44 /// Something that can encrypt or decrypt given data using ECE.
45 pub struct EceCrypt {
46 /// The crypto mode, make this encrypt or decrypt data.
47 mode: CryptMode,
48
49 /// The crypto input key material.
50 ikm: Vec<u8>,
51
52 /// The crypto key if known.
53 key: Option<Vec<u8>>,
54
55 /// The crypto base nonce if known, chunk nonces are derived from.
56 nonce: Option<Vec<u8>>,
57
58 /// The crypto salt if known.
59 salt: Option<Vec<u8>>,
60
61 /// Sequence number of the current chunk.
62 ///
63 /// This number increases when transforming chunks.
64 /// The ciphertext header is excluded from this sequence.
65 seq: u32,
66
67 /// The number of bytes fed into this crypter.
68 ///
69 /// When encrypting, this corresponds to the number of plaintext bytes (matches `cur`).
70 /// When decrypting, this corresponds to the number of ciphertext bytes including the header.
71 ///
72 /// Used to determine when the last chunk is reached.
73 cur_in: usize,
74
75 /// The number of encrypted/decrypted plaintext bytes.
76 cur: usize,
77
78 /// The total size in bytes of the plaintext.
79 len: usize,
80
81 /// The record size used for crypto chunks.
82 ///
83 /// This value is dynamic and changes depending on the crypto mode and current progress.
84 rs: u32,
85 }
86
87 impl EceCrypt {
88 /// Construct a new ECE crypter pipe.
89 ///
90 /// It is highly recommended to use the [`encrypt()`](Self::encrypt) and
91 /// [`decrypt()`](Self::decrypt) methods instead for constructing a new crypter.
92 ///
93 /// The size in bytes of the plaintext data must be given as `len`.
94 /// The input key material must be given as `ikm`.
95 /// When encrypting, a `salt` must be specified.
new(mode: CryptMode, len: usize, ikm: Vec<u8>, salt: Option<Vec<u8>>) -> Self96 pub fn new(mode: CryptMode, len: usize, ikm: Vec<u8>, salt: Option<Vec<u8>>) -> Self {
97 Self {
98 mode,
99 ikm,
100 key: None,
101 nonce: None,
102 salt,
103 seq: 0,
104 cur_in: 0,
105 cur: 0,
106 len,
107 rs: RS,
108 }
109 }
110
111 /// Create an ECE encryptor.
112 ///
113 /// The size in bytes of the plaintext data that is encrypted decrypt must be given as `len`.
114 /// The input key material must be given as `ikm`.
115 /// The `salt` is optional and will be randomly generated if `None`.
encrypt(len: usize, ikm: Vec<u8>, salt: Option<Vec<u8>>) -> Self116 pub fn encrypt(len: usize, ikm: Vec<u8>, salt: Option<Vec<u8>>) -> Self {
117 // Construct the encrypter, generate random salt if not set
118 let mut crypt = Self::new(
119 CryptMode::Encrypt,
120 len,
121 ikm,
122 salt.or_else(|| Some(generate_salt())),
123 );
124
125 // Derive the key and nonce
126 crypt.derive_key_and_nonce();
127
128 crypt
129 }
130
131 /// Create an ECE decryptor.
132 ///
133 /// The size in bytes of the plaintext data that is decrypted decrypt must be given as `len`.
134 /// The input key material must be given as `ikm`.
decrypt(len: usize, ikm: Vec<u8>) -> Self135 pub fn decrypt(len: usize, ikm: Vec<u8>) -> Self {
136 Self::new(CryptMode::Decrypt, len, ikm, None)
137 }
138
139 /// Get the current desired size of a payload chunk.
140 ///
141 /// This value is dynamic and changes depending on the crypto mode, and the current stage.
142 /// Data passed to the crypter must match the chunk size.
143 #[inline(always)]
chunk_size(&self) -> u32144 fn chunk_size(&self) -> u32 {
145 match self.mode {
146 // Record size with tag length and delimiter
147 CryptMode::Encrypt => self.rs - TAG_LEN as u32 - 1,
148
149 // Record size, header length for initial header chunk
150 CryptMode::Decrypt => {
151 if self.has_header() {
152 self.rs
153 } else {
154 HEADER_LEN
155 }
156 }
157 }
158 }
159
160 /// Encrypt the given `plaintext` data using this configured crypter.
161 ///
162 /// If a header hasn't been created yet, it is included in the output as well.
163 ///
164 /// This function returns `(read, out)` where `read` represents the number of read bytes from
165 /// `plaintext`, and `out` is a vector of now encrypted bytes.
166 ///
167 /// # Panics
168 ///
169 /// Panics if attempted to write more bytes than the length specified while configuring the
170 /// crypter.
pipe_encrypt(&mut self, input: Vec<u8>) -> (usize, Option<Vec<u8>>)171 fn pipe_encrypt(&mut self, input: Vec<u8>) -> (usize, Option<Vec<u8>>) {
172 // Encrypt the chunk, if the first chunk, the header must be created and prefixed
173 if !self.has_header() {
174 // Create header
175 let mut ciphertext = self.create_header();
176
177 // Encrypt chunk and append to header base ciphertext
178 let (read, chunk) = self.encrypt_chunk(input);
179 if let Some(chunk) = chunk {
180 ciphertext.extend_from_slice(&chunk)
181 }
182
183 // Increase chunk sequence number
184 self.increase_seq();
185
186 (read, Some(ciphertext))
187 } else {
188 // Encrypt chunk, increase chunk sequence number
189 let result = self.encrypt_chunk(input);
190 self.increase_seq();
191 result
192 }
193 }
194
195 /// Decrypt the given `ciphertext` using ECE crypto.
196 ///
197 /// If the header has not been read yet, it is parsed first to initialize the proper salt and
198 /// record size.
199 ///
200 /// This function returns `(read, plaintext)` where `read` represents the number of read bytes from
201 /// `ciphertext`, and `out` is a vector of the producted plaintext.
202 ///
203 /// # Panics
204 ///
205 /// Panics if attempted to write more bytes than the length specified while configuring the
206 /// crypter, or if decryption of a chunk failed.
207 /// size.
pipe_decrypt(&mut self, input: &[u8]) -> (usize, Option<Vec<u8>>)208 fn pipe_decrypt(&mut self, input: &[u8]) -> (usize, Option<Vec<u8>>) {
209 // Parse the header before decrypting anything
210 if !self.has_header() {
211 self.parse_header(input);
212 return (input.len(), None);
213 }
214
215 // Decrypt the chunk, increase chunk sequence number
216 let result = self.decrypt_chunk(input);
217 self.increase_seq();
218 result
219 }
220
221 /// Encrypt the given `plaintext` chunk data using this configured crypter.
222 ///
223 /// This function returns `(read, out)` where `read` represents the number of read bytes from
224 /// `plaintext`, and `out` is a vector of now encrypted bytes.
225 ///
226 /// # Panics
227 ///
228 /// Panics if attempted to write more bytes than the length specified while configuring the
229 /// crypter.
230 #[inline(always)]
encrypt_chunk(&mut self, mut plaintext: Vec<u8>) -> (usize, Option<Vec<u8>>)231 fn encrypt_chunk(&mut self, mut plaintext: Vec<u8>) -> (usize, Option<Vec<u8>>) {
232 // // Don't allow encrypting more than specified, when tag is obtained
233 // if self.has_tag() && !plaintext.is_empty() {
234 // panic!("could not write to AES-GCM encrypter, exceeding specified length");
235 // }
236
237 // Update transformed length
238 let read = plaintext.len();
239 self.cur += read;
240
241 // Generate the encryption nonce
242 let nonce = self.generate_nonce(self.seq);
243
244 // Pad the plaintext, encrypt the chunk, append tag
245 pad(&mut plaintext, self.rs as usize, self.is_last());
246
247 #[cfg(feature = "crypto-openssl")]
248 {
249 // Encrypt the chunk, append tag
250 let mut tag = vec![0u8; TAG_LEN];
251 let mut ciphertext = symm::encrypt_aead(
252 symm::Cipher::aes_128_gcm(),
253 self.key
254 .as_ref()
255 .expect("failed to encrypt ECE chunk, missing crypto key"),
256 Some(&nonce),
257 &[],
258 &plaintext,
259 &mut tag,
260 )
261 .expect("failed to encrypt ECE chunk");
262 ciphertext.extend_from_slice(&tag);
263
264 (read, Some(ciphertext))
265 }
266
267 #[cfg(feature = "crypto-ring")]
268 {
269 // Prepare sealing key
270 let nonce = aead::Nonce::try_assume_unique_for_key(&nonce)
271 .expect("failed to encrypt ECE chunk, invalid nonce");
272 let aad = aead::Aad::empty();
273 let key = self
274 .key
275 .as_ref()
276 .expect("failed to encrypt ECE chunk, missing crypto key");
277 let unbound_key = aead::UnboundKey::new(&aead::AES_128_GCM, key).unwrap();
278 let key = aead::LessSafeKey::new(unbound_key);
279
280 // Seal in place, return sealed
281 key.seal_in_place_append_tag(nonce, aad, &mut plaintext)
282 .expect("failed to encrypt ECE chunk");
283
284 (read, Some(plaintext.to_vec()))
285 }
286 }
287
288 /// Decrypt the given `ciphertext` chunk using ECE crypto.
289 ///
290 /// This function returns `(read, plaintext)` where `read` represents the number of read bytes
291 /// from `ciphertext`, and `out` is a vector of the producted plaintext.
292 ///
293 /// # Panics
294 ///
295 /// Panics if attempted to write more bytes than the length specified while configuring the
296 /// crypter, or if decryption of a chunk failed.
297 #[inline(always)]
decrypt_chunk(&mut self, ciphertext: &[u8]) -> (usize, Option<Vec<u8>>)298 fn decrypt_chunk(&mut self, ciphertext: &[u8]) -> (usize, Option<Vec<u8>>) {
299 // // Don't allow decrypting more than specified, when tag is obtained
300 // if self.has_tag() && !ciphertext.is_empty() {
301 // panic!("could not write to AES-GCM decrypter, exceeding specified lenght");
302 // }
303
304 // Generate encryption nonce
305 let nonce = self.generate_nonce(self.seq);
306
307 #[cfg(feature = "crypto-openssl")]
308 {
309 // Split payload and tag
310 let (payload, tag) = ciphertext.split_at(ciphertext.len() - TAG_LEN);
311
312 // Decrypt the chunk, and unpad decrypted payload
313 let mut plaintext = symm::decrypt_aead(
314 symm::Cipher::aes_128_gcm(),
315 self.key
316 .as_ref()
317 .expect("failed to decrypt ECE chunk, missing crypto key"),
318 Some(&nonce),
319 &[],
320 payload,
321 tag,
322 )
323 .expect("failed to decrypt ECE chunk");
324 unpad(&mut plaintext, self.is_last());
325
326 // Update transformed length
327 self.cur += plaintext.len();
328
329 (ciphertext.len(), Some(plaintext))
330 }
331
332 #[cfg(feature = "crypto-ring")]
333 {
334 // Clone ciphertext so we can modify in-place
335 let mut ciphertext = ciphertext.to_vec();
336
337 // Prepare opening key
338 let nonce = aead::Nonce::try_assume_unique_for_key(&nonce)
339 .expect("failed to decrypt ECE chunk, invalid nonce");
340 let aad = aead::Aad::empty();
341 let key = self
342 .key
343 .as_ref()
344 .expect("failed to decrypt ECE chunk, missing crypto key");
345 let unbound_key = aead::UnboundKey::new(&aead::AES_128_GCM, key).unwrap();
346 let key = aead::LessSafeKey::new(unbound_key);
347
348 // Decrypt the chunk, and unpad decrypted payload
349 let mut plaintext = key
350 .open_in_place(nonce, aad, &mut ciphertext)
351 .expect("failed to decrypt ECE chunk")
352 .to_vec();
353 unpad(&mut plaintext, self.is_last());
354
355 // Update transformed length
356 self.cur += plaintext.len();
357
358 (ciphertext.len(), Some(plaintext))
359 }
360 }
361
362 /// Create the ECE crypto header.
363 ///
364 /// This header includes the salt and record size as configured in this crypter instance.
365 /// The header bytes are returned.
366 ///
367 /// # Panics
368 ///
369 /// Panics if the salt is not set.
370 #[inline(always)]
create_header(&self) -> Vec<u8>371 fn create_header(&self) -> Vec<u8> {
372 // Allocate the header
373 let mut header = Vec::with_capacity(HEADER_LEN as usize);
374
375 // Add the salt
376 let salt = self
377 .salt
378 .as_ref()
379 .expect("failed to create ECE header, no crypto salt specified");
380 assert_eq!(salt.len(), SALT_LEN);
381 header.extend_from_slice(salt);
382
383 // Add the record size
384 let mut rs = [0u8; 4];
385 BigEndian::write_u32(&mut rs, self.rs);
386 header.extend_from_slice(&rs);
387
388 // Add length of unused key ID length
389 header.push(0);
390
391 header
392 }
393
394 /// Parse the given header bytes as ECE crypto header.
395 ///
396 /// This function attemts to parse the given header bytes.
397 /// A salt and record size is parsed, a key and nonce are derived from them.
398 /// The values are set in the inner `EceCrypt` instance and are automatically used for further
399 /// decryption.
400 ///
401 /// # Panics
402 ///
403 /// Panics if the given header bytes have an invalid size, or if the given header is not fully
404 /// parsed.
405 #[inline(always)]
parse_header(&mut self, header: &[u8])406 fn parse_header(&mut self, header: &[u8]) {
407 // Assert the header size
408 assert_eq!(
409 header.len() as u32,
410 HEADER_LEN,
411 "failed to decrypt, ECE header is not 21 bytes long",
412 );
413
414 // Parse the salt, record size and length
415 let (salt, header) = header.split_at(SALT_LEN);
416 let (rs, header) = header.split_at(RS_LEN);
417 self.salt = Some(salt.to_vec());
418 self.rs = BigEndian::read_u32(rs);
419
420 // Extracted in Send v3 code, but doesn't seem to be used
421 let (key_id_data, header) = header.split_at(1);
422 let key_id_len = key_id_data[0] as usize;
423 let _length = key_id_len + KEY_LEN + 5;
424
425 // Derive the key and nonce based on extracted salt
426 self.derive_key_and_nonce();
427
428 // Assert all header bytes have been consumed
429 // If this fails, update `len_encrypted` as well
430 assert!(
431 header.is_empty(),
432 "failed to decrypt, not all ECE header bytes are used",
433 );
434 }
435
436 /// Derive the crypto key and base nonce.
437 ///
438 /// These are derived based on `self.salt` and `self.ikm`, and must be configured.
439 ///
440 /// # Panics
441 ///
442 /// panics if either `self.salt` or `self.ikm` is not configured.
443 #[inline(always)]
derive_key_and_nonce(&mut self)444 fn derive_key_and_nonce(&mut self) {
445 self.key = Some(hkdf(
446 self.salt.as_ref().map(|s| s.as_slice()),
447 KEY_LEN,
448 &self.ikm,
449 Some(KEY_INFO.as_bytes()),
450 ));
451 self.nonce = Some(hkdf(
452 self.salt.as_ref().map(|s| s.as_slice()),
453 NONCE_LEN,
454 &self.ikm,
455 Some(NONCE_INFO.as_bytes()),
456 ));
457 }
458
459 /// Generate crypto nonce for sequence with index `seq`.
460 ///
461 /// Each payload chunk uses a different nonce.
462 /// This method generates the nonce to use.
463 #[inline(always)]
generate_nonce(&self, seq: u32) -> Vec<u8>464 fn generate_nonce(&self, seq: u32) -> Vec<u8> {
465 // Get the base nonce which we need to modify
466 let mut nonce = self
467 .nonce
468 .clone()
469 .expect("failed to generate nonce, no base nonce available");
470
471 // TODO: slice `nonce` only once, use that for mutating
472
473 let nonce_len = nonce.len();
474 let m = BigEndian::read_u32(&nonce[nonce_len - 4..nonce_len]);
475 let xor = m ^ seq;
476
477 BigEndian::write_u32(&mut nonce[nonce_len - 4..nonce_len], xor);
478
479 nonce
480 }
481
482 /// Check whehter the header is read.
483 ///
484 /// This checks whether the header has been read from the input while decrypting.
485 /// The header contains important information for the rest of the decryption process and must
486 /// be obtained and parsed first.
487 ///
488 /// TODO: better docs
489 #[inline(always)]
has_header(&self) -> bool490 fn has_header(&self) -> bool {
491 match self.mode {
492 CryptMode::Encrypt => self.cur > 0,
493 CryptMode::Decrypt => self.salt.is_some(),
494 }
495 }
496
497 /// Check if working with the last crypto chunk.
498 ///
499 /// This checks whether all data for the last chunk, determined by the plaintext length in
500 /// bytes, has entered this crypto pipe.
501 #[inline(always)]
is_last(&self) -> bool502 fn is_last(&self) -> bool {
503 self.is_last_with(0)
504 }
505
506 /// Check if working with the last crypto chunk including given `extra` bytes.
507 ///
508 /// This checks whether all data for the last chunk including `extra`, determined by the
509 /// plaintext length in bytes, has entered this crypto pipe.
510 #[inline(always)]
is_last_with(&self, extra: usize) -> bool511 fn is_last_with(&self, extra: usize) -> bool {
512 self.cur_in + extra >= self.len_in()
513 }
514
515 /// Increase the chunk sequence number.
516 ///
517 /// Called automatically by the `pipe_encrypt` and `pipe_decrypt` methods when a chunk is read.
518 /// This should never be invoked manually.
519 ///
520 /// # Panics
521 ///
522 /// Panics if the sequence number exceeds the maximum.
523 #[inline(always)]
increase_seq(&mut self)524 fn increase_seq(&mut self) {
525 self.seq = self
526 .seq
527 .checked_add(1)
528 .expect("failed to crypt ECE payload, record sequence number exceeds limit");
529 }
530 }
531
532 impl Pipe for EceCrypt {
533 type Reader = EceReader;
534 type Writer = EceWriter;
535
pipe(&mut self, input: &[u8]) -> (usize, Option<Vec<u8>>)536 fn pipe(&mut self, input: &[u8]) -> (usize, Option<Vec<u8>>) {
537 // Increase input byte counter
538 self.cur_in += input.len();
539
540 // Use mode specific pipe function
541 match self.mode {
542 CryptMode::Encrypt => self.pipe_encrypt(input.to_vec()),
543 CryptMode::Decrypt => self.pipe_decrypt(input),
544 }
545 }
546 }
547
548 impl Crypt for EceCrypt {}
549
550 impl PipeLen for EceCrypt {
len_in(&self) -> usize551 fn len_in(&self) -> usize {
552 match self.mode {
553 CryptMode::Encrypt => self.len,
554 CryptMode::Decrypt => len_encrypted(self.len, self.rs as usize),
555 }
556 }
557
len_out(&self) -> usize558 fn len_out(&self) -> usize {
559 match self.mode {
560 CryptMode::Encrypt => len_encrypted(self.len, self.rs as usize),
561 CryptMode::Decrypt => self.len,
562 }
563 }
564 }
565
566 pub struct EceReader {
567 crypt: EceCrypt,
568 inner: Box<dyn Read>,
569 buf_in: BytesMut,
570 buf_out: BytesMut,
571 }
572
573 pub struct EceWriter {
574 crypt: EceCrypt,
575 inner: Box<dyn Write>,
576 buf: BytesMut,
577 }
578
579 impl PipeRead<EceCrypt> for EceReader {
new(crypt: EceCrypt, inner: Box<dyn Read>) -> Self580 fn new(crypt: EceCrypt, inner: Box<dyn Read>) -> Self {
581 let chunk_size = crypt.chunk_size() as usize;
582
583 Self {
584 crypt,
585 inner,
586 buf_in: BytesMut::with_capacity(chunk_size),
587 buf_out: BytesMut::with_capacity(DEFAULT_BUF_SIZE),
588 }
589 }
590 }
591
592 impl PipeWrite<EceCrypt> for EceWriter {
new(crypt: EceCrypt, inner: Box<dyn Write>) -> Self593 fn new(crypt: EceCrypt, inner: Box<dyn Write>) -> Self {
594 let chunk_size = crypt.chunk_size() as usize;
595
596 Self {
597 crypt,
598 inner,
599 buf: BytesMut::with_capacity(chunk_size),
600 }
601 }
602 }
603
604 impl Read for EceReader {
read(&mut self, mut buf: &mut [u8]) -> io::Result<usize>605 fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
606 // Number of bytes written to given buffer
607 let mut total = 0;
608
609 // Write any output buffer bytes first
610 if !self.buf_out.is_empty() {
611 // Copy as much as possible from inner to output buffer, increase total
612 let write = min(self.buf_out.len(), buf.len());
613 total += write;
614 buf[..write].copy_from_slice(&self.buf_out.split_to(write));
615
616 // Return if given buffer is full, or slice to unwritten buffer
617 if total >= buf.len() {
618 return Ok(total);
619 }
620 buf = &mut buf[write..];
621 }
622
623 // Attempt to fill input buffer if has capacity upto the chunk size
624 let capacity = self.crypt.chunk_size() as usize - self.buf_in.len();
625 if capacity > 0 {
626 // Read from inner to input buffer
627 let mut inner_buf = vec![0u8; capacity];
628 let read = self.inner.read(&mut inner_buf)?;
629 self.buf_in.extend_from_slice(&inner_buf[..read]);
630
631 // Break if:
632 // - no new data was read
633 // - buffer doesn't have enough data to crypt, while there's data left to read
634 if read == 0 || (read != capacity && !self.crypt.is_last_with(read)) {
635 return Ok(total);
636 }
637 }
638
639 // Move input buffer into the crypter
640 let (read, out) = self.crypt.crypt(&self.buf_in);
641 let _ = self.buf_in.split_to(read);
642
643 // Write any crypter output to given buffer and remaining to output buffer
644 if let Some(out) = out {
645 // Copy as much data as possible from crypter output to read buffer
646 let write = min(out.len(), buf.len());
647 total += write;
648 buf[..write].copy_from_slice(&out[..write]);
649
650 // Copy remaining bytes into output buffer
651 if write < out.len() {
652 self.buf_out.extend_from_slice(&out[write..]);
653 }
654
655 // Return if given buffer is full, or slice to unwritten buffer
656 if write >= buf.len() {
657 return Ok(total);
658 }
659 buf = &mut buf[write..];
660 }
661
662 // Try again with remaining given buffer
663 self.read(buf).map(|n| n + total)
664 }
665 }
666
667 impl Write for EceWriter {
write(&mut self, buf: &[u8]) -> io::Result<usize>668 fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
669 // Get the chunk size to use
670 let chunk_size = self.crypt.chunk_size() as usize;
671
672 // Attempt to fill input buffer if has capacity upto the chunk size
673 let capacity = chunk_size - self.buf.len();
674 let read = min(capacity, buf.len());
675 if capacity > 0 {
676 self.buf.extend_from_slice(&buf[..read]);
677 }
678
679 // Transform input data through crypter if chunk data is available
680 if self.buf.len() >= chunk_size {
681 let (read, data) = self.crypt.crypt(&self.buf.split_off(0));
682 assert_eq!(read, chunk_size, "ECE crypto did not transform full chunk");
683 if let Some(data) = data {
684 self.inner.write_all(&data)?;
685 }
686 }
687
688 // If all expected data is provided, make sure to finish the last partial chunk
689 if self.crypt.is_last_with(self.buf.len()) {
690 if let (_, Some(data)) = self.crypt.crypt(&self.buf.split_off(0)) {
691 self.inner.write_all(&data)?;
692 }
693 }
694
695 Ok(read)
696 }
697
flush(&mut self) -> io::Result<()>698 fn flush(&mut self) -> io::Result<()> {
699 self.inner.flush()
700 }
701 }
702
703 impl PipeLen for EceReader {
len_in(&self) -> usize704 fn len_in(&self) -> usize {
705 self.crypt.len_in()
706 }
707
len_out(&self) -> usize708 fn len_out(&self) -> usize {
709 self.crypt.len_out()
710 }
711 }
712
713 impl ReadLen for EceReader {}
714
715 impl PipeLen for EceWriter {
len_in(&self) -> usize716 fn len_in(&self) -> usize {
717 self.crypt.len_in()
718 }
719
len_out(&self) -> usize720 fn len_out(&self) -> usize {
721 self.crypt.len_out()
722 }
723 }
724
725 impl WriteLen for EceWriter {}
726
727 unsafe impl Send for EceReader {}
728 unsafe impl Send for EceWriter {}
729
730 /// Pad a plaintext chunk for ECE encryption.
731 ///
732 /// Padding is a required step for ECE encryption.
733 /// This modifies the block in-place.
734 ///
735 /// The padding length in number of bytes must be passed to `pad_len`.
736 /// If this is the last chunk that will be encrypted, `last` must be `true`.
737 ///
738 /// This internally suffixes a padding delimiter to the block, and the padding bytes itself.
pad(block: &mut Vec<u8>, rs: usize, last: bool)739 fn pad(block: &mut Vec<u8>, rs: usize, last: bool) {
740 // Assert the data fits the records
741 assert!(
742 block.len() + TAG_LEN < rs,
743 "failed to pad ECE ciphertext, data too large for record size"
744 );
745
746 // Pad chunks with 1 delimiter and zeros, pad last chunk with single 2 delimiter
747 if !last {
748 let mut pad = vec![0u8; rs - block.len() - TAG_LEN];
749 pad[0] = 1;
750 block.extend(pad);
751 } else {
752 block.push(2);
753 }
754 }
755
756 /// Unpad an decrypted ECE ciphertext chunk.
757 ///
758 /// Unpadding is a required step to transform ECE decrypted data into plain text.
759 /// This modifies the block in-place.
760 ///
761 /// If this is the last chunk that will be decrypted, `last` must be `false`.
unpad(block: &mut Vec<u8>, last: bool)762 fn unpad(block: &mut Vec<u8>, last: bool) {
763 let pos = match block.iter().rposition(|&b| b != 0) {
764 Some(pos) => pos,
765 None => panic!("ciphertext is zero"),
766 };
767 let expected_delim = if last { 2 } else { 1 };
768 assert_eq!(block[pos], expected_delim, "ECE decrypt unpadding failure");
769
770 // Truncate the padded bytes
771 block.truncate(pos);
772 }
773
774 /// Generate a random salt for encryption.
generate_salt() -> Vec<u8>775 pub fn generate_salt() -> Vec<u8> {
776 let mut salt = vec![0u8; SALT_LEN];
777 rand_bytes(&mut salt).expect("failed to generate encryption salt");
778 salt
779 }
780
781 /// Calcualte length of ECE encrypted data.
782 ///
783 /// This function calculates the length in bytes of the ECE ciphertext.
784 /// The record size and length in bytes of the plaintext must be given as `rs` and `len`.
len_encrypted(len: usize, rs: usize) -> usize785 pub fn len_encrypted(len: usize, rs: usize) -> usize {
786 let chunk_meta = TAG_LEN + 1;
787 let chunk_data = rs - chunk_meta;
788 let header = HEADER_LEN as usize;
789 let chunks = (len as f64 / chunk_data as f64).ceil() as usize;
790
791 header + len + chunk_meta * chunks
792 }
793