use std::fmt;
#[cfg(feature = "serde-config")]
use serde::{Deserialize, Serialize};
use crate::error::*;
use crate::rr::dnssec::{Algorithm, Digest, DigestType};
use crate::rr::record_data::RData;
use crate::rr::Name;
use crate::serialize::binary::{
BinDecodable, BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath,
};
#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct DNSKEY {
zone_key: bool,
secure_entry_point: bool,
revoke: bool,
algorithm: Algorithm,
public_key: Vec<u8>,
}
impl DNSKEY {
pub fn new(
zone_key: bool,
secure_entry_point: bool,
revoke: bool,
algorithm: Algorithm,
public_key: Vec<u8>,
) -> Self {
Self {
zone_key,
secure_entry_point,
revoke,
algorithm,
public_key,
}
}
pub fn zone_key(&self) -> bool {
self.zone_key
}
pub fn secure_entry_point(&self) -> bool {
self.secure_entry_point
}
pub fn revoke(&self) -> bool {
self.revoke
}
pub fn algorithm(&self) -> Algorithm {
self.algorithm
}
pub fn public_key(&self) -> &[u8] {
&self.public_key
}
pub fn flags(&self) -> u16 {
let mut flags: u16 = 0;
if self.zone_key() {
flags |= 0b0000_0001_0000_0000
}
if self.secure_entry_point() {
flags |= 0b0000_0000_0000_0001
}
if self.revoke() {
flags |= 0b0000_0000_1000_0000
}
flags
}
#[cfg(any(feature = "openssl", feature = "ring"))]
#[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
pub fn to_digest(&self, name: &Name, digest_type: DigestType) -> ProtoResult<Digest> {
let mut buf: Vec<u8> = Vec::new();
{
let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut buf);
encoder.set_canonical_names(true);
if let Err(e) = name
.emit(&mut encoder)
.and_then(|_| emit(&mut encoder, self))
{
tracing::warn!("error serializing dnskey: {}", e);
return Err(format!("error serializing dnskey: {}", e).into());
}
}
digest_type.hash(&buf)
}
#[cfg(not(any(feature = "openssl", feature = "ring")))]
#[cfg_attr(docsrs, doc(cfg(not(any(feature = "openssl", feature = "ring")))))]
pub fn to_digest(&self, _: &Name, _: DigestType) -> ProtoResult<Digest> {
Err("Ring or OpenSSL must be enabled for this feature".into())
}
pub fn calculate_key_tag(&self) -> ProtoResult<u16> {
let mut bytes: Vec<u8> = Vec::with_capacity(512);
{
let mut e = BinEncoder::new(&mut bytes);
self::emit(&mut e, self)?;
}
Ok(Self::calculate_key_tag_internal(&bytes))
}
pub fn calculate_key_tag_internal(bytes: &[u8]) -> u16 {
let mut ac: u32 = 0;
for (i, k) in bytes.iter().enumerate() {
ac += u32::from(*k) << if i & 0x01 != 0 { 0 } else { 8 };
}
ac += ac >> 16;
(ac & 0xFFFF) as u16
}
}
impl From<DNSKEY> for RData {
fn from(key: DNSKEY) -> Self {
Self::DNSSEC(super::DNSSECRData::DNSKEY(key))
}
}
pub fn read(decoder: &mut BinDecoder<'_>, rdata_length: Restrict<u16>) -> ProtoResult<DNSKEY> {
let flags: u16 = decoder.read_u16()?.unverified();
let zone_key: bool = flags & 0b0000_0001_0000_0000 == 0b0000_0001_0000_0000;
let secure_entry_point: bool = flags & 0b0000_0000_0000_0001 == 0b0000_0000_0000_0001;
let revoke: bool = flags & 0b0000_0000_1000_0000 == 0b0000_0000_1000_0000;
let _protocol: u8 = decoder
.read_u8()?
.verify_unwrap(|protocol| {
*protocol == 3
})
.map_err(|protocol| ProtoError::from(ProtoErrorKind::DnsKeyProtocolNot3(protocol)))?;
let algorithm: Algorithm = Algorithm::read(decoder)?;
let key_len = rdata_length
.map(|u| u as usize)
.checked_sub(4)
.map_err(|_| ProtoError::from("invalid rdata length in DNSKEY"))?
.unverified();
let public_key: Vec<u8> =
decoder.read_vec(key_len)?.unverified();
Ok(DNSKEY::new(
zone_key,
secure_entry_point,
revoke,
algorithm,
public_key,
))
}
pub fn emit(encoder: &mut BinEncoder<'_>, rdata: &DNSKEY) -> ProtoResult<()> {
encoder.emit_u16(rdata.flags())?;
encoder.emit(3)?; rdata.algorithm().emit(encoder)?;
encoder.emit_vec(rdata.public_key())?;
Ok(())
}
impl fmt::Display for DNSKEY {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{flags} 3 {alg} {key}",
flags = self.flags(),
alg = u8::from(self.algorithm),
key = data_encoding::BASE64.encode(&self.public_key)
)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use super::*;
#[test]
#[cfg(any(feature = "openssl", feature = "ring"))]
fn test() {
let rdata = DNSKEY::new(
true,
true,
false,
Algorithm::RSASHA256,
vec![0, 1, 2, 3, 4, 5, 6, 7],
);
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 read_rdata = read(&mut decoder, Restrict::new(bytes.len() as u16));
let read_rdata = read_rdata.expect("error decoding");
assert_eq!(rdata, read_rdata);
assert!(rdata
.to_digest(
&Name::parse("www.example.com.", None).unwrap(),
DigestType::SHA256
)
.is_ok());
}
#[test]
fn test_calculate_key_tag_checksum() {
let test_text = "The quick brown fox jumps over the lazy dog";
let test_vectors = vec![
(vec![], 0),
(vec![0, 0, 0, 0], 0),
(vec![0xff, 0xff, 0xff, 0xff], 0xffff),
(vec![1, 0, 0, 0], 0x0100),
(vec![0, 1, 0, 0], 0x0001),
(vec![0, 0, 1, 0], 0x0100),
(test_text.as_bytes().to_vec(), 0x8d5b),
];
for &(ref input_data, exp_result) in test_vectors.iter() {
let result = DNSKEY::calculate_key_tag_internal(input_data);
assert_eq!(result, exp_result);
}
}
}