use alloc::{borrow::ToOwned, vec::Vec};
use core::str::FromStr;
use std::{fs, path::Path};
use crate::dnssec::PublicKey;
use crate::rr::LowerName;
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 {
root_public_keys: Vec<PublicKeyBuf>,
other_public_keys: Vec<(LowerName, 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 {
root_public_keys: vec![],
other_public_keys: vec![],
}
}
pub fn contains<P: PublicKey + ?Sized>(&self, other_key: &P) -> bool {
self.root_public_keys.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.root_public_keys.push(PublicKeyBuf::new(
public_key.public_bytes().to_vec(),
public_key.algorithm(),
));
true
}
pub fn contains_with_name<P: PublicKey + ?Sized>(
&self,
public_key: &P,
name: &LowerName,
) -> bool {
if name.is_root() {
return self.contains(public_key);
}
self.other_public_keys
.iter()
.any(|(trust_anchor_name, trust_anchor_public_key)| {
trust_anchor_name == name
&& trust_anchor_public_key.public_bytes() == public_key.public_bytes()
&& trust_anchor_public_key.algorithm() == public_key.algorithm()
})
}
pub fn insert_with_name<P: PublicKey + ?Sized>(
&mut self,
public_key: &P,
name: LowerName,
) -> bool {
if name.is_root() {
return self.insert(public_key);
}
if self.contains_with_name(public_key, &name) {
return false;
}
self.other_public_keys.push((
name,
PublicKeyBuf::new(public_key.public_bytes().to_vec(), public_key.algorithm()),
));
true
}
pub fn get(&self, idx: usize) -> Option<&PublicKeyBuf> {
self.root_public_keys.get(idx)
}
pub fn len(&self) -> usize {
self.root_public_keys.len()
}
pub fn is_empty(&self) -> bool {
self.root_public_keys.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 root_public_keys = Vec::new();
let mut other_public_keys = Vec::new();
for entry in entries {
let Entry::DNSKEY(record) = entry;
let dnskey = record.data();
let key = dnskey.key()?;
let public_key_buf = PublicKeyBuf::new(key.public_bytes().to_vec(), dnskey.algorithm());
if record.name().is_root() {
root_public_keys.push(public_key_buf);
} else {
other_public_keys.push((record.name().into(), public_key_buf));
}
}
Ok(Self {
root_public_keys,
other_public_keys,
})
}
}
impl Default for TrustAnchors {
fn default() -> Self {
Self {
root_public_keys: vec![
PublicKeyBuf::new(ROOT_ANCHOR_2018.to_owned(), Algorithm::RSASHA256),
PublicKeyBuf::new(ROOT_ANCHOR_2024.to_owned(), Algorithm::RSASHA256),
],
other_public_keys: vec![],
}
}
}
#[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());
}
}