use alloc::string::String;
use alloc::vec::Vec;
use core::slice;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::dnssec::crypto::Digest;
use crate::error::ProtoError;
use crate::rr::{Name, RData, Record, rdata::tsig::TsigAlgorithm};
use crate::serialize::binary::{BinEncodable, BinEncoder, DecodeError, NameEncoding};
mod algorithm;
pub use algorithm::Algorithm;
pub mod crypto;
mod ec_public_key;
mod proof;
pub use proof::{Proof, ProofFlags, Proven};
mod public_key;
pub use public_key::{PublicKey, PublicKeyBuf};
pub mod rdata;
mod rsa_public_key;
mod signer;
pub use signer::DnssecSigner;
mod supported_algorithm;
pub use supported_algorithm::SupportedAlgorithms;
mod tbs;
pub use tbs::TBS;
mod trust_anchor;
pub use trust_anchor::TrustAnchors;
mod verifier;
pub use verifier::Verifier;
pub struct DnssecIter<'a>(slice::Iter<'a, Record<RData>>);
impl<'a> DnssecIter<'a> {
pub fn new(records: &'a [Record<RData>]) -> Self {
Self(records.iter())
}
}
impl<'a> Iterator for DnssecIter<'a> {
type Item = Proven<&'a Record>;
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(Proven::from)
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
pub enum Nsec3HashAlgorithm {
#[default]
#[cfg_attr(feature = "serde", serde(rename = "SHA-1"))]
SHA1,
}
impl Nsec3HashAlgorithm {
pub fn hash(self, salt: &[u8], name: &Name, iterations: u16) -> Result<Digest, ProtoError> {
match self {
Self::SHA1 => {
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder = BinEncoder::new(&mut buf);
let mut encoder =
encoder.with_name_encoding(NameEncoding::UncompressedLowercase);
name.emit(&mut encoder)?;
}
Ok(Digest::iterated(salt, &buf, DigestType::SHA1, iterations)?)
}
}
}
}
impl TryFrom<u8> for Nsec3HashAlgorithm {
type Error = DecodeError;
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
1 => Ok(Self::SHA1),
_ => Err(DecodeError::UnknownNsec3HashAlgorithm(value)),
}
}
}
impl From<Nsec3HashAlgorithm> for u8 {
fn from(a: Nsec3HashAlgorithm) -> Self {
match a {
Nsec3HashAlgorithm::SHA1 => 1,
}
}
}
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]
#[non_exhaustive]
pub enum DigestType {
#[cfg_attr(feature = "serde", serde(rename = "SHA-1"))]
SHA1,
#[cfg_attr(feature = "serde", serde(rename = "SHA-256"))]
SHA256,
#[cfg_attr(feature = "serde", serde(rename = "SHA-384"))]
SHA384,
Unknown(u8),
}
impl DigestType {
pub fn is_supported(&self) -> bool {
!matches!(self, Self::Unknown(_))
}
}
impl From<u8> for DigestType {
fn from(value: u8) -> Self {
match value {
1 => Self::SHA1,
2 => Self::SHA256,
4 => Self::SHA384,
_ => Self::Unknown(value),
}
}
}
impl From<DigestType> for u8 {
fn from(a: DigestType) -> Self {
match a {
DigestType::SHA1 => 1,
DigestType::SHA256 => 2,
DigestType::SHA384 => 4,
DigestType::Unknown(other) => other,
}
}
}
pub trait SigningKey: Send + Sync + 'static {
fn sign(&self, tbs: &TBS) -> DnsSecResult<Vec<u8>>;
fn to_public_key(&self) -> DnsSecResult<PublicKeyBuf>;
fn algorithm(&self) -> Algorithm;
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum KeyFormat {
Der,
Pem,
Pkcs8,
}
pub type DnsSecResult<T> = ::core::result::Result<T, DnsSecError>;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DnsSecError {
#[error("hmac validation failure")]
HmacInvalid,
#[error("{0}")]
Message(&'static str),
#[error("{0}")]
Msg(String),
#[error("proto error: {0}")]
Proto(#[from] ProtoError),
#[error("ring error: {0}")]
RingKeyRejected(#[from] ring_like::KeyRejected),
#[error("ring error: {0}")]
RingUnspecified(#[from] ring_like::Unspecified),
#[error("Tsig unsupported mac algorithm")]
TsigUnsupportedMacAlgorithm(TsigAlgorithm),
#[error("Tsig key wrong key error")]
TsigWrongKey,
}
impl From<String> for DnsSecError {
fn from(msg: String) -> Self {
Self::Msg(msg)
}
}
impl From<&'static str> for DnsSecError {
fn from(msg: &'static str) -> Self {
Self::Message(msg)
}
}
impl Clone for DnsSecError {
fn clone(&self) -> Self {
use DnsSecError::*;
match self {
HmacInvalid => HmacInvalid,
Message(msg) => Message(msg),
Msg(msg) => Msg(msg.clone()),
Proto(proto) => Proto(proto.clone()),
RingKeyRejected(r) => Msg(format!("Ring rejected key: {r}")),
RingUnspecified(_r) => RingUnspecified(ring_like::Unspecified),
TsigUnsupportedMacAlgorithm(alg) => TsigUnsupportedMacAlgorithm(alg.clone()),
TsigWrongKey => TsigWrongKey,
}
}
}
#[derive(Clone, Copy, Debug)]
pub enum DnssecSummary {
Secure,
Bogus,
Insecure,
}
impl DnssecSummary {
pub fn from_records<'a>(records: impl Iterator<Item = &'a Record>) -> Self {
let mut all_secure = None;
for record in records {
match &record.proof {
Proof::Secure => {
all_secure.get_or_insert(true);
}
Proof::Bogus => return Self::Bogus,
_ => all_secure = Some(false),
}
}
if all_secure.unwrap_or(false) {
Self::Secure
} else {
Self::Insecure
}
}
}
#[cfg(all(feature = "dnssec-aws-lc-rs", not(feature = "dnssec-ring")))]
pub(crate) use aws_lc_rs_impl as ring_like;
#[cfg(feature = "dnssec-ring")]
pub(crate) use ring_impl as ring_like;
#[cfg(feature = "dnssec-aws-lc-rs")]
#[cfg_attr(feature = "dnssec-ring", allow(unused_imports))]
pub(crate) mod aws_lc_rs_impl {
pub(crate) use aws_lc_rs::{
digest,
error::{KeyRejected, Unspecified},
hmac,
rand::SystemRandom,
rsa::PublicKeyComponents,
signature::{
self, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
ED25519_PUBLIC_KEY_LEN, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RSA_PKCS1_SHA256,
RSA_PKCS1_SHA512, RsaKeyPair,
},
};
}
#[cfg(feature = "dnssec-ring")]
pub(crate) mod ring_impl {
pub(crate) use ring::{
digest,
error::{KeyRejected, Unspecified},
hmac,
rand::SystemRandom,
rsa::PublicKeyComponents,
signature::{
self, ECDSA_P256_SHA256_FIXED_SIGNING, ECDSA_P384_SHA384_FIXED_SIGNING,
ED25519_PUBLIC_KEY_LEN, EcdsaKeyPair, Ed25519KeyPair, KeyPair, RSA_PKCS1_SHA256,
RSA_PKCS1_SHA512, RsaKeyPair,
},
};
}
#[cfg(test)]
mod test_utils {
use rdata::DNSKEY;
use super::*;
pub(super) fn public_key_test(key: &dyn SigningKey) {
let pk = key.to_public_key().unwrap();
let tbs = TBS::from(&b"www.example.com"[..]);
let mut sig = key.sign(&tbs).unwrap();
assert!(
pk.verify(tbs.as_ref(), &sig).is_ok(),
"public_key_test() failed to verify (algorithm: {:?})",
key.algorithm(),
);
sig[10] = !sig[10];
assert!(
pk.verify(tbs.as_ref(), &sig).is_err(),
"algorithm: {:?} (public key, neg)",
key.algorithm(),
);
}
pub(super) fn hash_test(key: &dyn SigningKey, neg: &dyn SigningKey) {
let tbs = TBS::from(&b"www.example.com"[..]);
let pub_key = key.to_public_key().unwrap();
let neg_pub_key = neg.to_public_key().unwrap();
let sig = key.sign(&tbs).unwrap();
assert!(
pub_key.verify(tbs.as_ref(), &sig).is_ok(),
"algorithm: {:?}",
key.algorithm(),
);
let pub_key = key.to_public_key().unwrap();
let dns_key = DNSKEY::from_key(&pub_key);
assert!(
dns_key.verify(tbs.as_ref(), &sig).is_ok(),
"algorithm: {:?} (dnskey)",
pub_key.algorithm(),
);
assert!(
neg_pub_key.verify(tbs.as_ref(), &sig).is_err(),
"algorithm: {:?} (neg)",
neg_pub_key.algorithm(),
);
let neg_pub_key = neg.to_public_key().unwrap();
let neg_dns_key = DNSKEY::from_key(&neg_pub_key);
assert!(
neg_dns_key.verify(tbs.as_ref(), &sig).is_err(),
"algorithm: {:?} (dnskey, neg)",
neg_pub_key.algorithm(),
);
}
}