20260506_2 为CIR增加reject list基础能力
This commit is contained in:
parent
51663a9410
commit
752e746b97
95
src/bin/cir_dump_reject_list.rs
Normal file
95
src/bin/cir_dump_reject_list.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
struct Args {
|
||||
cir_path: Option<PathBuf>,
|
||||
limit: usize,
|
||||
}
|
||||
|
||||
fn usage() -> &'static str {
|
||||
"Usage: cir_dump_reject_list --cir <path> [--limit <n>]"
|
||||
}
|
||||
|
||||
fn parse_args(argv: &[String]) -> Result<Args, String> {
|
||||
let mut args = Args {
|
||||
cir_path: None,
|
||||
limit: 10,
|
||||
};
|
||||
let mut index = 1usize;
|
||||
while index < argv.len() {
|
||||
match argv[index].as_str() {
|
||||
"--cir" => {
|
||||
index += 1;
|
||||
let value = argv.get(index).ok_or("--cir requires a value")?;
|
||||
args.cir_path = Some(PathBuf::from(value));
|
||||
}
|
||||
"--limit" => {
|
||||
index += 1;
|
||||
let value = argv.get(index).ok_or("--limit requires a value")?;
|
||||
args.limit = value
|
||||
.parse::<usize>()
|
||||
.map_err(|_| format!("invalid --limit: {value}"))?;
|
||||
}
|
||||
"-h" | "--help" => return Err(usage().to_string()),
|
||||
other => return Err(format!("unknown argument: {other}\n{}", usage())),
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
if args.cir_path.is_none() {
|
||||
return Err(format!("--cir is required\n{}", usage()));
|
||||
}
|
||||
Ok(args)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if let Err(err) = real_main() {
|
||||
eprintln!("{err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
fn real_main() -> Result<(), String> {
|
||||
let argv: Vec<String> = std::env::args().collect();
|
||||
let args = parse_args(&argv)?;
|
||||
let cir_path = args.cir_path.expect("validated");
|
||||
let bytes = std::fs::read(&cir_path)
|
||||
.map_err(|e| format!("read cir failed: {}: {e}", cir_path.display()))?;
|
||||
let cir = rpki::cir::decode_cir(&bytes).map_err(|e| format!("decode cir failed: {e}"))?;
|
||||
|
||||
println!("reject_list_sha256={}", hex::encode(&cir.reject_list_sha256));
|
||||
println!("reject_count={}", cir.rejected_objects.len());
|
||||
for (index, item) in cir.rejected_objects.iter().take(args.limit).enumerate() {
|
||||
println!(
|
||||
"{:04} uri={} reason={}",
|
||||
index + 1,
|
||||
item.object_uri,
|
||||
item.reason.as_deref().unwrap_or("<none>")
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_requires_cir_path() {
|
||||
let err = parse_args(&["cir_dump_reject_list".to_string()]).expect_err("missing cir");
|
||||
assert!(err.contains("--cir is required"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_accepts_limit() {
|
||||
let args = parse_args(&[
|
||||
"cir_dump_reject_list".to_string(),
|
||||
"--cir".to_string(),
|
||||
"input.cir".to_string(),
|
||||
"--limit".to_string(),
|
||||
"5".to_string(),
|
||||
])
|
||||
.expect("parse args");
|
||||
assert_eq!(args.cir_path.as_deref(), Some(std::path::Path::new("input.cir")));
|
||||
assert_eq!(args.limit, 5);
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,8 @@ use std::path::PathBuf;
|
||||
|
||||
use rpki::blob_store::ExternalRepoBytesDb;
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir,
|
||||
};
|
||||
use sha2::Digest;
|
||||
|
||||
@ -108,7 +109,7 @@ fn main() -> Result<(), String> {
|
||||
.map_err(|e| format!("write repo bytes db failed: {e}"))?;
|
||||
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time,
|
||||
objects: vec![CirObject {
|
||||
@ -116,6 +117,8 @@ fn main() -> Result<(), String> {
|
||||
sha256: sha.to_vec(),
|
||||
}],
|
||||
tals: vec![CirTal { tal_uri, tal_bytes }],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let der = encode_cir(&cir).map_err(|e| format!("encode cir failed: {e}"))?;
|
||||
if let Some(parent) = cir_out.parent() {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::cir::model::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirRejectedObject,
|
||||
CirTal,
|
||||
};
|
||||
use crate::data_model::common::DerReader;
|
||||
use crate::data_model::oid::{OID_SHA256, OID_SHA256_RAW};
|
||||
@ -31,9 +32,9 @@ pub fn decode_cir(der: &[u8]) -> Result<CanonicalInputRepresentation, CirDecodeE
|
||||
}
|
||||
|
||||
let version = seq.take_uint_u64().map_err(CirDecodeError::Parse)? as u32;
|
||||
if version != CIR_VERSION_V1 {
|
||||
if version != CIR_VERSION_V2 {
|
||||
return Err(CirDecodeError::UnexpectedVersion {
|
||||
expected: CIR_VERSION_V1,
|
||||
expected: CIR_VERSION_V2,
|
||||
actual: version,
|
||||
});
|
||||
}
|
||||
@ -59,6 +60,21 @@ pub fn decode_cir(der: &[u8]) -> Result<CanonicalInputRepresentation, CirDecodeE
|
||||
tals.push(decode_tal(full)?);
|
||||
}
|
||||
|
||||
let reject_list_sha256 = seq
|
||||
.take_octet_string()
|
||||
.map_err(CirDecodeError::Parse)?
|
||||
.to_vec();
|
||||
|
||||
let rejected_der = seq.take_tag(0x30).map_err(CirDecodeError::Parse)?;
|
||||
let mut rejected_reader = DerReader::new(rejected_der);
|
||||
let mut rejected_objects = Vec::new();
|
||||
while !rejected_reader.is_empty() {
|
||||
let (_tag, full, _value) = rejected_reader
|
||||
.take_any_full()
|
||||
.map_err(CirDecodeError::Parse)?;
|
||||
rejected_objects.push(decode_rejected_object(full)?);
|
||||
}
|
||||
|
||||
if !seq.is_empty() {
|
||||
return Err(CirDecodeError::Parse("trailing fields in CIR".into()));
|
||||
}
|
||||
@ -69,6 +85,8 @@ pub fn decode_cir(der: &[u8]) -> Result<CanonicalInputRepresentation, CirDecodeE
|
||||
validation_time,
|
||||
objects,
|
||||
tals,
|
||||
reject_list_sha256,
|
||||
rejected_objects,
|
||||
};
|
||||
cir.validate().map_err(CirDecodeError::Validate)?;
|
||||
Ok(cir)
|
||||
@ -124,6 +142,34 @@ fn decode_tal(der: &[u8]) -> Result<CirTal, CirDecodeError> {
|
||||
Ok(CirTal { tal_uri, tal_bytes })
|
||||
}
|
||||
|
||||
fn decode_rejected_object(der: &[u8]) -> Result<CirRejectedObject, CirDecodeError> {
|
||||
let mut top = DerReader::new(der);
|
||||
let mut seq = top.take_sequence().map_err(CirDecodeError::Parse)?;
|
||||
if !top.is_empty() {
|
||||
return Err(CirDecodeError::Parse(
|
||||
"trailing bytes after CirRejectedObject".into(),
|
||||
));
|
||||
}
|
||||
let object_uri = std::str::from_utf8(seq.take_tag(0x16).map_err(CirDecodeError::Parse)?)
|
||||
.map_err(|e| CirDecodeError::Parse(e.to_string()))?
|
||||
.to_string();
|
||||
let reason = if seq.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
std::str::from_utf8(seq.take_octet_string().map_err(CirDecodeError::Parse)?)
|
||||
.map_err(|e| CirDecodeError::Parse(e.to_string()))?
|
||||
.to_string(),
|
||||
)
|
||||
};
|
||||
if !seq.is_empty() {
|
||||
return Err(CirDecodeError::Parse(
|
||||
"trailing fields in CirRejectedObject".into(),
|
||||
));
|
||||
}
|
||||
Ok(CirRejectedObject { object_uri, reason })
|
||||
}
|
||||
|
||||
fn oid_string(raw_body: &[u8]) -> Result<String, CirDecodeError> {
|
||||
let der = {
|
||||
let mut out = Vec::with_capacity(raw_body.len() + 2);
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
use crate::cir::model::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirRejectedObject,
|
||||
CirTal,
|
||||
};
|
||||
use crate::data_model::oid::OID_SHA256_RAW;
|
||||
|
||||
@ -29,6 +30,13 @@ pub fn encode_cir(cir: &CanonicalInputRepresentation) -> Result<Vec<u8>, CirEnco
|
||||
.map(encode_tal)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
),
|
||||
encode_octet_string(&cir.reject_list_sha256),
|
||||
encode_sequence(
|
||||
&cir.rejected_objects
|
||||
.iter()
|
||||
.map(encode_rejected_object)
|
||||
.collect::<Result<Vec<_>, _>>()?,
|
||||
),
|
||||
]))
|
||||
}
|
||||
|
||||
@ -48,6 +56,15 @@ fn encode_tal(tal: &CirTal) -> Result<Vec<u8>, CirEncodeError> {
|
||||
]))
|
||||
}
|
||||
|
||||
fn encode_rejected_object(item: &CirRejectedObject) -> Result<Vec<u8>, CirEncodeError> {
|
||||
item.validate().map_err(CirEncodeError::Validate)?;
|
||||
let mut fields = vec![encode_ia5_string(item.object_uri.as_bytes())];
|
||||
if let Some(reason) = &item.reason {
|
||||
fields.push(encode_octet_string(reason.as_bytes()));
|
||||
}
|
||||
Ok(encode_sequence(&fields))
|
||||
}
|
||||
|
||||
fn encode_generalized_time(t: time::OffsetDateTime) -> Vec<u8> {
|
||||
let t = t.to_offset(time::UtcOffset::UTC);
|
||||
let s = format!(
|
||||
@ -143,5 +160,5 @@ fn encode_len_into(len: usize, out: &mut Vec<u8>) {
|
||||
|
||||
#[allow(dead_code)]
|
||||
const _: () = {
|
||||
let _ = CIR_VERSION_V1;
|
||||
let _ = CIR_VERSION_V2;
|
||||
};
|
||||
|
||||
@ -5,7 +5,8 @@ use std::path::Path;
|
||||
use crate::audit::{AuditObjectResult, PublicationPointAudit};
|
||||
use crate::cir::encode::{CirEncodeError, encode_cir};
|
||||
use crate::cir::model::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirRejectedObject,
|
||||
CirTal, compute_reject_list_sha256,
|
||||
};
|
||||
use crate::cir::static_pool::{
|
||||
CirStaticPoolError, CirStaticPoolExportSummary, export_hashes_from_store,
|
||||
@ -181,7 +182,7 @@ pub fn build_cir_from_run_multi(
|
||||
tals.sort_by(|a, b| a.tal_uri.cmp(&b.tal_uri));
|
||||
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: validation_time.to_offset(time::UtcOffset::UTC),
|
||||
objects: objects
|
||||
@ -192,6 +193,35 @@ pub fn build_cir_from_run_multi(
|
||||
})
|
||||
.collect(),
|
||||
tals,
|
||||
reject_list_sha256: Vec::new(),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let object_uri_set = cir
|
||||
.objects
|
||||
.iter()
|
||||
.map(|item| item.rsync_uri.as_str())
|
||||
.collect::<BTreeSet<_>>();
|
||||
let mut rejected_objects = publication_points
|
||||
.iter()
|
||||
.flat_map(|pp| pp.objects.iter())
|
||||
.filter(|item| {
|
||||
item.result == AuditObjectResult::Error
|
||||
&& object_uri_set.contains(item.rsync_uri.as_str())
|
||||
})
|
||||
.map(|item| CirRejectedObject {
|
||||
object_uri: item.rsync_uri.clone(),
|
||||
reason: item.detail.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
rejected_objects.sort_by(|a, b| a.object_uri.cmp(&b.object_uri));
|
||||
rejected_objects.dedup_by(|a, b| a.object_uri == b.object_uri);
|
||||
|
||||
let cir = CanonicalInputRepresentation {
|
||||
reject_list_sha256: compute_reject_list_sha256(
|
||||
rejected_objects.iter().map(|item| item.object_uri.as_str()),
|
||||
),
|
||||
rejected_objects,
|
||||
..cir
|
||||
};
|
||||
cir.validate().map_err(CirExportError::Validate)?;
|
||||
Ok(cir)
|
||||
@ -464,7 +494,7 @@ mod tests {
|
||||
&[],
|
||||
)
|
||||
.expect("build cir");
|
||||
assert_eq!(cir.version, CIR_VERSION_V1);
|
||||
assert_eq!(cir.version, CIR_VERSION_V2);
|
||||
assert_eq!(cir.tals.len(), 1);
|
||||
assert_eq!(cir.tals[0].tal_uri, "https://example.test/root.tal");
|
||||
assert!(
|
||||
@ -738,6 +768,128 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cir_from_run_multi_exports_rejected_objects_from_error_audit_only() {
|
||||
let td = tempfile::tempdir().unwrap();
|
||||
let store = RocksStore::open(td.path()).unwrap();
|
||||
let ta = sample_trust_anchor();
|
||||
let current_repo_objects = vec![
|
||||
CurrentRepoObject {
|
||||
rsync_uri: "rsync://example.test/repo/a.roa".to_string(),
|
||||
current_hash_hex: "11".repeat(32),
|
||||
repository_source: "https://rrdp.example.test/notification.xml".to_string(),
|
||||
object_type: Some("roa".to_string()),
|
||||
},
|
||||
CurrentRepoObject {
|
||||
rsync_uri: "rsync://example.test/repo/b.asa".to_string(),
|
||||
current_hash_hex: "22".repeat(32),
|
||||
repository_source: "https://rrdp.example.test/notification.xml".to_string(),
|
||||
object_type: Some("aspa".to_string()),
|
||||
},
|
||||
];
|
||||
let publication_points = vec![PublicationPointAudit {
|
||||
objects: vec![
|
||||
crate::audit::ObjectAuditEntry {
|
||||
rsync_uri: "rsync://example.test/repo/a.roa".to_string(),
|
||||
sha256_hex: "11".repeat(32),
|
||||
kind: crate::audit::AuditObjectKind::Roa,
|
||||
result: crate::audit::AuditObjectResult::Error,
|
||||
detail: Some("invalid roa".to_string()),
|
||||
},
|
||||
crate::audit::ObjectAuditEntry {
|
||||
rsync_uri: "rsync://example.test/repo/b.asa".to_string(),
|
||||
sha256_hex: "22".repeat(32),
|
||||
kind: crate::audit::AuditObjectKind::Aspa,
|
||||
result: crate::audit::AuditObjectResult::Skipped,
|
||||
detail: Some("skipped".to_string()),
|
||||
},
|
||||
crate::audit::ObjectAuditEntry {
|
||||
rsync_uri: "rsync://example.test/repo/c.roa".to_string(),
|
||||
sha256_hex: "33".repeat(32),
|
||||
kind: crate::audit::AuditObjectKind::Roa,
|
||||
result: crate::audit::AuditObjectResult::Error,
|
||||
detail: Some("not in cir object set".to_string()),
|
||||
},
|
||||
],
|
||||
..PublicationPointAudit::default()
|
||||
}];
|
||||
|
||||
let cir = build_cir_from_run_multi(
|
||||
&store,
|
||||
&[CirTalBinding {
|
||||
trust_anchor: &ta,
|
||||
tal_uri: "https://example.test/root.tal",
|
||||
}],
|
||||
sample_time(),
|
||||
&publication_points,
|
||||
Some(¤t_repo_objects),
|
||||
)
|
||||
.expect("build cir");
|
||||
|
||||
assert_eq!(cir.rejected_objects.len(), 1);
|
||||
assert_eq!(
|
||||
cir.rejected_objects[0].object_uri,
|
||||
"rsync://example.test/repo/a.roa"
|
||||
);
|
||||
assert_eq!(
|
||||
cir.rejected_objects[0].reason.as_deref(),
|
||||
Some("invalid roa")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cir_from_run_multi_reject_digest_ignores_reason_text() {
|
||||
let td = tempfile::tempdir().unwrap();
|
||||
let store = RocksStore::open(td.path()).unwrap();
|
||||
let ta = sample_trust_anchor();
|
||||
let current_repo_objects = vec![CurrentRepoObject {
|
||||
rsync_uri: "rsync://example.test/repo/a.roa".to_string(),
|
||||
current_hash_hex: "11".repeat(32),
|
||||
repository_source: "https://rrdp.example.test/notification.xml".to_string(),
|
||||
object_type: Some("roa".to_string()),
|
||||
}];
|
||||
|
||||
let mk_pp = |detail: &str| PublicationPointAudit {
|
||||
objects: vec![crate::audit::ObjectAuditEntry {
|
||||
rsync_uri: "rsync://example.test/repo/a.roa".to_string(),
|
||||
sha256_hex: "11".repeat(32),
|
||||
kind: crate::audit::AuditObjectKind::Roa,
|
||||
result: crate::audit::AuditObjectResult::Error,
|
||||
detail: Some(detail.to_string()),
|
||||
}],
|
||||
..PublicationPointAudit::default()
|
||||
};
|
||||
|
||||
let cir_a = build_cir_from_run_multi(
|
||||
&store,
|
||||
&[CirTalBinding {
|
||||
trust_anchor: &ta,
|
||||
tal_uri: "https://example.test/root.tal",
|
||||
}],
|
||||
sample_time(),
|
||||
&[mk_pp("reason-a")],
|
||||
Some(¤t_repo_objects),
|
||||
)
|
||||
.expect("build cir a");
|
||||
let cir_b = build_cir_from_run_multi(
|
||||
&store,
|
||||
&[CirTalBinding {
|
||||
trust_anchor: &ta,
|
||||
tal_uri: "https://example.test/root.tal",
|
||||
}],
|
||||
sample_time(),
|
||||
&[mk_pp("reason-b")],
|
||||
Some(¤t_repo_objects),
|
||||
)
|
||||
.expect("build cir b");
|
||||
|
||||
assert_eq!(cir_a.reject_list_sha256, cir_b.reject_list_sha256);
|
||||
assert_ne!(
|
||||
cir_a.rejected_objects[0].reason,
|
||||
cir_b.rejected_objects[0].reason
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn build_cir_from_run_multi_rejects_invalid_tal_uri_and_missing_rsync_ta_uri() {
|
||||
let td = tempfile::tempdir().unwrap();
|
||||
|
||||
@ -415,7 +415,8 @@ mod tests {
|
||||
};
|
||||
use crate::blob_store::{ExternalRawStoreDb, ExternalRepoBytesDb};
|
||||
use crate::cir::model::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject,
|
||||
CirRejectedObject, CirTal, compute_reject_list_sha256,
|
||||
};
|
||||
use sha2::Digest;
|
||||
use std::path::{Path, PathBuf};
|
||||
@ -429,8 +430,12 @@ mod tests {
|
||||
}
|
||||
|
||||
fn sample_cir() -> CanonicalInputRepresentation {
|
||||
let rejected_objects = vec![CirRejectedObject {
|
||||
object_uri: "rsync://example.net/repo/rejected-a.roa".to_string(),
|
||||
reason: Some("invalid roa".to_string()),
|
||||
}];
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -453,12 +458,16 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(
|
||||
rejected_objects.iter().map(|item| item.object_uri.as_str()),
|
||||
),
|
||||
rejected_objects,
|
||||
}
|
||||
}
|
||||
|
||||
fn cir_with_real_hashes(a: &[u8], b: &[u8]) -> CanonicalInputRepresentation {
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -475,6 +484,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -618,7 +629,7 @@ mod tests {
|
||||
let a = b"a".to_vec();
|
||||
let b = b"b".to_vec();
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -635,6 +646,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
|
||||
{
|
||||
@ -739,7 +752,7 @@ mod tests {
|
||||
let raw_store_path = td.path().join("raw-store.db");
|
||||
let mirror_root = td.path().join("mirror");
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![CirObject {
|
||||
@ -753,6 +766,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
{
|
||||
let raw_store = ExternalRawStoreDb::open(&raw_store_path).unwrap();
|
||||
@ -789,7 +804,7 @@ mod tests {
|
||||
let a = b"a".to_vec();
|
||||
let b = b"b".to_vec();
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -806,6 +821,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
|
||||
{
|
||||
|
||||
@ -20,7 +20,8 @@ pub use materialize::{
|
||||
materialize_cir_from_repo_bytes, mirror_relative_path_for_rsync_uri, resolve_static_pool_file,
|
||||
};
|
||||
pub use model::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
CIR_VERSION_V1, CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject,
|
||||
CirRejectedObject, CirTal, compute_reject_list_sha256,
|
||||
};
|
||||
pub use sequence::{CirSequenceManifest, CirSequenceStep, CirSequenceStepKind};
|
||||
#[cfg(feature = "full")]
|
||||
@ -33,8 +34,8 @@ pub use static_pool::{
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
decode_cir, encode_cir,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject,
|
||||
CirRejectedObject, CirTal, compute_reject_list_sha256, decode_cir, encode_cir,
|
||||
};
|
||||
|
||||
fn sample_time() -> time::OffsetDateTime {
|
||||
@ -46,8 +47,18 @@ mod tests {
|
||||
}
|
||||
|
||||
fn sample_cir() -> CanonicalInputRepresentation {
|
||||
let rejected_objects = vec![
|
||||
CirRejectedObject {
|
||||
object_uri: "rsync://example.net/repo/rejected-a.roa".to_string(),
|
||||
reason: Some("invalid roa".to_string()),
|
||||
},
|
||||
CirRejectedObject {
|
||||
object_uri: "rsync://example.net/repo/rejected-b.asa".to_string(),
|
||||
reason: None,
|
||||
},
|
||||
];
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -65,6 +76,10 @@ mod tests {
|
||||
tal_bytes: b"https://tal.example.net/ta.cer\nrsync://example.net/repo/ta.cer\nMIIB"
|
||||
.to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(
|
||||
rejected_objects.iter().map(|item| item.object_uri.as_str()),
|
||||
),
|
||||
rejected_objects,
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,7 +114,7 @@ mod tests {
|
||||
#[test]
|
||||
fn cir_roundtrip_minimal_succeeds() {
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: Vec::new(),
|
||||
@ -107,6 +122,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/minimal.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/ta.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let der = encode_cir(&cir).expect("encode minimal cir");
|
||||
let decoded = decode_cir(&der).expect("decode minimal cir");
|
||||
@ -116,7 +133,7 @@ mod tests {
|
||||
#[test]
|
||||
fn cir_model_rejects_unsorted_duplicate_objects() {
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![
|
||||
@ -133,6 +150,8 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&cir).expect_err("unsorted objects must fail");
|
||||
assert!(err.to_string().contains("CIR.objects"), "{err}");
|
||||
@ -141,7 +160,7 @@ mod tests {
|
||||
#[test]
|
||||
fn cir_model_rejects_duplicate_tals() {
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: Vec::new(),
|
||||
@ -155,6 +174,8 @@ mod tests {
|
||||
tal_bytes: b"b".to_vec(),
|
||||
},
|
||||
],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&cir).expect_err("duplicate tals must fail");
|
||||
assert!(err.to_string().contains("CIR.tals"), "{err}");
|
||||
@ -165,9 +186,13 @@ mod tests {
|
||||
let mut der = encode_cir(&sample_cir()).expect("encode cir");
|
||||
let pos = der
|
||||
.windows(3)
|
||||
.position(|window| window == [0x02, 0x01, CIR_VERSION_V1 as u8])
|
||||
.position(|window| window == [0x02, 0x01, CIR_VERSION_V2 as u8])
|
||||
.or_else(|| {
|
||||
der.windows(3)
|
||||
.position(|window| window == [0x02, 0x01, CIR_VERSION_V2 as u8])
|
||||
})
|
||||
.expect("find version integer");
|
||||
der[pos + 2] = 2;
|
||||
der[pos + 2] = 3;
|
||||
let err = decode_cir(&der).expect_err("wrong version must fail");
|
||||
assert!(err.to_string().contains("unexpected CIR version"), "{err}");
|
||||
}
|
||||
@ -203,7 +228,7 @@ mod tests {
|
||||
#[test]
|
||||
fn cir_model_rejects_non_rsync_object_uri_and_empty_tals() {
|
||||
let bad_object = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![CirObject {
|
||||
@ -214,16 +239,20 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&bad_object).expect_err("non-rsync object uri must fail");
|
||||
assert!(err.to_string().contains("rsync://"), "{err}");
|
||||
|
||||
let no_tals = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: Vec::new(),
|
||||
tals: Vec::new(),
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&no_tals).expect_err("empty tals must fail");
|
||||
assert!(
|
||||
@ -235,7 +264,7 @@ mod tests {
|
||||
#[test]
|
||||
fn cir_model_rejects_non_utc_time_bad_hash_len_and_non_http_tal_uri() {
|
||||
let bad_time = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time().to_offset(time::UtcOffset::from_hms(8, 0, 0).unwrap()),
|
||||
objects: Vec::new(),
|
||||
@ -243,12 +272,14 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&bad_time).expect_err("non-utc validation time must fail");
|
||||
assert!(err.to_string().contains("UTC"), "{err}");
|
||||
|
||||
let bad_hash = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: vec![CirObject {
|
||||
@ -259,12 +290,14 @@ mod tests {
|
||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&bad_hash).expect_err("bad digest len must fail");
|
||||
assert!(err.to_string().contains("32 bytes"), "{err}");
|
||||
|
||||
let bad_tal_uri = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: sample_time(),
|
||||
objects: Vec::new(),
|
||||
@ -272,6 +305,8 @@ mod tests {
|
||||
tal_uri: "ftp://tal.example.net/root.tal".to_string(),
|
||||
tal_bytes: b"x".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let err = encode_cir(&bad_tal_uri).expect_err("bad tal uri must fail");
|
||||
assert!(err.to_string().contains("http:// or https://"), "{err}");
|
||||
@ -308,11 +343,13 @@ mod tests {
|
||||
let bad = test_encode_tlv(
|
||||
0x30,
|
||||
&[
|
||||
test_encode_tlv(0x02, &[CIR_VERSION_V1 as u8]),
|
||||
test_encode_tlv(0x02, &[CIR_VERSION_V2 as u8]),
|
||||
test_encode_tlv(0x06, crate::data_model::oid::OID_SHA256_RAW),
|
||||
test_encode_tlv(0x18, b"20260407123456Z"),
|
||||
test_encode_tlv(0x30, &object),
|
||||
test_encode_tlv(0x30, &tal),
|
||||
test_encode_tlv(0x04, &[0x33; 32]),
|
||||
test_encode_tlv(0x30, &[]),
|
||||
]
|
||||
.concat(),
|
||||
);
|
||||
@ -347,4 +384,17 @@ mod tests {
|
||||
let err = decode_cir(&der).expect_err("invalid utf8 tal uri must fail");
|
||||
assert!(err.to_string().contains("utf-8"), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cir_model_rejects_unsorted_rejected_objects_and_bad_digest() {
|
||||
let mut cir = sample_cir();
|
||||
cir.rejected_objects.swap(0, 1);
|
||||
let err = encode_cir(&cir).expect_err("unsorted rejected objects must fail");
|
||||
assert!(err.to_string().contains("CIR.rejectedObjects"), "{err}");
|
||||
|
||||
let mut cir = sample_cir();
|
||||
cir.reject_list_sha256 = vec![0x55; 32];
|
||||
let err = encode_cir(&cir).expect_err("bad reject list digest must fail");
|
||||
assert!(err.to_string().contains("rejectListSha256"), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
use crate::data_model::oid::OID_SHA256;
|
||||
|
||||
pub const CIR_VERSION_V1: u32 = 1;
|
||||
pub const CIR_VERSION_V2: u32 = 2;
|
||||
pub const DIGEST_LEN_SHA256: usize = 32;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@ -23,13 +24,15 @@ pub struct CanonicalInputRepresentation {
|
||||
pub validation_time: time::OffsetDateTime,
|
||||
pub objects: Vec<CirObject>,
|
||||
pub tals: Vec<CirTal>,
|
||||
pub reject_list_sha256: Vec<u8>,
|
||||
pub rejected_objects: Vec<CirRejectedObject>,
|
||||
}
|
||||
|
||||
impl CanonicalInputRepresentation {
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if self.version != CIR_VERSION_V1 {
|
||||
if self.version != CIR_VERSION_V2 {
|
||||
return Err(format!(
|
||||
"CIR version must be {CIR_VERSION_V1}, got {}",
|
||||
"CIR version must be {CIR_VERSION_V2}, got {}",
|
||||
self.version
|
||||
));
|
||||
}
|
||||
@ -47,15 +50,38 @@ impl CanonicalInputRepresentation {
|
||||
self.tals.iter().map(|item| item.tal_uri.as_str()),
|
||||
"CIR.tals must be sorted by talUri and unique",
|
||||
)?;
|
||||
validate_sorted_unique_strings(
|
||||
self.rejected_objects
|
||||
.iter()
|
||||
.map(|item| item.object_uri.as_str()),
|
||||
"CIR.rejectedObjects must be sorted by objectUri and unique",
|
||||
)?;
|
||||
if self.tals.is_empty() {
|
||||
return Err("CIR.tals must be non-empty".into());
|
||||
}
|
||||
if self.reject_list_sha256.len() != DIGEST_LEN_SHA256 {
|
||||
return Err(format!(
|
||||
"CIR.rejectListSha256 must be {DIGEST_LEN_SHA256} bytes, got {}",
|
||||
self.reject_list_sha256.len()
|
||||
));
|
||||
}
|
||||
for object in &self.objects {
|
||||
object.validate()?;
|
||||
}
|
||||
for tal in &self.tals {
|
||||
tal.validate()?;
|
||||
}
|
||||
for item in &self.rejected_objects {
|
||||
item.validate()?;
|
||||
}
|
||||
let expected_digest = compute_reject_list_sha256(
|
||||
self.rejected_objects
|
||||
.iter()
|
||||
.map(|item| item.object_uri.as_str()),
|
||||
);
|
||||
if self.reject_list_sha256 != expected_digest {
|
||||
return Err("CIR.rejectListSha256 does not match rejectedObjects".into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -105,6 +131,36 @@ impl CirTal {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct CirRejectedObject {
|
||||
pub object_uri: String,
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
||||
impl CirRejectedObject {
|
||||
pub fn validate(&self) -> Result<(), String> {
|
||||
if !self.object_uri.starts_with("rsync://") {
|
||||
return Err(format!(
|
||||
"CirRejectedObject.object_uri must start with rsync://, got {}",
|
||||
self.object_uri
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compute_reject_list_sha256<'a>(uris: impl IntoIterator<Item = &'a str>) -> Vec<u8> {
|
||||
use sha2::Digest;
|
||||
|
||||
let mut body = Vec::new();
|
||||
for uri in uris {
|
||||
let bytes = uri.as_bytes();
|
||||
body.extend_from_slice(&(bytes.len() as u32).to_be_bytes());
|
||||
body.extend_from_slice(bytes);
|
||||
}
|
||||
sha2::Sha256::digest(body).to_vec()
|
||||
}
|
||||
|
||||
fn validate_sorted_unique_strings<'a>(
|
||||
items: impl IntoIterator<Item = &'a str>,
|
||||
message: &str,
|
||||
|
||||
@ -6,7 +6,8 @@ use rpki::ccr::{
|
||||
encode_content_info,
|
||||
};
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir,
|
||||
};
|
||||
|
||||
fn skip_heavy_blackbox_test() -> bool {
|
||||
@ -43,7 +44,7 @@ fn cir_full_and_delta_pair_reuses_shared_repo_bytes_db() {
|
||||
};
|
||||
|
||||
let full_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:49:15Z",
|
||||
@ -58,9 +59,11 @@ fn cir_full_and_delta_pair_reuses_shared_repo_bytes_db() {
|
||||
tal_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let delta_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:50:15Z",
|
||||
@ -82,6 +85,8 @@ fn cir_full_and_delta_pair_reuses_shared_repo_bytes_db() {
|
||||
objects
|
||||
},
|
||||
tals: full_cir.tals.clone(),
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
|
||||
@ -7,7 +7,8 @@ use rpki::ccr::{
|
||||
encode_content_info,
|
||||
};
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir,
|
||||
};
|
||||
|
||||
#[test]
|
||||
@ -33,7 +34,7 @@ fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
|
||||
.expect("write repo bytes");
|
||||
|
||||
let cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-09T00:00:00Z",
|
||||
@ -48,6 +49,8 @@ fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
std::fs::write(&cir_path, encode_cir(&cir).unwrap()).unwrap();
|
||||
|
||||
|
||||
@ -3,8 +3,8 @@ use std::process::Command;
|
||||
|
||||
use rpki::blob_store::ExternalRepoBytesDb;
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
materialize_cir_from_repo_bytes,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||
};
|
||||
|
||||
fn skip_heavy_script_replay_test() -> bool {
|
||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
@ -51,6 +51,8 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
|
||||
@ -3,8 +3,8 @@ use std::process::Command;
|
||||
|
||||
use rpki::blob_store::ExternalRepoBytesDb;
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
materialize_cir_from_repo_bytes,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||
};
|
||||
|
||||
fn skip_heavy_script_replay_test() -> bool {
|
||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
@ -51,6 +51,8 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
|
||||
@ -6,7 +6,8 @@ use rpki::ccr::{
|
||||
encode_content_info,
|
||||
};
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir,
|
||||
};
|
||||
|
||||
fn skip_heavy_blackbox_test() -> bool {
|
||||
@ -35,7 +36,7 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
||||
.unwrap();
|
||||
|
||||
let mk_cir = |uri: &str, hash_hex: &str, vt: &str| CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
vt,
|
||||
@ -50,6 +51,8 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
||||
tal_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let full_hash = {
|
||||
use sha2::{Digest, Sha256};
|
||||
@ -65,7 +68,7 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
||||
"2026-03-16T11:49:15Z",
|
||||
);
|
||||
let delta_cir = CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-03-16T11:50:15Z",
|
||||
@ -84,6 +87,8 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
||||
objects
|
||||
},
|
||||
tals: full_cir.tals.clone(),
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
};
|
||||
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||
version: 0,
|
||||
|
||||
@ -3,8 +3,8 @@ use std::process::Command;
|
||||
|
||||
use rpki::blob_store::ExternalRepoBytesDb;
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
materialize_cir_from_repo_bytes,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||
};
|
||||
|
||||
fn skip_heavy_script_replay_test() -> bool {
|
||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
@ -51,6 +51,8 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
|
||||
@ -3,8 +3,8 @@ use std::process::Command;
|
||||
|
||||
use rpki::blob_store::ExternalRepoBytesDb;
|
||||
use rpki::cir::{
|
||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
||||
materialize_cir_from_repo_bytes,
|
||||
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||
};
|
||||
|
||||
fn skip_heavy_script_replay_test() -> bool {
|
||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
};
|
||||
(
|
||||
CanonicalInputRepresentation {
|
||||
version: CIR_VERSION_V1,
|
||||
version: CIR_VERSION_V2,
|
||||
hash_alg: CirHashAlgorithm::Sha256,
|
||||
validation_time: time::OffsetDateTime::parse(
|
||||
"2026-04-07T00:00:00Z",
|
||||
@ -51,6 +51,8 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
||||
tal_uri: "https://example.test/root.tal".to_string(),
|
||||
tal_bytes,
|
||||
}],
|
||||
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||
rejected_objects: Vec::new(),
|
||||
},
|
||||
ta_bytes,
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user