use alloc::string::String;
use core::{fmt, ops::BitOr};
use bitflags::bitflags;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use thiserror::Error;
use super::{Algorithm, DnsSecError};
use crate::{
error::ProtoError,
op::Query,
rr::{Name, RecordType},
};
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[must_use = "Proof is a flag on Record data, it should be interrogated before using a record"]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Ord, PartialOrd)]
#[repr(u8)]
pub enum Proof {
Secure = 3,
Insecure = 2,
Bogus = 1,
#[default]
Indeterminate = 0,
}
impl Proof {
#[inline]
pub fn is_secure(&self) -> bool {
*self == Self::Secure
}
#[inline]
pub fn is_insecure(&self) -> bool {
*self == Self::Insecure
}
#[inline]
pub fn is_bogus(&self) -> bool {
*self == Self::Bogus
}
#[inline]
pub fn is_indeterminate(&self) -> bool {
*self == Self::Indeterminate
}
}
impl fmt::Display for Proof {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Secure => "Secure",
Self::Insecure => "Insecure",
Self::Bogus => "Bogus",
Self::Indeterminate => "Indeterminate",
};
f.write_str(s)
}
}
impl std::error::Error for Proof {}
#[test]
fn test_order() {
assert!(Proof::Secure > Proof::Insecure);
assert!(Proof::Insecure > Proof::Bogus);
assert!(Proof::Bogus > Proof::Indeterminate);
}
bitflags! {
pub struct ProofFlags: u32 {
const SECURE = 1 << Proof::Secure as u8;
const INSECURE = 1 << Proof::Insecure as u8;
const BOGUS = 1 << Proof::Bogus as u8;
const INDETERMINATE = 1 << Proof::Indeterminate as u8;
}
}
impl From<Proof> for ProofFlags {
fn from(proof: Proof) -> Self {
match proof {
Proof::Secure => Self::SECURE,
Proof::Insecure => Self::INSECURE,
Proof::Bogus => Self::BOGUS,
Proof::Indeterminate => Self::INDETERMINATE,
}
}
}
impl BitOr for Proof {
type Output = ProofFlags;
fn bitor(self, rhs: Self) -> Self::Output {
ProofFlags::from(self) | ProofFlags::from(rhs)
}
}
#[allow(unreachable_pub)]
#[derive(Debug, Error, Clone)]
#[non_exhaustive]
pub enum ProofErrorKind {
#[error("{0}")]
Message(&'static str),
#[error("{0}")]
Msg(String),
#[error("algorithm mismatch rrsig: {rrsig} dnskey: {dnskey}")]
AlgorithmMismatch {
rrsig: Algorithm,
dnskey: Algorithm,
},
#[error("ssl error: {0}")]
DnsSecError(#[from] DnsSecError),
#[error("dnskey and rrset failed to verify: {name} key_tag: {key_tag}")]
DnsKeyVerifyRrsig {
name: Name,
key_tag: u16,
error: ProtoError,
},
#[error("no dnskey was found: {name}")]
DnskeyNotFound {
name: Name,
},
#[error("dnskey revoked: {name}, key_tag: {key_tag}")]
DnsKeyRevoked {
name: Name,
key_tag: u16,
},
#[error("dnskey has no ds: {name}")]
DnsKeyHasNoDs {
name: Name,
},
#[error("ds has no dnssec proof: {name}")]
DsHasNoDnssecProof {
name: Name,
},
#[error("ds record exists, but no dnskey: {name}")]
DsRecordsButNoDnskey {
name: Name,
},
#[error("ds record should exist: {name}")]
DsRecordShouldExist {
name: Name,
},
#[error("ds response empty: {name}")]
DsResponseEmpty {
name: Name,
},
#[error("ds record does not exist: {name}")]
DsResponseNsec {
name: Name,
},
#[error("internal error computing the key tag for: {name}")]
ErrorComputingKeyTag {
name: Name,
},
#[error("dnskey insecure: {name}, key_tag: {key_tag}")]
InsecureDnsKey {
name: Name,
key_tag: u16,
},
#[error("not a zone signing key: {name} key_tag: {key_tag}")]
NotZoneDnsKey {
name: Name,
key_tag: u16,
},
#[error("communication failure for query: {query}: {proto}")]
Proto {
query: Query,
proto: ProtoError,
},
#[error("rrsigs are not present for: {name} record_type: {record_type}")]
RrsigsNotPresent {
name: Name,
record_type: RecordType,
},
#[error("rrsigs were not able to be verified: {name}, type: {record_type}")]
RrsigsUnverified {
name: Name,
record_type: RecordType,
},
#[error("self-signed dnskey is invalid: {name}")]
SelfSignedKeyInvalid {
name: Name,
},
#[error("unknown or reserved key algorithm")]
UnknownKeyAlgorithm,
#[error("unsupported key algorithms")]
UnsupportedKeyAlgorithm,
}
#[derive(Debug, Clone, Error)]
pub struct ProofError {
pub proof: Proof,
pub kind: ProofErrorKind,
}
impl ProofError {
pub fn new(proof: Proof, kind: ProofErrorKind) -> Self {
Self { proof, kind }
}
pub fn kind(&self) -> &ProofErrorKind {
&self.kind
}
pub fn ds_should_exist(name: Name) -> Self {
Self {
proof: Proof::Bogus,
kind: ProofErrorKind::DsRecordShouldExist { name },
}
}
}
impl fmt::Display for ProofError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}: {}", self.proof, self.kind)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Proven<T> {
proof: Proof,
value: T,
}
impl<T> Proven<T> {
pub fn new(proof: Proof, value: T) -> Self {
Self { proof, value }
}
pub fn proof(&self) -> Proof {
self.proof
}
pub fn require_as_ref<I: Into<ProofFlags>>(&self, flags: I) -> Result<&T, Proof> {
if flags.into().contains(ProofFlags::from(self.proof)) {
Ok(&self.value)
} else {
Err(self.proof)
}
}
pub fn require<I: Into<ProofFlags>>(self, flags: I) -> Result<T, Self> {
if flags.into().contains(ProofFlags::from(self.proof)) {
Ok(self.value)
} else {
Err(self)
}
}
pub fn map<U, F>(self, f: F) -> Proven<U>
where
F: FnOnce(T) -> U,
{
Proven {
proof: self.proof,
value: f(self.value),
}
}
pub fn into_parts(self) -> (Proof, T) {
let Self { proof, value } = self;
(proof, value)
}
}
impl<T> Proven<Option<T>> {
pub fn transpose(self) -> Option<Proven<T>> {
if let Some(value) = self.value {
Some(Proven {
proof: self.proof,
value,
})
} else {
None
}
}
}