|
@@ -0,0 +1,191 @@
|
|
|
+use curve25519_dalek::scalar::Scalar;
|
|
|
+use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
|
|
|
+use lox_library::bridge_table::BridgeLine;
|
|
|
+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()
|
|
|
+}
|
|
|
+
|
|
|
+/// 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] },
|
|
|
+}
|
|
|
+
|
|
|
+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
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+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()
|
|
|
+ }
|
|
|
+}
|