diff --git a/Cargo.lock b/Cargo.lock index a8c97516d..d659c4bd7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,7 @@ version = "1.4.0" dependencies = [ "clippy 0.0.85 (registry+https://github.com/rust-lang/crates.io-index)", "ethabi 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ethcore-devtools 1.4.0", "ethcore-rpc 1.4.0", "ethcore-util 1.4.0", "hyper 0.9.4 (git+https://github.com/ethcore/hyper)", @@ -502,6 +503,7 @@ dependencies = [ "table 0.1.0", "target_info 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "tiny-keccak 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", "using_queue 0.1.0", "vergen 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index 0119b921e..347c8da5f 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -30,7 +30,8 @@ use hyper::Control; use hyper::status::StatusCode; use random_filename; -use util::Mutex; +use util::{Mutex, H256}; +use util::sha3::sha3; use page::LocalPageEndpoint; use handlers::{ContentHandler, AppFetcherHandler, DappHandler}; use endpoint::{Endpoint, EndpointPath, Handler}; @@ -137,10 +138,12 @@ impl AppFetcher { #[derive(Debug)] pub enum ValidationError { - ManifestNotFound, - ManifestSerialization(String), Io(io::Error), Zip(zip::result::ZipError), + InvalidDappId, + ManifestNotFound, + ManifestSerialization(String), + HashMismatch { expected: H256, got: H256, }, } impl From for ValidationError { @@ -198,8 +201,15 @@ impl DappHandler for DappInstaller { fn validate_and_install(&self, app_path: PathBuf) -> Result { trace!(target: "dapps", "Opening dapp bundle at {:?}", app_path); - // TODO [ToDr] Validate file hash - let file = try!(fs::File::open(app_path)); + let mut file = try!(fs::File::open(app_path)); + let hash = try!(sha3(&mut file)); + let dapp_id = try!(self.dapp_id.as_str().parse().map_err(|_| ValidationError::InvalidDappId)); + if dapp_id != hash { + return Err(ValidationError::HashMismatch { + expected: dapp_id, + got: hash, + }); + } // Unpack archive let mut zip = try!(zip::ZipArchive::new(file)); // First find manifest file diff --git a/dapps/src/lib.rs b/dapps/src/lib.rs index 3373f5c58..574c38acf 100644 --- a/dapps/src/lib.rs +++ b/dapps/src/lib.rs @@ -55,11 +55,11 @@ extern crate rand; extern crate ethabi; extern crate jsonrpc_core; extern crate jsonrpc_http_server; +extern crate mime_guess; +extern crate rustc_serialize; extern crate parity_dapps; extern crate ethcore_rpc; extern crate ethcore_util as util; -extern crate mime_guess; -extern crate rustc_serialize; mod endpoint; mod apps; diff --git a/devtools/src/random_path.rs b/devtools/src/random_path.rs index f9c454c30..d58042512 100644 --- a/devtools/src/random_path.rs +++ b/devtools/src/random_path.rs @@ -67,10 +67,18 @@ impl RandomTempPath { } } +impl AsRef for RandomTempPath { + fn as_ref(&self) -> &Path { + self.as_path() + } +} + impl Drop for RandomTempPath { fn drop(&mut self) { - if let Err(e) = fs::remove_dir_all(self.as_path()) { - panic!("Failed to remove temp directory. Here's what prevented this from happening: ({})", e); + if let Err(_) = fs::remove_dir_all(&self) { + if let Err(e) = fs::remove_file(&self) { + panic!("Failed to remove temp directory. Here's what prevented this from happening: ({})", e); + } } } } diff --git a/util/Cargo.toml b/util/Cargo.toml index 3a9505e15..719e4c255 100644 --- a/util/Cargo.toml +++ b/util/Cargo.toml @@ -32,6 +32,7 @@ parking_lot = "0.2.6" using_queue = { path = "using_queue" } table = { path = "table" } ansi_term = "0.7" +tiny-keccak= "1.0" [features] default = [] diff --git a/util/src/lib.rs b/util/src/lib.rs index 91c4e7d50..f8dc34af8 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -112,6 +112,7 @@ extern crate parking_lot; pub extern crate using_queue; pub extern crate table; extern crate ansi_term; +extern crate tiny_keccak; pub mod bloom; pub mod standard; diff --git a/util/src/sha3.rs b/util/src/sha3.rs index d2a071759..0dcde2ccb 100644 --- a/util/src/sha3.rs +++ b/util/src/sha3.rs @@ -17,7 +17,9 @@ //! Wrapper around tiny-keccak crate. extern crate sha3 as sha3_ext; +use std::io; use std::mem::uninitialized; +use tiny_keccak::Keccak; use bytes::{BytesConvertable, Populatable}; use hash::{H256, FixedHash}; use self::sha3_ext::*; @@ -64,12 +66,56 @@ impl Hashable for T where T: BytesConvertable { } } -#[test] -fn sha3_empty() { - assert_eq!([0u8; 0].sha3(), SHA3_EMPTY); -} -#[test] -fn sha3_as() { - assert_eq!([0x41u8; 32].sha3(), From::from("59cad5948673622c1d64e2322488bf01619f7ff45789741b15a9f782ce9290a8")); +/// Calculate SHA3 of given stream. +pub fn sha3(r: &mut R) -> Result { + let mut output = [0u8; 32]; + let mut input = [0u8; 1024]; + let mut sha3 = Keccak::new_keccak256(); + + // read file + loop { + let some = try!(r.read(&mut input)); + if some == 0 { + break; + } + sha3.update(&input[0..some]); + } + + sha3.finalize(&mut output); + Ok(output.into()) } +#[cfg(test)] +mod tests { + use std::fs; + use std::io::Write; + use super::*; + + #[test] + fn sha3_empty() { + assert_eq!([0u8; 0].sha3(), SHA3_EMPTY); + } + #[test] + fn sha3_as() { + assert_eq!([0x41u8; 32].sha3(), From::from("59cad5948673622c1d64e2322488bf01619f7ff45789741b15a9f782ce9290a8")); + } + + #[test] + fn should_sha3_a_file() { + // given + use devtools::RandomTempPath; + let path = RandomTempPath::new(); + // Prepare file + { + let mut file = fs::File::create(&path).unwrap(); + file.write_all(b"something").unwrap(); + } + + let mut file = fs::File::open(&path).unwrap(); + // when + let hash = sha3(&mut file).unwrap(); + + // then + assert_eq!(format!("{:?}", hash), "68371d7e884c168ae2022c82bd837d51837718a7f7dfb7aa3f753074a35e1d87"); + } +}