use alloc::string::String;
use alloc::vec::Vec;
use crate::error::{Error, Result};
use crate::format::{Reader, Writer, read_mpint};
use purecrypto::hash::{Digest, Sha1, Sha256};
const KRL_MAGIC: u64 = 0x5353_484b_524c_0a00;
const KRL_FORMAT_VERSION: u32 = 1;
const SECTION_CERTIFICATES: u8 = 1;
const SECTION_EXPLICIT_KEY: u8 = 2;
const SECTION_FINGERPRINT_SHA1: u8 = 3;
const SECTION_SIGNATURE: u8 = 4;
const SECTION_FINGERPRINT_SHA256: u8 = 5;
const SECTION_EXTENSION: u8 = 255;
const CERT_SERIAL_LIST: u8 = 0x20;
const CERT_SERIAL_RANGE: u8 = 0x21;
const CERT_SERIAL_BITMAP: u8 = 0x22;
const CERT_KEY_ID: u8 = 0x23;
const CERT_EXTENSION: u8 = 0x39;
const MAX_BITMAP_BYTES: usize = 1 << 20;
struct CaRevocation {
ca_key_blob: Vec<u8>,
serials: Vec<u64>,
serial_ranges: Vec<(u64, u64)>,
serial_bitmaps: Vec<(u64, Vec<u8>)>,
key_ids: Vec<String>,
}
impl CaRevocation {
fn covers(&self, serial: u64, key_id: &str) -> bool {
if self.serials.contains(&serial) {
return true;
}
for &(lo, hi) in &self.serial_ranges {
if serial >= lo && serial <= hi {
return true;
}
}
for (offset, bits) in &self.serial_bitmaps {
if serial >= *offset {
let idx = serial - *offset;
if bit_is_set(bits, idx) {
return true;
}
}
}
if !key_id.is_empty() && self.key_ids.iter().any(|k| k == key_id) {
return true;
}
false
}
}
#[derive(Default)]
pub struct Krl {
version: u64,
comment: String,
cert_revocations: Vec<CaRevocation>,
explicit_keys: Vec<Vec<u8>>,
fp_sha1: Vec<[u8; 20]>,
fp_sha256: Vec<[u8; 32]>,
}
fn bit_is_set(magnitude: &[u8], idx: u64) -> bool {
let byte_from_end = (idx / 8) as usize;
if byte_from_end >= magnitude.len() {
return false;
}
let byte = magnitude[magnitude.len() - 1 - byte_from_end];
let bit = (idx % 8) as u8;
(byte >> bit) & 1 == 1
}
impl Krl {
pub fn parse(blob: &[u8]) -> Result<Self> {
let mut r = Reader::new(blob);
if r.read_u64()? != KRL_MAGIC {
return Err(Error::Format("krl: bad magic"));
}
if r.read_u32()? != KRL_FORMAT_VERSION {
return Err(Error::Format("krl: unsupported format version"));
}
let version = r.read_u64()?;
let _generated_date = r.read_u64()?;
let _flags = r.read_u64()?;
let _reserved = r.read_string()?;
let comment = String::from_utf8_lossy(r.read_string()?).into_owned();
let mut krl = Krl {
version,
comment,
cert_revocations: Vec::new(),
explicit_keys: Vec::new(),
fp_sha1: Vec::new(),
fp_sha256: Vec::new(),
};
while !r.is_empty() {
let section_type = r.read_u8()?;
let body = r.read_string()?;
match section_type {
SECTION_CERTIFICATES => krl.parse_certificates(body)?,
SECTION_EXPLICIT_KEY => parse_blob_list(body, &mut krl.explicit_keys)?,
SECTION_FINGERPRINT_SHA1 => parse_hash_list(body, &mut krl.fp_sha1)?,
SECTION_FINGERPRINT_SHA256 => parse_hash_list(body, &mut krl.fp_sha256)?,
SECTION_SIGNATURE => {}
SECTION_EXTENSION => {}
_ => {}
}
}
Ok(krl)
}
fn parse_certificates(&mut self, body: &[u8]) -> Result<()> {
let mut r = Reader::new(body);
let ca_key_blob = r.read_string()?.to_vec();
let _reserved = r.read_string()?;
let mut rev = CaRevocation {
ca_key_blob,
serials: Vec::new(),
serial_ranges: Vec::new(),
serial_bitmaps: Vec::new(),
key_ids: Vec::new(),
};
while !r.is_empty() {
let sub_type = r.read_u8()?;
let sub = r.read_string()?;
match sub_type {
CERT_SERIAL_LIST => {
let mut sr = Reader::new(sub);
while !sr.is_empty() {
rev.serials.push(sr.read_u64()?);
}
}
CERT_SERIAL_RANGE => {
let mut sr = Reader::new(sub);
let lo = sr.read_u64()?;
let hi = sr.read_u64()?;
if !sr.is_empty() {
return Err(Error::Format("krl: trailing serial-range data"));
}
rev.serial_ranges.push((lo, hi));
}
CERT_SERIAL_BITMAP => {
let mut sr = Reader::new(sub);
let offset = sr.read_u64()?;
let bits = read_mpint(&mut sr)?;
if bits.len() > MAX_BITMAP_BYTES {
return Err(Error::Format("krl: serial bitmap too large"));
}
if !sr.is_empty() {
return Err(Error::Format("krl: trailing serial-bitmap data"));
}
rev.serial_bitmaps.push((offset, bits.to_vec()));
}
CERT_KEY_ID => {
let mut sr = Reader::new(sub);
while !sr.is_empty() {
let id = sr.read_string()?;
let s = core::str::from_utf8(id)
.map_err(|_| Error::Format("krl: non-utf8 key-id"))?;
rev.key_ids.push(s.into());
}
}
CERT_EXTENSION => {}
_ => {}
}
}
self.cert_revocations.push(rev);
Ok(())
}
pub fn is_revoked_cert(&self, ca_key_blob: &[u8], serial: u64, key_id: &str) -> bool {
self.cert_revocations.iter().any(|rev| {
(rev.ca_key_blob.is_empty() || rev.ca_key_blob == ca_key_blob)
&& rev.covers(serial, key_id)
})
}
pub fn is_revoked_key(&self, pubkey_blob: &[u8]) -> bool {
if self.explicit_keys.iter().any(|k| k == pubkey_blob) {
return true;
}
if !self.fp_sha1.is_empty() {
let h = Sha1::digest(pubkey_blob);
if self.fp_sha1.iter().any(|fp| fp == &h) {
return true;
}
}
if !self.fp_sha256.is_empty() {
let h = Sha256::digest(pubkey_blob);
if self.fp_sha256.iter().any(|fp| fp == &h) {
return true;
}
}
false
}
pub fn is_empty(&self) -> bool {
self.cert_revocations.is_empty()
&& self.explicit_keys.is_empty()
&& self.fp_sha1.is_empty()
&& self.fp_sha256.is_empty()
}
pub fn new() -> Self {
Self::default()
}
pub fn merge(&mut self, other: Krl) {
self.cert_revocations.extend(other.cert_revocations);
for k in other.explicit_keys {
self.revoke_key(&k);
}
for h in other.fp_sha1 {
self.revoke_sha1(h);
}
for h in other.fp_sha256 {
self.revoke_sha256(h);
}
}
pub fn version(&self) -> u64 {
self.version
}
pub fn set_version(&mut self, v: u64) {
self.version = v;
}
pub fn comment(&self) -> &str {
&self.comment
}
pub fn set_comment(&mut self, c: &str) {
self.comment = c.into();
}
pub fn revoke_key(&mut self, blob: &[u8]) {
if !self.explicit_keys.iter().any(|k| k == blob) {
self.explicit_keys.push(blob.to_vec());
}
}
pub fn revoke_sha1(&mut self, hash: [u8; 20]) {
if !self.fp_sha1.contains(&hash) {
self.fp_sha1.push(hash);
}
}
pub fn revoke_sha256(&mut self, hash: [u8; 32]) {
if !self.fp_sha256.contains(&hash) {
self.fp_sha256.push(hash);
}
}
fn ca_entry(&mut self, ca_key_blob: &[u8]) -> &mut CaRevocation {
let pos = match self
.cert_revocations
.iter()
.position(|c| c.ca_key_blob == ca_key_blob)
{
Some(p) => p,
None => {
self.cert_revocations.push(CaRevocation {
ca_key_blob: ca_key_blob.to_vec(),
serials: Vec::new(),
serial_ranges: Vec::new(),
serial_bitmaps: Vec::new(),
key_ids: Vec::new(),
});
self.cert_revocations.len() - 1
}
};
&mut self.cert_revocations[pos]
}
pub fn revoke_cert_serials(&mut self, ca_key_blob: &[u8], lo: u64, hi: u64) {
let ca = self.ca_entry(ca_key_blob);
if lo == hi {
if !ca.serials.contains(&lo) {
ca.serials.push(lo);
}
} else if !ca.serial_ranges.contains(&(lo, hi)) {
ca.serial_ranges.push((lo.min(hi), lo.max(hi)));
}
}
pub fn revoke_cert_key_id(&mut self, ca_key_blob: &[u8], key_id: &str) {
let ca = self.ca_entry(ca_key_blob);
if !ca.key_ids.iter().any(|k| k == key_id) {
ca.key_ids.push(key_id.into());
}
}
pub fn to_bytes(&self, generated_date: u64) -> Vec<u8> {
let mut w = Writer::new();
w.write_u64(KRL_MAGIC);
w.write_u32(KRL_FORMAT_VERSION);
w.write_u64(self.version);
w.write_u64(generated_date);
w.write_u64(0); w.write_string(b""); w.write_string(self.comment.as_bytes());
for ca in &self.cert_revocations {
let mut body = Writer::new();
body.write_string(&ca.ca_key_blob);
body.write_string(b"");
let mut serials = ca.serials.clone();
serials.sort_unstable();
if !serials.is_empty() {
let mut sub = Writer::new();
for s in serials {
sub.write_u64(s);
}
body.write_u8(CERT_SERIAL_LIST);
body.write_string(sub.as_slice());
}
let mut ranges = ca.serial_ranges.clone();
ranges.sort_unstable();
for (lo, hi) in ranges {
let mut sub = Writer::new();
sub.write_u64(lo);
sub.write_u64(hi);
body.write_u8(CERT_SERIAL_RANGE);
body.write_string(sub.as_slice());
}
for (offset, bits) in &ca.serial_bitmaps {
let mut sub = Writer::new();
sub.write_u64(*offset);
crate::format::write_mpint(&mut sub, bits);
body.write_u8(CERT_SERIAL_BITMAP);
body.write_string(sub.as_slice());
}
if !ca.key_ids.is_empty() {
let mut ids = ca.key_ids.clone();
ids.sort();
let mut sub = Writer::new();
for id in ids {
sub.write_string(id.as_bytes());
}
body.write_u8(CERT_KEY_ID);
body.write_string(sub.as_slice());
}
w.write_u8(SECTION_CERTIFICATES);
w.write_string(body.as_slice());
}
let mut write_blobs = |section: u8, blobs: Vec<&[u8]>| {
if blobs.is_empty() {
return;
}
let mut body = Writer::new();
for b in blobs {
body.write_string(b);
}
w.write_u8(section);
w.write_string(body.as_slice());
};
let mut keys: Vec<&[u8]> = self.explicit_keys.iter().map(Vec::as_slice).collect();
keys.sort_unstable();
write_blobs(SECTION_EXPLICIT_KEY, keys);
let mut sha1: Vec<&[u8]> = self.fp_sha1.iter().map(|h| &h[..]).collect();
sha1.sort_unstable();
write_blobs(SECTION_FINGERPRINT_SHA1, sha1);
let mut sha256: Vec<&[u8]> = self.fp_sha256.iter().map(|h| &h[..]).collect();
sha256.sort_unstable();
write_blobs(SECTION_FINGERPRINT_SHA256, sha256);
w.into_vec()
}
}
fn parse_blob_list(body: &[u8], out: &mut Vec<Vec<u8>>) -> Result<()> {
let mut r = Reader::new(body);
while !r.is_empty() {
out.push(r.read_string()?.to_vec());
}
Ok(())
}
fn parse_hash_list<const N: usize>(body: &[u8], out: &mut Vec<[u8; N]>) -> Result<()> {
let mut r = Reader::new(body);
while !r.is_empty() {
let h = r.read_string()?;
let arr: [u8; N] = h
.try_into()
.map_err(|_| Error::Format("krl: bad fingerprint length"))?;
out.push(arr);
}
Ok(())
}
#[cfg(test)]
mod tests;