Compare commits

..

21 Commits

Author SHA1 Message Date
Avril d76b77fbf1
Fixed build fail with async feature.
3 years ago
Avril 903bfcd7c4
Added `from_parts()`, as counterpart to `into_parts()`.
3 years ago
Avril 0645e61590
Exposed `cha` module for keygen and `Crypter` creation.
3 years ago
Avril 73b83e4d5c
Whoops...
3 years ago
Avril d2db1f30df
Merge branch 'read-stream-wrapper-async'
3 years ago
Avril 28f36401ec
Merge branch 'read-stream-wrapper'
3 years ago
Avril e656f78a9a
Added `Source` and `AsyncSource`. All tests currently passing.
3 years ago
Avril e7f56fb6ee
Added AsyncSource: Feature `async` version of `Source`. (Does not include the buffer config that the syncronous `Source` uses.)
3 years ago
Avril 868c6d6c61
Added crate-level re-exports.
3 years ago
Avril 9784208fc8
Added crate-level re-exports.
3 years ago
Avril 5d1ac4fa7a
Started AsyncSource.
3 years ago
Avril e536d6b232
Moved async_stream::Sink into seperate files.
3 years ago
Avril 20199e3610 Added basic docs to `Source`.
3 years ago
Avril 1feb9b209b
Added basic docs to `Source`.
3 years ago
Avril ea70fdc2d1
Fix failing tests of buffer.
3 years ago
Avril 907862f554
Source: Encrypt and Decrypt tests passing. (For both internal and external buffer.)
3 years ago
Avril 30416382ec
Renamed `reuse-buffer` to `ad-hoc-buffer`
3 years ago
Avril 843616e35e
impl `Read` for `Source`: Implemented for internal and external buffer usage
3 years ago
Avril 6cbbf2bd2c
XXX: Can"t update crypter with an in-place buffer...
3 years ago
Avril 404d4511f8
Added compile-time distunguishing of internal/external buffer usage
3 years ago
Avril e18b166514
`stream`: Move `Sink` and `Source` to seperate files
3 years ago

@ -1,6 +1,6 @@
[package]
name = "chacha20stream"
version = "1.2.0"
version = "2.2.1"
keywords = ["chacha20_poly1305", "stream", "wrapper", "encryption", "decryption"]
description = "A writable wrapper stream for encryption and decryption with the stream cipher chacha20_poly1305"
homepage = "https://git.flanchan.moe/flanchan/chacha20stream"
@ -28,10 +28,8 @@ async = ["tokio", "pin-project"]
# Explicitly clear in-memory buffers with `explicit_bzero()` instead of normal `bzero()`.
explicit_clear = []
# Reuse the output buffer for `Source`'s raw bytes read from the backing stream
#
# This can increase speed as no allocations are needed, however it can leak potentially sensitive data so is unsafe.
reuse-buffer = []
# Use a stack (up to a max limit) allocated buffer for `Source`'s raw bytes read from the backing stream instead of a reused backing stream
ad-hoc-buffer = []
# Build with C interface bindings
ffi = ["libc"]
@ -44,6 +42,7 @@ openssl = "0.10"
pin-project = {version = "1.0.6", optional = true}
serde = {version = "1.0", features = ["derive"], optional = true}
smallvec = {version = "1.6", features=["union"], optional = true}
stackalloc = "1.1.1"
tokio = {version = "0.2", optional = true}
[build-dependencies]

@ -50,15 +50,23 @@ fn decrypt_message_to<W: Write + ?Sized>(output: &mut W, encrypted: &[u8], key:
#[macro_use] mod ext; #[allow(unused_imports)] use ext::*;
mod private
{
/// This trait cannot be subtraited by downstream crates.
pub trait Sealed{}
}
pub mod key;
mod cha;
pub mod cha;
mod stream;
mod bytes;
#[cfg(feature="async")] mod stream_async;
#[cfg(feature="async")] pub use stream_async::Sink as AsyncSink;
#[cfg(feature="async")] pub use stream_async::Source as AsyncSource;
pub use stream::Sink;
pub use stream::Source;
pub use key::{
Key, IV,
};

@ -1,395 +0,0 @@
#![allow(dead_code)]
use super::*;
use key::*;
use std::io::{self, Write, Read};
use std::fmt;
use openssl::{
symm::Crypter,
error::ErrorStack,
};
/// Size of the in-structure buffer
#[cfg(feature="smallvec")]
pub const BUFFER_SIZE: usize = 32;
#[cfg(feature="smallvec")]
type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>;
#[cfg(not(feature="smallvec"))]
type BufferVec = Vec<u8>;
pub type Error = ErrorStack;
/// ChaCha Sink
///
/// # Encryption
/// To create an encrypting wrapper stream:
/// ```
/// # use chacha20stream::Sink;
/// # use std::io::Write;
/// # let (key, iv) = chacha20stream::keygen();
/// # let mut backing_stream = Vec::new();
/// let mut stream = Sink::encrypt(&mut backing_stream, key, iv).expect("Failed to create encryptor");
/// /* do work with `stream` */
///
/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer.
/// stream.flush().unwrap();
/// ```
///
/// # Decryption
/// To create a decrypting wrapper stream:
/// ```
/// # use chacha20stream::Sink;
/// # use std::io::Write;
/// # let (key, iv) = chacha20stream::keygen();
/// # let mut backing_stream = Vec::new();
/// let mut stream = Sink::decrypt(&mut backing_stream, key, iv).expect("Failed to create decryptor");
/// /* do work with `stream` */
///
/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer.
/// stream.flush().unwrap();
/// ```
///
/// # Note
/// When writing, a temporary buffer stored in the structure is used. This buffer is **not** cleared after a write, for efficiency reasons. This may leave sensitive information in the buffer after the write operation.
/// The `flush()` implementation *does* clear this buffer.
/// You can use the `prune()` function to zero out this buffer manually too.
//#[derive(Debug)]
pub struct Sink<W: ?Sized>
{
crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times.
buffer: BufferVec, // used to buffer the operation
stream: W,
}
/// TODO: Document
//#[derive(Debug)]
pub struct Source<R: ?Sized>
{
crypter: Crypter,
#[cfg(not(feature="reuse-buffer"))] buffer: BufferVec, // When `reuse-buffer` is enabled, this isn't needed. We re-use the output buffer for the initial read of untransformed data from `stream` and the actual transformation of the read bytes.
stream: R
}
impl<W: ?Sized+ fmt::Debug> fmt::Debug for Sink<W>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Sink({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity())
}
}
impl<R: ?Sized+ fmt::Debug> fmt::Debug for Source<R>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
#[cfg(feature="reuse-buffer")]
return write!(f, "Source({:?}, (unbounded buffer cap))", &self.stream);
#[cfg(not(feature="reuse-buffer"))]
return write!(f, "Source({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity());
}
}
impl<R: ?Sized> Source<R>
where R: Read
{
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
#[inline] pub fn inner(&self) -> &R
{
&self.stream
}
/// The inner stream
#[inline] pub fn inner_mut(&mut self) -> &mut R
{
&mut self.stream
}
#[cfg(not(feature="reuse-buffer"))]
/// Grow the inner buffer to fix this size, if needed.
fn grow_to_fit(&mut self, sz: usize)
{
if sz > self.buffer.len() {
self.buffer.resize(sz, 0);
}
}
#[cfg(not(feature="reuse-buffer"))]
/// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated.
fn transform(&mut self, bufsz: usize, output: &mut [u8]) -> Result<usize, ErrorStack>
{
//self.grow_to_fix(output.len());
//let bufsz = self.stream.read(&mut self.buffer[..bufsz])?;
let n = self.crypter.update(&self.buffer[..bufsz], &mut output[..])?;
let _f = self.crypter.finalize(&mut output[..n])?;
debug_assert_eq!(_f, 0);
Ok(n)
/*
if buf.len() > self.buffer.len() {
self.buffer.resize(buf.len(), 0);
}
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed.
debug_assert_eq!(_f, 0);
Ok(n)*/
}
#[cfg(not(feature="reuse-buffer"))]
/// Clear the internal buffer while keeping it allocated for further use.
///
/// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
#[inline]
pub fn prune(&mut self)
{
#[cfg(feature="explicit_clear")]
{
bytes::explicit_prune(&mut self.buffer[..]);
return;
}
#[cfg(not(feature="explicit_clear"))]
unsafe {
std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len());
}
}
}
impl<W: ?Sized> Sink<W>
where W: Write
{
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
#[inline] pub fn inner(&self) -> &W
{
&self.stream
}
/// The inner stream
#[inline] pub fn inner_mut(&mut self) -> &mut W
{
&mut self.stream
}
/// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated.
fn transform(&mut self, buf: &[u8]) -> Result<usize, ErrorStack>
{
if buf.len() > self.buffer.len() {
self.buffer.resize(buf.len(), 0);
}
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed.
debug_assert_eq!(_f, 0);
Ok(n)
}
/// Clear the internal buffer while keeping it allocated for further use.
///
/// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
#[inline]
pub fn prune(&mut self)
{
#[cfg(feature="explicit_clear")]
{
bytes::explicit_prune(&mut self.buffer[..]);
return;
}
#[cfg(not(feature="explicit_clear"))]
unsafe {
std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len());
}
}
}
impl<W> Sink<W>
where W: Write
{
/// Create a new Chacha Sink stream wrapper
#[inline] fn new(stream: W, crypter: Crypter) -> Self
{
Self{stream, crypter, buffer: BufferVec::new()}
}
/// Create an encrypting Chacha Sink stream wrapper
pub fn encrypt(stream: W, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::encrypter(key, iv)?))
}
/// Create a decrypting Chacha Sink stream wrapper
pub fn decrypt(stream: W, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::decrypter(key, iv)?))
}
/// Consume into the inner stream
#[inline] pub fn into_inner(self) -> W
{
self.stream
}
/// Consume into the inner stream and crypter
#[inline] pub fn into_parts(self) -> (W, Crypter)
{
(self.stream, self.crypter)
}
}
impl<R: ?Sized> Read for Source<R>
where R: Read
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
(#[cfg(feature="reuse-buffer")] {
todo!()
},
#[cfg(not(feature="reuse-buffer"))] {
self.grow_to_fit(buf.len());
let read = self.stream.read(&mut self.buffer[..buf.len()])?;
Ok(self.transform(read, &mut buf[..read])?)
},).0
}
}
impl<W: ?Sized + Write> Write for Sink<W>
{
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let n = self.transform(buf)?;
self.stream.write(&self.buffer[..n])
}
#[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
let n = self.transform(buf)?;
self.stream.write_all(&self.buffer[..n])
}
#[inline] fn flush(&mut self) -> io::Result<()> {
#[cfg(feature="explicit_clear")] self.prune();
self.buffer.clear();
self.stream.flush()
}
}
#[cfg(test)]
mod tests
{
use super::*;
const INPUT: &'static str = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!";
fn enc_stream(input: impl AsRef<[u8]>, key: Key, iv: IV) -> Sink<Vec<u8>>
{
let enc_buffer = Vec::new();
let input = input.as_ref();
eprintln!("(enc) Key: {}, IV: {}, Input: ({}, {})", key, iv, input.len(), input.hex());
let mut stream = Sink::encrypt(enc_buffer, key, iv).expect("sink::enc");
assert_eq!(stream.write(input).unwrap(), input.len());
stream.flush().unwrap();
eprintln!("Output encrypted: {}", stream.inner().hex());
stream
}
#[test]
fn enc()
{
let (key, iv) = cha::keygen();
eprintln!("Sink ends: {:?}", enc_stream(INPUT.as_bytes(), key, iv));
}
#[test]
fn dec()
{
println!(">>> Sink's size with ref is {}", std::mem::size_of::<Sink<&mut Vec<u8>>>());
let (key, iv) = cha::keygen();
eprintln!("Input unencrypted: {}", INPUT.hex());
let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner();
let mut dec_buffer = Vec::new();
{
let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::dec");
stream.write_all(&input[..]).unwrap();
stream.flush().unwrap();
eprintln!("Output decrypted: {}", stream.inner().hex());
}
assert_eq!(&dec_buffer[..], INPUT.as_bytes());
}
/// Checks if explicit clear is actually clearing.
#[cfg(feature="explicit_clear")]
#[test]
fn remainder()
{
let mut dec_buffer = Vec::new();
let (buf, off, _s) = {
let (key, iv) = cha::keygen();
let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner();
{
let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::rem");
stream.write_all(&input[..]).unwrap();
let by = stream.buffer[0];
//stream.prune();
stream.flush().unwrap();
(by, (stream.buffer.as_ptr() as u64), stream)
}
};
// Check to see if the buffer remains in our process's memory.
use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Read};
let mut file = OpenOptions::new().read(true).open("/proc/self/mem").unwrap();
file.seek(SeekFrom::Start(off)).unwrap();
let mut chk = [0u8; 10];
file.read_exact(&mut chk).unwrap();
assert!(buf != chk[0]);
}
}

@ -0,0 +1,177 @@
#![allow(dead_code)]
use super::*;
use key::*;
use std::io::{self, Write, Read};
use std::fmt;
use openssl::{
symm::Crypter,
error::ErrorStack,
};
/// Size of the in-structure buffer
#[cfg(feature="smallvec")]
pub const BUFFER_SIZE: usize = 32;
#[cfg(feature="smallvec")]
type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>;
#[cfg(not(feature="smallvec"))]
type BufferVec = Vec<u8>;
pub type Error = ErrorStack;
pub mod sink;
pub mod source;
pub use sink::Sink;
pub use source::Source;
//TODO: Stream: Wrapper over both Sink and Source?
#[cfg(test)]
mod tests
{
use super::*;
use std::io::Cursor;
const INPUT: &'static str = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!";
fn enc_stream(input: impl AsRef<[u8]>, key: Key, iv: IV) -> Sink<Vec<u8>>
{
let enc_buffer = Vec::new();
let input = input.as_ref();
eprintln!("(enc) Key: {}, IV: {}, Input: ({}, {})", key, iv, input.len(), input.hex());
let mut stream = Sink::encrypt(enc_buffer, key, iv).expect("sink::enc");
assert_eq!(stream.write(input).unwrap(), input.len());
stream.flush().unwrap();
eprintln!("Output encrypted: {}", stream.inner().hex());
stream
}
/// Create a source from a slice of bytes with this key and IV.
fn create_source<'a, T: ?Sized + AsRef<[u8]> +'a>(source: &'a T, key: Key, iv: IV, enc: bool) -> Source<Cursor<&'a [u8]>>
{
eprintln!("({}) Key: {}, IV: {}, Input: ({}: {})", ["dec", "enc"][enc as usize], key, iv, source.as_ref().len(), source.as_ref().hex());
let stream = if enc {
Source::encrypt(Cursor::new(source.as_ref()), key, iv)
} else {
Source::decrypt(Cursor::new(source.as_ref()), key, iv)
}.expect("sink::enc");
stream
}
#[test]
fn enc()
{
let (key, iv) = cha::keygen();
eprintln!("Sink ends: {:?}", enc_stream(INPUT.as_bytes(), key, iv));
}
#[test]
fn source_enc()
{
let (key, iv) = cha::keygen();
const INPUT: &'static [u8] = b"Hello world!";
println!("Input ({} bytes, hex): {}", INPUT.len(), INPUT.hex());
let mut source = create_source(INPUT, key, iv, true);
let mut output = Vec::with_capacity(INPUT.len());
io::copy(&mut source, &mut output).expect("Failed to copy source to output");
println!("Output ({} bytes, hex): {}", output.len(), output.hex());
}
#[test]
fn source_dec()
{
let (key, iv) = cha::keygen();
const INPUT: &'static [u8] = b"Hello world!";
println!("Input ({} bytes, hex): {}", INPUT.len(), INPUT.hex());
let mut source = create_source(INPUT, key, iv, true);
let mut temp = Vec::with_capacity(INPUT.len());
io::copy(&mut source, &mut temp).expect("Failed to copy source to output (encrypt)");
println!("Encrypted ({} bytes, hex): {}", temp.len(), temp.hex());
// decrypt
let mut source = create_source(&mut temp, key, iv, false);
let mut temp = Vec::with_capacity(INPUT.len());
io::copy(&mut source, &mut temp).expect("Failed to copy source to output (decrypt)");
println!("Decrypted ({} bytes, hex): {}", temp.len(), temp.hex());
assert_eq!(INPUT, &temp[..]);
}
#[test]
fn dec()
{
println!(">>> Sink's size with ref is {}", std::mem::size_of::<Sink<&mut Vec<u8>>>());
let (key, iv) = cha::keygen();
eprintln!("Input unencrypted: {}", INPUT.hex());
let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner();
let mut dec_buffer = Vec::new();
{
let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::dec");
stream.write_all(&input[..]).unwrap();
stream.flush().unwrap();
eprintln!("Output decrypted: {}", stream.inner().hex());
}
assert_eq!(&dec_buffer[..], INPUT.as_bytes());
}
/// Checks if explicit clear is actually clearing.
#[cfg(feature="explicit_clear")]
#[test]
fn remainder()
{
let mut dec_buffer = Vec::new();
let (buf, off, _s) = {
let (key, iv) = cha::keygen();
let input = enc_stream(INPUT.as_bytes(), key.clone(), iv.clone()).into_inner();
{
let mut stream = Sink::decrypt(&mut dec_buffer, key, iv).expect("sink::rem");
stream.write_all(&input[..]).unwrap();
let by = stream.buffer[0];
//stream.prune();
stream.flush().unwrap();
(by, (stream.buffer.as_ptr() as u64), stream)
}
};
// Check to see if the buffer remains in our process's memory.
use std::fs::OpenOptions;
use std::io::{Seek, SeekFrom, Read};
let mut file = OpenOptions::new().read(true).open("/proc/self/mem").unwrap();
file.seek(SeekFrom::Start(off)).unwrap();
let mut chk = [0u8; 10];
file.read_exact(&mut chk).unwrap();
assert!(buf != chk[0]);
}
}

@ -0,0 +1,178 @@
//! Syncronous stream `Write` componant.
use super::*;
/// ChaCha Sink
///
/// # Encryption
/// To create an encrypting wrapper stream:
/// ```
/// # use chacha20stream::Sink;
/// # use std::io::Write;
/// # let (key, iv) = chacha20stream::keygen();
/// # let mut backing_stream = Vec::new();
/// let mut stream = Sink::encrypt(&mut backing_stream, key, iv).expect("Failed to create encryptor");
/// /* do work with `stream` */
///
/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer.
/// stream.flush().unwrap();
/// ```
///
/// # Decryption
/// To create a decrypting wrapper stream:
/// ```
/// # use chacha20stream::Sink;
/// # use std::io::Write;
/// # let (key, iv) = chacha20stream::keygen();
/// # let mut backing_stream = Vec::new();
/// let mut stream = Sink::decrypt(&mut backing_stream, key, iv).expect("Failed to create decryptor");
/// /* do work with `stream` */
///
/// // It is recommended to `flush` the stream to clear out any remaining data in the internal transformation buffer.
/// stream.flush().unwrap();
/// ```
///
/// # Note
/// When writing, a temporary buffer stored in the structure is used. This buffer is **not** cleared after a write, for efficiency reasons. This may leave sensitive information in the buffer after the write operation.
/// The `flush()` implementation *does* clear this buffer.
/// You can use the `prune()` function to zero out this buffer manually too.
//#[derive(Debug)]
pub struct Sink<W: ?Sized>
{
crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times.
pub(super) buffer: BufferVec, // used to buffer the operation
stream: W,
}
impl<W: ?Sized+ fmt::Debug> fmt::Debug for Sink<W>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Sink({:?}, ({} buffer cap))", &self.stream, self.buffer.capacity())
}
}
impl<W: ?Sized> Sink<W>
where W: Write
{
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
#[inline] pub fn inner(&self) -> &W
{
&self.stream
}
/// The inner stream
#[inline] pub fn inner_mut(&mut self) -> &mut W
{
&mut self.stream
}
/// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated.
fn transform(&mut self, buf: &[u8]) -> Result<usize, ErrorStack>
{
if buf.len() > self.buffer.len() {
self.buffer.resize(buf.len(), 0);
}
let n = self.crypter.update(&buf[..], &mut self.buffer[..])?;
let _f = self.crypter.finalize(&mut self.buffer[..n])?; // I don't know if this is needed.
debug_assert_eq!(_f, 0);
Ok(n)
}
/// Clear the internal buffer while keeping it allocated for further use.
///
/// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
#[inline]
pub fn prune(&mut self)
{
#[cfg(feature="explicit_clear")]
{
bytes::explicit_prune(&mut self.buffer[..]);
return;
}
#[cfg(not(feature="explicit_clear"))]
unsafe {
std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len());
}
}
}
impl<W> Sink<W>
where W: Write
{
/// Create a new Chacha Sink stream wrapper
#[inline] fn new(stream: W, crypter: Crypter) -> Self
{
Self{stream, crypter, buffer: BufferVec::new()}
}
/// Create an encrypting Chacha Sink stream wrapper
pub fn encrypt(stream: W, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::encrypter(key, iv)?))
}
/// Create a decrypting Chacha Sink stream wrapper
pub fn decrypt(stream: W, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::decrypter(key, iv)?))
}
/// Consume into the inner stream
#[inline] pub fn into_inner(self) -> W
{
self.stream
}
/// Consume into the inner stream and crypter
#[inline] pub fn into_parts(self) -> (W, Crypter)
{
(self.stream, self.crypter)
}
/// Create a sink from a stream and a crypter
///
/// The counterpart to `into_parts()`.
#[inline] pub fn from_parts(stream: W, crypter: Crypter) -> Self
{
Self::new(stream, crypter)
}
}
impl<W: ?Sized + Write> Write for Sink<W>
{
#[inline] fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let n = self.transform(buf)?;
self.stream.write(&self.buffer[..n])
}
#[inline] fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
let n = self.transform(buf)?;
self.stream.write_all(&self.buffer[..n])
}
#[inline] fn flush(&mut self) -> io::Result<()> {
#[cfg(feature="explicit_clear")] self.prune();
self.buffer.clear();
self.stream.flush()
}
}

@ -0,0 +1,315 @@
//! Syncronous stream `Read` componant.
use super::*;
/// Max number of bytes to stackalloc
///
/// Only used with `UseBufferExternal`.
pub const STACK_MAX_BYTES: usize = 4096;
/// How buffers are used.
pub trait BufferKind : private::Sealed
{
type InternalBuffer;
fn create_buffer(cap: usize) -> Self::InternalBuffer;
fn buffer_len<R: ?Sized>(source: &Source<R, Self>) -> usize;
fn buffer_cap<R: ?Sized>(source: &Source<R, Self>) -> usize;
fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8];
fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8];
fn buffer_resize<R: ?Sized>(source: &mut Source<R, Self>, to: usize);
}
/// Use struct-internal buffer for `Read`s
#[derive(Debug)]
pub struct UseBufferInternal;
/// Reuse the output buffer for `Read`s
#[derive(Debug)]
pub struct UseBufferExternal;
impl private::Sealed for UseBufferInternal{}
impl BufferKind for UseBufferInternal
{
type InternalBuffer = BufferVec;
#[inline] fn create_buffer(cap: usize) -> Self::InternalBuffer {
if cap == 0 {
BufferVec::new()
} else {
BufferVec::with_capacity(cap)
}
}
#[inline(always)] fn buffer_cap<R: ?Sized>(source: &Source<R, Self>) -> usize {
source.buffer.capacity()
}
#[inline(always)] fn buffer_len<R: ?Sized>(source: &Source<R, Self>) -> usize {
source.buffer.len()
}
#[inline(always)] fn buffer_bytes_mut(source: &mut Self::InternalBuffer) -> &'_ mut [u8]
{
&mut source[..]
}
#[inline(always)] fn buffer_bytes(source: &Self::InternalBuffer) -> &'_ [u8]
{
&source[..]
}
#[inline(always)] fn buffer_resize<R: ?Sized>(source: &mut Source<R, Self>, to: usize)
{
source.buffer.resize(to, 0);
}
}
impl private::Sealed for UseBufferExternal{}
impl BufferKind for UseBufferExternal
{
type InternalBuffer = ();
// -- always used --
#[inline(always)] fn create_buffer(_: usize) -> Self::InternalBuffer {
()
}
#[inline(always)] fn buffer_cap<R: ?Sized>(_: &Source<R, Self>) -> usize {
0
}
// -- conditional --
#[cold]
#[inline(never)] fn buffer_len<R: ?Sized>(_: &Source<R, Self>) -> usize {
panic!("Phantom buffer length cannot be checked")
}
#[cold]
#[inline(never)] fn buffer_bytes_mut(_: &mut Self::InternalBuffer) -> &'_ mut [u8]
{
panic!("Cannot mutref non-existent ibuf.")
}
#[cold]
#[inline(never)] fn buffer_bytes(_: &Self::InternalBuffer) -> &'_ [u8]
{
panic!("Cannot ref non-existent ibuf.")
}
#[cold]
#[inline(never)] fn buffer_resize<R: ?Sized>(_: &mut Source<R, Self>, _: usize)
{
panic!("Cannot resize non-existent ibuf.")
}
}
#[cfg(not(feature="ad-hoc-buffer"))]
pub type DefaultBuffer = UseBufferInternal;
#[cfg(feature="ad-hoc-buffer")]
pub type DefaultBuffer = UseBufferExternal;
/// ChaCha source.
/// En/decrypts information from the source reader.
///
/// This is the `Read` implementing counterpart to `Sink`.
//#[derive(Debug)]
pub struct Source<R: ?Sized, Buffer: ?Sized + BufferKind = DefaultBuffer>
{
crypter: Crypter,
pub(super) buffer: Buffer::InternalBuffer, // When `ad-hoc-buffer` is enabled, this isn't needed. We re-use the output buffer for the initial read of untransformed data from `stream` and the actual transformation of the read bytes.
stream: R
}
impl<R: ?Sized+ fmt::Debug, K: ?Sized + BufferKind> fmt::Debug for Source<R, K>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
use std::any::type_name;
write!(f, "Source<Wraps: {}, BufferKind: {}>", type_name::<R>(), type_name::<K>())?;
match K::buffer_cap(self) {
0 => write!(f, "({:?}, (unbounded buffer cap))", &self.stream),
cap => write!(f, "({:?}, ({} buffer cap))", &self.stream, cap),
}
}
}
impl<R: ?Sized, K: ?Sized + BufferKind> Source<R, K>
where R: Read
{
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
#[inline] pub fn inner(&self) -> &R
{
&self.stream
}
/// The inner stream
#[inline] pub fn inner_mut(&mut self) -> &mut R
{
&mut self.stream
}
/// Grow the inner buffer to fix this size, if needed.
fn grow_to_fit(&mut self, sz: usize)
{
if sz > K::buffer_len(self) {
K::buffer_resize(self, sz);
}
}
/// Perform the cipher transform on this `buffer` to the output buffer, returning the number of bytes updated.
fn transform_into(&mut self, buffer: &[u8], output: &mut [u8]) -> Result<usize, ErrorStack>
{
let n = self.crypter.update(buffer, &mut output[..])?;
let _f = self.crypter.finalize(&mut output[..n])?;
debug_assert_eq!(_f, 0);
Ok(n)
}
/// Perform the cipher transform on the inner buffer bytes to the `output` buffer, returning the number of bytes updated.
///
/// # Panics
/// If the inner buffer is phantom
fn transform(&mut self, bufsz: usize, output: &mut [u8]) -> Result<usize, ErrorStack>
{
let n = self.crypter.update(& K::buffer_bytes(&self.buffer)[..bufsz], &mut output[..])?;
let _f = self.crypter.finalize(&mut output[..n])?;
debug_assert_eq!(_f, 0);
Ok(n)
}
/// Clear the internal buffer while keeping it allocated for further use.
///
/// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
#[inline]
pub fn prune(&mut self)
{
#[cfg(feature="explicit_clear")]
{
bytes::explicit_prune(K::buffer_bytes_mut(&mut self.buffer));
return;
}
#[cfg(not(feature="explicit_clear"))]
unsafe {
std::ptr::write_bytes(K::buffer_bytes_mut(&mut self.buffer).as_mut_ptr(), 0, K::buffer_len(self));
}
}
}
impl<R, K: ?Sized + BufferKind> Source<R, K>
where R: Read
{
/// Create a new Chacha Source stream wrapper from a reader
#[inline] fn new(stream: R, crypter: Crypter) -> Self
{
Self{stream, crypter, buffer: K::create_buffer(0)}
}
/// Create an encrypting Chacha Source stream wrapper
pub fn encrypt(stream: R, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::encrypter(key, iv)?))
}
/// Create a decrypting Chacha Source stream wrapper
pub fn decrypt(stream: R, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::decrypter(key, iv)?))
}
/// Consume into the inner stream
#[inline] pub fn into_inner(self) -> R
{
self.stream
}
/// Consume into the inner stream and crypter
#[inline] pub fn into_parts(self) -> (R, Crypter)
{
(self.stream, self.crypter)
}
/// Create a source from a stream and a crypter
///
/// The counterpart to `into_parts()`.
#[inline] pub fn from_parts(stream: R, crypter: Crypter) -> Self
{
Self::new(stream, crypter)
}
}
impl<R> Source<R, UseBufferExternal>
{
/// Convert this instance to use internal buffer (instead of external.)
pub fn with_internal_buffer(self) -> Source<R, UseBufferInternal>
{
Source {
buffer: UseBufferInternal::create_buffer(UseBufferExternal::buffer_cap(&self)),
crypter: self.crypter,
stream: self.stream,
}
}
}
impl<R> Source<R, UseBufferInternal>
{
/// Convert this instance to use external buffer (instead of internal.)
pub fn with_reused_buffer(self) -> Source<R, UseBufferExternal>
{
Source {
buffer: UseBufferExternal::create_buffer(UseBufferInternal::buffer_cap(&self)),
crypter: self.crypter,
stream: self.stream,
}
}
}
fn try_alloca<T>(sz: usize, cb: impl for<'a> FnOnce(&'a mut [u8]) -> T) -> T
{
if sz > STACK_MAX_BYTES {
let mut bytes = vec![0u8; sz];
cb(&mut bytes[..])
} else {
stackalloc::alloca_zeroed(sz, cb)
}
}
impl<R: ?Sized, K: ?Sized + BufferKind> Read for Source<R, K>
where R: Read
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if cfg!(feature="ad-hoc-buffer") {
//XXX: FUck, we can't `crypter.update()` in place....
try_alloca(buf.len(), move |temp| -> io::Result<usize> {
let read = self.stream.read(temp)?;
let b = self.transform_into(&temp[..read], &mut buf[..read])?;
#[cfg(feature="explicit_clear")] bytes::explicit_prune(&mut temp[..b]);
Ok(b)
})
}
else {
self.grow_to_fit(buf.len());
let read = self.stream.read(&mut K::buffer_bytes_mut(&mut self.buffer)[..buf.len()])?;
let b = self.transform(read, &mut buf[..read])?;
#[cfg(feature="explicit_clear")] bytes::explicit_prune(&mut K::buffer_bytes_mut(&mut self.buffer)[..b]);
Ok(b)
}
}
}

@ -0,0 +1,173 @@
use super::*;
use key::*;
use std::io;
use tokio::io::AsyncWrite;
use std::{
pin::Pin,
task::{Context, Poll},
};
use std::fmt;
use openssl::{
symm::Crypter,
error::ErrorStack,
};
/// Size of the in-structure buffer
#[cfg(feature="smallvec")]
pub const BUFFER_SIZE: usize = 32;
#[cfg(feature="smallvec")]
type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>;
#[cfg(not(feature="smallvec"))]
type BufferVec = Vec<u8>;
pub type Error = ErrorStack;
pub mod sink;
pub use sink::Sink;
pub mod source;
pub use source::Source;
#[cfg(test)]
mod test
{
use tokio::prelude::*;
#[tokio::test]
async fn async_source_enc_dec()
{
use crate::ext::*;
const INPUT: &'static [u8] = b"Hello world!";
let (key, iv) = crate::cha::keygen();
println!("Input: {}", INPUT.hex());
let mut enc = super::Source::encrypt(&INPUT[..], key, iv).expect("Failed to create encryptor");
let mut enc_out = Vec::with_capacity(INPUT.len());
tokio::io::copy(&mut enc, &mut enc_out).await.expect("Failed to copy encrypted output");
println!("(enc) output: {}", enc_out.hex());
let mut dec = super::Source::decrypt(&enc_out[..], key, iv).expect("Failed to create decryptor");
let mut dec_out = Vec::with_capacity(INPUT.len());
tokio::io::copy(&mut dec, &mut dec_out).await.expect("Failed to copy decrypted output");
println!("(dec) output: {}", dec_out.hex());
assert_eq!(&dec_out[..], INPUT);
}
#[tokio::test]
async fn sink_sync()
{
let mut output = Vec::new();
let input = "Hello world!";
let (key, iv) = crate::cha::keygen();
let encrypted = {
let mut sink = super::Sink::encrypt(&mut output, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
sink.into_inner().clone()
};
output.clear();
let decrypted = {
let mut sink = super::Sink::decrypt(&mut output, key, iv).expect("Sink::decrypt");
sink.write_all(&encrypted[..]).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
sink.into_inner().clone()
};
assert_eq!(&decrypted[..], input.as_bytes());
}
#[tokio::test]
async fn sink_mem()
{
const BACKLOG: usize = 4;
let (mut client, mut server) = tokio::io::duplex(BACKLOG);
let (key, iv) = crate::cha::keygen();
let input = "Hello!";
let enctask = tokio::spawn(async move {
let mut sink = super::Sink::encrypt(&mut client, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
drop(client);
});
let (mut declient, mut deserver) = tokio::io::duplex(BACKLOG * 2);
let dectask = tokio::spawn(async move {
let mut sink = super::Sink::decrypt(&mut declient, key, iv).expect("Sink::encrypt");
tokio::io::copy(&mut server, &mut sink).await.expect("Copy to sink failed");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
});
let (de, en) = tokio::join![dectask, enctask];
de.expect("Dec task panic");
en.expect("Enc task panic");
let mut output = Vec::new();
tokio::io::copy(&mut deserver, &mut output).await.expect("Copy into vec");
println!("In: {}, Out: {}", String::from_utf8_lossy(&output[..]), input);
assert_eq!(&output[..], input.as_bytes());
}
#[tokio::test]
async fn sink_files()
{
let mut output = tokio::fs::File::from_std(tempfile::tempfile().unwrap());
let input = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!";
let (key, iv) = crate::cha::keygen();
{
let mut sink = super::Sink::encrypt(&mut output, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
}
let mut encrypted = output;
encrypted.seek(tokio::io::SeekFrom::Start(0)).await.unwrap();
let mut output = tokio::fs::File::from_std(tempfile::tempfile().unwrap());
{
let mut sink = super::Sink::decrypt(&mut output, key, iv).expect("Sink::decrypt");
tokio::io::copy(&mut encrypted, &mut sink).await.expect("Copy to sinl");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
}
let mut decrypted = output;
let (r1, r2) = tokio::join![encrypted.sync_data(),
decrypted.sync_data()];
r1.expect("enc sync");
r2.expect("dec sync");
let decrypted = {
decrypted.seek(tokio::io::SeekFrom::Start(0)).await.unwrap();
let mut output = vec![0u8; input.len()];
decrypted.read_exact(&mut output[..]).await.expect("Read decrypted");
output
};
assert_eq!(&decrypted[..], input.as_bytes());
}
}

@ -1,29 +1,5 @@
//! Asyncronous `AsyncWrite` wrapper.
use super::*;
use key::*;
use std::io;
use tokio::io::AsyncWrite;
use std::{
pin::Pin,
task::{Context, Poll},
};
use std::fmt;
use openssl::{
symm::Crypter,
error::ErrorStack,
};
/// Size of the in-structure buffer
#[cfg(feature="smallvec")]
pub const BUFFER_SIZE: usize = 32;
#[cfg(feature="smallvec")]
type BufferVec = smallvec::SmallVec<[u8; BUFFER_SIZE]>;
#[cfg(not(feature="smallvec"))]
type BufferVec = Vec<u8>;
pub type Error = ErrorStack;
/// Async ChaCha Sink
///
@ -133,6 +109,14 @@ impl<W: AsyncWrite> Sink<W>
(self.stream, self.crypter)
}
/// Create a sink from a stream and a crypter
///
/// The counterpart to `into_parts()`.
#[inline] pub fn from_parts(stream: W, crypter: Crypter) -> Self
{
Self::new(stream, crypter)
}
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
@ -214,120 +198,3 @@ impl<W: AsyncWrite> AsyncWrite for Sink<W>
poll
}
}
#[cfg(test)]
mod test
{
use tokio::prelude::*;
#[tokio::test]
async fn sink_sync()
{
let mut output = Vec::new();
let input = "Hello world!";
let (key, iv) = crate::cha::keygen();
let encrypted = {
let mut sink = super::Sink::encrypt(&mut output, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
sink.into_inner().clone()
};
output.clear();
let decrypted = {
let mut sink = super::Sink::decrypt(&mut output, key, iv).expect("Sink::decrypt");
sink.write_all(&encrypted[..]).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
sink.into_inner().clone()
};
assert_eq!(&decrypted[..], input.as_bytes());
}
#[tokio::test]
async fn sink_mem()
{
const BACKLOG: usize = 4;
let (mut client, mut server) = tokio::io::duplex(BACKLOG);
let (key, iv) = crate::cha::keygen();
let input = "Hello!";
let enctask = tokio::spawn(async move {
let mut sink = super::Sink::encrypt(&mut client, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
drop(client);
});
let (mut declient, mut deserver) = tokio::io::duplex(BACKLOG * 2);
let dectask = tokio::spawn(async move {
let mut sink = super::Sink::decrypt(&mut declient, key, iv).expect("Sink::encrypt");
tokio::io::copy(&mut server, &mut sink).await.expect("Copy to sink failed");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
});
let (de, en) = tokio::join![dectask, enctask];
de.expect("Dec task panic");
en.expect("Enc task panic");
let mut output = Vec::new();
tokio::io::copy(&mut deserver, &mut output).await.expect("Copy into vec");
println!("In: {}, Out: {}", String::from_utf8_lossy(&output[..]), input);
assert_eq!(&output[..], input.as_bytes());
}
#[tokio::test]
async fn sink_files()
{
let mut output = tokio::fs::File::from_std(tempfile::tempfile().unwrap());
let input = "Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!Hello world!";
let (key, iv) = crate::cha::keygen();
{
let mut sink = super::Sink::encrypt(&mut output, key, iv).expect("Sink::encrypt");
sink.write_all(input.as_bytes()).await.expect("Sink::write_all");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
}
let mut encrypted = output;
encrypted.seek(tokio::io::SeekFrom::Start(0)).await.unwrap();
let mut output = tokio::fs::File::from_std(tempfile::tempfile().unwrap());
{
let mut sink = super::Sink::decrypt(&mut output, key, iv).expect("Sink::decrypt");
tokio::io::copy(&mut encrypted, &mut sink).await.expect("Copy to sinl");
sink.flush().await.expect("Sink::flush");
sink.shutdown().await.expect("Sink::shutdown");
}
let mut decrypted = output;
let (r1, r2) = tokio::join![encrypted.sync_data(),
decrypted.sync_data()];
r1.expect("enc sync");
r2.expect("dec sync");
let decrypted = {
decrypted.seek(tokio::io::SeekFrom::Start(0)).await.unwrap();
let mut output = vec![0u8; input.len()];
decrypted.read_exact(&mut output[..]).await.expect("Read decrypted");
output
};
assert_eq!(&decrypted[..], input.as_bytes());
}
}

@ -0,0 +1,158 @@
//! Asyncronous `AsyncRead` wrapper.
use super::*;
use tokio::io::AsyncRead;
/// Asyncronous ChaCha source.
/// En/decrypts information from the source async reader.
///
/// This is the `Read` implementing counterpart to `AsyncSink`.
//#[derive(Debug)]
#[pin_project]
pub struct Source<R>
{
#[pin] stream: R,
crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times.
buffer: BufferVec, // used to buffer the operation (ad-hoc-buffer wouldn't work for async operations as the buffer may need to be saved over yields.)
}
impl<R: fmt::Debug> fmt::Debug for Source<R>
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "Source({:?}, ({} buffer cap))", self.stream, self.buffer.capacity())
}
}
/// Perform the cipher transform on the inner buffer, writing to the output buffer, returning the number of bytes updated.
fn transform(crypter: &mut Crypter, buf: &[u8], buffer: &mut [u8]) -> Result<usize, ErrorStack>
{
//if buf.len() > self.buffer.len() {
//buf.resize(buffer.len(), 0);
//}
let n = crypter.update(&buf[..], &mut buffer[..])?;
let _f = crypter.finalize(&mut buffer[..n])?; // I don't know if this is needed.
debug_assert_eq!(_f, 0);
Ok(n)
}
impl<R: AsyncRead> Source<R>
{
/// Create a new async Chacha Source stream wrapper
#[inline] fn new(stream: R, crypter: Crypter) -> Self
{
Self{stream, crypter, buffer: BufferVec::new()}
}
/// Create an encrypting Chacha Source stream wrapper
pub fn encrypt(stream: R, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::encrypter(key, iv)?))
}
/// Create a decrypting Chacha Source stream wrapper
pub fn decrypt(stream: R, key: Key, iv: IV) -> Result<Self, Error>
{
Ok(Self::new(stream, cha::decrypter(key, iv)?))
}
/// Consume into the inner stream
#[inline] pub fn into_inner(self) -> R
{
self.stream
}
/// Consume into the inner stream and crypter
#[inline] pub fn into_parts(self) -> (R, Crypter)
{
(self.stream, self.crypter)
}
/// Create a source from a stream and a crypter
///
/// The counterpart to `into_parts()`.
#[inline] pub fn from_parts(stream: R, crypter: Crypter) -> Self
{
Self::new(stream, crypter)
}
/// The crypter of this instance
#[inline] pub fn crypter(&self) -> &Crypter
{
&self.crypter
}
/// The crypter of this instance
#[inline] pub fn crypter_mut(&mut self) -> &mut Crypter
{
&mut self.crypter
}
/// The inner stream
#[inline] pub fn inner(&self) -> &R
{
&self.stream
}
/// The inner stream
#[inline] pub fn inner_mut(&mut self) -> &mut R
{
&mut self.stream
}
/// Clear the internal buffer while keeping it allocated for further use.
///
/// This does not affect operations at all, all it does is 0 out the left-over temporary buffer from the last operation(s).
#[inline]
pub fn prune(&mut self)
{
#[cfg(feature="explicit_clear")]
{
bytes::explicit_prune(&mut self.buffer[..]);
return;
}
#[cfg(not(feature="explicit_clear"))]
unsafe {
std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len());
}
}
}
//When implementing `poll`, we check if buffer is empty on poll, and if it isn't, poll backing stream to write it. Then, clear buffer after `Poll::Ready` on backing stream's write.
impl<R: AsyncRead> AsyncRead for Source<R>
{
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut [u8]) -> Poll<Result<usize, io::Error>> {
let this = self.project();
if this.buffer.is_empty() {
this.buffer.resize(buf.len(), 0);
}
debug_assert_eq!(buf.len(), this.buffer.len());
let poll = this.stream.poll_read(cx, &mut this.buffer[..]);
match poll {
Poll::Ready(Ok(read)) => {
// Data read, perform transform.
let n = transform(this.crypter, &this.buffer[..read], &mut buf[..read])?;
debug_assert_eq!(n, read);
// Reset buffer size to 0, so we know the next call will be on a new buffer, and we can resize it to the correct size again
if cfg!(feature="explicit_clear") {
bytes::explicit_prune(&mut this.buffer[..]);
} // XXX: Should we blank the buffer here? Or is just a `.clear()` alright?
this.buffer.clear();
Poll::Ready(Ok(n))
},
other => other
}
}
}
Loading…
Cancel
Save