170 lines
5.6 KiB
Rust
170 lines
5.6 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use rpki::storage::RocksStore;
|
|
use rpki::sync::rrdp::{Fetcher, parse_notification_snapshot, sync_from_notification_snapshot};
|
|
use sha2::Digest;
|
|
|
|
struct MapFetcher {
|
|
by_uri: HashMap<String, Vec<u8>>,
|
|
}
|
|
|
|
impl Fetcher for MapFetcher {
|
|
fn fetch(&self, uri: &str) -> Result<Vec<u8>, String> {
|
|
self.by_uri
|
|
.get(uri)
|
|
.cloned()
|
|
.ok_or_else(|| format!("not found: {uri}"))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn notification_parses_and_snapshot_is_applied_and_state_written() {
|
|
let notification_xml =
|
|
std::fs::read("tests/fixtures/rrdp/notification.xml").expect("read notification");
|
|
let snapshot_xml = std::fs::read("tests/fixtures/rrdp/snapshot.xml").expect("read snapshot");
|
|
|
|
let notif = parse_notification_snapshot(¬ification_xml).expect("parse notification");
|
|
assert_eq!(notif.serial, 1);
|
|
assert_eq!(notif.snapshot_uri, "https://example.net/rrdp/snapshot.xml");
|
|
|
|
let mut fetcher = MapFetcher {
|
|
by_uri: HashMap::new(),
|
|
};
|
|
fetcher.by_uri.insert(
|
|
"https://example.net/rrdp/snapshot.xml".to_string(),
|
|
snapshot_xml,
|
|
);
|
|
|
|
let temp = tempfile::tempdir().expect("tempdir");
|
|
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
|
|
|
let published = sync_from_notification_snapshot(
|
|
&store,
|
|
"https://example.net/rrdp/notification.xml",
|
|
¬ification_xml,
|
|
&fetcher,
|
|
)
|
|
.expect("sync");
|
|
assert_eq!(published, 2);
|
|
|
|
let obj1 = store
|
|
.get_raw("rsync://example.net/repo/obj1.cer")
|
|
.expect("get obj1")
|
|
.expect("obj1 exists");
|
|
assert_eq!(obj1, b"abc");
|
|
|
|
let obj2 = store
|
|
.get_raw("rsync://example.net/repo/obj2.crl")
|
|
.expect("get obj2")
|
|
.expect("obj2 exists");
|
|
assert_eq!(obj2, b"def");
|
|
|
|
let state_bytes = store
|
|
.get_rrdp_state("https://example.net/rrdp/notification.xml")
|
|
.expect("get state")
|
|
.expect("state exists");
|
|
let state = rpki::sync::rrdp::RrdpState::decode(&state_bytes).expect("decode state");
|
|
assert_eq!(
|
|
state.session_id,
|
|
"9df4b597-af9e-4dca-bdda-719cce2c4e28".to_string()
|
|
);
|
|
assert_eq!(state.serial, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn snapshot_hash_mismatch_is_rejected() {
|
|
let mut notification_xml =
|
|
String::from_utf8(std::fs::read("tests/fixtures/rrdp/notification.xml").unwrap()).unwrap();
|
|
notification_xml = notification_xml.replace(
|
|
"dcb1ce91401d568d7ddf7a4c9f70c65d8428c3a5e7135f82db99c4de30413551",
|
|
"0000000000000000000000000000000000000000000000000000000000000000",
|
|
);
|
|
|
|
let snapshot_xml = std::fs::read("tests/fixtures/rrdp/snapshot.xml").expect("read snapshot");
|
|
let fetcher = MapFetcher {
|
|
by_uri: HashMap::from([(
|
|
"https://example.net/rrdp/snapshot.xml".to_string(),
|
|
snapshot_xml,
|
|
)]),
|
|
};
|
|
|
|
let temp = tempfile::tempdir().expect("tempdir");
|
|
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
|
let err = sync_from_notification_snapshot(
|
|
&store,
|
|
"https://example.net/rrdp/notification.xml",
|
|
notification_xml.as_bytes(),
|
|
&fetcher,
|
|
)
|
|
.expect_err("hash mismatch rejected");
|
|
assert!(err.to_string().contains("hash mismatch"));
|
|
}
|
|
|
|
#[test]
|
|
fn session_id_mismatch_is_rejected() {
|
|
let notification_xml =
|
|
std::fs::read("tests/fixtures/rrdp/notification.xml").expect("read notification");
|
|
let mut snapshot_xml =
|
|
String::from_utf8(std::fs::read("tests/fixtures/rrdp/snapshot.xml").unwrap()).unwrap();
|
|
snapshot_xml = snapshot_xml.replace(
|
|
"9df4b597-af9e-4dca-bdda-719cce2c4e28",
|
|
"00000000-0000-4000-8000-000000000000",
|
|
);
|
|
let snapshot_xml = snapshot_xml.into_bytes();
|
|
|
|
// Recompute snapshot hash and patch notification to keep hash correct, so we test
|
|
// the session_id mismatch check.
|
|
let mut notif_str = String::from_utf8(notification_xml).unwrap();
|
|
let digest = sha2::Sha256::digest(&snapshot_xml);
|
|
let hex_hash = hex::encode(digest);
|
|
notif_str = notif_str.replace(
|
|
"dcb1ce91401d568d7ddf7a4c9f70c65d8428c3a5e7135f82db99c4de30413551",
|
|
&hex_hash,
|
|
);
|
|
|
|
let fetcher = MapFetcher {
|
|
by_uri: HashMap::from([(
|
|
"https://example.net/rrdp/snapshot.xml".to_string(),
|
|
snapshot_xml,
|
|
)]),
|
|
};
|
|
|
|
let temp = tempfile::tempdir().expect("tempdir");
|
|
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
|
let err = sync_from_notification_snapshot(
|
|
&store,
|
|
"https://example.net/rrdp/notification.xml",
|
|
notif_str.as_bytes(),
|
|
&fetcher,
|
|
)
|
|
.expect_err("session_id mismatch rejected");
|
|
|
|
assert!(err.to_string().contains("session_id mismatch"));
|
|
}
|
|
|
|
#[test]
|
|
fn serial_zero_is_rejected() {
|
|
let notification_xml =
|
|
String::from_utf8(std::fs::read("tests/fixtures/rrdp/notification.xml").unwrap()).unwrap();
|
|
let notification_xml = notification_xml.replace("serial=\"1\"", "serial=\"0\"");
|
|
|
|
let snapshot_xml = std::fs::read("tests/fixtures/rrdp/snapshot.xml").expect("read snapshot");
|
|
let fetcher = MapFetcher {
|
|
by_uri: HashMap::from([(
|
|
"https://example.net/rrdp/snapshot.xml".to_string(),
|
|
snapshot_xml,
|
|
)]),
|
|
};
|
|
|
|
let temp = tempfile::tempdir().expect("tempdir");
|
|
let store = RocksStore::open(temp.path()).expect("open rocksdb");
|
|
let err = sync_from_notification_snapshot(
|
|
&store,
|
|
"https://example.net/rrdp/notification.xml",
|
|
notification_xml.as_bytes(),
|
|
&fetcher,
|
|
)
|
|
.expect_err("serial=0 rejected");
|
|
assert!(err.to_string().contains("serial invalid"));
|
|
}
|