Browse Source

Test dropping NRs with repeated nonces and DB storage before processing

Vecna 2 months ago
parent
commit
c5a05be6d8
2 changed files with 307 additions and 4 deletions
  1. 4 4
      src/negative_report.rs
  2. 303 0
      src/tests.rs

+ 4 - 4
src/negative_report.rs

@@ -20,7 +20,7 @@ pub enum NegativeReportError {
 }
 
 /// A report that the user was unable to connect to the bridge
-#[derive(Eq, PartialEq, Ord, PartialOrd)]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
 pub struct NegativeReport {
     /// hashed fingerprint (SHA-1 hash of 20-byte bridge ID)
     pub fingerprint: [u8; 20],
@@ -200,7 +200,7 @@ impl SerializableNegativeReport {
 }
 
 /// Proof that the user knows (and should be able to access) a given bridge
-#[derive(Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
 pub enum ProofOfBridgeKnowledge {
     /// Hash of bridge line as proof of knowledge of bridge line
     HashOfBridgeLine(HashOfBridgeLine),
@@ -210,7 +210,7 @@ pub enum ProofOfBridgeKnowledge {
 }
 
 /// Hash of bridge line to prove knowledge of that bridge
-#[derive(Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
 pub struct HashOfBridgeLine {
     hash: [u8; 32],
 }
@@ -227,7 +227,7 @@ impl HashOfBridgeLine {
 }
 
 /// Hash of bucket ID to prove knowledge of bridges in that bucket
-#[derive(Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
+#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
 pub struct HashOfBucket {
     hash: [u8; 32],
 }

+ 303 - 0
src/tests.rs

@@ -327,6 +327,237 @@ fn test_negative_reports() {
 
     assert!(!invalid_report_4.verify(&bridge_info_1));
     assert!(!invalid_report_5.verify(&bridge_info_2));
+
+    // Test that reports with duplicate nonces are rejected
+
+    // Open test database
+    let db: Db = sled::open("test_db").unwrap();
+
+    // Delete all data in test DB
+    db.clear().unwrap();
+    assert!(!db.contains_key("nrs-to-process").unwrap());
+
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+
+    // A valid report
+    let valid_report_1 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report which reuses this nonce
+    let invalid_report_1 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // This is the same report
+    assert_eq!(valid_report_1, invalid_report_1);
+
+    // Report which reuses this nonce for a different bridge
+    let invalid_report_2 = NegativeReport::new(
+        bridges[1].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[1], date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report which uses this nonce but on a different day
+    let valid_report_2 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
+            &bridges[0],
+            date - 1,
+            nonce,
+        )),
+        "ru".to_string(),
+        date - 1,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report with different nonce
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+
+    let valid_report_3 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    let map_key_1 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", valid_report_1.fingerprint),
+        "ru".to_string(),
+        date
+    );
+    save_negative_report_to_process(&db, valid_report_1);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    save_negative_report_to_process(&db, invalid_report_1); // no change
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    let map_key_2 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", invalid_report_2.fingerprint),
+        "ru".to_string(),
+        date
+    );
+    save_negative_report_to_process(&db, invalid_report_2); // no change
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    assert!(!nrs_to_process.contains_key(&map_key_2));
+
+    let map_key_3 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", valid_report_2.fingerprint),
+        "ru".to_string(),
+        date - 1
+    );
+    save_negative_report_to_process(&db, valid_report_2);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    save_negative_report_to_process(&db, valid_report_3);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 2);
+
+    // Same tests, but use hash of bucket
+
+    // Delete all data in test DB
+    db.clear().unwrap();
+    assert!(!db.contains_key("nrs-to-process").unwrap());
+
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+
+    // A valid report
+    let valid_report_1 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report which reuses this nonce
+    let invalid_report_1 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // This is the same report
+    assert_eq!(valid_report_1, invalid_report_1);
+
+    // Report which reuses this nonce for a different bridge
+    let invalid_report_2 = NegativeReport::new(
+        bridges[1].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report which uses this nonce but on a different day
+    let valid_report_2 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date - 1, nonce)),
+        "ru".to_string(),
+        date - 1,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    // Report with different nonce
+    let mut nonce = [0; 32];
+    rng.fill_bytes(&mut nonce);
+
+    let valid_report_3 = NegativeReport::new(
+        bridges[0].fingerprint,
+        ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
+        "ru".to_string(),
+        date,
+        nonce,
+        BridgeDistributor::Lox,
+    );
+
+    let map_key_1 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", valid_report_1.fingerprint),
+        "ru".to_string(),
+        date
+    );
+    save_negative_report_to_process(&db, valid_report_1);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    save_negative_report_to_process(&db, invalid_report_1); // no change
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    let map_key_2 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", invalid_report_2.fingerprint),
+        "ru".to_string(),
+        date
+    );
+    save_negative_report_to_process(&db, invalid_report_2); // no change
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    assert!(!nrs_to_process.contains_key(&map_key_2));
+
+    let map_key_3 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", valid_report_2.fingerprint),
+        "ru".to_string(),
+        date - 1
+    );
+    save_negative_report_to_process(&db, valid_report_2);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
+    assert_eq!(negative_reports.len(), 1);
+
+    save_negative_report_to_process(&db, valid_report_3);
+    let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
+        bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
+    let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(negative_reports.len(), 2);
 }
 
 #[test]
@@ -469,4 +700,76 @@ fn test_positive_reports() {
 
     assert!(invalid_report_4.to_report().is_err());
     assert!(invalid_report_5.to_report().is_err());
+
+    // Test storing to-be-processed positive reports to database
+
+    // Create reports
+    let report_1 = PositiveReport::from_lox_credential(
+        bridges[0].fingerprint,
+        None,
+        &cred,
+        &th.ba.lox_pub,
+        "ru".to_string(),
+    )
+    .unwrap();
+    let report_2 = PositiveReport::from_lox_credential(
+        bridges[0].fingerprint,
+        None,
+        &cred,
+        &th.ba.lox_pub,
+        "ru".to_string(),
+    )
+    .unwrap();
+    let report_3 = PositiveReport::from_lox_credential(
+        bridges[1].fingerprint,
+        None,
+        &cred,
+        &th.ba.lox_pub,
+        "ru".to_string(),
+    )
+    .unwrap();
+
+    // Open test database
+    let db: Db = sled::open("test_db").unwrap();
+
+    // Delete all data in test DB
+    db.clear().unwrap();
+    assert!(!db.contains_key("prs-to-process").unwrap());
+
+    let map_key_1 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", report_1.fingerprint),
+        &report_1.country,
+        &report_1.date
+    );
+    let map_key_2 = format!(
+        "{}_{}_{}",
+        array_bytes::bytes2hex("", report_3.fingerprint),
+        &report_3.country,
+        &report_3.date
+    );
+
+    save_positive_report_to_process(&db, report_1);
+    let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
+        bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
+    let positive_reports = prs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(positive_reports.len(), 1);
+    assert!(!prs_to_process.contains_key(&map_key_2));
+
+    save_positive_report_to_process(&db, report_2);
+    let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
+        bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
+    let positive_reports = prs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(positive_reports.len(), 2);
+    assert!(!prs_to_process.contains_key(&map_key_2));
+
+    save_positive_report_to_process(&db, report_3);
+    let prs_to_process: BTreeMap<String, Vec<SerializablePositiveReport>> =
+        bincode::deserialize(&db.get("prs-to-process").unwrap().unwrap()).unwrap();
+    // Check that this has not changed
+    let positive_reports = prs_to_process.get(&map_key_1).unwrap();
+    assert_eq!(positive_reports.len(), 2);
+    // New report added to its own collection
+    let positive_reports = prs_to_process.get(&map_key_2).unwrap();
+    assert_eq!(positive_reports.len(), 1);
 }