use alloc::vec::Vec;
use core::fmt;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
dnssec::{Algorithm, PublicKeyBuf},
error::ProtoResult,
rr::{RData, RecordData, RecordDataDecodable, RecordType},
serialize::binary::{
BinDecoder, BinEncodable, BinEncoder, DecodeError, Restrict, RestrictedMath,
},
};
use super::DNSSECRData;
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct CDNSKEY {
flags: u16,
algorithm: Option<Algorithm>,
public_key: Vec<u8>,
}
impl CDNSKEY {
pub fn new(
zone_key: bool,
secure_entry_point: bool,
revoke: bool,
algorithm: Option<Algorithm>,
public_key: Vec<u8>,
) -> Self {
let mut flags: u16 = 0;
if zone_key {
flags |= 0b0000_0001_0000_0000;
}
if secure_entry_point {
flags |= 0b0000_0000_0000_0001;
}
if revoke {
flags |= 0b0000_0000_1000_0000;
}
Self::with_flags(flags, algorithm, public_key)
}
pub fn with_flags(flags: u16, algorithm: Option<Algorithm>, public_key: Vec<u8>) -> Self {
Self {
flags,
algorithm,
public_key,
}
}
pub fn zone_key(&self) -> bool {
self.flags & 0b0000_0001_0000_0000 != 0
}
pub fn secure_entry_point(&self) -> bool {
self.flags & 0b0000_0000_0000_0001 != 0
}
pub fn revoke(&self) -> bool {
self.flags & 0b0000_0000_1000_0000 != 0
}
pub fn algorithm(&self) -> Option<Algorithm> {
self.algorithm
}
pub fn is_delete(&self) -> bool {
self.algorithm.is_none()
}
pub fn public_key(&self) -> Option<PublicKeyBuf> {
Some(PublicKeyBuf::new(self.public_key.clone(), self.algorithm?))
}
pub fn flags(&self) -> u16 {
self.flags
}
}
impl From<CDNSKEY> for RData {
fn from(value: CDNSKEY) -> Self {
Self::DNSSEC(DNSSECRData::CDNSKEY(value))
}
}
impl BinEncodable for CDNSKEY {
fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
encoder.emit_u16(self.flags())?;
encoder.emit(3)?;
match self.algorithm() {
Some(algorithm) => algorithm.emit(encoder)?,
None => encoder.emit_u8(0)?,
}
encoder.emit_vec(&self.public_key)?;
Ok(())
}
}
impl<'r> RecordDataDecodable<'r> for CDNSKEY {
fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> Result<Self, DecodeError> {
let flags = decoder.read_u16()?.unverified();
let _protocol = decoder
.read_u8()?
.verify_unwrap(|protocol| *protocol == 3)
.map_err(DecodeError::DnsKeyProtocolNot3)?;
let algorithm_value = decoder.read_u8()?.unverified();
let algorithm = match algorithm_value {
0 => None,
_ => Some(Algorithm::from_u8(algorithm_value)),
};
let key_len = length
.map(|u| u as usize)
.checked_sub(4)
.map_err(|len| DecodeError::IncorrectRDataLengthRead { read: 4, len })?
.unverified();
let public_key = decoder
.read_vec(key_len)?
.unverified();
Ok(Self::with_flags(flags, algorithm, public_key))
}
}
impl RecordData for CDNSKEY {
fn try_borrow(data: &RData) -> Option<&Self> {
match data {
RData::DNSSEC(DNSSECRData::CDNSKEY(cdnskey)) => Some(cdnskey),
_ => None,
}
}
fn record_type(&self) -> RecordType {
RecordType::CDNSKEY
}
fn into_rdata(self) -> RData {
RData::DNSSEC(DNSSECRData::CDNSKEY(self))
}
}
impl fmt::Display for CDNSKEY {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
write!(
f,
"{flags} 3 {alg} {key}",
flags = self.flags,
alg = self.algorithm.map(u8::from).unwrap_or(0),
key = data_encoding::BASE64.encode(&self.public_key)
)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::dbg_macro, clippy::print_stdout)]
use alloc::vec::Vec;
use std::println;
use crate::{
dnssec::Algorithm,
rr::RecordDataDecodable,
serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict},
};
use super::CDNSKEY;
#[test]
fn test() {
let rdata = CDNSKEY::new(
true,
true,
false,
Some(Algorithm::ECDSAP256SHA256),
vec![1u8, 2u8, 3u8, 4u8],
);
let mut bytes = Vec::new();
let mut encoder = BinEncoder::new(&mut bytes);
rdata.emit(&mut encoder).expect("error encoding");
let bytes = encoder.into_bytes();
println!("bytes: {bytes:?}");
let mut decoder = BinDecoder::new(bytes);
let read_rdata = CDNSKEY::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("error decoding");
assert_eq!(rdata, read_rdata);
}
#[test]
fn test_delete() {
let rdata = CDNSKEY::with_flags(0, None, vec![0u8]);
let mut bytes = Vec::new();
let mut encoder = BinEncoder::new(&mut bytes);
rdata.emit(&mut encoder).expect("error encoding");
let bytes = encoder.into_bytes();
println!("bytes: {bytes:?}");
let mut decoder = BinDecoder::new(bytes);
let read_rdata = CDNSKEY::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
.expect("error decoding");
assert_eq!(rdata, read_rdata);
}
}