use curve25519_dalek::scalar::Scalar; use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey}; use lox_library::bridge_table::{BridgeLine, MAX_BRIDGES_PER_BUCKET}; use serde::{Deserialize, Serialize}; use sha1::{Digest, Sha1}; use sha3::Sha3_256; // for generating ed25519 keys during initial development use rand::rngs::OsRng; /// Get Julian date pub fn today() -> 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 { hash: [u8; 32] }, /// Hash of bucket ID for Lox user HashOfBucket { hash: [u8; 32] }, } 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 trait Report { fn verify(&self) -> bool; } /// A report that the user was unable to connect to the bridge #[derive(Serialize, Deserialize)] 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, /// user's country code, may be an empty string pub country: String, /// today's Julian date pub today: u32, } impl NegativeUserReport { pub fn new(bridge_id: [u8; 20], bucket: Scalar, 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, bridge_pok, country, today, } } } impl Report for NegativeUserReport { fn verify(&self) -> bool { // possibly include check that self.today is recent as well self.today <= today() && self.bridge_pok.verify(self.fingerprint) } } /// A report that the user was able to connect to the bridge #[derive(Serialize, Deserialize)] 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: BridgeToken, // TODO: proof of level, something involving credential show /// user's country code, may be an empty string pub country: String, /// today's Julian date pub today: u32, } impl PositiveUserReport { pub fn new(bridge_id: [u8; 20], bridge_token: BridgeToken, country: String) -> Self { let mut hasher = Sha1::new(); hasher.update(bridge_id); let fingerprint: [u8; 20] = hasher.finalize().into(); let today = today(); Self { fingerprint, bridge_token, country, today, } } } impl Report for PositiveUserReport { fn verify(&self) -> bool { // possibly include check that self.today is recent as well self.today == self.bridge_token.unsigned_bridge_token.today && self.today <= today() && self.bridge_token.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 today: 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 today = today(); Self { fingerprint, country, today, } } } /// 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.today <= today() && pubkey .verify( &bincode::serialize(&self.unsigned_bridge_token).unwrap(), &self.sig, ) .is_ok() } }