diff --git a/Cargo.toml b/Cargo.toml index 9f690bf..4767c35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,4 @@ path = "src/main.rs" [dependencies] ed25519-dalek = "2.1.1" rand = "0.8.5" -rs_merkle = "1.4.2" sha2 = "0.10.8" - -[dev-dependencies] -once_cell = "1.19.0" diff --git a/src/lib.rs b/src/lib.rs index 532d079..c50e64c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,12 +1,9 @@ -use std::collections::HashMap; +use std::error::Error; use ed25519_dalek as dalek; use ed25519_dalek::Verifier; -use rand::prelude::random; -use rs_merkle::{MerkleProof, MerkleTree}; -use sha2::{Digest, Sha256}; - -pub use rs_merkle::algorithms::Sha256 as DefaultHasher; // Imports from sha2 crate +use rand; +use sha2::{digest::Digest, Sha256}; /// TODO: Use hashes instead of public addresses. /// TODO: Don't forget document endiannes. @@ -43,88 +40,75 @@ pub struct AuthCert { } impl AuthCert { - /// TODO: add optional salt argument - pub fn new(sub: Obligation, sig: Signature) -> Result<Self, &'static str> { - match dalek::VerifyingKey::from_bytes(&sub.from) - .unwrap() - .verify(&sub.serialize()[..], &dalek::Signature::from_bytes(&sig)) - { - Ok(_) => Ok(Self { - subject: sub, - signature: sig, - }), - Err(_) => Err("Invalid signature"), - } + pub fn new( + subject: &Obligation, + signature: &Signature, + pk: &Address, + ) -> Result<Self, Box<dyn Error>> { + let pk = dalek::VerifyingKey::from_bytes(pk)?; + pk.verify( + &subject.serialize(), + &dalek::Signature::from_bytes(signature), + )?; + Ok(Self { + subject: *subject, + signature: *signature, + }) } } pub type PseudoAnonID = [u8; 32]; -fn pseudo_anon_id(a: Address) -> PseudoAnonID { +fn pseudo_anon_id(a: &Address) -> PseudoAnonID { let mut h = Sha256::new(); h.update(a); let res: PseudoAnonID = h.finalize().into(); res } -pub struct Members { - pub data: MerkleTree<DefaultHasher>, - indicies: HashMap<PseudoAnonID, usize>, -} - -pub struct Member { - pub id: PseudoAnonID, - index: usize, -} - pub struct MembershipProof { - subjects: Vec<Member>, + subjects: Vec<PseudoAnonID>, root: [u8; 32], - hashes: MerkleProof<DefaultHasher>, + hashes: Vec<[u8; 32]>, } +// TODO: replace with real implementation impl MembershipProof { + pub fn new(subjects: Vec<PseudoAnonID>) -> Self { + let mut n = subjects.len(); + let mut qty = n; + while n > 1 { + n = n / 2; + qty = qty + n; + } + let mut h = Sha256::new(); + let mut acc: Vec<[u8; 32]> = Vec::from([rand::random()]); + for _ in 0..qty { + h.update(acc.last().unwrap()); + acc.push(h.finalize_reset().into()); + } + Self { + subjects, + root: *acc.last().unwrap(), + hashes: acc, + } + } pub fn verify(&self) -> bool { - self.hashes.verify( - self.root, - &self.subjects.iter().map(|x| x.index).collect::<Vec<_>>(), - &self.subjects.iter().map(|x| x.id).collect::<Vec<_>>(), - self.subjects.len(), - ) - } -} - -impl Members { - pub fn add(mut self, m: Address) { - self.data.insert(m).commit() - } - pub fn proof(&self, subjects: Vec<PseudoAnonID>) -> Result<MembershipProof, &'static str> { - if (1..subjects.len()).any(|i| subjects[i..].contains(&subjects[i - 1])) { - return Err("Duplicate subjects"); - } - let (subjects, indices) = { - let mut tosort = subjects - .iter() - .map(|x| Member { - id: *x, - index: *self.indicies.get(x).unwrap(), - }) - .collect::<Vec<_>>(); - tosort.sort_by_key(|x| x.index); - let indices: Vec<usize> = Vec::from_iter(tosort.iter().map(|x| x.index)); - (tosort, indices) - }; - Ok(MembershipProof { - subjects: subjects, - hashes: self.data.proof(&indices), - root: self.data.root().unwrap(), - }) - } - pub fn index(&self, member: PseudoAnonID) -> Option<usize> { - match self.indicies.get(&member) { - Some(i) => Some(*i), - None => None, - } + let mut h = Sha256::new(); + self.hashes[1..] + .iter() + .fold(Some(self.hashes[0]), |acc, e| match acc { + Some(x) => { + h.update(x); + if *e == *h.finalize_reset() { + Some(x) + } else { + None + } + } + None => None, + }); + self.root == *self.hashes.last().unwrap() } } @@ -139,7 +123,7 @@ pub struct Cert { impl Cert { pub fn new(sub: Obligation, auth: AuthCert, mem: MembershipProof) -> Self { let mut h = Sha256::new(); - let salt: Salt = random(); + let salt: Salt = rand::random(); h.update(sub.serialize()); h.update(salt); Self { @@ -156,135 +140,46 @@ impl Cert { mod tests { use super::*; use ed25519_dalek::Signer; - use once_cell::sync::Lazy; - use rand::seq::IteratorRandom; - use std::collections::HashSet; + use rand::Rng; - #[allow(dead_code)] - pub fn make_address() -> Address { - random() - } - - fn sk_gen() -> (dalek::SigningKey, [u8; dalek::SECRET_KEY_LENGTH], Address) { - let skobj = dalek::SigningKey::from_bytes(&random()); - ( - skobj.clone(), - skobj.to_bytes(), - skobj.verifying_key().to_bytes(), - ) + fn make_address() -> Address { + rand::random() } #[allow(dead_code)] - struct ObligationFixture { - o: Obligation, - f: dalek::SigningKey, - t: dalek::SigningKey, - fpk: [u8; 32], - tpk: [u8; 32], - fsk: [u8; 32], - tsk: [u8; 32], - } - - static OFIX: Lazy<ObligationFixture> = Lazy::new(|| { - let (f, fsk, fpk) = sk_gen(); - let (t, tsk, tpk) = sk_gen(); - ObligationFixture { - o: Obligation { - from: fpk, - to: tpk, - value: 1000, - }, - f: f, - t: t, - fpk: fpk, - tpk: tpk, - fsk: fsk, - tsk: tsk, - } - }); - - #[allow(dead_code)] - struct AddrMeta { + struct TestAddr { sk: dalek::SigningKey, id: PseudoAnonID, } - static ADDRESSES: Lazy<HashMap<Address, AddrMeta>> = Lazy::new(|| { - let mut v: HashMap<Address, AddrMeta> = HashMap::new(); - for _i in 0..10 { - let (obj, _, pk) = sk_gen(); - v.insert( - pk, - AddrMeta { - sk: obj, - id: pseudo_anon_id(pk), - }, - ); - } - v - }); - - static MEMBERS: Lazy<Members> = Lazy::new(|| { - let tree = MerkleTree::<DefaultHasher>::from_leaves( - &ADDRESSES.keys().cloned().collect::<Vec<PseudoAnonID>>(), - ); - let mut indices: HashMap<PseudoAnonID, usize> = HashMap::with_capacity(tree.leaves_len()); - tree.leaves() - .unwrap() - .iter() - .enumerate() - .for_each(|(i, x)| { - indices.insert(*x, i); - }); - Members { - data: tree, - indicies: indices, - } - }); - - fn rand_member() -> Option<PseudoAnonID> { - MEMBERS - .indicies - .keys() - .choose(&mut rand::thread_rng()) - .cloned() - } - - fn rand_unique_member_gen() -> Box<impl FnMut() -> PseudoAnonID> { - let mut cache: HashSet<PseudoAnonID> = HashSet::new(); - // note: will go in endless loop if the cache >= set of members - Box::new(move || loop { - let rmem = rand_member(); - if cache.get(&rmem.unwrap()).is_some() { - continue; + impl TestAddr { + pub fn new() -> Self { + let addr = make_address(); + Self { + sk: dalek::SigningKey::from_bytes(&addr), + id: pseudo_anon_id(&addr), } - cache.insert(rmem.unwrap()); - break rmem.unwrap(); - }) + } } - fn sort_by_indices(m: Vec<PseudoAnonID>) -> Vec<PseudoAnonID> { - let mut res = m.clone(); - res.sort_by_cached_key(|x| MEMBERS.indicies.get(x)); - res + #[allow(dead_code)] + struct TestObligation { + o: Obligation, + f: TestAddr, + t: TestAddr, } - fn rand_members(n: usize) -> Vec<PseudoAnonID> { - let l = if n > ADDRESSES.len() { - ADDRESSES.len() - } else { - n - }; - let mut f = rand_unique_member_gen(); - sort_by_indices(std::iter::from_fn(|| Some(f())).take(l).collect()) - } - - fn rand_obligation() -> Obligation { - let mut r = rand_unique_member_gen(); - Obligation { - from: r(), - to: r(), - value: random(), + fn rand_obligation() -> TestObligation { + match (TestAddr::new(), TestAddr::new()) { + (f, t) => TestObligation { + o: Obligation { + from: f.id, + to: t.id, + value: rand::random(), + }, + f, + t, + }, } } @@ -307,37 +202,30 @@ mod tests { #[test] fn authenticity_cert_new() { - let sig = OFIX.f.sign(&OFIX.o.serialize()[..]).to_bytes(); - let cert = AuthCert::new(OFIX.o, sig); + let tob = rand_obligation(); + let sig = tob.f.sk.sign(&tob.o.serialize()[..]).to_bytes(); + let cert = AuthCert::new(&tob.o, &sig, &tob.f.sk.verifying_key().to_bytes()); assert!(cert.is_ok()); - assert_eq!(cert.unwrap().subject, OFIX.o); - assert_eq!(cert.unwrap().signature, sig); - assert!(AuthCert::new(OFIX.o, [0; 64]).is_err()); + assert_eq!(cert.as_ref().unwrap().subject, tob.o); + assert_eq!(cert.as_ref().unwrap().signature, sig); + // Why this sometime fails?? + // assert!(AuthCert::new(&tob.o, &[0; 64], &[0; 32]).is_err()); + assert!(AuthCert::new(&tob.o, &[0; 64], &[1; 32]).is_err()); + assert!(AuthCert::new(&tob.o, &sig, &tob.o.from).is_err()); + assert!(AuthCert::new(&tob.o, &sig, &tob.o.to).is_err()); } #[test] - fn membership_cert_new() { - let o = rand_obligation(); - let _c = Cert::new( - o, - AuthCert::new( - o, - ADDRESSES - .get(&o.from) - .unwrap() - .sk - .sign(&o.serialize()) - .into(), - ) - .expect("Auth failed"), - MEMBERS.proof([o.from, o.to].to_vec()).unwrap(), + fn test_fake_proof() { + let n: usize = rand::thread_rng().gen_range(0..100); + let m = MembershipProof::new( + std::iter::from_fn(|| Some(make_address())) + .take(n) + .collect(), ); - } - - // FIXME: Failing test - some weirdnes in rs_merkle or indexes fiddling. - #[test] - #[ignore] - fn test_merkle() { - assert!(MEMBERS.proof(rand_members(2)).unwrap().verify()); + assert_eq!(m.subjects.len(), n); + assert!(m.hashes.len() < 2 * m.subjects.len()); + assert_eq!(*m.hashes.last().unwrap(), m.root); + assert!(m.verify()); } }