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::blob_store::ExternalRepoBytesDb;
|
||||||
use rpki::cir::{
|
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;
|
use sha2::Digest;
|
||||||
|
|
||||||
@ -108,7 +109,7 @@ fn main() -> Result<(), String> {
|
|||||||
.map_err(|e| format!("write repo bytes db failed: {e}"))?;
|
.map_err(|e| format!("write repo bytes db failed: {e}"))?;
|
||||||
|
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time,
|
validation_time,
|
||||||
objects: vec![CirObject {
|
objects: vec![CirObject {
|
||||||
@ -116,6 +117,8 @@ fn main() -> Result<(), String> {
|
|||||||
sha256: sha.to_vec(),
|
sha256: sha.to_vec(),
|
||||||
}],
|
}],
|
||||||
tals: vec![CirTal { tal_uri, tal_bytes }],
|
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}"))?;
|
let der = encode_cir(&cir).map_err(|e| format!("encode cir failed: {e}"))?;
|
||||||
if let Some(parent) = cir_out.parent() {
|
if let Some(parent) = cir_out.parent() {
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use crate::cir::model::{
|
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::common::DerReader;
|
||||||
use crate::data_model::oid::{OID_SHA256, OID_SHA256_RAW};
|
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;
|
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 {
|
return Err(CirDecodeError::UnexpectedVersion {
|
||||||
expected: CIR_VERSION_V1,
|
expected: CIR_VERSION_V2,
|
||||||
actual: version,
|
actual: version,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -59,6 +60,21 @@ pub fn decode_cir(der: &[u8]) -> Result<CanonicalInputRepresentation, CirDecodeE
|
|||||||
tals.push(decode_tal(full)?);
|
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() {
|
if !seq.is_empty() {
|
||||||
return Err(CirDecodeError::Parse("trailing fields in CIR".into()));
|
return Err(CirDecodeError::Parse("trailing fields in CIR".into()));
|
||||||
}
|
}
|
||||||
@ -69,6 +85,8 @@ pub fn decode_cir(der: &[u8]) -> Result<CanonicalInputRepresentation, CirDecodeE
|
|||||||
validation_time,
|
validation_time,
|
||||||
objects,
|
objects,
|
||||||
tals,
|
tals,
|
||||||
|
reject_list_sha256,
|
||||||
|
rejected_objects,
|
||||||
};
|
};
|
||||||
cir.validate().map_err(CirDecodeError::Validate)?;
|
cir.validate().map_err(CirDecodeError::Validate)?;
|
||||||
Ok(cir)
|
Ok(cir)
|
||||||
@ -124,6 +142,34 @@ fn decode_tal(der: &[u8]) -> Result<CirTal, CirDecodeError> {
|
|||||||
Ok(CirTal { tal_uri, tal_bytes })
|
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> {
|
fn oid_string(raw_body: &[u8]) -> Result<String, CirDecodeError> {
|
||||||
let der = {
|
let der = {
|
||||||
let mut out = Vec::with_capacity(raw_body.len() + 2);
|
let mut out = Vec::with_capacity(raw_body.len() + 2);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
use crate::cir::model::{
|
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;
|
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)
|
.map(encode_tal)
|
||||||
.collect::<Result<Vec<_>, _>>()?,
|
.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> {
|
fn encode_generalized_time(t: time::OffsetDateTime) -> Vec<u8> {
|
||||||
let t = t.to_offset(time::UtcOffset::UTC);
|
let t = t.to_offset(time::UtcOffset::UTC);
|
||||||
let s = format!(
|
let s = format!(
|
||||||
@ -143,5 +160,5 @@ fn encode_len_into(len: usize, out: &mut Vec<u8>) {
|
|||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
const _: () = {
|
const _: () = {
|
||||||
let _ = CIR_VERSION_V1;
|
let _ = CIR_VERSION_V2;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,7 +5,8 @@ use std::path::Path;
|
|||||||
use crate::audit::{AuditObjectResult, PublicationPointAudit};
|
use crate::audit::{AuditObjectResult, PublicationPointAudit};
|
||||||
use crate::cir::encode::{CirEncodeError, encode_cir};
|
use crate::cir::encode::{CirEncodeError, encode_cir};
|
||||||
use crate::cir::model::{
|
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::{
|
use crate::cir::static_pool::{
|
||||||
CirStaticPoolError, CirStaticPoolExportSummary, export_hashes_from_store,
|
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));
|
tals.sort_by(|a, b| a.tal_uri.cmp(&b.tal_uri));
|
||||||
|
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: validation_time.to_offset(time::UtcOffset::UTC),
|
validation_time: validation_time.to_offset(time::UtcOffset::UTC),
|
||||||
objects: objects
|
objects: objects
|
||||||
@ -192,6 +193,35 @@ pub fn build_cir_from_run_multi(
|
|||||||
})
|
})
|
||||||
.collect(),
|
.collect(),
|
||||||
tals,
|
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)?;
|
cir.validate().map_err(CirExportError::Validate)?;
|
||||||
Ok(cir)
|
Ok(cir)
|
||||||
@ -464,7 +494,7 @@ mod tests {
|
|||||||
&[],
|
&[],
|
||||||
)
|
)
|
||||||
.expect("build cir");
|
.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.len(), 1);
|
||||||
assert_eq!(cir.tals[0].tal_uri, "https://example.test/root.tal");
|
assert_eq!(cir.tals[0].tal_uri, "https://example.test/root.tal");
|
||||||
assert!(
|
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]
|
#[test]
|
||||||
fn build_cir_from_run_multi_rejects_invalid_tal_uri_and_missing_rsync_ta_uri() {
|
fn build_cir_from_run_multi_rejects_invalid_tal_uri_and_missing_rsync_ta_uri() {
|
||||||
let td = tempfile::tempdir().unwrap();
|
let td = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
@ -415,7 +415,8 @@ mod tests {
|
|||||||
};
|
};
|
||||||
use crate::blob_store::{ExternalRawStoreDb, ExternalRepoBytesDb};
|
use crate::blob_store::{ExternalRawStoreDb, ExternalRepoBytesDb};
|
||||||
use crate::cir::model::{
|
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 sha2::Digest;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -429,8 +430,12 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sample_cir() -> CanonicalInputRepresentation {
|
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 {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -453,12 +458,16 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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 {
|
fn cir_with_real_hashes(a: &[u8], b: &[u8]) -> CanonicalInputRepresentation {
|
||||||
CanonicalInputRepresentation {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -475,6 +484,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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 a = b"a".to_vec();
|
||||||
let b = b"b".to_vec();
|
let b = b"b".to_vec();
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -635,6 +646,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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 raw_store_path = td.path().join("raw-store.db");
|
||||||
let mirror_root = td.path().join("mirror");
|
let mirror_root = td.path().join("mirror");
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![CirObject {
|
objects: vec![CirObject {
|
||||||
@ -753,6 +766,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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();
|
let raw_store = ExternalRawStoreDb::open(&raw_store_path).unwrap();
|
||||||
@ -789,7 +804,7 @@ mod tests {
|
|||||||
let a = b"a".to_vec();
|
let a = b"a".to_vec();
|
||||||
let b = b"b".to_vec();
|
let b = b"b".to_vec();
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -806,6 +821,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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,
|
materialize_cir_from_repo_bytes, mirror_relative_path_for_rsync_uri, resolve_static_pool_file,
|
||||||
};
|
};
|
||||||
pub use model::{
|
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};
|
pub use sequence::{CirSequenceManifest, CirSequenceStep, CirSequenceStepKind};
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
@ -33,8 +34,8 @@ pub use static_pool::{
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::{
|
use super::{
|
||||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject,
|
||||||
decode_cir, encode_cir,
|
CirRejectedObject, CirTal, compute_reject_list_sha256, decode_cir, encode_cir,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn sample_time() -> time::OffsetDateTime {
|
fn sample_time() -> time::OffsetDateTime {
|
||||||
@ -46,8 +47,18 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sample_cir() -> CanonicalInputRepresentation {
|
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 {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -65,6 +76,10 @@ mod tests {
|
|||||||
tal_bytes: b"https://tal.example.net/ta.cer\nrsync://example.net/repo/ta.cer\nMIIB"
|
tal_bytes: b"https://tal.example.net/ta.cer\nrsync://example.net/repo/ta.cer\nMIIB"
|
||||||
.to_vec(),
|
.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]
|
#[test]
|
||||||
fn cir_roundtrip_minimal_succeeds() {
|
fn cir_roundtrip_minimal_succeeds() {
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: Vec::new(),
|
objects: Vec::new(),
|
||||||
@ -107,6 +122,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/minimal.tal".to_string(),
|
tal_uri: "https://tal.example.net/minimal.tal".to_string(),
|
||||||
tal_bytes: b"rsync://example.net/repo/ta.cer\nMIIB".to_vec(),
|
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 der = encode_cir(&cir).expect("encode minimal cir");
|
||||||
let decoded = decode_cir(&der).expect("decode minimal cir");
|
let decoded = decode_cir(&der).expect("decode minimal cir");
|
||||||
@ -116,7 +133,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cir_model_rejects_unsorted_duplicate_objects() {
|
fn cir_model_rejects_unsorted_duplicate_objects() {
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![
|
objects: vec![
|
||||||
@ -133,6 +150,8 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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");
|
let err = encode_cir(&cir).expect_err("unsorted objects must fail");
|
||||||
assert!(err.to_string().contains("CIR.objects"), "{err}");
|
assert!(err.to_string().contains("CIR.objects"), "{err}");
|
||||||
@ -141,7 +160,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cir_model_rejects_duplicate_tals() {
|
fn cir_model_rejects_duplicate_tals() {
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: Vec::new(),
|
objects: Vec::new(),
|
||||||
@ -155,6 +174,8 @@ mod tests {
|
|||||||
tal_bytes: b"b".to_vec(),
|
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");
|
let err = encode_cir(&cir).expect_err("duplicate tals must fail");
|
||||||
assert!(err.to_string().contains("CIR.tals"), "{err}");
|
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 mut der = encode_cir(&sample_cir()).expect("encode cir");
|
||||||
let pos = der
|
let pos = der
|
||||||
.windows(3)
|
.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");
|
.expect("find version integer");
|
||||||
der[pos + 2] = 2;
|
der[pos + 2] = 3;
|
||||||
let err = decode_cir(&der).expect_err("wrong version must fail");
|
let err = decode_cir(&der).expect_err("wrong version must fail");
|
||||||
assert!(err.to_string().contains("unexpected CIR version"), "{err}");
|
assert!(err.to_string().contains("unexpected CIR version"), "{err}");
|
||||||
}
|
}
|
||||||
@ -203,7 +228,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cir_model_rejects_non_rsync_object_uri_and_empty_tals() {
|
fn cir_model_rejects_non_rsync_object_uri_and_empty_tals() {
|
||||||
let bad_object = CanonicalInputRepresentation {
|
let bad_object = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![CirObject {
|
objects: vec![CirObject {
|
||||||
@ -214,16 +239,20 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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");
|
let err = encode_cir(&bad_object).expect_err("non-rsync object uri must fail");
|
||||||
assert!(err.to_string().contains("rsync://"), "{err}");
|
assert!(err.to_string().contains("rsync://"), "{err}");
|
||||||
|
|
||||||
let no_tals = CanonicalInputRepresentation {
|
let no_tals = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: Vec::new(),
|
objects: Vec::new(),
|
||||||
tals: 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");
|
let err = encode_cir(&no_tals).expect_err("empty tals must fail");
|
||||||
assert!(
|
assert!(
|
||||||
@ -235,7 +264,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cir_model_rejects_non_utc_time_bad_hash_len_and_non_http_tal_uri() {
|
fn cir_model_rejects_non_utc_time_bad_hash_len_and_non_http_tal_uri() {
|
||||||
let bad_time = CanonicalInputRepresentation {
|
let bad_time = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time().to_offset(time::UtcOffset::from_hms(8, 0, 0).unwrap()),
|
validation_time: sample_time().to_offset(time::UtcOffset::from_hms(8, 0, 0).unwrap()),
|
||||||
objects: Vec::new(),
|
objects: Vec::new(),
|
||||||
@ -243,12 +272,14 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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");
|
let err = encode_cir(&bad_time).expect_err("non-utc validation time must fail");
|
||||||
assert!(err.to_string().contains("UTC"), "{err}");
|
assert!(err.to_string().contains("UTC"), "{err}");
|
||||||
|
|
||||||
let bad_hash = CanonicalInputRepresentation {
|
let bad_hash = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: vec![CirObject {
|
objects: vec![CirObject {
|
||||||
@ -259,12 +290,14 @@ mod tests {
|
|||||||
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
tal_uri: "https://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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");
|
let err = encode_cir(&bad_hash).expect_err("bad digest len must fail");
|
||||||
assert!(err.to_string().contains("32 bytes"), "{err}");
|
assert!(err.to_string().contains("32 bytes"), "{err}");
|
||||||
|
|
||||||
let bad_tal_uri = CanonicalInputRepresentation {
|
let bad_tal_uri = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: sample_time(),
|
validation_time: sample_time(),
|
||||||
objects: Vec::new(),
|
objects: Vec::new(),
|
||||||
@ -272,6 +305,8 @@ mod tests {
|
|||||||
tal_uri: "ftp://tal.example.net/root.tal".to_string(),
|
tal_uri: "ftp://tal.example.net/root.tal".to_string(),
|
||||||
tal_bytes: b"x".to_vec(),
|
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");
|
let err = encode_cir(&bad_tal_uri).expect_err("bad tal uri must fail");
|
||||||
assert!(err.to_string().contains("http:// or https://"), "{err}");
|
assert!(err.to_string().contains("http:// or https://"), "{err}");
|
||||||
@ -308,11 +343,13 @@ mod tests {
|
|||||||
let bad = test_encode_tlv(
|
let bad = test_encode_tlv(
|
||||||
0x30,
|
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(0x06, crate::data_model::oid::OID_SHA256_RAW),
|
||||||
test_encode_tlv(0x18, b"20260407123456Z"),
|
test_encode_tlv(0x18, b"20260407123456Z"),
|
||||||
test_encode_tlv(0x30, &object),
|
test_encode_tlv(0x30, &object),
|
||||||
test_encode_tlv(0x30, &tal),
|
test_encode_tlv(0x30, &tal),
|
||||||
|
test_encode_tlv(0x04, &[0x33; 32]),
|
||||||
|
test_encode_tlv(0x30, &[]),
|
||||||
]
|
]
|
||||||
.concat(),
|
.concat(),
|
||||||
);
|
);
|
||||||
@ -347,4 +384,17 @@ mod tests {
|
|||||||
let err = decode_cir(&der).expect_err("invalid utf8 tal uri must fail");
|
let err = decode_cir(&der).expect_err("invalid utf8 tal uri must fail");
|
||||||
assert!(err.to_string().contains("utf-8"), "{err}");
|
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;
|
use crate::data_model::oid::OID_SHA256;
|
||||||
|
|
||||||
pub const CIR_VERSION_V1: u32 = 1;
|
pub const CIR_VERSION_V1: u32 = 1;
|
||||||
|
pub const CIR_VERSION_V2: u32 = 2;
|
||||||
pub const DIGEST_LEN_SHA256: usize = 32;
|
pub const DIGEST_LEN_SHA256: usize = 32;
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
@ -23,13 +24,15 @@ pub struct CanonicalInputRepresentation {
|
|||||||
pub validation_time: time::OffsetDateTime,
|
pub validation_time: time::OffsetDateTime,
|
||||||
pub objects: Vec<CirObject>,
|
pub objects: Vec<CirObject>,
|
||||||
pub tals: Vec<CirTal>,
|
pub tals: Vec<CirTal>,
|
||||||
|
pub reject_list_sha256: Vec<u8>,
|
||||||
|
pub rejected_objects: Vec<CirRejectedObject>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CanonicalInputRepresentation {
|
impl CanonicalInputRepresentation {
|
||||||
pub fn validate(&self) -> Result<(), String> {
|
pub fn validate(&self) -> Result<(), String> {
|
||||||
if self.version != CIR_VERSION_V1 {
|
if self.version != CIR_VERSION_V2 {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
"CIR version must be {CIR_VERSION_V1}, got {}",
|
"CIR version must be {CIR_VERSION_V2}, got {}",
|
||||||
self.version
|
self.version
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@ -47,15 +50,38 @@ impl CanonicalInputRepresentation {
|
|||||||
self.tals.iter().map(|item| item.tal_uri.as_str()),
|
self.tals.iter().map(|item| item.tal_uri.as_str()),
|
||||||
"CIR.tals must be sorted by talUri and unique",
|
"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() {
|
if self.tals.is_empty() {
|
||||||
return Err("CIR.tals must be non-empty".into());
|
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 {
|
for object in &self.objects {
|
||||||
object.validate()?;
|
object.validate()?;
|
||||||
}
|
}
|
||||||
for tal in &self.tals {
|
for tal in &self.tals {
|
||||||
tal.validate()?;
|
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(())
|
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>(
|
fn validate_sorted_unique_strings<'a>(
|
||||||
items: impl IntoIterator<Item = &'a str>,
|
items: impl IntoIterator<Item = &'a str>,
|
||||||
message: &str,
|
message: &str,
|
||||||
|
|||||||
@ -6,7 +6,8 @@ use rpki::ccr::{
|
|||||||
encode_content_info,
|
encode_content_info,
|
||||||
};
|
};
|
||||||
use rpki::cir::{
|
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 {
|
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 {
|
let full_cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-03-16T11:49:15Z",
|
"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_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
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 {
|
let delta_cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-03-16T11:50:15Z",
|
"2026-03-16T11:50:15Z",
|
||||||
@ -82,6 +85,8 @@ fn cir_full_and_delta_pair_reuses_shared_repo_bytes_db() {
|
|||||||
objects
|
objects
|
||||||
},
|
},
|
||||||
tals: full_cir.tals.clone(),
|
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 {
|
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||||
version: 0,
|
version: 0,
|
||||||
|
|||||||
@ -7,7 +7,8 @@ use rpki::ccr::{
|
|||||||
encode_content_info,
|
encode_content_info,
|
||||||
};
|
};
|
||||||
use rpki::cir::{
|
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]
|
#[test]
|
||||||
@ -33,7 +34,7 @@ fn cir_drop_report_counts_dropped_roa_objects_and_vrps() {
|
|||||||
.expect("write repo bytes");
|
.expect("write repo bytes");
|
||||||
|
|
||||||
let cir = CanonicalInputRepresentation {
|
let cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-04-09T00:00:00Z",
|
"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_uri: "https://example.test/root.tal".to_string(),
|
||||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
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();
|
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::blob_store::ExternalRepoBytesDb;
|
||||||
use rpki::cir::{
|
use rpki::cir::{
|
||||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||||
materialize_cir_from_repo_bytes,
|
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn skip_heavy_script_replay_test() -> bool {
|
fn skip_heavy_script_replay_test() -> bool {
|
||||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
|||||||
};
|
};
|
||||||
(
|
(
|
||||||
CanonicalInputRepresentation {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-04-07T00:00:00Z",
|
"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_uri: "https://example.test/root.tal".to_string(),
|
||||||
tal_bytes,
|
tal_bytes,
|
||||||
}],
|
}],
|
||||||
|
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||||
|
rejected_objects: Vec::new(),
|
||||||
},
|
},
|
||||||
ta_bytes,
|
ta_bytes,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,8 +3,8 @@ use std::process::Command;
|
|||||||
|
|
||||||
use rpki::blob_store::ExternalRepoBytesDb;
|
use rpki::blob_store::ExternalRepoBytesDb;
|
||||||
use rpki::cir::{
|
use rpki::cir::{
|
||||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||||
materialize_cir_from_repo_bytes,
|
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn skip_heavy_script_replay_test() -> bool {
|
fn skip_heavy_script_replay_test() -> bool {
|
||||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
|||||||
};
|
};
|
||||||
(
|
(
|
||||||
CanonicalInputRepresentation {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-04-07T00:00:00Z",
|
"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_uri: "https://example.test/root.tal".to_string(),
|
||||||
tal_bytes,
|
tal_bytes,
|
||||||
}],
|
}],
|
||||||
|
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||||
|
rejected_objects: Vec::new(),
|
||||||
},
|
},
|
||||||
ta_bytes,
|
ta_bytes,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -6,7 +6,8 @@ use rpki::ccr::{
|
|||||||
encode_content_info,
|
encode_content_info,
|
||||||
};
|
};
|
||||||
use rpki::cir::{
|
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 {
|
fn skip_heavy_blackbox_test() -> bool {
|
||||||
@ -35,7 +36,7 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let mk_cir = |uri: &str, hash_hex: &str, vt: &str| CanonicalInputRepresentation {
|
let mk_cir = |uri: &str, hash_hex: &str, vt: &str| CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
vt,
|
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_uri: "https://rpki.apnic.net/tal/apnic-rfc7730-https.tal".to_string(),
|
||||||
tal_bytes: b"rsync://example.net/repo/root.cer\nMIIB".to_vec(),
|
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 = {
|
let full_hash = {
|
||||||
use sha2::{Digest, Sha256};
|
use sha2::{Digest, Sha256};
|
||||||
@ -65,7 +68,7 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
|||||||
"2026-03-16T11:49:15Z",
|
"2026-03-16T11:49:15Z",
|
||||||
);
|
);
|
||||||
let delta_cir = CanonicalInputRepresentation {
|
let delta_cir = CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-03-16T11:50:15Z",
|
"2026-03-16T11:50:15Z",
|
||||||
@ -84,6 +87,8 @@ fn cir_offline_sequence_writes_parseable_sequence_json_and_steps() {
|
|||||||
objects
|
objects
|
||||||
},
|
},
|
||||||
tals: full_cir.tals.clone(),
|
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 {
|
let empty_ccr = CcrContentInfo::new(RpkiCanonicalCacheRepresentation {
|
||||||
version: 0,
|
version: 0,
|
||||||
|
|||||||
@ -3,8 +3,8 @@ use std::process::Command;
|
|||||||
|
|
||||||
use rpki::blob_store::ExternalRepoBytesDb;
|
use rpki::blob_store::ExternalRepoBytesDb;
|
||||||
use rpki::cir::{
|
use rpki::cir::{
|
||||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||||
materialize_cir_from_repo_bytes,
|
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn skip_heavy_script_replay_test() -> bool {
|
fn skip_heavy_script_replay_test() -> bool {
|
||||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
|||||||
};
|
};
|
||||||
(
|
(
|
||||||
CanonicalInputRepresentation {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-04-07T00:00:00Z",
|
"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_uri: "https://example.test/root.tal".to_string(),
|
||||||
tal_bytes,
|
tal_bytes,
|
||||||
}],
|
}],
|
||||||
|
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||||
|
rejected_objects: Vec::new(),
|
||||||
},
|
},
|
||||||
ta_bytes,
|
ta_bytes,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -3,8 +3,8 @@ use std::process::Command;
|
|||||||
|
|
||||||
use rpki::blob_store::ExternalRepoBytesDb;
|
use rpki::blob_store::ExternalRepoBytesDb;
|
||||||
use rpki::cir::{
|
use rpki::cir::{
|
||||||
CIR_VERSION_V1, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal, encode_cir,
|
CIR_VERSION_V2, CanonicalInputRepresentation, CirHashAlgorithm, CirObject, CirTal,
|
||||||
materialize_cir_from_repo_bytes,
|
compute_reject_list_sha256, encode_cir, materialize_cir_from_repo_bytes,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn skip_heavy_script_replay_test() -> bool {
|
fn skip_heavy_script_replay_test() -> bool {
|
||||||
@ -36,7 +36,7 @@ fn build_ta_only_cir() -> (CanonicalInputRepresentation, Vec<u8>) {
|
|||||||
};
|
};
|
||||||
(
|
(
|
||||||
CanonicalInputRepresentation {
|
CanonicalInputRepresentation {
|
||||||
version: CIR_VERSION_V1,
|
version: CIR_VERSION_V2,
|
||||||
hash_alg: CirHashAlgorithm::Sha256,
|
hash_alg: CirHashAlgorithm::Sha256,
|
||||||
validation_time: time::OffsetDateTime::parse(
|
validation_time: time::OffsetDateTime::parse(
|
||||||
"2026-04-07T00:00:00Z",
|
"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_uri: "https://example.test/root.tal".to_string(),
|
||||||
tal_bytes,
|
tal_bytes,
|
||||||
}],
|
}],
|
||||||
|
reject_list_sha256: compute_reject_list_sha256(std::iter::empty::<&str>()),
|
||||||
|
rejected_objects: Vec::new(),
|
||||||
},
|
},
|
||||||
ta_bytes,
|
ta_bytes,
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user