Browse Source

Include nonce in negative reports

Vecna 1 year ago
parent
commit
c82e604e3d
5 changed files with 130 additions and 23 deletions
  1. 1 1
      Cargo.toml
  2. 0 0
      src/lib.rs
  3. 46 12
      src/negative_report.rs
  4. 8 2
      src/positive_report.rs
  5. 75 8
      src/tests.rs

+ 1 - 1
Cargo.toml

@@ -21,6 +21,7 @@ hyper-util = { version = "0.1", features = ["full"] }
 julianday = "1.2.0"
 lazy_static = "1"
 lox-library = { git = "https://gitlab.torproject.org/vecna/lox.git", version = "0.1.0" }
+rand = { version = "0.8" }
 #select = "0.6.0"
 serde = "1.0.197"
 serde_json = "1.0"
@@ -34,4 +35,3 @@ tokio-cron = "0.1.2"
 
 [dev-dependencies]
 base64 = "0.21.7"
-rand = "0.8.5"

File diff suppressed because it is too large
+ 0 - 0
src/lib.rs


+ 46 - 12
src/negative_report.rs

@@ -1,10 +1,11 @@
 use crate::{
     bridge_verification_info::BridgeVerificationInfo, get_date, BridgeDistributor, COUNTRY_CODES,
+    MAX_BACKDATE,
 };
 
 use curve25519_dalek::scalar::Scalar;
 use lox_library::{bridge_table::BridgeLine, cred::Lox};
-
+use rand::RngCore;
 use serde::{Deserialize, Serialize};
 use sha1::{Digest, Sha1};
 use sha3::Sha3_256;
@@ -12,6 +13,7 @@ use sha3::Sha3_256;
 #[derive(Debug, Serialize)]
 pub enum NegativeReportError {
     DateInFuture,
+    DateInPast,          // report is more than MAX_BACKDATE days old
     FailedToDeserialize, // couldn't deserialize to SerializableNegativeReport
     InvalidCountryCode,
     MissingCountryCode,
@@ -32,6 +34,9 @@ pub struct NegativeReport {
     /// today's Julian date
     pub date: u32,
 
+    /// a random nonce used in the bridge_pok
+    pub nonce: [u8; 32],
+
     /// the bridge distributor, e.g., Lox, Https, or Moat
     pub distributor: BridgeDistributor,
 }
@@ -42,6 +47,7 @@ impl NegativeReport {
         bridge_pok: ProofOfBridgeKnowledge,
         country: String,
         date: u32,
+        nonce: [u8; 32],
         distributor: BridgeDistributor,
     ) -> Self {
         let mut hasher = Sha1::new();
@@ -52,6 +58,7 @@ impl NegativeReport {
             bridge_pok,
             country,
             date,
+            nonce,
             distributor,
         }
     }
@@ -62,24 +69,42 @@ impl NegativeReport {
         distributor: BridgeDistributor,
     ) -> Self {
         let date = get_date();
-        let bridge_pok =
-            ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridgeline, date));
-        NegativeReport::new(
+        let mut rng = rand::thread_rng();
+        let mut nonce = [0; 32];
+        rng.fill_bytes(&mut nonce);
+        let bridge_pok = ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
+            &bridgeline,
+            date,
+            nonce,
+        ));
+        Self::new(
             bridgeline.fingerprint,
             bridge_pok,
             country,
             date,
+            nonce,
             distributor,
         )
     }
 
     pub fn from_lox_bucket(bridge_id: [u8; 20], bucket: Scalar, country: String) -> Self {
         let date = get_date();
-        let bridge_pok = ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&bucket, date));
-        NegativeReport::new(bridge_id, bridge_pok, country, date, BridgeDistributor::Lox)
+        let mut rng = rand::thread_rng();
+        let mut nonce = [0; 32];
+        rng.fill_bytes(&mut nonce);
+        let bridge_pok =
+            ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&bucket, date, nonce));
+        Self::new(
+            bridge_id,
+            bridge_pok,
+            country,
+            date,
+            nonce,
+            BridgeDistributor::Lox,
+        )
     }
 
-    pub fn from_lox_credential(bridge_id: [u8; 20], cred: Lox, country: String) -> Self {
+    pub fn from_lox_credential(bridge_id: [u8; 20], cred: &Lox, country: String) -> Self {
         NegativeReport::from_lox_bucket(bridge_id, cred.bucket, country)
     }
 
@@ -90,6 +115,7 @@ impl NegativeReport {
             bridge_pok: self.bridge_pok,
             country: self.country,
             date: self.date,
+            nonce: self.nonce,
             distributor: self.distributor,
         }
     }
@@ -119,12 +145,12 @@ impl NegativeReport {
     pub fn verify(self, bridge_info: &BridgeVerificationInfo) -> bool {
         match self.bridge_pok {
             ProofOfBridgeKnowledge::HashOfBridgeLine(pok) => {
-                let hash = HashOfBridgeLine::new(&bridge_info.bridge_line, self.date);
+                let hash = HashOfBridgeLine::new(&bridge_info.bridge_line, self.date, self.nonce);
                 hash == pok
             }
             ProofOfBridgeKnowledge::HashOfBucket(pok) => {
                 for b in &bridge_info.buckets {
-                    let hash = HashOfBucket::new(&b, self.date);
+                    let hash = HashOfBucket::new(&b, self.date, self.nonce);
                     if hash == pok {
                         return true;
                     }
@@ -143,6 +169,7 @@ pub struct SerializableNegativeReport {
     bridge_pok: ProofOfBridgeKnowledge,
     pub country: String,
     pub date: u32,
+    pub nonce: [u8; 32],
     pub distributor: BridgeDistributor,
 }
 
@@ -154,14 +181,19 @@ impl SerializableNegativeReport {
         if !COUNTRY_CODES.contains(self.country.as_str()) {
             return Err(NegativeReportError::InvalidCountryCode);
         }
-        if self.date > get_date().into() {
+        let date = get_date();
+        if self.date > date {
             return Err(NegativeReportError::DateInFuture);
         }
+        if self.date < date - MAX_BACKDATE {
+            return Err(NegativeReportError::DateInPast);
+        }
         Ok(NegativeReport {
             fingerprint: self.fingerprint,
             bridge_pok: self.bridge_pok,
             country: self.country.to_string(),
             date: self.date.try_into().unwrap(),
+            nonce: self.nonce,
             distributor: self.distributor,
         })
     }
@@ -184,9 +216,10 @@ pub struct HashOfBridgeLine {
 }
 
 impl HashOfBridgeLine {
-    pub fn new(bl: &BridgeLine, date: u32) -> Self {
+    pub fn new(bl: &BridgeLine, date: u32, nonce: [u8; 32]) -> Self {
         let mut hasher = Sha3_256::new();
         hasher.update(date.to_le_bytes());
+        hasher.update(nonce);
         hasher.update(bincode::serialize(&bl).unwrap());
         let hash: [u8; 32] = hasher.finalize().into();
         Self { hash }
@@ -200,9 +233,10 @@ pub struct HashOfBucket {
 }
 
 impl HashOfBucket {
-    pub fn new(bucket: &Scalar, date: u32) -> Self {
+    pub fn new(bucket: &Scalar, date: u32, nonce: [u8; 32]) -> Self {
         let mut hasher = Sha3_256::new();
         hasher.update(date.to_le_bytes());
+        hasher.update(nonce);
         hasher.update(bucket.to_bytes());
         let hash: [u8; 32] = hasher.finalize().into();
         Self { hash }

+ 8 - 2
src/positive_report.rs

@@ -1,7 +1,9 @@
 // For Lox-related code where points are uppercase and scalars are lowercase
 #![allow(non_snake_case)]
 
-use crate::{bridge_verification_info::BridgeVerificationInfo, get_date, COUNTRY_CODES};
+use crate::{
+    bridge_verification_info::BridgeVerificationInfo, get_date, COUNTRY_CODES, MAX_BACKDATE,
+};
 
 use curve25519_dalek::ristretto::RistrettoBasepointTable;
 use ed25519_dalek::{Signature, Signer, SigningKey, Verifier};
@@ -15,6 +17,7 @@ pub const REQUIRE_BRIDGE_TOKEN: bool = false;
 #[derive(Debug, Serialize)]
 pub enum PositiveReportError {
     DateInFuture,
+    DateInPast,          // report is more than MAX_BACKDATE days old
     FailedToDeserialize, // couldn't deserialize to SerializablePositiveReport
     InvalidBridgeToken,
     InvalidCountryCode,
@@ -180,10 +183,13 @@ impl SerializablePositiveReport {
         if !COUNTRY_CODES.contains(self.country.as_str()) {
             return Err(PositiveReportError::InvalidCountryCode);
         }
-        let date: u32 = get_date().into();
+        let date: u32 = get_date();
         if self.date > date {
             return Err(PositiveReportError::DateInFuture);
         }
+        if self.date < date - MAX_BACKDATE {
+            return Err(PositiveReportError::DateInPast);
+        }
         if self.lox_proof.date != date {
             return Err(PositiveReportError::InvalidLoxProof);
         }

+ 75 - 8
src/tests.rs

@@ -210,12 +210,64 @@ fn test_negative_reports() {
     let report_2 =
         NegativeReport::from_lox_bucket(bridges[1].fingerprint, cred.bucket, "ru".to_string());
     let report_3 =
-        NegativeReport::from_lox_credential(bridges[2].fingerprint, cred, "ru".to_string());
+        NegativeReport::from_lox_credential(bridges[2].fingerprint, &cred, "ru".to_string());
+
+    // Backdated reports
+    let date = get_date();
+    let mut rng = rand::thread_rng();
+
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+    let report_4 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
+            &bridges[0],
+            date - 1,
+            nonce,
+        )),
+        "ru".to_string(),
+        date - 1,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+    let report_5 = NegativeReport::new(
+        bridges[1].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
+            &bridges[1],
+            date - 2,
+            nonce,
+        )),
+        "ru".to_string(),
+        date - 2,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+    let report_6 = NegativeReport::new(
+        bridges[2].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
+            &bridges[2],
+            date - 3,
+            nonce,
+        )),
+        "ru".to_string(),
+        date - 3,
+        nonce,
+        BridgeDistributor::Lox,
+    );
 
     // Verify reports
     assert!(report_1.verify(&bridge_info_1));
     assert!(report_2.verify(&bridge_info_2));
     assert!(report_3.verify(&bridge_info_3));
+    assert!(report_4.verify(&bridge_info_1));
+    assert!(report_5.verify(&bridge_info_2));
+    assert!(report_6.verify(&bridge_info_3));
 
     // Check that deserialization fails under invalid conditions
 
@@ -225,41 +277,56 @@ fn test_negative_reports() {
             .to_serializable_report();
     invalid_report_1.date = invalid_report_1.date + 2;
 
+    // Date too far in past
+    let mut invalid_report_2 =
+        NegativeReport::from_bridgeline(bridges[1], "ru".to_string(), BridgeDistributor::Lox)
+            .to_serializable_report();
+    invalid_report_2.date = invalid_report_2.date - MAX_BACKDATE - 1;
+
     // Invalid country code
-    let invalid_report_2 =
-        NegativeReport::from_bridgeline(bridges[1], "xx".to_string(), BridgeDistributor::Lox)
+    let invalid_report_3 =
+        NegativeReport::from_bridgeline(bridges[2], "xx".to_string(), BridgeDistributor::Lox)
             .to_serializable_report();
 
     assert!(invalid_report_1.to_report().is_err());
     assert!(invalid_report_2.to_report().is_err());
+    assert!(invalid_report_3.to_report().is_err());
 
     // Check that verification fails with incorrect data
 
     let date = get_date();
+    let mut rng = rand::thread_rng();
 
     // Incorrect BridgeLine hash
-    let invalid_report_3 = NegativeReport::new(
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+    let invalid_report_4 = NegativeReport::new(
         bridges[0].fingerprint,
         ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
             &BridgeLine::default(),
             date,
+            nonce,
         )),
         "ru".to_string(),
         date,
+        nonce,
         BridgeDistributor::Lox,
     );
 
     // Incorrect bucket hash
-    let invalid_report_4 = NegativeReport::new(
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+    let invalid_report_5 = NegativeReport::new(
         bridges[1].fingerprint,
-        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date)),
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date, nonce)),
         "ru".to_string(),
         date,
+        nonce,
         BridgeDistributor::Lox,
     );
 
-    assert!(!invalid_report_3.verify(&bridge_info_1));
-    assert!(!invalid_report_4.verify(&bridge_info_2));
+    assert!(!invalid_report_4.verify(&bridge_info_1));
+    assert!(!invalid_report_5.verify(&bridge_info_2));
 }
 
 #[test]

Some files were not shown because too many files changed in this diff