use blake2::{Blake2b, Digest}; use generic_array::GenericArray; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::{ error::Error, fmt, fs::File, io::{BufRead, Read, Write}, path::PathBuf, }; use structopt::StructOpt; #[derive(StructOpt)] struct Options { #[structopt(subcommand)] command: Command, } #[derive(StructOpt)] enum Command { Generate { secret_out: PathBuf, }, Lottery { #[structopt(long)] secret: PathBuf, }, Verify { #[structopt(long)] commitment: Option, }, Digest, } type DynResult = Result>; fn main() -> DynResult<()> { let options = Options::from_args(); let stdin = std::io::stdin(); let mut stdout = std::io::stdout(); match options.command { Command::Generate { secret_out } => { let mut out = File::create(&secret_out)?; do_generate(&mut out, &mut stdout) } Command::Lottery { secret } => { let secret = std::fs::read(&secret)?; do_lottery(&secret, stdin.lock(), &mut stdout) } Command::Verify { commitment } => do_verify(stdin, commitment.as_ref()), Command::Digest => { for line in stdin.lock().lines() { let line = line?; let hash = Hash::of(&[line.as_bytes()]); writeln!(stdout, "{} \"{}\"", hash.to_string(), line)?; } Ok(()) } } } fn do_generate(secret_out: &mut impl Write, stdout: &mut impl Write) -> DynResult<()> { use rand::RngCore; let mut rng = rand::thread_rng(); let mut random_bytes = [0; 64]; rng.fill_bytes(&mut random_bytes); let hex_bytes = hex::encode(&random_bytes); secret_out.write(hex_bytes.as_bytes())?; let commitment = Hash::of(&[&random_bytes]); writeln!(stdout, "{}", commitment.to_string())?; Ok(()) } fn do_lottery(secret: &[u8], stdin: impl BufRead, stdout: &mut impl Write) -> DynResult<()> { let entries = stdin.lines().collect::, _>>()?; let outcome = Outcome::generate(entries, hex::decode(secret)?); serde_json::to_writer_pretty(stdout, &outcome)?; Ok(()) } fn do_verify(stdin: impl Read, commitment: Option<&String>) -> DynResult<()> { let outcome: Outcome = serde_json::from_reader(stdin)?; outcome.verify(); if let Some(commitment) = commitment { let hash = Hash::parse(commitment)?; outcome.verify_commitment(hash); } Ok(()) } #[derive(PartialOrd, Ord, PartialEq, Eq, Debug)] struct Hash(GenericArray::OutputSize>); impl Hash { fn of(sequences: &[&[u8]]) -> Self { let mut hasher = Blake2b::new(); for bytes in sequences { hasher.update(bytes); } Hash(hasher.finalize()) } fn parse(s: &str) -> DynResult { Ok(serde_json::from_str(&format!("\"{}\"", s))?) } } impl Serialize for Hash { fn serialize(&self, serializer: S) -> Result where S: Serializer, { serializer.serialize_str(&hex::encode(self.0)) } } impl<'de> Deserialize<'de> for Hash { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct StringVisitor; impl<'de> serde::de::Visitor<'de> for StringVisitor { type Value = Hash; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("a 64 byte hex string") } fn visit_str(self, s: &str) -> Result where E: serde::de::Error, { let bytes = hex::decode(s).map_err(E::custom)?; let array = GenericArray::from_exact_iter(bytes).ok_or(E::custom("incorrect length"))?; Ok(Hash(array)) } } deserializer.deserialize_str(StringVisitor) } } impl ToString for Hash { fn to_string(&self) -> String { hex::encode(self.0) } } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct Outcome { ordered_entries: Vec, secret: Vec, } #[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] struct Entry { entrant: String, ticket: Hash, } impl PartialOrd for Entry { fn partial_cmp(&self, other: &Self) -> Option { self.ticket.partial_cmp(&other.ticket) } } impl Ord for Entry { fn cmp(&self, other: &Self) -> std::cmp::Ordering { self.ticket.cmp(&other.ticket) } } impl Outcome { fn generate(entrants: Vec, secret: Vec) -> Outcome { let mut hashed_entries = entrants .into_iter() .map(|entrant| Entry { ticket: Hash::of(&[&entrant.as_bytes(), &secret]), entrant, }) .collect::>(); hashed_entries.sort(); Outcome { ordered_entries: hashed_entries, secret, } } fn verify(&self) { let entries = self .ordered_entries .iter() .map(|e| &e.entrant) .cloned() .collect(); let generated = Outcome::generate(entries, self.secret.clone()); assert_eq!(self, &generated); } fn verify_commitment(&self, hash: Hash) { let expected = Hash::of(&[&self.secret]); assert_eq!(hash, expected); } }