lib: Remove dependency on rs_merkle

…using fake Membership proof for now.
This commit is contained in:
Davie Li 2024-04-25 00:45:16 +00:00
parent a57de247d4
commit e4a4e2d97b
2 changed files with 103 additions and 219 deletions

View file

@ -10,8 +10,4 @@ path = "src/main.rs"
[dependencies] [dependencies]
ed25519-dalek = "2.1.1" ed25519-dalek = "2.1.1"
rand = "0.8.5" rand = "0.8.5"
rs_merkle = "1.4.2"
sha2 = "0.10.8" sha2 = "0.10.8"
[dev-dependencies]
once_cell = "1.19.0"

View file

@ -1,12 +1,9 @@
use std::collections::HashMap; use std::error::Error;
use ed25519_dalek as dalek; use ed25519_dalek as dalek;
use ed25519_dalek::Verifier; use ed25519_dalek::Verifier;
use rand::prelude::random; use rand;
use rs_merkle::{MerkleProof, MerkleTree}; use sha2::{digest::Digest, Sha256};
use sha2::{Digest, Sha256};
pub use rs_merkle::algorithms::Sha256 as DefaultHasher; // Imports from sha2 crate
/// TODO: Use hashes instead of public addresses. /// TODO: Use hashes instead of public addresses.
/// TODO: Don't forget document endiannes. /// TODO: Don't forget document endiannes.
@ -43,88 +40,75 @@ pub struct AuthCert {
} }
impl AuthCert { impl AuthCert {
/// TODO: add optional salt argument pub fn new(
pub fn new(sub: Obligation, sig: Signature) -> Result<Self, &'static str> { subject: &Obligation,
match dalek::VerifyingKey::from_bytes(&sub.from) signature: &Signature,
.unwrap() pk: &Address,
.verify(&sub.serialize()[..], &dalek::Signature::from_bytes(&sig)) ) -> Result<Self, Box<dyn Error>> {
{ let pk = dalek::VerifyingKey::from_bytes(pk)?;
Ok(_) => Ok(Self { pk.verify(
subject: sub, &subject.serialize(),
signature: sig, &dalek::Signature::from_bytes(signature),
}), )?;
Err(_) => Err("Invalid signature"), Ok(Self {
} subject: *subject,
signature: *signature,
})
} }
} }
pub type PseudoAnonID = [u8; 32]; pub type PseudoAnonID = [u8; 32];
fn pseudo_anon_id(a: Address) -> PseudoAnonID { fn pseudo_anon_id(a: &Address) -> PseudoAnonID {
let mut h = Sha256::new(); let mut h = Sha256::new();
h.update(a); h.update(a);
let res: PseudoAnonID = h.finalize().into(); let res: PseudoAnonID = h.finalize().into();
res res
} }
pub struct Members {
pub data: MerkleTree<DefaultHasher>,
indicies: HashMap<PseudoAnonID, usize>,
}
pub struct Member {
pub id: PseudoAnonID,
index: usize,
}
pub struct MembershipProof { pub struct MembershipProof {
subjects: Vec<Member>, subjects: Vec<PseudoAnonID>,
root: [u8; 32], root: [u8; 32],
hashes: MerkleProof<DefaultHasher>, hashes: Vec<[u8; 32]>,
} }
// TODO: replace with real implementation
impl MembershipProof { 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 { pub fn verify(&self) -> bool {
self.hashes.verify( let mut h = Sha256::new();
self.root, self.hashes[1..]
&self.subjects.iter().map(|x| x.index).collect::<Vec<_>>(), .iter()
&self.subjects.iter().map(|x| x.id).collect::<Vec<_>>(), .fold(Some(self.hashes[0]), |acc, e| match acc {
self.subjects.len(), Some(x) => {
) h.update(x);
} if *e == *h.finalize_reset() {
} Some(x)
} else {
impl Members { None
pub fn add(mut self, m: Address) { }
self.data.insert(m).commit() }
} None => None,
pub fn proof(&self, subjects: Vec<PseudoAnonID>) -> Result<MembershipProof, &'static str> { });
if (1..subjects.len()).any(|i| subjects[i..].contains(&subjects[i - 1])) { self.root == *self.hashes.last().unwrap()
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,
}
} }
} }
@ -139,7 +123,7 @@ pub struct Cert {
impl Cert { impl Cert {
pub fn new(sub: Obligation, auth: AuthCert, mem: MembershipProof) -> Self { pub fn new(sub: Obligation, auth: AuthCert, mem: MembershipProof) -> Self {
let mut h = Sha256::new(); let mut h = Sha256::new();
let salt: Salt = random(); let salt: Salt = rand::random();
h.update(sub.serialize()); h.update(sub.serialize());
h.update(salt); h.update(salt);
Self { Self {
@ -156,135 +140,46 @@ impl Cert {
mod tests { mod tests {
use super::*; use super::*;
use ed25519_dalek::Signer; use ed25519_dalek::Signer;
use once_cell::sync::Lazy; use rand::Rng;
use rand::seq::IteratorRandom;
use std::collections::HashSet;
#[allow(dead_code)] fn make_address() -> Address {
pub fn make_address() -> Address { rand::random()
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(),
)
} }
#[allow(dead_code)] #[allow(dead_code)]
struct ObligationFixture { struct TestAddr {
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 {
sk: dalek::SigningKey, sk: dalek::SigningKey,
id: PseudoAnonID, id: PseudoAnonID,
} }
static ADDRESSES: Lazy<HashMap<Address, AddrMeta>> = Lazy::new(|| { impl TestAddr {
let mut v: HashMap<Address, AddrMeta> = HashMap::new(); pub fn new() -> Self {
for _i in 0..10 { let addr = make_address();
let (obj, _, pk) = sk_gen(); Self {
v.insert( sk: dalek::SigningKey::from_bytes(&addr),
pk, id: pseudo_anon_id(&addr),
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;
} }
cache.insert(rmem.unwrap()); }
break rmem.unwrap();
})
} }
fn sort_by_indices(m: Vec<PseudoAnonID>) -> Vec<PseudoAnonID> { #[allow(dead_code)]
let mut res = m.clone(); struct TestObligation {
res.sort_by_cached_key(|x| MEMBERS.indicies.get(x)); o: Obligation,
res f: TestAddr,
t: TestAddr,
} }
fn rand_members(n: usize) -> Vec<PseudoAnonID> { fn rand_obligation() -> TestObligation {
let l = if n > ADDRESSES.len() { match (TestAddr::new(), TestAddr::new()) {
ADDRESSES.len() (f, t) => TestObligation {
} else { o: Obligation {
n from: f.id,
}; to: t.id,
let mut f = rand_unique_member_gen(); value: rand::random(),
sort_by_indices(std::iter::from_fn(|| Some(f())).take(l).collect()) },
} f,
t,
fn rand_obligation() -> Obligation { },
let mut r = rand_unique_member_gen();
Obligation {
from: r(),
to: r(),
value: random(),
} }
} }
@ -307,37 +202,30 @@ mod tests {
#[test] #[test]
fn authenticity_cert_new() { fn authenticity_cert_new() {
let sig = OFIX.f.sign(&OFIX.o.serialize()[..]).to_bytes(); let tob = rand_obligation();
let cert = AuthCert::new(OFIX.o, sig); 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!(cert.is_ok());
assert_eq!(cert.unwrap().subject, OFIX.o); assert_eq!(cert.as_ref().unwrap().subject, tob.o);
assert_eq!(cert.unwrap().signature, sig); assert_eq!(cert.as_ref().unwrap().signature, sig);
assert!(AuthCert::new(OFIX.o, [0; 64]).is_err()); // 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] #[test]
fn membership_cert_new() { fn test_fake_proof() {
let o = rand_obligation(); let n: usize = rand::thread_rng().gen_range(0..100);
let _c = Cert::new( let m = MembershipProof::new(
o, std::iter::from_fn(|| Some(make_address()))
AuthCert::new( .take(n)
o, .collect(),
ADDRESSES
.get(&o.from)
.unwrap()
.sk
.sign(&o.serialize())
.into(),
)
.expect("Auth failed"),
MEMBERS.proof([o.from, o.to].to_vec()).unwrap(),
); );
} assert_eq!(m.subjects.len(), n);
assert!(m.hashes.len() < 2 * m.subjects.len());
// FIXME: Failing test - some weirdnes in rs_merkle or indexes fiddling. assert_eq!(*m.hashes.last().unwrap(), m.root);
#[test] assert!(m.verify());
#[ignore]
fn test_merkle() {
assert!(MEMBERS.proof(rand_members(2)).unwrap().verify());
} }
} }