From 141f048d13d4862b6b64fe88c26d3a3bc2834a7e Mon Sep 17 00:00:00 2001 From: shimunn Date: Sat, 7 Sep 2019 21:24:58 +0200 Subject: [PATCH] encrypt using chacha --- Cargo.lock | 36 +++++++++++++++++++++++++++++ Cargo.toml | 3 +++ src/chacha_io.rs | 45 ++++++++++++++++++++++++++++++++++++ src/main.rs | 60 ++++++++++++++++++++++++++++++++++++------------ src/pgp.rs | 30 ++++++++++++------------ 5 files changed, 144 insertions(+), 30 deletions(-) create mode 100644 src/chacha_io.rs diff --git a/Cargo.lock b/Cargo.lock index ac5cd7f..880b084 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -164,12 +164,15 @@ name = "brownpaper" version = "0.1.0" dependencies = [ "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "iron 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "sequoia-openpgp 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sha2 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "snap 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -216,6 +219,17 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "c2-chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "cc" version = "1.0.42" @@ -433,6 +447,11 @@ dependencies = [ "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "httparse" version = "1.3.3" @@ -771,6 +790,11 @@ dependencies = [ "typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ppv-lite86" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "precomputed-hash" version = "0.1.1" @@ -1077,6 +1101,14 @@ dependencies = [ "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "stream-cipher" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "string_cache" version = "0.7.3" @@ -1334,6 +1366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum bzip2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "42b7c3cbf0fa9c1b82308d57191728ca0256cb821220f4e2fd410a72ade26e3b" "checksum bzip2-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6584aa36f5ad4c9247f5323b0a42f37802b37a836f0ad87084d7a33961abe25f" +"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101" "checksum cc 1.0.42 (registry+https://github.com/rust-lang/crates.io-index)" = "a61c7bce55cd2fae6ec8cb935ebd76256c2959a1f95790f6118a441c2cd5b406" "checksum cexpr 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7fa24eb00d5ffab90eaeaf1092ac85c04c64aaf358ea6f84505b8116d24c6af" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" @@ -1361,6 +1394,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum getrandom 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "473a1265acc8ff1e808cd0a1af8cee3c2ee5200916058a2ca113c29f2d903571" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum httparse 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "e8734b0cfd3bc3e101ec59100e101c2eecd19282202e87808b3037b442777a83" "checksum hyper 0.10.15 (registry+https://github.com/rust-lang/crates.io-index)" = "df0caae6b71d266b91b4a83111a61d2b94ed2e2bea024c532b933dcff867e58c" "checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" @@ -1403,6 +1437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0" "checksum pkg-config 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c1d2cfa5a714db3b5f24f0915e74fcdf91d09d496ba61329705dda7774d2af" "checksum plugin 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "1a6a0dc3910bc8db877ffed8e457763b317cf880df4ae19109b9f77d277cf6e0" +"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b" "checksum precomputed-hash 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum proc-macro2 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "175a40b9cf564ce9bf050654633dbf339978706b8ead1a907bb970b63185dd95" @@ -1438,6 +1473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" "checksum snap 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "37877bedec5e9d6b3324b4f99886f118844903a4e7532e5e59c7afd264da7b4f" +"checksum stream-cipher 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" "checksum string_cache 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25d70109977172b127fe834e5449e5ab1740b9ba49fa18a2020f509174f25423" "checksum string_cache_codegen 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eea1eee654ef80933142157fdad9dd8bc43cf7c74e999e369263496f04ff4da" "checksum string_cache_shared 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1884d1bc09741d466d9b14e6d37ac89d6909cbcac41dd9ae982d4d063bbedfc" diff --git a/Cargo.toml b/Cargo.toml index f72d70f..a94c909 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,3 +18,6 @@ byteorder = "1.3.2" chrono = "0.4.9" sequoia-openpgp = "0.9.0" lazy_static = "1.4.0" +c2-chacha = "0.2.2" +sha2 = "0.8.0" +hex = "0.3.2" diff --git a/src/chacha_io.rs b/src/chacha_io.rs new file mode 100644 index 0000000..cd94ee9 --- /dev/null +++ b/src/chacha_io.rs @@ -0,0 +1,45 @@ +use c2_chacha::stream_cipher::{NewStreamCipher, SyncStreamCipher, SyncStreamCipherSeek}; +use c2_chacha::{ChaCha12, ChaCha20}; +use std::convert::TryInto; +use std::io::{Read, Result, Write}; + +pub struct ChaChaReader<'a>(ChaCha20, &'a mut Read); + +impl<'a> ChaChaReader<'a> { + pub fn new(key: &[u8], nonce: &[u8], source: &'a mut Read) -> ChaChaReader<'a> { + ChaChaReader(ChaCha20::new_var(key, nonce).unwrap(), source) + } +} + +impl<'a> Read for ChaChaReader<'a> { + fn read(&mut self, buf: &mut [u8]) -> Result { + let red = self.1.read(buf)?; + self.0.apply_keystream(buf); + Ok(red) + } +} + +pub struct ChaChaWriter<'a>(ChaCha20, &'a mut Write); + +impl<'a> ChaChaWriter<'a> { + pub fn new(key: &[u8], nonce: &[u8], sink: &'a mut Write) -> ChaChaWriter<'a> { + ChaChaWriter(ChaCha20::new_var(key, nonce).unwrap(), sink) + } +} + +impl<'a> Write for ChaChaWriter<'a> { + fn write(&mut self, buf: &[u8]) -> Result { + let mut cipher_text = [0u8; 256]; + let mut written = 0usize; + for chunk in buf.chunks(cipher_text.len()) { + cipher_text[0..chunk.len()].copy_from_slice(&chunk); + self.0.apply_keystream(&mut cipher_text[0..chunk.len()]); + written += self.1.write(&cipher_text[0..chunk.len()])?; + } + Ok(written) + } + + fn flush(&mut self) -> Result<()> { + self.1.flush() + } +} diff --git a/src/main.rs b/src/main.rs index e40a324..1eb9cce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,10 +6,12 @@ extern crate rand; extern crate sequoia_openpgp as openpgp; extern crate snap; +mod chacha_io; mod pgp; use crate::pgp::KnownKeys; use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; +use chacha_io::{ChaChaReader, ChaChaWriter}; use chrono::*; use core::cell::RefCell; use iron::method::Method; @@ -17,7 +19,9 @@ use iron::modifiers::Redirect; use iron::prelude::*; use iron::url::Url; use rand::Rng; +use sha2::{Digest, Sha256}; use std::borrow::BorrowMut; +use std::convert::TryInto; use std::fs; use std::fs::File; use std::io; @@ -46,7 +50,7 @@ impl<'a> Snippet<'a> { Snippet::new( &rand::thread_rng() .gen_ascii_chars() - .take(6) + .take(8) .collect::(), storage, ) @@ -59,13 +63,30 @@ impl<'a> Snippet<'a> { } } + pub fn file_id(&self) -> String { + SnippetStorage::file_id(&self.id) + } + + pub fn passphrase(&self) -> ([u8; 8], [u8; 32]) { + let mut hasher = Sha256::new(); + hasher.input(self.id.as_bytes()); + let res = hasher.result(); + let nonce: [u8; 8] = res[0..8].try_into().unwrap(); + let mut hasher = Sha256::new(); + hasher.input(self.id.as_bytes()); + hasher.input(b"pass"); + let pass: [u8; 32] = res[0..32].try_into().unwrap(); + (nonce, pass) + } + pub fn path(&self) -> PathBuf { - self.storage.directory.join(&self.id) + self.storage.directory.join(&self.file_id()) } pub fn metadata(&self) -> Result { let mut file = File::open(self.path())?; - self.metadata_via_handle(&mut file) + let (nonce, key) = self.passphrase(); + self.metadata_via_handle(&mut ChaChaReader::new(&key, &nonce, &mut file)) } fn metadata_via_handle(&self, hdl: &mut impl Read) -> Result { @@ -83,38 +104,42 @@ impl<'a> Snippet<'a> { fn contents(&self) -> Result { let mut file = File::open(self.path())?; - let meta = self.metadata_via_handle(&mut file)?; + let (nonce, key) = self.passphrase(); + let mut reader = ChaChaReader::new(&key, &nonce, &mut file); + let meta = self.metadata_via_handle(&mut reader)?; fn read_string(r: &mut impl Read) -> Result { let mut text = String::new(); r.read_to_string(&mut text)?; Ok(text) } - dbg!((&meta.compression, &meta.created)); + dbg!((&meta.compression, &meta.created, self.file_id())); match meta.compression { Some(ref comp) if comp == "snap" => { - let mut r = snap::Reader::new(&mut file); + let mut r = snap::Reader::new(&mut reader); read_string(&mut r) } - _ => read_string(&mut file), + _ => read_string(&mut reader), } } fn write(self, content: &str) -> Result, io::Error> { let mut file = File::create(self.path())?; - file.write_i64::(Utc::now().timestamp())?; + let (nonce, key) = self.passphrase(); + let mut writer = ChaChaWriter::new(&key, &nonce, &mut file); + writer.write_i64::(Utc::now().timestamp())?; let comp = if content.len() > 2048 { Some("snap") } else { None }; - file.write_u16::(comp.map(|s| s.len() as u16).unwrap_or(0u16))?; - file.write(comp.map(|s| s.as_bytes()).unwrap_or(&[0u8; 0]))?; + writer.write_u16::(comp.map(|s| s.len() as u16).unwrap_or(0u16))?; + writer.write(comp.map(|s| s.as_bytes()).unwrap_or(&[0u8; 0]))?; match comp { Some(ref comp) if comp == &"snap" => { - let mut w = snap::Writer::new(&mut file); + let mut w = snap::Writer::new(&mut writer); w.write_all(content.as_bytes())? } - _ => file.write_all(content.as_bytes())?, + _ => writer.write_all(content.as_bytes())?, }; Ok(Snippet::new(&self.id, self.storage)) } @@ -126,9 +151,13 @@ impl<'a> SnippetStorage<'a> { directory: directory, } } - + pub fn file_id(id: &str) -> String { + let mut hasher = Sha256::new(); + hasher.input(id.as_bytes()); + hex::encode(&hasher.result()[0..12]) + } fn has(&self, id: &str) -> bool { - self.directory.join(id).exists() + self.directory.join(Self::file_id(id)).exists() } fn open(&self, id: &str) -> Option { @@ -174,7 +203,8 @@ fn handle(req: &mut Request) -> IronResult { .map_err(|err| IronError::new(err, "Invalid utf8"))? }; let b_text = KNOWN_KEYS - .lock().unwrap()//.map_err(|_| IronError::new(std::error::Error::from("Mutex Err"), "PGP Context unavailable"))? + .lock() + .unwrap() //.map_err(|_| IronError::new(std::error::Error::from("Mutex Err"), "PGP Context unavailable"))? .verify(pgp_text.as_bytes()) .map_err(|err| IronError::new(err, "Untrusted signature"))?; let text = String::from_utf8(b_text).unwrap(); diff --git a/src/pgp.rs b/src/pgp.rs index 6e92a2f..2855e52 100644 --- a/src/pgp.rs +++ b/src/pgp.rs @@ -1,18 +1,16 @@ -use std::io; -use std::fs; -use std::fs::{File}; -use std::path::Path; -use std::io::prelude::*; +use openpgp::parse::stream::*; use openpgp::parse::Parse; use openpgp::*; -use openpgp::parse::stream::*; - +use std::fs; +use std::fs::File; +use std::io; +use std::io::prelude::*; +use std::path::Path; pub struct KnownKeys { - keys: Vec + keys: Vec, } impl VerificationHelper for &KnownKeys { - fn get_public_keys(&mut self, _ids: &[KeyID]) -> Result> { Ok(self.keys.clone()) } @@ -33,19 +31,21 @@ impl KnownKeys { println!("Fingerprint: {}", tpk.fingerprint()); keys.push(tpk); } - Ok(KnownKeys{ - keys: keys - }) + Ok(KnownKeys { keys: keys }) } pub fn verify(&mut self, r: impl Read) -> io::Result> { let mut content = Vec::with_capacity(2048); let helper = &*self; - let mut v = Verifier::<&KnownKeys>::from_reader(r, helper, None).map_err(|e| dbg!(e)).unwrap(); + let mut v = Verifier::<&KnownKeys>::from_reader(r, helper, None) + .map_err(|e| dbg!(e)) + .unwrap(); if v.read_to_end(&mut content).is_err() { - return Err(io::Error::new(io::ErrorKind::InvalidData, "Signature Mismatch")); + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "Signature Mismatch", + )); } Ok(content) } } -