diff options
author | Bryan Newbold <bnewbold@robocracy.org> | 2017-10-17 10:46:24 -0700 |
---|---|---|
committer | Bryan Newbold <bnewbold@robocracy.org> | 2017-10-17 10:46:24 -0700 |
commit | b30dcccd4f6e3098e6bba7cc58ee12e4196a54e4 (patch) | |
tree | 88bc457a6e8a02fbbee63b11e9a4eacb79a1a4d4 | |
parent | 884baebe2d14a1326998be7704beb42564d1e9fd (diff) | |
download | geniza-b30dcccd4f6e3098e6bba7cc58ee12e4196a54e4.tar.gz geniza-b30dcccd4f6e3098e6bba7cc58ee12e4196a54e4.zip |
initial work on register directories
-rw-r--r-- | Cargo.lock | 1 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/bin/geniza-register.rs | 68 | ||||
-rw-r--r-- | src/bin/geniza-sleep.rs | 4 | ||||
-rw-r--r-- | src/lib.rs | 274 |
5 files changed, 341 insertions, 7 deletions
@@ -6,6 +6,7 @@ dependencies = [ "env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "integer-encoding 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.17 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -9,3 +9,4 @@ integer-encoding = "^1.0" clap = "2" error-chain = "0.7" env_logger = "0.3" +rand = "0.3" diff --git a/src/bin/geniza-register.rs b/src/bin/geniza-register.rs new file mode 100644 index 0000000..6962be1 --- /dev/null +++ b/src/bin/geniza-register.rs @@ -0,0 +1,68 @@ + +#[macro_use] +extern crate clap; + +extern crate geniza; + +// TODO: more careful import +use geniza::*; +use std::path::Path; + +use clap::{App, SubCommand}; + +fn run() -> Result<()> { + + let matches = App::new("geniza-register") + .version(env!("CARGO_PKG_VERSION")) + .subcommand(SubCommand::with_name("info") + .about("Reads a SLEEP dir register and shows some basic metadata") + .arg_from_usage("<DIR> 'directory containing files'") + .arg_from_usage("<prefix> 'prefix for each data file'")) + .subcommand(SubCommand::with_name("create") + .about("Creates an SLEEP directory register (with header)") + .arg_from_usage("<DIR> 'directory containing files'") + .arg_from_usage("<prefix> 'prefix for each data file'")) + .get_matches(); + + + match matches.subcommand() { + ("info", Some(subm)) => { + let dir = Path::new(subm.value_of("DIR").unwrap()); + let prefix = subm.value_of("prefix").unwrap(); + let sdr = SleepDirRegister::open(dir, prefix, false)?; + //debug!(println!("{:?}", sdr)); + println!("Entry count: {}", sdr.len()?); + }, + ("create", Some(subm)) => { + let dir = Path::new(subm.value_of("DIR").unwrap()); + let prefix = subm.value_of("prefix").unwrap(); + SleepDirRegister::create(dir, prefix)?; + println!("Done!"); + }, + _ => { + println!("Missing or unimplemented command!"); + println!("{}", matches.usage()); + ::std::process::exit(-1); + }, + } + Ok(()) +} + +// TODO: is there a shorter error_chain 'main()' to use here? +fn main() { + if let Err(ref e) = run() { + println!("error: {}", e); + + for e in e.iter().skip(1) { + println!("caused by: {}", e); + } + + // The backtrace is not always generated. Try to run this example + // with `RUST_BACKTRACE=1`. + if let Some(backtrace) = e.backtrace() { + println!("backtrace: {:?}", backtrace); + } + + ::std::process::exit(1); + } +} diff --git a/src/bin/geniza-sleep.rs b/src/bin/geniza-sleep.rs index b7bb88e..5061dc9 100644 --- a/src/bin/geniza-sleep.rs +++ b/src/bin/geniza-sleep.rs @@ -37,7 +37,7 @@ fn run() -> Result<()> { println!("Magic: 0x{:X}", sf.get_magic()); println!("Algorithm: '{}'", sf.get_algorithm().or(Some("".to_string())).unwrap()); println!("Entry Size (bytes): {}", sf.get_entry_size()); - println!("Entry count: {}", sf.length()?); + println!("Entry count: {}", sf.len()?); }, ("create", Some(subm)) => { let path = Path::new(subm.value_of("FILE").unwrap()); @@ -57,7 +57,7 @@ fn run() -> Result<()> { ("read-all", Some(subm)) => { let path = Path::new(subm.value_of("FILE").unwrap()); let mut sf = SleepFile::open(path, false)?; - for i in 0..sf.length()? { + for i in 0..sf.len()? { println!("{}: {:?}", i, sf.read(i)); } }, @@ -2,6 +2,8 @@ #[macro_use] extern crate error_chain; extern crate integer_encoding; +extern crate crypto; +extern crate rand; use std::io::prelude::*; use std::io::SeekFrom; @@ -9,6 +11,10 @@ use std::path::Path; use std::fs::File; use integer_encoding::FixedInt; use std::fs::OpenOptions; +use crypto::ed25519; +use crypto::blake2b::Blake2b; +use crypto::digest::Digest; +use rand::Rng; mod errors { // Create the Error, ErrorKind, ResultExt, and Result types @@ -27,7 +33,7 @@ pub trait SleepStorage { fn get_entry_size(&self) -> u16; fn read(&mut self, index: u64) -> Result<Vec<u8>>; fn write(&mut self, index: u64, data: &[u8]) -> Result<()>; - fn length(&self) -> Result<u64>; + fn len(&self) -> Result<u64>; } #[derive(Debug)] @@ -43,6 +49,7 @@ impl SleepFile { // TODO: 'from' pre-existing File + // Something here to allow paths as references or actual Path... pub fn open(path: &Path, writable: bool) -> Result<SleepFile> { let mut f = OpenOptions::new() @@ -63,12 +70,15 @@ impl SleepFile { let algorithm_name = if algo_len == 0 { None } else { Some(String::from_utf8_lossy(&header[8..(8+(algo_len as usize))]).into_owned()) }; - Ok(SleepFile { + let sf = SleepFile { file: f, magic: u32::from_be(FixedInt::decode_fixed(&header[0..4])), entry_size: u16::from_be(FixedInt::decode_fixed(&header[5..7])), algorithm_name: algorithm_name, - }) + }; + // call length for consistency checks + sf.len()?; + Ok(sf) } pub fn create(path: &Path, magic: u32, entry_size: u16, algo: Option<String>) -> Result<SleepFile> { @@ -113,7 +123,7 @@ impl SleepStorage for SleepFile { fn read(&mut self, index: u64) -> Result<Vec<u8>> { let entry_size = self.entry_size as usize; - if index + 1 > self.length()? { + if index + 1 > self.len()? { return Err("Tried to read beyond end of SLEEP file".into()); } let mut entry = vec![0; entry_size]; @@ -132,7 +142,7 @@ impl SleepStorage for SleepFile { Ok(()) } - fn length(&self) -> Result<u64> { + fn len(&self) -> Result<u64> { let length = self.file.metadata()?.len(); if length < 32 || (length - 32) % (self.entry_size as u64) != 0 { return Err("Bad SLEEP file: missing header or not multiple of entry_size".into()); @@ -140,3 +150,257 @@ impl SleepStorage for SleepFile { return Ok((length - 32) / (self.entry_size as u64)) } } + + +// Abstract access to hypercore register +pub trait HyperRegister { + fn has(&self, index: u64) -> Result<bool>; + fn has_all(&self) -> Result<bool>; + fn has_range(&self, start: u64, end: u64) -> Result<bool>; + fn get(&mut self, index: u64) -> Result<Vec<u8>>; + fn append(&mut self, data: &[u8]) -> Result<u64>; + fn len(&self) -> Result<u64>; + fn len_bytes(&self) -> Result<u64>; + fn verify(&self) -> Result<()>; + fn check(&self) -> Result<()>; + fn writable(&self) -> bool; +} + +#[derive(Debug)] +pub struct SleepDirRegister { + tree_sleep: SleepFile, + sign_sleep: SleepFile, + bitfield_sleep: SleepFile, + data_file: Option<File>, + // Except, these should be Ed25519 keys, not bytes + pub_key: Vec<u8>, + secret_key: Option<Vec<u8>>, +} + +impl SleepDirRegister { + + pub fn open(directory: &Path, prefix: &str, writable: bool) -> Result<SleepDirRegister> { + // read public key from disk + let mut pub_key: Vec<u8> = vec![]; + { + let mut key_file = OpenOptions::new() + .read(true) + .write(false) + .open(directory.join(Path::new(&(prefix.to_owned() + ".key"))))?; + // TODO: check key length? + key_file.read_to_end(&mut pub_key)?; + } + let data_path = &(prefix.to_owned() + ".data"); + let data_path = Path::new(data_path); + let data_file = if data_path.is_file() { + Some(OpenOptions::new() + .read(true) + .write(writable) + .open(data_path)?) + } else { + None + }; + let tree_sleep = SleepFile::open( + &directory.join(Path::new(&(prefix.to_owned() + ".tree"))), writable)?; + let sign_sleep = SleepFile::open( + &directory.join(Path::new(&(prefix.to_owned() + ".signatures"))), writable)?; + let bitfield_sleep = SleepFile::open( + &directory.join(Path::new(&(prefix.to_owned() + ".bitfield"))), writable)?; + let sf = SleepDirRegister { + tree_sleep, + sign_sleep, + bitfield_sleep, + data_file, + pub_key, + secret_key: None, + }; + sf.check()?; + Ok(sf) + } + + pub fn create(directory: &Path, prefix: &str) -> Result<SleepDirRegister> { + // TODO: audit this for crypto strength... is rand appropriate? + let mut rand_seed = vec![0; 32]; + let mut rng = rand::OsRng::new()?; + rng.fill_bytes(&mut rand_seed); + let (secret_key, pub_key) = ed25519::keypair(&rand_seed); + println!("{:?}", directory.join(Path::new(&(prefix.to_owned() + ".key")))); + { + let mut key_file = OpenOptions::new() + .write(true) + .create_new(true) + .open(directory.join(Path::new(&(prefix.to_owned() + ".key"))))?; + key_file.write_all(&pub_key)?; + } + let data_file = OpenOptions::new() + .read(true) + .write(true) + .create_new(true) + .open(directory.join(Path::new(&(prefix.to_owned() + ".data"))))?; + let tree_sleep = SleepFile::create( + &directory.join(Path::new(&(prefix.to_owned() + ".tree"))), + 0x05025702, + 40, + Some("BLAKE2b".to_string()))?; + let sign_sleep = SleepFile::create( + &directory.join(Path::new(&(prefix.to_owned() + ".signatures"))), + 0x05025701, + 65, + Some("Ed25519".to_string()))?; + let bitfield_sleep = SleepFile::create( + &directory.join(Path::new(&(prefix.to_owned() + ".bitfield"))), + 0x05025700, + 3328, + None)?; + let sf = SleepDirRegister { + tree_sleep, + sign_sleep, + bitfield_sleep, + data_file: Some(data_file), + pub_key: pub_key.to_vec(), + secret_key: Some(secret_key.to_vec()), + }; + sf.check()?; + Ok(sf) + } +} + +impl HyperRegister { + + fn hash_leaf(data: &[u8]) -> [u8; 40] { + let mut buf = [0; 40]; + u64::to_be(data.len() as u64) + .encode_fixed(&mut buf[32..40]); + let mut hash = Blake2b::new(32); + hash.input(&[0; 1]); + hash.input(&buf[32..40]); + hash.input(&data); + hash.result(&mut buf[0..32]); + buf + } + + fn hash_parent(lhash: &[u8; 40], rhash: &[u8; 40]) -> [u8; 40] { + let mut buf = [0; 40]; + // TODO: check overflow + let sum_size = u64::from_be(FixedInt::decode_fixed(&lhash[32..40])) + + u64::from_be(FixedInt::decode_fixed(&rhash[32..40])); + u64::to_be(sum_size as u64) + .encode_fixed(&mut buf[32..40]); + + let mut hash = Blake2b::new(32); + hash.input(&[1; 1]); + hash.input(&buf[32..40]); + hash.input(&lhash[..]); + hash.input(&rhash[..]); + hash.result(&mut buf[0..32]); + buf + } + + fn root_nodes(len: u64) -> Vec<u64> { + // Calculates the root notes for a given length + // Basically factorize by powers of 2? + unimplemented!() + } +} + +/* +#[test] +root_index: + 0 -> [] + 1 -> 1 + 2 -> 2 + 3 -> + 8 -> 7 +*/ + +impl HyperRegister for SleepDirRegister { + + fn has(&self, index: u64) -> Result<bool> { + // looks in bitfield + unimplemented!() + } + + fn has_all(&self) -> Result<bool> { + self.has_range(0, self.len()?) + } + + fn has_range(&self, start: u64, end: u64) -> Result<bool> { + assert!(end > start); + for i in start..end { + if !self.has(i)? { + return Ok(false); + } + } + Ok(true) + } + + fn get(&mut self, index: u64) -> Result<Vec<u8>> { + // Do we even have this chunk? + if !self.has(index)? { + return Err("Don't have that chunk".into()); + } + // Get metadata about chunk (offset and length) + // Read chunk + unimplemented!() + } + + fn append(&mut self, data: &[u8]) -> Result<u64> { + let mut data_file = if let Some(ref df) = self.data_file { + df + } else { + return Err("No data file in this register".into()); + }; + let index = self.len(); + // 1. Hash data chunk + // 2. Append data to data file + data_file.seek(SeekFrom::End(0))?; + data_file.write_all(data)?; + // 3. Add hash to tree + // 4. Add signature to signature file + // 5. Update bitfile + unimplemented!() + } + + fn len(&self) -> Result<u64> { + // Length in entry count. + let tree_len = self.tree_sleep.len()?; + if tree_len == 0 { + Ok(0) + } else if tree_len % 2 != 1 { + Err("Even number of tree file SLEEP entries".into()) + } else { + Ok((self.tree_sleep.len()? / 2) + 1) + } + } + + fn len_bytes(&self) -> Result<u64> { + // Total binary size of data file. + let mut data_file = if let Some(ref df) = self.data_file { + df + } else { + return Err("No data file in this register".into()); + }; + // Elaborate version will iterate through tree root nodes. + Ok(data_file.metadata()?.len()) + } + + fn verify(&self) -> Result<()> { + unimplemented!() + } + + fn check(&self) -> Result<()> { + /* XXX: + let sign_len = self.sign_sleep.len()?; + let tree_len = self.tree_sleep.len()?; + if tree_len != sign_len * 2 { + return Err("Inconsistent SLEEP file sizes".into()); + } + */ + Ok(()) + } + + fn writable(&self) -> bool { + unimplemented!() + //self.sign_sleep.file.writable() + } +} |