diff --git a/Cargo.lock b/Cargo.lock index 959d337..01fcc95 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -47,9 +47,10 @@ version = "0.1.0" dependencies = [ "base64", "getrandom 0.2.2", - "hex-literal 0.3.1", + "hex-literal", "khash", "openssl", + "smallvec", ] [[package]] @@ -114,39 +115,20 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] -[[package]] -name = "hex-literal" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "961de220ec9a91af2e1e5bd80d02109155695e516771762381ef8581317066e0" -dependencies = [ - "hex-literal-impl", - "proc-macro-hack", -] - [[package]] name = "hex-literal" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5af1f635ef1bc545d78392b136bfe1c9809e029023c84a3638a864a10b8819c8" -[[package]] -name = "hex-literal-impl" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "853f769599eb31de176303197b7ba4973299c38c7a7604a6bc88c3eef05b9b46" -dependencies = [ - "proc-macro-hack", -] - [[package]] name = "khash" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "193f6499a7299fa3f470fac8c79bbd148370f6085b0c2fa4f32a03f517f7a6e7" +checksum = "c266778a8846ff4ebcdeb6bbd7ac651d2c02a2cabb53fbc7198398dcfcd98daa" dependencies = [ "getrandom 0.1.16", - "hex-literal 0.2.1", + "hex-literal", "rustc_version", "sha2", ] @@ -202,12 +184,6 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" -[[package]] -name = "proc-macro-hack" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" - [[package]] name = "rustc_version" version = "0.2.3" @@ -245,6 +221,12 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "smallvec" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e" + [[package]] name = "typenum" version = "1.13.0" diff --git a/Cargo.toml b/Cargo.toml index ebfd2aa..bd71a30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,8 +7,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -base64 = "0.13.0" -getrandom = "0.2.2" -hex-literal = "0.3.1" -khash = {version = "2.0.3", default-features=false} -openssl = "0.10.32" +base64 = "0.13" +getrandom = "0.2" +hex-literal = "0.3" +khash = {version = "2.0.4", default-features=false} +openssl = "0.10" +smallvec = {version = "1.6.1", features=["write", "union"]} diff --git a/src/ext.rs b/src/ext.rs index 10e127c..a8917cd 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -114,3 +114,9 @@ impl + Clone> fmt::Display for HexStringIter self.clone().consume(f) } } + +#[macro_export] macro_rules! prog1 { + ($first:expr, $($rest:expr);+ $(;)?) => { + ($first, $( $rest ),+).0 + } +} diff --git a/src/main.rs b/src/main.rs index 5c58c12..9edcf1b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,11 @@ #[macro_use] extern crate hex_literal; -mod ext; use ext::*; +mod ext; #[macro_use] use ext::*; mod key; mod cha; +mod stream; use key::{Key, IV}; @@ -19,7 +20,10 @@ fn encrypt((key, iv): &(Key, IV), input: impl AsRef<[u8]>) -> Result> {}", (&output[..n]).hex()); + assert!(enc.finalize(&mut output[..n])? == 0); + println!(">> {}", (&output[..n]).hex()); Ok(base64::encode(&output[..n])) } @@ -35,7 +39,11 @@ fn decrypt((key, iv): &(Key, IV), input: impl AsRef) -> Result, ope let n = dec.update(&input[..], &mut output[..])?; eprintln!("(dec) Written {} bytes", n); - dec.finalize(&mut output[..n])?; + + println!(">> {}", (&output[..n]).hex()); + assert!(dec.finalize(&mut output[..n])? == 0); +// assert!(dec.finalize(&mut output[..n])? == 0); + println!(">> {}", (&output[..n]).hex()); output.truncate(n); Ok(output) diff --git a/src/stream.rs b/src/stream.rs new file mode 100644 index 0000000..4c5d0a5 --- /dev/null +++ b/src/stream.rs @@ -0,0 +1,175 @@ +#![allow(dead_code)] + +use super::*; +use key::*; + +use std::io::{self, Write}; +use std::fmt; +use openssl::{ + symm::Crypter, + error::ErrorStack, +}; +use smallvec::SmallVec; + +pub const BUFFER_SIZE: usize = 1024; +pub type Error = ErrorStack; + +/// ChaCha Sink +//#[derive(Debug)] +pub struct Sink +{ + stream: W, + crypter: Crypter, // for chacha, finalize does nothing it seems. we can also call it multiple times. + + buffer: SmallVec<[u8; BUFFER_SIZE]> // used to buffer the operation +} + +impl fmt::Debug for Sink +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result + { + write!(f, "Sink({:?}, ({} buffer cap))", self.stream, self.buffer.capacity()) + } +} + +impl Sink +where W: Write +{ + /// Create a new Chacha Sink stream wrapper + pub fn new(stream: W, crypter: Crypter) -> Self + { + Self{stream, crypter, buffer: SmallVec::new()} + } + + /// Create an encrypting Chacha Sink stream wrapper + pub fn encrypt(stream: W, key: Key, iv: IV) -> Result + { + 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 + { + Ok(Self::new(stream, cha::decrypter(key, iv)?)) + } + + /// Consume into the inner stream + pub fn into_inner(self) -> W + { + self.stream + } + + /// Consume into the inner stream and crypter + pub fn into_parts(self) -> (W, Crypter) + { + (self.stream, self.crypter) + } + + /// The crypter of this instance + pub fn crypter(&self) -> &Crypter + { + &self.crypter + } + + /// The crypter of this instance + pub fn crypter_mut(&mut self) -> &mut Crypter + { + &mut self.crypter + } + + /// The inner stream + pub fn inner(&self) -> &W + { + &self.stream + } + + /// The inner stream + pub fn inner_mut(&mut self) -> &mut W + { + &mut self.stream + } +} + +impl Write for Sink +{ + fn write(&mut self, buf: &[u8]) -> io::Result { + prog1!{ + { + self.buffer.write_all(buf).unwrap(); + let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; + self.crypter.finalize(&mut self.buffer[..n])?; // I don't think this is needed + + self.stream.write(&self.buffer[..n]) + }, + self.buffer.clear(); + } + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + prog1!{ + { + self.buffer.write_all(buf).unwrap(); + let n = self.crypter.update(&buf[..], &mut self.buffer[..])?; + self.crypter.finalize(&mut self.buffer[..n])?; + + self.stream.write_all(&self.buffer[..n]) + }, + self.buffer.clear(); + } + } + #[inline] fn flush(&mut self) -> io::Result<()> { + self.stream.flush() + } +} + +#[cfg(test)] +mod tests +{ + use super::*; + + const INPUT: &'static str = "Hello world!"; + + fn enc_stream(input: impl AsRef<[u8]>, key: Key, iv: IV) -> Sink> + { + 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() + { + 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()); + } +} +