diff --git a/src/negative_report.rs b/src/negative_report.rs index e6e870a..45a4480 100644 --- a/src/negative_report.rs +++ b/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], } diff --git a/src/tests.rs b/src/tests.rs index 8787b14..f437169 100644 --- a/src/tests.rs +++ b/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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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> = + 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); }