hickory_proto/dnssec/rdata/
cdnskey.rs

1// Copyright 2015-2023 Benjamin Fry <benjaminfry@me.com>
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// https://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// https://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! CDNSKEY type and related implementations
9
10use alloc::vec::Vec;
11use core::fmt;
12
13#[cfg(feature = "serde")]
14use serde::{Deserialize, Serialize};
15
16use crate::{
17    ProtoError, ProtoErrorKind,
18    dnssec::{Algorithm, PublicKeyBuf},
19    error::ProtoResult,
20    rr::{RData, RecordData, RecordDataDecodable, RecordType},
21    serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath},
22};
23
24use super::DNSSECRData;
25
26/// Child DNSKEY. See RFC 8078.
27#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
28#[derive(Debug, PartialEq, Eq, Hash, Clone)]
29pub struct CDNSKEY {
30    flags: u16,
31    algorithm: Option<Algorithm>,
32    public_key: Vec<u8>,
33}
34
35impl CDNSKEY {
36    /// Construct a new CDNSKEY RData
37    ///
38    /// # Arguments
39    ///
40    /// * `zone_key` - this key is used to sign Zone resource records
41    /// * `secure_entry_point` - this key is used to sign DNSKeys that sign the Zone records
42    /// * `revoke` - this key has been revoked
43    /// * `algorithm` - the key's algorithm, or `None` to request deletion
44    /// * `public_key` - the public key encoded as a byte array
45    ///
46    /// # Return
47    ///
48    /// A new CDNSKEY RData for use in a Resource Record
49    pub fn new(
50        zone_key: bool,
51        secure_entry_point: bool,
52        revoke: bool,
53        algorithm: Option<Algorithm>,
54        public_key: Vec<u8>,
55    ) -> Self {
56        let mut flags: u16 = 0;
57        if zone_key {
58            flags |= 0b0000_0001_0000_0000;
59        }
60        if secure_entry_point {
61            flags |= 0b0000_0000_0000_0001;
62        }
63        if revoke {
64            flags |= 0b0000_0000_1000_0000;
65        }
66        Self::with_flags(flags, algorithm, public_key)
67    }
68
69    /// Construct a new CDNSKEY RData
70    ///
71    /// # Arguments
72    ///
73    /// * `flags` - flags associated with this key
74    /// * `algorithm` - the key's algorithm, or `None` to request deletion
75    /// * `public_key` - the public key encoded as a byte array
76    ///
77    /// # Return
78    ///
79    /// A new CDNSKEY RData for use in a Resource Record
80    pub fn with_flags(flags: u16, algorithm: Option<Algorithm>, public_key: Vec<u8>) -> Self {
81        Self {
82            flags,
83            algorithm,
84            public_key,
85        }
86    }
87
88    /// Returns the value of the Zone Key flag
89    pub fn zone_key(&self) -> bool {
90        self.flags & 0b0000_0001_0000_0000 != 0
91    }
92
93    /// Returns the value of the Secure Entry Point flag
94    pub fn secure_entry_point(&self) -> bool {
95        self.flags & 0b0000_0000_0000_0001 != 0
96    }
97
98    /// Returns the value of the Revoke flag.
99    pub fn revoke(&self) -> bool {
100        self.flags & 0b0000_0000_1000_0000 != 0
101    }
102
103    /// Returns the Algorithm field. This is `None` if deletion is requested, or the key's algorithm
104    /// if an update is requested.
105    pub fn algorithm(&self) -> Option<Algorithm> {
106        self.algorithm
107    }
108
109    /// Returns whether this record is requesting deletion of the DS RRset.
110    pub fn is_delete(&self) -> bool {
111        self.algorithm.is_none()
112    }
113
114    /// Returns the public key, or `None` if deletion is requested.
115    pub fn public_key(&self) -> Option<PublicKeyBuf> {
116        Some(PublicKeyBuf::new(self.public_key.clone(), self.algorithm?))
117    }
118
119    /// Returns the Flags field
120    pub fn flags(&self) -> u16 {
121        self.flags
122    }
123}
124
125impl From<CDNSKEY> for RData {
126    fn from(value: CDNSKEY) -> Self {
127        Self::DNSSEC(DNSSECRData::CDNSKEY(value))
128    }
129}
130
131impl BinEncodable for CDNSKEY {
132    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
133        encoder.emit_u16(self.flags())?;
134        encoder.emit(3)?;
135        match self.algorithm() {
136            Some(algorithm) => algorithm.emit(encoder)?,
137            None => encoder.emit_u8(0)?,
138        }
139        encoder.emit_vec(&self.public_key)?;
140
141        Ok(())
142    }
143}
144
145impl<'r> RecordDataDecodable<'r> for CDNSKEY {
146    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
147        let flags = decoder.read_u16()?.unverified(/* used as a bitfield, this is safe */);
148
149        // protocol is defined to only be '3' right now
150        let _protocol = decoder
151            .read_u8()?
152            .verify_unwrap(|protocol| *protocol == 3)
153            .map_err(|protocol| ProtoError::from(ProtoErrorKind::DnsKeyProtocolNot3(protocol)))?;
154
155        let algorithm_value = decoder.read_u8()?.unverified(/* no further validation required */);
156        let algorithm = match algorithm_value {
157            0 => None,
158            _ => Some(Algorithm::from_u8(algorithm_value)),
159        };
160
161        // The public key is the remaining bytes, excluding the first four bytes for the above
162        // fields. This subtraction is safe, as the first three fields must have been in the RDATA,
163        // otherwise there would have been an earlier return.
164        let key_len = length
165            .map(|u| u as usize)
166            .checked_sub(4)
167            .map_err(|_| ProtoError::from("invalid rdata length in DNSKEY"))?
168            .unverified(/* used only as length safely */);
169        let public_key = decoder
170            .read_vec(key_len)?
171            .unverified(/* signature verification will fail if the public key is invalid */);
172
173        Ok(Self::with_flags(flags, algorithm, public_key))
174    }
175}
176
177impl RecordData for CDNSKEY {
178    fn try_from_rdata(data: RData) -> Result<Self, RData> {
179        match data {
180            RData::DNSSEC(DNSSECRData::CDNSKEY(cdnskey)) => Ok(cdnskey),
181            _ => Err(data),
182        }
183    }
184
185    fn try_borrow(data: &RData) -> Option<&Self> {
186        match data {
187            RData::DNSSEC(DNSSECRData::CDNSKEY(cdnskey)) => Some(cdnskey),
188            _ => None,
189        }
190    }
191
192    fn record_type(&self) -> RecordType {
193        RecordType::CDNSKEY
194    }
195
196    fn into_rdata(self) -> RData {
197        RData::DNSSEC(DNSSECRData::CDNSKEY(self))
198    }
199}
200
201impl fmt::Display for CDNSKEY {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
203        write!(
204            f,
205            "{flags} 3 {alg} {key}",
206            flags = self.flags,
207            alg = self.algorithm.map(u8::from).unwrap_or(0),
208            key = data_encoding::BASE64.encode(&self.public_key)
209        )
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    #![allow(clippy::dbg_macro, clippy::print_stdout)]
216
217    use alloc::vec::Vec;
218    use std::println;
219
220    use crate::{
221        dnssec::Algorithm,
222        rr::RecordDataDecodable,
223        serialize::binary::{BinDecoder, BinEncodable, BinEncoder, Restrict},
224    };
225
226    use super::CDNSKEY;
227
228    #[test]
229    fn test() {
230        let rdata = CDNSKEY::new(
231            true,
232            true,
233            false,
234            Some(Algorithm::ECDSAP256SHA256),
235            vec![1u8, 2u8, 3u8, 4u8],
236        );
237
238        let mut bytes = Vec::new();
239        let mut encoder = BinEncoder::new(&mut bytes);
240        rdata.emit(&mut encoder).expect("error encoding");
241        let bytes = encoder.into_bytes();
242
243        println!("bytes: {bytes:?}");
244
245        let mut decoder = BinDecoder::new(bytes);
246        let read_rdata = CDNSKEY::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
247            .expect("error decoding");
248
249        assert_eq!(rdata, read_rdata);
250    }
251
252    #[test]
253    fn test_delete() {
254        let rdata = CDNSKEY::with_flags(0, None, vec![0u8]);
255
256        let mut bytes = Vec::new();
257        let mut encoder = BinEncoder::new(&mut bytes);
258        rdata.emit(&mut encoder).expect("error encoding");
259        let bytes = encoder.into_bytes();
260
261        println!("bytes: {bytes:?}");
262
263        let mut decoder = BinDecoder::new(bytes);
264        let read_rdata = CDNSKEY::read_data(&mut decoder, Restrict::new(bytes.len() as u16))
265            .expect("error decoding");
266
267        assert_eq!(rdata, read_rdata);
268    }
269}