#![allow(clippy::indexing_slicing)]
use prikk_error::Result;
use super::{
TrustKeyEntry, TrustKeyRecordStatus, TrustPolicyRecordStatus, TrustPolicySnapshotEntry,
decode_trust_key_records, decode_trust_policy_records, encode_trust_key_record,
encode_trust_policy_record,
};
fn sample_public_key(seed: u8) -> [u8; 32] {
[seed; 32]
}
#[test]
fn a_single_key_entry_round_trips_through_decode() -> Result<()> {
let entry = TrustKeyEntry {
key_id: "maintainer".to_string(),
public_key: sample_public_key(1),
};
let bytes = encode_trust_key_record(&entry)?;
let replay = decode_trust_key_records(&bytes)?;
assert_eq!(replay.entries, vec![entry]);
assert_eq!(replay.trailing_partial_bytes, 0);
assert!(
replay
.record_outcomes
.iter()
.all(|outcome| matches!(outcome.status, TrustKeyRecordStatus::Evaluated))
);
Ok(())
}
#[test]
fn trailing_partial_key_bytes_are_tolerated_not_treated_as_corruption() -> Result<()> {
let entry = TrustKeyEntry {
key_id: "maintainer".to_string(),
public_key: sample_public_key(1),
};
let mut bytes = encode_trust_key_record(&entry)?;
bytes.extend_from_slice(&[0_u8; 10]);
let replay = decode_trust_key_records(&bytes)?;
assert_eq!(replay.entries, vec![entry]);
assert_eq!(replay.trailing_partial_bytes, 10);
Ok(())
}
#[test]
fn isolates_a_damaged_key_entry_and_reads_sound_entries_around_it() -> Result<()> {
let first = TrustKeyEntry {
key_id: "alice".to_string(),
public_key: sample_public_key(1),
};
let damaged_source = TrustKeyEntry {
key_id: "bob".to_string(),
public_key: sample_public_key(2),
};
let third = TrustKeyEntry {
key_id: "carol".to_string(),
public_key: sample_public_key(3),
};
let mut bytes = encode_trust_key_record(&first)?;
let damaged_start = bytes.len();
let damaged_record = encode_trust_key_record(&damaged_source)?;
let damaged_end = damaged_start + damaged_record.len();
bytes.extend_from_slice(&damaged_record);
bytes.extend_from_slice(&encode_trust_key_record(&third)?);
bytes[damaged_end - 1] ^= 0x01;
let replay = decode_trust_key_records(&bytes)?;
let failed_offsets: Vec<_> = replay
.record_outcomes
.iter()
.filter_map(|outcome| match &outcome.status {
TrustKeyRecordStatus::Failed { .. } => Some(outcome.offset),
TrustKeyRecordStatus::Evaluated => None,
})
.collect();
assert_eq!(failed_offsets, vec![damaged_start]);
assert_eq!(replay.entries, vec![first, third]);
Ok(())
}
#[test]
fn a_single_policy_snapshot_round_trips_through_decode() -> Result<()> {
let entry = TrustPolicySnapshotEntry {
key_ids: vec!["alice".to_string(), "bob".to_string()],
};
let bytes = encode_trust_policy_record(&entry)?;
let replay = decode_trust_policy_records(&bytes)?;
assert_eq!(replay.entries, vec![entry]);
assert_eq!(replay.trailing_partial_bytes, 0);
Ok(())
}
#[test]
fn a_second_snapshot_supersedes_the_first_at_replay() -> Result<()> {
let first = TrustPolicySnapshotEntry {
key_ids: vec!["alice".to_string(), "bob".to_string()],
};
let second = TrustPolicySnapshotEntry {
key_ids: vec!["alice".to_string()],
};
let mut bytes = encode_trust_policy_record(&first)?;
bytes.extend_from_slice(&encode_trust_policy_record(&second)?);
let replay = decode_trust_policy_records(&bytes)?;
assert_eq!(replay.entries, vec![first, second.clone()]);
assert_eq!(replay.entries.last(), Some(&second));
Ok(())
}
#[test]
fn decode_rejects_a_snapshot_listing_the_same_key_id_twice() -> Result<()> {
let entry = TrustPolicySnapshotEntry {
key_ids: vec!["alice".to_string(), "alice".to_string()],
};
let bytes = encode_trust_policy_record(&entry)?;
let replay = decode_trust_policy_records(&bytes)?;
assert!(replay.has_item_failure());
assert!(replay.record_outcomes.iter().any(
|outcome| matches!(&outcome.status, TrustPolicyRecordStatus::Failed { message }
if message.contains("more than once"))
));
Ok(())
}