diff --git a/src/lib.rs b/src/lib.rs index d568c28..1ea34d2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ use curve25519_dalek::scalar::Scalar; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET}; +use lox_library::cred::Lox; use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use sha3::Sha3_256; @@ -51,38 +52,86 @@ pub fn get_bucket(beta_hash: &[u8; 32]) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] #[derive(Serialize, Deserialize)] pub enum ProofOfBridgeKnowledge { /// Hash of bridge line as proof of knowledge of bridge line - HashOfBridgeLine { hash: [u8; 32] }, + HashOfBridgeLine(HashOfBridgeLine), /// Hash of bucket ID for Lox user - HashOfBucket { hash: [u8; 32] }, + HashOfBucket(HashOfBucket), } impl ProofOfBridgeKnowledge { - fn verify(&self, fingerprint: [u8; 20]) -> bool { - match *self { - ProofOfBridgeKnowledge::HashOfBridgeLine { ref hash } => { - let bl = get_bridge_line(&fingerprint); - let mut hasher = Sha3_256::new(); - hasher.update(bincode::serialize(&bl).unwrap()); - let bl_hash: [u8; 32] = hasher.finalize().into(); - hash == &bl_hash - } - ProofOfBridgeKnowledge::HashOfBucket { ref hash } => { - let bucket = get_bucket(&hash); - for bl in bucket { - let mut hasher = Sha1::new(); - hasher.update(bl.uid_fingerprint.to_le_bytes()); - if fingerprint == <[u8; 20]>::from(hasher.finalize()) { - return true; - } - } - false + pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool { + // TODO: It seems like there ought to be a cleaner way to do this? + match self { + ProofOfBridgeKnowledge::HashOfBridgeLine(bl_hash) => bl_hash.verify(bridge_fingerprint), + ProofOfBridgeKnowledge::HashOfBucket(bucket_hash) => { + bucket_hash.verify(bridge_fingerprint) } } } } -pub trait Report { - fn verify(&self) -> bool; +/// Hash of bridge line to prove knowledge of that bridge +#[derive(PartialEq, Serialize, Deserialize)] +pub struct HashOfBridgeLine { + hash: [u8; 32], +} + +impl HashOfBridgeLine { + pub fn new(bl: BridgeLine) -> Self { + let mut hasher = Sha3_256::new(); + hasher.update(bincode::serialize(&bl).unwrap()); + let hash: [u8; 32] = hasher.finalize().into(); + Self { hash } + } + + pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool { + let bl = get_bridge_line(&bridge_fingerprint); + self == &HashOfBridgeLine::new(bl) + } +} + +/// Hash of bucket ID to prove knowledge of bridges in that bucket +#[derive(PartialEq, Serialize, Deserialize)] +pub struct HashOfBucket { + hash: [u8; 32], +} + +impl HashOfBucket { + pub fn new(bucket: Scalar) -> Self { + let mut hasher = Sha3_256::new(); + hasher.update(bucket.to_bytes()); + let hash: [u8; 32] = hasher.finalize().into(); + Self { hash } + } + + pub fn verify(&self, bridge_fingerprint: [u8; 20]) -> bool { + let bucket = get_bucket(&self.hash); + for bl in bucket { + let mut hasher = Sha1::new(); + hasher.update(bl.uid_fingerprint.to_le_bytes()); + if bridge_fingerprint == <[u8; 20]>::from(hasher.finalize()) { + return true; + } + } + false + } +} + +/// Reports from users about whether or not their bridges are blocked +#[derive(Serialize, Deserialize)] +pub enum Report { + /// Negative report indicating user was unable to connect + NegativeUserReport(NegativeUserReport), + /// Positive report indicating user was able to connect + PositiveUserReport(PositiveUserReport), +} + +impl Report { + fn verify(&self) -> bool { + match self { + Report::NegativeUserReport(report) => report.verify(), + Report::PositiveUserReport(report) => report.verify(), + } + } } /// A report that the user was unable to connect to the bridge @@ -91,7 +140,7 @@ pub struct NegativeUserReport { /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID) pub fingerprint: [u8; 20], /// some way to prove knowledge of bridge - pub bridge_pok: ProofOfBridgeKnowledge, + bridge_pok: ProofOfBridgeKnowledge, /// user's country code, may be an empty string pub country: String, /// today's Julian date @@ -99,14 +148,10 @@ pub struct NegativeUserReport { } impl NegativeUserReport { - pub fn new(bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self { + pub fn new(bridge_id: [u8; 20], bridge_pok: ProofOfBridgeKnowledge, country: String) -> Self { let mut hasher = Sha1::new(); hasher.update(bridge_id); let fingerprint: [u8; 20] = hasher.finalize().into(); - let mut hasher = Sha3_256::new(); - hasher.update(bucket.to_bytes()); - let bucket_hash: [u8; 32] = hasher.finalize().into(); - let bridge_pok = ProofOfBridgeKnowledge::HashOfBridgeLine { hash: bucket_hash }; let today = today(); Self { fingerprint, @@ -115,9 +160,30 @@ impl NegativeUserReport { today, } } -} -impl Report for NegativeUserReport { + pub fn from_bridgeline( + &self, + bridge_id: [u8; 20], + bridgeline: BridgeLine, + country: String, + ) -> Self { + let bridge_pok = + ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(bridgeline)); + NegativeUserReport::new(bridge_id, bridge_pok, country) + } + + pub fn from_bucket(&self, bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self { + let mut hasher = Sha3_256::new(); + hasher.update(bucket.to_bytes()); + let bucket_hash: [u8; 32] = hasher.finalize().into(); + let bridge_pok = ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket { hash: bucket_hash }); + NegativeUserReport::new(bridge_id, bridge_pok, country) + } + + pub fn from_lox_credential(&self, bridge_id: [u8; 20], cred: Lox, country: String) -> Self { + self.from_bucket(bridge_id, cred.bucket, country) + } + fn verify(&self) -> bool { // possibly include check that self.today is recent as well self.today <= today() && self.bridge_pok.verify(self.fingerprint) @@ -130,7 +196,7 @@ pub struct PositiveUserReport { /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID) pub fingerprint: [u8; 20], /// token from the bridge indicating it was reached - pub bridge_token: Option, + bridge_token: Option, // TODO: proof of level, something involving credential show /// user's country code, may be an empty string pub country: String, @@ -151,9 +217,7 @@ impl PositiveUserReport { today, } } -} -impl Report for PositiveUserReport { fn verify(&self) -> bool { // possibly include check that self.today is recent as well self.today <= today()