use std::fmt::{self, Display, Formatter};
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::rr::dnssec::{Algorithm, DigestType};
use crate::serialize::binary::*;
use crate::rr::dnssec::rdata::DNSKEY;
use crate::rr::Name;
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct DS {
key_tag: u16,
algorithm: Algorithm,
digest_type: DigestType,
digest: Vec<u8>,
}
impl DS {
pub fn new(
key_tag: u16,
algorithm: Algorithm,
digest_type: DigestType,
digest: Vec<u8>,
) -> Self {
Self {
key_tag,
algorithm,
digest_type,
digest,
}
}
pub fn key_tag(&self) -> u16 {
self.key_tag
}
pub fn algorithm(&self) -> Algorithm {
self.algorithm
}
pub fn digest_type(&self) -> DigestType {
self.digest_type
}
pub fn digest(&self) -> &[u8] {
&self.digest
}
#[cfg(any(feature = "openssl", feature = "ring"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
pub fn covers(&self, name: &Name, key: &DNSKEY) -> ProtoResult<bool> {
key.to_digest(name, self.digest_type())
.map(|hash| hash.as_ref() == self.digest())
}
#[cfg(not(any(feature = "openssl", feature = "ring")))]
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "openssl", feature = "ring")))))]
pub fn covers(&self, _: &Name, _: &DNSKEY) -> ProtoResult<bool> {
Err("Ring or OpenSSL must be enabled for this feature".into())
}
}
pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<DS> {
let start_idx = decoder.index();
let key_tag: u16 = decoder.read_u16()?.unverified();
let algorithm: Algorithm = Algorithm::read(decoder)?;
let digest_type: DigestType =
DigestType::from_u8(decoder.read_u8()?.unverified())?;
let bytes_read = decoder.index() - start_idx;
let left: usize = rdata_length
.map(|u| u as usize)
.checked_sub(bytes_read)
.map_err(|_| ProtoError::from("invalid rdata length in DS"))?
.unverified();
let digest =
decoder.read_vec(left)?.unverified();
Ok(DS::new(key_tag, algorithm, digest_type, digest))
}
pub fn emit(encoder: &mut BinEncoder<'_>, rdata: &DS) -> ProtoResult<()> {
encoder.emit_u16(rdata.key_tag())?;
rdata.algorithm().emit(encoder)?; encoder.emit(rdata.digest_type().into())?;
encoder.emit_vec(rdata.digest())?;
Ok(())
}
impl Display for DS {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{tag} {alg} {ty} {digest}",
tag = self.key_tag,
alg = u8::from(self.algorithm),
ty = u8::from(self.digest_type),
digest = data_encoding::HEXUPPER_PERMISSIVE.encode(&self.digest)
)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use super::*;
#[test]
fn test() {
let rdata = DS::new(
0xF00F,
Algorithm::RSASHA256,
DigestType::SHA256,
vec![5, 6, 7, 8],
);
let mut bytes = Vec::new();
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
assert!(emit(&mut encoder, &rdata).is_ok());
let bytes = encoder.into_bytes();
println!("bytes: {:?}", bytes);
let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
let restrict = Restrict::new(bytes.len() as u16);
let read_rdata = read(&mut decoder, restrict).expect("Decoding error");
assert_eq!(rdata, read_rdata);
}
#[test]
#[cfg(any(feature = "openssl", feature = "ring"))]
pub(crate) fn test_covers() {
use crate::rr::dnssec::rdata::DNSKEY;
let name = Name::parse("www.example.com.", None).unwrap();
let dnskey_rdata = DNSKEY::new(true, true, false, Algorithm::RSASHA256, vec![1, 2, 3, 4]);
let ds_rdata = DS::new(
0,
Algorithm::RSASHA256,
DigestType::SHA256,
dnskey_rdata
.to_digest(&name, DigestType::SHA256)
.unwrap()
.as_ref()
.to_owned(),
);
assert!(ds_rdata.covers(&name, &dnskey_rdata).unwrap());
}
}