+/// Enables simultaneously reading and decrypting a ChaCha20Poly1305RFC stream from a std::io::Read.
+struct ChaChaPolyReader<'a, R: Read> {
+ pub chacha: &'a mut ChaCha20Poly1305RFC,
+ pub read: R,
+}
+
+impl<'a, R: Read> Read for ChaChaPolyReader<'a, R> {
+ // Decrypt bytes from Self::read into `dest`.
+ // `ChaCha20Poly1305RFC::finish_and_check_tag` must be called to check the tag after all reads
+ // complete.
+ fn read(&mut self, dest: &mut [u8]) -> Result<usize, io::Error> {
+ let res = self.read.read(dest)?;
+ if res > 0 {
+ self.chacha.decrypt_in_place(&mut dest[0..res]);
+ }
+ Ok(res)
+ }
+}
+
+/// Enables simultaneously writing and encrypting a byte stream into a Writer.
+struct ChaChaPolyWriter<'a, W: Writer> {
+ pub chacha: &'a mut ChaCha20Poly1305RFC,
+ pub write: &'a mut W,
+}
+
+impl<'a, W: Writer> Writer for ChaChaPolyWriter<'a, W> {
+ // Encrypt then write bytes from `src` into Self::write.
+ // `ChaCha20Poly1305RFC::finish_and_get_tag` can be called to retrieve the tag after all writes
+ // complete.
+ fn write_all(&mut self, src: &[u8]) -> Result<(), io::Error> {
+ let mut src_idx = 0;
+ while src_idx < src.len() {
+ let mut write_buffer = [0; 8192];
+ let bytes_written = (&mut write_buffer[..]).write(&src[src_idx..]).expect("In-memory writes can't fail");
+ self.chacha.encrypt_in_place(&mut write_buffer[..bytes_written]);
+ self.write.write_all(&write_buffer[..bytes_written])?;
+ src_idx += bytes_written;
+ }
+ Ok(())
+ }
+}
+
+/// Enables the use of the serialization macros for objects that need to be simultaneously encrypted and
+/// serialized. This allows us to avoid an intermediate Vec allocation.
+pub(crate) struct ChaChaPolyWriteAdapter<'a, W: Writeable> {
+ pub rho: [u8; 32],
+ pub writeable: &'a W,
+}
+
+impl<'a, W: Writeable> ChaChaPolyWriteAdapter<'a, W> {
+ #[allow(unused)] // This will be used for onion messages soon
+ pub fn new(rho: [u8; 32], writeable: &'a W) -> ChaChaPolyWriteAdapter<'a, W> {
+ Self { rho, writeable }
+ }
+}
+
+impl<'a, T: Writeable> Writeable for ChaChaPolyWriteAdapter<'a, T> {
+ // Simultaneously write and encrypt Self::writeable.
+ fn write<W: Writer>(&self, w: &mut W) -> Result<(), io::Error> {
+ let mut chacha = ChaCha20Poly1305RFC::new(&self.rho, &[0; 12], &[]);
+ let mut chacha_stream = ChaChaPolyWriter { chacha: &mut chacha, write: w };
+ self.writeable.write(&mut chacha_stream)?;
+ let mut tag = [0 as u8; 16];
+ chacha.finish_and_get_tag(&mut tag);
+ tag.write(w)?;
+
+ Ok(())
+ }
+}
+
+/// Enables the use of the serialization macros for objects that need to be simultaneously decrypted and
+/// deserialized. This allows us to avoid an intermediate Vec allocation.
+pub(crate) struct ChaChaPolyReadAdapter<R: Readable> {
+ pub readable: R,
+}
+
+impl<T: Readable> LengthReadableArgs<[u8; 32]> for ChaChaPolyReadAdapter<T> {
+ // Simultaneously read and decrypt an object from a LengthRead, storing it in Self::readable.
+ // LengthRead must be used instead of std::io::Read because we need the total length to separate
+ // out the tag at the end.
+ fn read<R: LengthRead>(mut r: &mut R, secret: [u8; 32]) -> Result<Self, DecodeError> {
+ if r.total_bytes() < 16 { return Err(DecodeError::InvalidValue) }
+
+ let mut chacha = ChaCha20Poly1305RFC::new(&secret, &[0; 12], &[]);
+ let decrypted_len = r.total_bytes() - 16;
+ let s = FixedLengthReader::new(&mut r, decrypted_len);
+ let mut chacha_stream = ChaChaPolyReader { chacha: &mut chacha, read: s };
+ let readable: T = Readable::read(&mut chacha_stream)?;
+ chacha_stream.read.eat_remaining()?;
+
+ let mut tag = [0 as u8; 16];
+ r.read_exact(&mut tag)?;
+ if !chacha.finish_and_check_tag(&tag) {
+ return Err(DecodeError::InvalidValue)
+ }
+
+ Ok(Self { readable })
+ }
+}
+