use alloc::string::String;
use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "backtrace")]
use backtrace::Backtrace;
use rdata::tsig::TsigAlgorithm;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::error::{ProtoError, ProtoErrorKind};
#[cfg(feature = "backtrace")]
use crate::trace;
mod algorithm;
mod dnssec_dns_handle;
pub use dnssec_dns_handle::DnssecDnsHandle;
#[doc(hidden)]
pub use dnssec_dns_handle::verify_nsec;
pub mod crypto;
mod ec_public_key;
mod nsec3;
pub mod proof;
pub mod public_key;
pub mod rdata;
mod rsa_public_key;
mod signer;
mod supported_algorithm;
pub mod tbs;
mod trust_anchor;
pub mod tsig;
mod verifier;
pub use self::algorithm::Algorithm;
pub use self::nsec3::Nsec3HashAlgorithm;
pub use self::proof::{Proof, ProofError, ProofErrorKind, ProofFlags, Proven};
pub use self::public_key::{PublicKey, PublicKeyBuf};
pub use self::signer::SigSigner;
pub use self::supported_algorithm::SupportedAlgorithms;
pub use self::tbs::TBS;
pub use self::trust_anchor::TrustAnchors;
pub use self::verifier::Verifier;
#[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 {
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, Clone, Error)]
pub struct DnsSecError {
kind: DnsSecErrorKind,
#[cfg(feature = "backtrace")]
backtrack: Option<Backtrace>,
}
impl DnsSecError {
pub fn kind(&self) -> &DnsSecErrorKind {
&self.kind
}
}
impl fmt::Display for DnsSecError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
cfg_if::cfg_if! {
if #[cfg(feature = "backtrace")] {
if let Some(backtrace) = &self.backtrack {
fmt::Display::fmt(&self.kind, f)?;
fmt::Debug::fmt(backtrace, f)
} else {
fmt::Display::fmt(&self.kind, f)
}
} else {
fmt::Display::fmt(&self.kind, f)
}
}
}
}
impl From<DnsSecErrorKind> for DnsSecError {
fn from(kind: DnsSecErrorKind) -> Self {
Self {
kind,
#[cfg(feature = "backtrace")]
backtrack: trace!(),
}
}
}
impl From<&'static str> for DnsSecError {
fn from(msg: &'static str) -> Self {
DnsSecErrorKind::Message(msg).into()
}
}
impl From<String> for DnsSecError {
fn from(msg: String) -> Self {
DnsSecErrorKind::Msg(msg).into()
}
}
impl From<ProtoError> for DnsSecError {
fn from(e: ProtoError) -> Self {
match e.kind() {
ProtoErrorKind::Timeout => DnsSecErrorKind::Timeout.into(),
_ => DnsSecErrorKind::from(e).into(),
}
}
}
impl From<ring_like::KeyRejected> for DnsSecError {
fn from(e: ring_like::KeyRejected) -> Self {
DnsSecErrorKind::from(e).into()
}
}
impl From<ring_like::Unspecified> for DnsSecError {
fn from(e: ring_like::Unspecified) -> Self {
DnsSecErrorKind::from(e).into()
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum DnsSecErrorKind {
#[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("request timed out")]
Timeout,
#[error("Tsig unsupported mac algorithm")]
TsigUnsupportedMacAlgorithm(TsigAlgorithm),
#[error("Tsig key wrong key error")]
TsigWrongKey,
}
impl Clone for DnsSecErrorKind {
fn clone(&self) -> Self {
use DnsSecErrorKind::*;
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),
Timeout => Timeout,
TsigUnsupportedMacAlgorithm(ref alg) => TsigUnsupportedMacAlgorithm(alg.clone()),
TsigWrongKey => TsigWrongKey,
}
}
}
#[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(),
);
}
}