Bläddra i källkod

Define initial data structures

Vecna 1 år sedan
förälder
incheckning
e153d58fd2
2 ändrade filer med 204 tillägg och 0 borttagningar
  1. 13 0
      Cargo.toml
  2. 191 0
      src/lib.rs

+ 13 - 0
Cargo.toml

@@ -6,3 +6,16 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+array-bytes = "6.2.0"
+bincode = "1"
+curve25519-dalek = { version = "4", default-features = false, features = ["serde", "rand_core", "digest"] }
+ed25519-dalek = { version = "2", features = ["serde", "rand_core"] }
+lox-library = { git = "https://gitlab.torproject.org/tpo/anti-censorship/lox.git", version = "0.1.0" }
+serde = "1.0.192"
+serde_with = {version = "3.4.0", features = ["json"]}
+sha1 = "0.10"
+sha3 = "0.10"
+time = "0.3.30"
+
+# probably not needed once I can query an API
+rand = { version = "0.8", features = ["std_rng"]}

+ 191 - 0
src/lib.rs

@@ -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()
+    }
+}