diff --git a/Cargo.toml b/Cargo.toml index c091c78..7355dba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [features] -default = ["smallvec", "async"] +default = ["smallvec"] # Enable async version with tokio v2.0 AsyncRead/AsyncWrite. async = ["tokio", "pin-project"] diff --git a/src/cha.rs b/src/cha.rs index 9395f10..30818dc 100644 --- a/src/cha.rs +++ b/src/cha.rs @@ -7,7 +7,9 @@ use openssl::{ }; use crate::key::{Key, IV}; +/// Size of the key used for the cipher pub const KEY_SIZE: usize = 32; +/// Size of the IV used for the cipher pub const IV_SIZE: usize = 12; static NEW_CIPHER: fn() -> Cipher = Cipher::chacha20_poly1305; @@ -31,7 +33,7 @@ static NEW_CIPHER: fn() -> Cipher = Cipher::chacha20_poly1305; ) } -/// Generate a random key and IV. +/// Generate a random key and IV for the chacha20_poly1305 cipher #[inline(always)] pub fn keygen() -> (Key, IV) { (Key::new(), IV::new()) diff --git a/src/ext.rs b/src/ext.rs index 4bbaf41..1c1f277 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -115,8 +115,10 @@ impl + Clone> fmt::Display for HexStringIter } } +/* #[macro_export] macro_rules! prog1 { ($first:expr, $($rest:expr);+ $(;)?) => { ($first, $( $rest ),+).0 } } +*/ diff --git a/src/key.rs b/src/key.rs index f53734c..6a49f1c 100644 --- a/src/key.rs +++ b/src/key.rs @@ -1,3 +1,5 @@ +//! Key and IV structures for the cipher + use getrandom::getrandom; use std::{fmt, str}; pub use crate::cha::{ @@ -6,20 +8,48 @@ pub use crate::cha::{ }; use crate::ext::*; +/// A 32 byte key for the chacha20_poly1305 cipher +/// +/// # Generation +/// You can generate a random key with `Key::new()`. +/// To create a key structure from bytes, you can use `Key::from_bytes()` if the size of the buffer is exact, or you can write to an empty `Key` as it implements `Default`. +/// ``` +/// # use chacha20stream::{Key, key::KEY_SIZE}; +/// # let key_bytes = [0u8; 32]; +/// let mut key = Key::default(); +/// key.as_mut().copy_from_slice(&key_bytes[..KEY_SIZE]); +/// ``` +/// +/// You can also generate a random key/IV pair with `chacha20stream::keygen()`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Default)] #[repr(transparent)] pub struct Key([u8; KEY_SIZE]); +/// A 12 byte IV for the chacha20_poly1305 cipher +/// +/// # Generation +/// You can generate a random IV with `IV::new()`. +/// To create an IV structure from bytes, you can use `IV::from_bytes()` if the size of the buffer is exact, or you can write to an empty `IV` as it implements `Default`. +/// ``` +/// # use chacha20stream::{IV, key::IV_SIZE}; +/// # let iv_bytes = [0u8; 12]; +/// let mut iv = IV::default(); +/// iv.as_mut().copy_from_slice(&iv_bytes[..IV_SIZE]); +/// ``` +/// +/// You can also generate a random key/IV pair with `chacha20stream::keygen()`. #[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, Default)] #[repr(transparent)] pub struct IV([u8; IV_SIZE]); impl Key { + /// Construct a `Key` from an exact length (32 bytes) buffer. #[inline] pub fn from_bytes(k: [u8; KEY_SIZE]) -> Self { Self(k) } + /// Create a new random 32 byte chacha20_poly1305 `Key`. pub fn new() -> Self { let mut output = [0u8; KEY_SIZE]; @@ -31,10 +61,12 @@ impl Key impl IV { + /// Construct a `IV` from an exact length (12 bytes) buffer. #[inline] pub fn from_bytes(k: [u8; IV_SIZE]) -> Self { Self(k) } + /// Create a new random 12 byte chacha20_poly1305 `IV`. pub fn new() -> Self { let mut output = [0u8; IV_SIZE]; diff --git a/src/lib.rs b/src/lib.rs index da3fbc1..c0293bd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,43 @@ +/*! +# chacha20_poly1305 stream wrapper +Contains a writable stream that wraps another, applying the chacha20_poly1305 cipher to the input before writing for either encryption or decryption. + +## Examples +Encrypt a message to an in-memory buffer. +``` +# use chacha20stream::Sink; +# use std::io::Write; +// Generate random key and IV for the operations. +let (key, iv) = chacha20stream::keygen(); + +let input = "Hello world!"; + +let mut sink = Sink::encrypt(Vec::new(), key, iv).expect("Failed to create encryptor"); +sink.write_all(input.as_bytes()).unwrap(); +sink.flush().unwrap(); // `flush` also clears the in-memory buffer if there is left over data in it. + +let output_encrypted = sink.into_inner(); +``` +Decrypting a message: +``` +# use chacha20stream::{Sink, Key, IV}; +# use std::io::{self, Write}; +fn decrypt_message_to(output: &mut W, encrypted: &[u8], key: Key, iv: IV) -> io::Result<()> +{ + let mut sink = Sink::decrypt(output, key, iv)?; + sink.write_all(&encrypted[..])?; + sink.flush().unwrap(); // `flush` also clears the in-memory buffer if there is left over data in it. + + Ok(()) +} +``` + +# Features +* **smallvec** - Use `smallvec` crate to store the in-memory buffer on the stack if it's smalle enough (*default*) +* **async** - Enable `AsyncSink` with tokio 0.2 `AsyncWrite` +* **explicit_clear** - Explicitly clear in-memory buffer after operations. +*/ + #![cfg_attr(nightly, feature(asm))] #![allow(dead_code)] diff --git a/src/stream.rs b/src/stream.rs index 1b893c8..989d59b 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -10,6 +10,7 @@ use openssl::{ error::ErrorStack, }; +/// Size of the in-structure buffer #[cfg(feature="smallvec")] pub const BUFFER_SIZE: usize = 32; @@ -22,6 +23,34 @@ 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. diff --git a/src/stream_async.rs b/src/stream_async.rs index 4b5bfbc..bca460f 100644 --- a/src/stream_async.rs +++ b/src/stream_async.rs @@ -14,6 +14,7 @@ use openssl::{ error::ErrorStack, }; +/// Size of the in-structure buffer #[cfg(feature="smallvec")] pub const BUFFER_SIZE: usize = 32; @@ -26,6 +27,39 @@ pub type Error = ErrorStack; /// Async ChaCha Sink /// +/// # Encryption +/// To create an encrypting wrapper stream: +/// ``` +/// # use chacha20stream::AsyncSink; +/// # use tokio::prelude::*; +/// # let (key, iv) = chacha20stream::keygen(); +/// # let mut backing_stream = Vec::new(); +/// # async move { +/// let mut stream = AsyncSink::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().await.unwrap(); +/// # }; +/// ``` +/// +/// # Decryption +/// To create a decrypting wrapper stream: +/// ``` +/// # use chacha20stream::AsyncSink; +/// # use tokio::prelude::*; +/// # let (key, iv) = chacha20stream::keygen(); +/// # let mut backing_stream = Vec::new(); +/// # async move { +/// let mut stream = AsyncSink::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().await.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.