use super::*;
use alloc::vec;
macro_rules! hex {
($s:literal) => {{
const S: &str = $s;
const N: usize = S.len() / 2;
const fn nib(c: u8) -> u8 {
match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => 0,
}
}
const fn decode() -> [u8; N] {
let bytes = S.as_bytes();
let mut out = [0u8; N];
let mut i = 0;
while i < N {
out[i] = (nib(bytes[i * 2]) << 4) | nib(bytes[i * 2 + 1]);
i += 1;
}
out
}
decode()
}};
}
const CA_BLOB: &[u8] = &hex!(
"0000000b7373682d65643235353139000000203659ea41e0a2e1c0267e70c0254840aef8de6f3200bd86b2400bcd6261d9279a"
);
const USERKEY_BLOB: &[u8] = &hex!(
"0000000b7373682d6564323535313900000020dac8c367fff423cc766d20abff4f2620880f421ea7e2c58a47100892e490547f"
);
const KRL_SERIAL: &[u8] = &hex!(
"5353484b524c0a00000000010000000000000000000000006a31ff2800000000000000000000000000000000010000005d000000330000000b7373682d65643235353139000000203659ea41e0a2e1c0267e70c0254840aef8de6f3200bd86b2400bcd6261d9279a000000002000000008000000000000000a2100000010000000000000006400000000000000c8"
);
const KRL_EXPLICIT: &[u8] = &hex!(
"5353484b524c0a00000000010000000000000000000000006a31ff28000000000000000000000000000000000200000037000000330000000b7373682d6564323535313900000020dac8c367fff423cc766d20abff4f2620880f421ea7e2c58a47100892e490547f"
);
const KRL_KEYID: &[u8] = &hex!(
"5353484b524c0a00000000010000000000000000000000006a31ff28000000000000000000000000000000000100000050000000330000000b7373682d65643235353139000000203659ea41e0a2e1c0267e70c0254840aef8de6f3200bd86b2400bcd6261d9279a0000000023000000100000000c7265766f6b65642d69642d31"
);
const KRL_BITMAP: &[u8] = &hex!(
"5353484b524c0a00000000010000000000000000000000006a31ff3900000000000000000000000000000000010000004e000000330000000b7373682d65643235353139000000203659ea41e0a2e1c0267e70c0254840aef8de6f3200bd86b2400bcd6261d9279a00000000220000000e0000000000000001000000020097"
);
#[test]
fn serial_list_and_range() {
let krl = Krl::parse(KRL_SERIAL).expect("parse");
assert!(krl.is_revoked_cert(CA_BLOB, 10, ""));
assert!(!krl.is_revoked_cert(CA_BLOB, 11, ""));
assert!(krl.is_revoked_cert(CA_BLOB, 100, ""));
assert!(krl.is_revoked_cert(CA_BLOB, 150, ""));
assert!(krl.is_revoked_cert(CA_BLOB, 200, ""));
assert!(!krl.is_revoked_cert(CA_BLOB, 99, ""));
assert!(!krl.is_revoked_cert(CA_BLOB, 201, ""));
assert!(!krl.is_revoked_cert(USERKEY_BLOB, 10, ""));
assert!(!krl.is_revoked_key(USERKEY_BLOB));
}
#[test]
fn explicit_key() {
let krl = Krl::parse(KRL_EXPLICIT).expect("parse");
assert!(krl.is_revoked_key(USERKEY_BLOB));
assert!(!krl.is_revoked_key(CA_BLOB));
assert!(!krl.is_revoked_cert(CA_BLOB, 10, ""));
}
#[test]
fn cert_key_id() {
let krl = Krl::parse(KRL_KEYID).expect("parse");
assert!(krl.is_revoked_cert(CA_BLOB, 0, "revoked-id-1"));
assert!(krl.is_revoked_cert(CA_BLOB, 12345, "revoked-id-1"));
assert!(!krl.is_revoked_cert(CA_BLOB, 0, "other-id"));
assert!(!krl.is_revoked_cert(CA_BLOB, 0, ""));
}
#[test]
fn serial_bitmap() {
let krl = Krl::parse(KRL_BITMAP).expect("parse");
for s in [1u64, 2, 3, 5, 8] {
assert!(
krl.is_revoked_cert(CA_BLOB, s, ""),
"serial {s} should revoke"
);
}
for s in [4u64, 6, 7, 9, 100] {
assert!(
!krl.is_revoked_cert(CA_BLOB, s, ""),
"serial {s} should NOT revoke"
);
}
}
#[test]
fn fingerprint_sha1_and_sha256() {
let h1 = Sha1::digest(USERKEY_BLOB);
let h256 = Sha256::digest(USERKEY_BLOB);
let mut blob = vec![];
blob.extend_from_slice(&KRL_MAGIC.to_be_bytes());
blob.extend_from_slice(&KRL_FORMAT_VERSION.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u32.to_be_bytes()); blob.extend_from_slice(&0u32.to_be_bytes());
let push_section = |out: &mut alloc::vec::Vec<u8>, ty: u8, body: &[u8]| {
out.push(ty);
out.extend_from_slice(&(body.len() as u32).to_be_bytes());
out.extend_from_slice(body);
};
let push_hash = |out: &mut alloc::vec::Vec<u8>, h: &[u8]| {
out.extend_from_slice(&(h.len() as u32).to_be_bytes());
out.extend_from_slice(h);
};
let mut body1 = vec![];
push_hash(&mut body1, &h1);
push_section(&mut blob, SECTION_FINGERPRINT_SHA1, &body1);
let mut body256 = vec![];
push_hash(&mut body256, &h256);
push_section(&mut blob, SECTION_FINGERPRINT_SHA256, &body256);
let krl = Krl::parse(&blob).expect("parse");
assert!(krl.is_revoked_key(USERKEY_BLOB));
assert!(!krl.is_revoked_key(CA_BLOB));
}
fn krl_with_cert_section(sec: &[u8]) -> Vec<u8> {
let mut blob = vec![];
blob.extend_from_slice(&KRL_MAGIC.to_be_bytes());
blob.extend_from_slice(&KRL_FORMAT_VERSION.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u64.to_be_bytes()); blob.extend_from_slice(&0u32.to_be_bytes()); blob.extend_from_slice(&0u32.to_be_bytes()); blob.push(SECTION_CERTIFICATES);
blob.extend_from_slice(&(sec.len() as u32).to_be_bytes());
blob.extend_from_slice(sec);
blob
}
#[test]
fn serial_zero_revocable_by_list() {
let mut sec = vec![];
sec.extend_from_slice(&0u32.to_be_bytes()); sec.extend_from_slice(&0u32.to_be_bytes()); sec.push(super::CERT_SERIAL_LIST);
sec.extend_from_slice(&8u32.to_be_bytes()); sec.extend_from_slice(&0u64.to_be_bytes());
let krl = Krl::parse(&krl_with_cert_section(&sec)).expect("parse");
assert!(
krl.is_revoked_cert(CA_BLOB, 0, ""),
"serial 0 must be revocable by serial-list"
);
assert!(!krl.is_revoked_cert(CA_BLOB, 1, ""));
}
#[test]
fn serial_zero_revocable_by_range() {
let mut sec = vec![];
sec.extend_from_slice(&0u32.to_be_bytes()); sec.extend_from_slice(&0u32.to_be_bytes()); sec.push(super::CERT_SERIAL_RANGE);
sec.extend_from_slice(&16u32.to_be_bytes()); sec.extend_from_slice(&0u64.to_be_bytes()); sec.extend_from_slice(&5u64.to_be_bytes());
let krl = Krl::parse(&krl_with_cert_section(&sec)).expect("parse");
assert!(
krl.is_revoked_cert(CA_BLOB, 0, ""),
"serial 0 must be revocable by serial-range"
);
assert!(krl.is_revoked_cert(CA_BLOB, 5, ""));
assert!(!krl.is_revoked_cert(CA_BLOB, 6, ""));
}
#[test]
fn wildcard_ca_applies_to_all() {
let mut sec = vec![];
sec.extend_from_slice(&0u32.to_be_bytes()); sec.extend_from_slice(&0u32.to_be_bytes()); sec.push(super::CERT_SERIAL_LIST);
sec.extend_from_slice(&8u32.to_be_bytes()); sec.extend_from_slice(&7u64.to_be_bytes());
let mut blob = vec![];
blob.extend_from_slice(&KRL_MAGIC.to_be_bytes());
blob.extend_from_slice(&KRL_FORMAT_VERSION.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u32.to_be_bytes());
blob.extend_from_slice(&0u32.to_be_bytes());
blob.push(SECTION_CERTIFICATES);
blob.extend_from_slice(&(sec.len() as u32).to_be_bytes());
blob.extend_from_slice(&sec);
let krl = Krl::parse(&blob).expect("parse");
assert!(krl.is_revoked_cert(CA_BLOB, 7, ""));
assert!(krl.is_revoked_cert(USERKEY_BLOB, 7, ""));
assert!(!krl.is_revoked_cert(CA_BLOB, 8, ""));
}
#[test]
fn signature_section_is_parse_and_ignore() {
let mut blob = KRL_SERIAL.to_vec();
blob.push(SECTION_SIGNATURE);
let sig_body = [0xAAu8; 16];
blob.extend_from_slice(&(sig_body.len() as u32).to_be_bytes());
blob.extend_from_slice(&sig_body);
let krl = Krl::parse(&blob).expect("signed KRL parses");
assert!(krl.is_revoked_cert(CA_BLOB, 10, ""));
}
#[test]
fn rejects_bad_magic() {
let mut blob = KRL_SERIAL.to_vec();
blob[0] ^= 0xff;
assert!(Krl::parse(&blob).is_err());
}
#[test]
fn rejects_truncated() {
assert!(Krl::parse(&KRL_SERIAL[..KRL_SERIAL.len() - 4]).is_err());
assert!(Krl::parse(b"SSHK").is_err());
assert!(Krl::parse(b"").is_err());
}
#[test]
fn empty_krl_revokes_nothing() {
let mut blob = vec![];
blob.extend_from_slice(&KRL_MAGIC.to_be_bytes());
blob.extend_from_slice(&KRL_FORMAT_VERSION.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u64.to_be_bytes());
blob.extend_from_slice(&0u32.to_be_bytes());
blob.extend_from_slice(&0u32.to_be_bytes());
let krl = Krl::parse(&blob).expect("parse");
assert!(krl.is_empty());
assert!(!krl.is_revoked_cert(CA_BLOB, 1, "x"));
assert!(!krl.is_revoked_key(USERKEY_BLOB));
}