use alloc::{borrow::ToOwned, vec::Vec};
use core::str::FromStr;
use std::{fs, path::Path};
use crate::dnssec::PublicKey;
use crate::serialize::txt::ParseError;
use crate::serialize::txt::trust_anchor::{self, Entry};
use super::Verifier;
use super::{Algorithm, PublicKeyBuf};
const ROOT_ANCHOR_2018: &[u8] = include_bytes!("roots/20326.rsa");
const ROOT_ANCHOR_2024: &[u8] = include_bytes!("roots/38696.rsa");
#[derive(Clone)]
pub struct TrustAnchors {
pkeys: Vec<PublicKeyBuf>,
}
impl TrustAnchors {
pub fn from_file(path: &Path) -> Result<Self, ParseError> {
Self::from_str(&fs::read_to_string(path)?)
}
pub fn empty() -> Self {
Self { pkeys: vec![] }
}
pub fn contains<P: PublicKey + ?Sized>(&self, other_key: &P) -> bool {
self.pkeys.iter().any(|k| {
other_key.public_bytes() == k.public_bytes() && other_key.algorithm() == k.algorithm()
})
}
pub fn insert<P: PublicKey + ?Sized>(&mut self, public_key: &P) -> bool {
if self.contains(public_key) {
return false;
}
self.pkeys.push(PublicKeyBuf::new(
public_key.public_bytes().to_vec(),
public_key.algorithm(),
));
true
}
pub fn get(&self, idx: usize) -> Option<&PublicKeyBuf> {
self.pkeys.get(idx)
}
pub fn len(&self) -> usize {
self.pkeys.len()
}
pub fn is_empty(&self) -> bool {
self.pkeys.is_empty()
}
}
impl FromStr for TrustAnchors {
type Err = ParseError;
fn from_str(input: &str) -> Result<Self, Self::Err> {
let parser = trust_anchor::Parser::new(input);
let entries = parser.parse()?;
let mut pkeys = Vec::new();
for entry in entries {
let Entry::DNSKEY(record) = entry;
let dnskey = record.data();
let key = dnskey.key()?;
pkeys.push(PublicKeyBuf::new(
key.public_bytes().to_vec(),
dnskey.algorithm(),
));
}
Ok(Self { pkeys })
}
}
impl Default for TrustAnchors {
fn default() -> Self {
Self {
pkeys: vec![
PublicKeyBuf::new(ROOT_ANCHOR_2018.to_owned(), Algorithm::RSASHA256),
PublicKeyBuf::new(ROOT_ANCHOR_2024.to_owned(), Algorithm::RSASHA256),
],
}
}
}
#[cfg(test)]
mod tests {
use crate::dnssec::{
Algorithm, PublicKey, PublicKeyBuf,
trust_anchor::{ROOT_ANCHOR_2024, TrustAnchors},
};
use alloc::borrow::ToOwned;
#[test]
fn test_contains_dnskey_bytes() {
let trust = TrustAnchors::default();
assert_eq!(trust.get(1).unwrap().public_bytes(), ROOT_ANCHOR_2024);
let pub_key = PublicKeyBuf::new(ROOT_ANCHOR_2024.to_owned(), Algorithm::RSASHA256);
assert!(trust.contains(&pub_key));
}
#[test]
fn can_load_trust_anchor_file() {
let input = include_str!("../../tests/test-data/root.key");
let trust_anchor = input.parse::<TrustAnchors>().unwrap();
assert_eq!(3, trust_anchor.len());
}
}