diff --git a/include/librustzcash.h b/include/librustzcash.h index f5d7d1e..adab766 100644 --- a/include/librustzcash.h +++ b/include/librustzcash.h @@ -20,8 +20,11 @@ extern "C" { /// paths as necessary. Only called once. void librustzcash_init_zksnark_params( const char* spend_path, + const char* spend_hash, const char* output_path, - const char* sprout_path + const char* output_hash, + const char* sprout_path, + const char* sprout_hash ); /// Validates the provided Equihash solution against diff --git a/src/hashreader.rs b/src/hashreader.rs new file mode 100644 index 0000000..a422d52 --- /dev/null +++ b/src/hashreader.rs @@ -0,0 +1,42 @@ +use blake2_rfc::blake2b::Blake2b; +use std::io::{self, Read}; + +/// Abstraction over a reader which hashes the data being read. +pub struct HashReader { + reader: R, + hasher: Blake2b, +} + +impl HashReader { + /// Construct a new `HashReader` given an existing `reader` by value. + pub fn new(reader: R) -> Self { + HashReader { + reader: reader, + hasher: Blake2b::new(64), + } + } + + /// Destroy this reader and return the hash of what was read. + pub fn into_hash(self) -> String { + let hash = self.hasher.finalize(); + + let mut s = String::new(); + for c in hash.as_bytes().iter() { + s += &format!("{:02x}", c); + } + + s + } +} + +impl Read for HashReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let bytes = self.reader.read(buf)?; + + if bytes > 0 { + self.hasher.update(&buf[0..bytes]); + } + + Ok(bytes) + } +} diff --git a/src/rustzcash.rs b/src/rustzcash.rs index 5eb1dcf..8ca9647 100644 --- a/src/rustzcash.rs +++ b/src/rustzcash.rs @@ -6,6 +6,8 @@ extern crate pairing; extern crate rand; extern crate sapling_crypto; +mod hashreader; + #[macro_use] extern crate lazy_static; @@ -36,7 +38,7 @@ use blake2_rfc::blake2s::Blake2s; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use rand::{OsRng, Rand, Rng}; -use std::io::BufReader; +use std::io::{self, BufReader}; use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t}; use std::ffi::CStr; @@ -110,12 +112,30 @@ fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point::read(&mut spend_fs, false) .expect("couldn't deserialize Sapling spend parameters file"); let output_params = Parameters::::read(&mut output_fs, false) .expect("couldn't deserialize Sapling spend parameters file"); + + // We only deserialize the verifying key for the Sprout parameters, which + // appears at the beginning of the parameter file. The rest is loaded + // during proving time. let sprout_vk = VerifyingKey::::read(&mut sprout_fs) .expect("couldn't deserialize Sprout Groth16 verifying key"); + // There is extra stuff (the transcript) at the end of the parameter file which is + // used to verify the parameter validity, but we're not interested in that. We do + // want to read it, though, so that the BLAKE2b computed afterward is consistent + // with `b2sum` on the files. + let mut sink = io::sink(); + io::copy(&mut spend_fs, &mut sink) + .expect("couldn't finish reading Sapling spend parameter file"); + io::copy(&mut output_fs, &mut sink) + .expect("couldn't finish reading Sapling output parameter file"); + io::copy(&mut sprout_fs, &mut sink) + .expect("couldn't finish reading Sprout groth16 parameter file"); + + if spend_fs.into_hash() != spend_hash { + panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + + if output_fs.into_hash() != output_hash { + panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + + if sprout_fs.into_hash() != sprout_hash { + panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`."); + } + // Prepare verifying keys let spend_vk = prepare_verifying_key(&spend_params.vk); let output_vk = prepare_verifying_key(&output_params.vk);