diff --git a/Cargo.lock b/Cargo.lock index 68be4c2..0cb224d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,7 @@ dependencies = [ "base64", "getrandom", "openssl", + "rustc_version", "smallvec", ] @@ -113,6 +114,30 @@ version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "smallvec" version = "1.6.1" diff --git a/Cargo.toml b/Cargo.toml index db95376..1cb501f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,21 @@ [package] name = "chacha20" description = "chacha20_poly1305 encryption tool" -version = "1.0.2" +version = "1.1.0" authors = ["Avril "] edition = "2018" license = "gpl-3.0-or-later" +[features] + +# Explicitly clear buffers and cache after use +explicit_clear = [] + [dependencies] base64 = "0.13" getrandom = "0.2" openssl = "0.10" smallvec = {version = "1.6", features=["union"]} + +[build-dependencies] +rustc_version = "0.2" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..6399463 --- /dev/null +++ b/build.rs @@ -0,0 +1,24 @@ + +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Set cfg flags depending on release channel + match version_meta().unwrap().channel { + Channel::Stable => { + println!("cargo:rustc-cfg=stable"); + } + Channel::Beta => { + println!("cargo:rustc-cfg=beta"); + } + Channel::Nightly => { + println!("cargo:rustc-cfg=nightly"); + } + Channel::Dev => { + println!("cargo:rustc-cfg=dev"); + } + } +} diff --git a/src/main.rs b/src/main.rs index aeccbda..67606ca 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ -//#![feature(test)] +#![cfg_attr(nightly, feature(asm))] + #![allow(dead_code)] diff --git a/src/stream.rs b/src/stream.rs index 62c9bf6..df96f91 100644 --- a/src/stream.rs +++ b/src/stream.rs @@ -93,6 +93,36 @@ where W: Write { &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). + pub fn prune(&mut self) + { + #[cfg(feature="explicit_clear")] + { + use std::ffi::c_void; + extern "C" { + fn explicit_bzero(_: *mut c_void, _:usize); + } + unsafe { + explicit_bzero(self.buffer.as_mut_ptr() as *mut c_void, self.buffer.len()); + + #[cfg(nightly)] + if cfg!(target_arch = "x86_64") || cfg!(target_arch = "x86"){ + asm!( + "clflush [{}]", + in(reg) self.buffer.as_mut_ptr() + ); + } + } + return; + } + #[cfg(not(feature="explicit_clear"))] + unsafe { + std::ptr::write_bytes(self.buffer.as_mut_ptr(), 0, self.buffer.len()); + } + } /// Perform the cipher transform on this input to the inner buffer, returning the number of bytes updated. fn transform(&mut self, buf: &[u8]) -> Result @@ -123,6 +153,8 @@ impl Write for Sink 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() @@ -180,6 +212,41 @@ mod tests } 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]); + } }