use curve25519_dalek::scalar::Scalar; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use lazy_static::lazy_static; use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET}; use lox_library::cred::Lox; use lox_library::proto::positive_report; use lox_library::IssuerPubKey; use serde::de::{self, Deserializer, MapAccess, SeqAccess, Unexpected, Visitor}; use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use sha3::Sha3_256; use std::collections::HashSet; use std::fmt; use std::option::Option; // for generating ed25519 keys during initial development use rand::rngs::OsRng; // TODO: These should be loaded from config file pub const REQUIRE_BRIDGE_TOKEN: bool = false; lazy_static! { // known country codes, TODO: Verify these are correct pub static ref COUNTRY_CODES: HashSet<&'static str> = HashSet::from(["ac","af","ax","al","dz","ad","ao","ai","aq","ag","ar","am","aw","au","at","az","bs","bh","bd","bb","by","be","bz","bj","bm","bt","bo","ba","bw","bv","br","io","vg","bn","bg","bf","bi","kh","cm","ca","cv","ky","cf","td","cl","cn","cx","cc","co","km","cg","cd","ck","cr","ci","hr","cu","cy","cz","dk","dj","dm","do","tp","ec","eg","sv","gq","ee","et","fk","fo","fj","fi","fr","fx","gf","pf","tf","ga","gm","ge","de","gh","gi","gr","gl","gd","gp","gu","gt","gn","gw","gy","ht","hm","hn","hk","hu","is","in","id","ir","iq","ie","im","il","it","jm","jp","jo","kz","ke","ki","kp","kr","kw","kg","la","lv","lb","ls","lr","ly","li","lt","lu","mo","mk","mg","mw","my","mv","ml","mt","mh","mq","mr","mu","yt","mx","fm","md","mc","mn","me","ms","ma","mz","mm","na","nr","np","an","nl","nc","nz","ni","ne","ng","nu","nf","mp","no","om","pk","pw","ps","pa","pg","py","pe","ph","pn","pl","pt","pr","qa","re","ro","ru","rw","ws","sm","st","sa","uk","sn","rs","sc","sl","sg","sk","si","sb","so","as","za","gs","su","es","lk","sh","kn","lc","pm","vc","sd","sr","sj","sz","se","ch","sy","tw","tj","tz","th","tg","tk","to","tt","tn","tr","tm","tc","tv","ug","ua","ae","gb","uk","us","um","uy","uz","vu","va","ve","vn","vi","wf","eh","ye","zm","zw"]); } /// The minimum trust level a Lox credential must have to be allowed to /// submit a positive report pub const PR_MIN_TRUST_LEVEL: u32 = 3; /// Get Julian date pub fn get_date() -> u32 { time::OffsetDateTime::now_utc() .date() .to_julian_day() .try_into() .unwrap() } /// Get bridge line for a bridge, requires some oracle pub fn get_bridge_line(fingerprint: &[u8; 20]) -> BridgeLine { // TODO // for now just return empty bridgeline BridgeLine::default() } /// Get verifying key for a bridge, requires some oracle pub fn get_bridge_signing_pubkey(fingerprint: &[u8; 20]) -> VerifyingKey { // TODO // for now just return new pubkey let mut csprng = OsRng {}; let keypair = SigningKey::generate(&mut csprng); keypair.verifying_key() } /// Get bucket from hash of bucket ID, requires some oracle pub fn get_bucket(beta_hash: &[u8; 32]) -> [BridgeLine; MAX_BRIDGES_PER_BUCKET] { // TODO // for now just return bucket of empty bridgelines [ BridgeLine::default(), BridgeLine::default(), BridgeLine::default(), ] } /// Proof that the user knows (and should be able to access) a given bridge #[derive(Serialize, Deserialize)] pub enum ProofOfBridgeKnowledge { /// Hash of bridge line as proof of knowledge of bridge line HashOfBridgeLine(HashOfBridgeLine), /// Hash of bucket ID for Lox user HashOfBucket(HashOfBucket), } impl ProofOfBridgeKnowledge { 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) } } } } /// 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 NegativeReport(NegativeReport), /// Positive report indicating user was able to connect PositiveReport(PositiveReport), } impl Report { fn verify(&self) -> bool { match self { Report::NegativeReport(report) => report.verify(), Report::PositiveReport(report) => report.verify(), } } } /// A report that the user was unable to connect to the bridge #[derive(Serialize)] pub struct NegativeReport { /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID) pub fingerprint: [u8; 20], /// some way to prove knowledge of bridge bridge_pok: ProofOfBridgeKnowledge, /// user's country code, may be an empty string pub country: String, /// today's Julian date pub date: u32, } // Ensure public fields are legal while deserializing // Based on https://serde.rs/deserialize-struct.html impl<'de> Deserialize<'de> for NegativeReport { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { enum Field { Fingerprint, BridgePOK, Country, Date, } impl<'de> Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`fingerprint`, `bridge_pok`, `country`, or `date`") } fn visit_str(self, value: &str) -> Result where E: de::Error, { match value { "fingerprint" => Ok(Field::Fingerprint), "bridge_pok" => Ok(Field::BridgePOK), "country" => Ok(Field::Country), "date" => Ok(Field::Date), _ => Err(de::Error::unknown_field(value, FIELDS)), } } } deserializer.deserialize_identifier(FieldVisitor) } } struct NegativeReportVisitor; impl<'de> de::Visitor<'de> for NegativeReportVisitor { type Value = NegativeReport; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str( "negative report with valid country code and date no later than today", ) } fn visit_seq(self, mut seq: V) -> Result where V: SeqAccess<'de>, { let fingerprint = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; let bridge_pok = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; let country = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(2, &self))?; let date = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(3, &self))?; Ok(NegativeReport { fingerprint, bridge_pok, country, date, }) } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de>, { let mut fingerprint = None; let mut bridge_pok = None; let mut country: Option = None; let mut date = None; while let Some(key) = map.next_key()? { match key { Field::Fingerprint => { if fingerprint.is_some() { return Err(de::Error::duplicate_field("fingerprint")); } fingerprint = Some(map.next_value()?); } Field::BridgePOK => { if bridge_pok.is_some() { return Err(de::Error::duplicate_field("bridge_pok")); } bridge_pok = Some(map.next_value()?); } Field::Country => { if country.is_some() { return Err(de::Error::duplicate_field("country")); } country = Some(map.next_value()?); } Field::Date => { if date.is_some() { return Err(de::Error::duplicate_field("date")); } date = Some(map.next_value()?); } } } let fingerprint = fingerprint.ok_or_else(|| de::Error::missing_field("fingerprint"))?; let bridge_pok = bridge_pok.ok_or_else(|| de::Error::missing_field("bridge_pok"))?; let country = country.ok_or_else(|| de::Error::missing_field("country"))?; if country != "" && !COUNTRY_CODES.contains(country.as_str()) { return Err(de::Error::invalid_value( Unexpected::Str(&country), &"a country code or empty string", )); } let date = date.ok_or_else(|| de::Error::missing_field("date"))?; if date > get_date().into() { return Err(de::Error::invalid_value( Unexpected::Unsigned(date), &"report date no later than today", )); } Ok(NegativeReport { fingerprint: fingerprint, bridge_pok: bridge_pok, country: country.to_string(), date: date.try_into().unwrap(), }) } } const FIELDS: &'static [&'static str] = &["fingerprint", "bridge_pok", "country", "date"]; deserializer.deserialize_struct("NegativeReport", FIELDS, NegativeReportVisitor) } } impl NegativeReport { 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 date = get_date(); Self { fingerprint, bridge_pok, country, date, } } pub fn from_bridgeline(bridge_id: [u8; 20], bridgeline: BridgeLine, country: String) -> Self { let bridge_pok = ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(bridgeline)); NegativeReport::new(bridge_id, bridge_pok, country) } pub fn from_bucket(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 }); NegativeReport::new(bridge_id, bridge_pok, country) } pub fn from_lox_credential(bridge_id: [u8; 20], cred: Lox, country: String) -> Self { NegativeReport::from_bucket(bridge_id, cred.bucket, country) } fn verify(&self) -> bool { self.bridge_pok.verify(self.fingerprint) } } /// A report that the user was able to connect to the bridge #[derive(Serialize)] pub struct PositiveReport { /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID) pub fingerprint: [u8; 20], /// token from the bridge indicating it was reached bridge_token: Option, // proof of Lox cred with level >= 3 and this bridge lox_proof: positive_report::Request, /// user's country code, may be an empty string pub country: String, /// today's Julian date pub date: u32, } // Ensure public fields are legal while deserializing // Based on https://serde.rs/deserialize-struct.html impl<'de> Deserialize<'de> for PositiveReport { fn deserialize(deserializer: D) -> Result where D: de::Deserializer<'de>, { enum Field { Fingerprint, BridgeToken, LoxProof, Country, Date, } impl<'de> Deserialize<'de> for Field { fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str( "`fingerprint`, `bridge_token`, `lox_proof`, `country`, or `date`", ) } fn visit_str(self, value: &str) -> Result where E: de::Error, { match value { "fingerprint" => Ok(Field::Fingerprint), "bridge_token" => Ok(Field::BridgeToken), "lox_proof" => Ok(Field::LoxProof), "country" => Ok(Field::Country), "date" => Ok(Field::Date), _ => Err(de::Error::unknown_field(value, FIELDS)), } } } deserializer.deserialize_identifier(FieldVisitor) } } struct PositiveReportVisitor; impl<'de> de::Visitor<'de> for PositiveReportVisitor { type Value = PositiveReport; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str( "positive report with valid country code and date no later than today", ) } fn visit_seq(self, mut seq: V) -> Result where V: SeqAccess<'de>, { let fingerprint = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; let bridge_token = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; let lox_proof = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(2, &self))?; let country = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(3, &self))?; let date = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(4, &self))?; Ok(PositiveReport { fingerprint, bridge_token, lox_proof, country, date, }) } fn visit_map(self, mut map: V) -> Result where V: MapAccess<'de>, { let mut fingerprint = None; let mut bridge_token: Option> = None; let mut lox_proof = None; let mut country: Option = None; let mut date = None; while let Some(key) = map.next_key()? { match key { Field::Fingerprint => { if fingerprint.is_some() { return Err(de::Error::duplicate_field("fingerprint")); } fingerprint = Some(map.next_value()?); } Field::BridgeToken => { if bridge_token.is_some() { return Err(de::Error::duplicate_field("bridge_token")); } bridge_token = Some(map.next_value()?); } Field::LoxProof => { if lox_proof.is_some() { return Err(de::Error::duplicate_field("lox_proof")); } lox_proof = Some(map.next_value()?); } Field::Country => { if country.is_some() { return Err(de::Error::duplicate_field("country")); } country = Some(map.next_value()?); } Field::Date => { if date.is_some() { return Err(de::Error::duplicate_field("date")); } date = Some(map.next_value()?); } } } let fingerprint = fingerprint.ok_or_else(|| de::Error::missing_field("fingerprint"))?; let bridge_token = bridge_token.ok_or_else(|| de::Error::missing_field("bridge_token"))?; if REQUIRE_BRIDGE_TOKEN && bridge_token.is_none() { return Err(de::Error::invalid_value( Unexpected::Option, &"a bridge token (mandatory, per system configuration)", )); } let lox_proof = lox_proof.ok_or_else(|| de::Error::missing_field("lox_proof"))?; let country = country.ok_or_else(|| de::Error::missing_field("country"))?; if country != "" && !COUNTRY_CODES.contains(country.as_str()) { return Err(de::Error::invalid_value( Unexpected::Str(&country), &"a country code or empty string", )); } let date = date.ok_or_else(|| de::Error::missing_field("date"))?; if date > get_date().into() { return Err(de::Error::invalid_value( Unexpected::Unsigned(date), &"report date no later than today", )); } Ok(PositiveReport { fingerprint: fingerprint, bridge_token: bridge_token, lox_proof: lox_proof, country: country.to_string(), date: date.try_into().unwrap(), }) } } const FIELDS: &'static [&'static str] = &[ "fingerprint", "bridge_token", "lox_proof", "country", "date", ]; deserializer.deserialize_struct("PositiveReport", FIELDS, PositiveReportVisitor) } } impl PositiveReport { pub fn new( bridge_id: [u8; 20], bridge_token: Option, lox_proof: positive_report::Request, country: String, ) -> Self { if REQUIRE_BRIDGE_TOKEN && bridge_token.is_none() { panic!("Bridge tokens are required for positive reports."); } let mut hasher = Sha1::new(); hasher.update(bridge_id); let fingerprint: [u8; 20] = hasher.finalize().into(); let date = get_date(); Self { fingerprint, bridge_token, lox_proof, country, date, } } pub fn from_lox_credential( bridge_id: [u8; 20], bridge_token: Option, lox_cred: &Lox, lox_pub: &IssuerPubKey, country: String, ) -> Self { let lox_proof = positive_report::request(lox_cred, lox_pub).unwrap(); PositiveReport::new(bridge_id, bridge_token, lox_proof, country) } fn verify(&self) -> bool { !REQUIRE_BRIDGE_TOKEN || { if self.bridge_token.is_none() { false } else { let bt = self.bridge_token.as_ref().unwrap(); bt.verify() } } } } /// An unsigned token which indicates that the bridge was reached #[derive(Serialize, Deserialize)] pub struct UnsignedBridgeToken { /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID) pub fingerprint: [u8; 20], /// client's country code pub country: String, /// today's Julian date pub date: u32, } impl UnsignedBridgeToken { pub fn new(bridge_id: [u8; 20], country: String) -> Self { let mut hasher = Sha1::new(); hasher.update(bridge_id); let fingerprint: [u8; 20] = hasher.finalize().into(); let date = get_date(); Self { fingerprint, country, date, } } } /// A signed token which indicates that the bridge was reached #[derive(Serialize, Deserialize)] pub struct BridgeToken { /// the unsigned version of this token pub unsigned_bridge_token: UnsignedBridgeToken, /// signature from bridge's ed25519 key pub sig: Signature, } impl BridgeToken { pub fn new(unsigned_bridge_token: UnsignedBridgeToken, keypair: SigningKey) -> Self { let sig = keypair.sign(&bincode::serialize(&unsigned_bridge_token).unwrap()); Self { unsigned_bridge_token, sig, } } pub fn verify(&self) -> bool { let pubkey = get_bridge_signing_pubkey(&self.unsigned_bridge_token.fingerprint); self.unsigned_bridge_token.date <= get_date() && pubkey .verify( &bincode::serialize(&self.unsigned_bridge_token).unwrap(), &self.sig, ) .is_ok() } }