hickory_proto/rr/dnssec/rdata/
dnskey.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//! public key record data for signing zone records
9
10use std::fmt;
11
12#[cfg(feature = "serde-config")]
13use serde::{Deserialize, Serialize};
14
15use crate::{
16    error::{ProtoError, ProtoErrorKind, ProtoResult},
17    rr::{
18        dnssec::{Algorithm, Digest, DigestType},
19        record_data::RData,
20        Name, RecordData, RecordDataDecodable, RecordType,
21    },
22    serialize::binary::{
23        BinDecodable, BinDecoder, BinEncodable, BinEncoder, Restrict, RestrictedMath,
24    },
25};
26
27use super::DNSSECRData;
28
29/// [RFC 4034](https://tools.ietf.org/html/rfc4034#section-2), DNSSEC Resource Records, March 2005
30///
31/// ```text
32/// 2.  The DNSKEY Resource Record
33///
34///    DNSSEC uses public key cryptography to sign and authenticate DNS
35///    resource record sets (RRsets).  The public keys are stored in DNSKEY
36///    resource records and are used in the DNSSEC authentication process
37///    described in [RFC4035]: A zone signs its authoritative RRsets by
38///    using a private key and stores the corresponding public key in a
39///    DNSKEY RR.  A resolver can then use the public key to validate
40///    signatures covering the RRsets in the zone, and thus to authenticate
41///    them.
42///
43///    The DNSKEY RR is not intended as a record for storing arbitrary
44///    public keys and MUST NOT be used to store certificates or public keys
45///    that do not directly relate to the DNS infrastructure.
46///
47///    The Type value for the DNSKEY RR type is 48.
48///
49///    The DNSKEY RR is class independent.
50///
51///    The DNSKEY RR has no special TTL requirements.
52///
53/// 2.1.  DNSKEY RDATA Wire Format
54///
55///    The RDATA for a DNSKEY RR consists of a 2 octet Flags Field, a 1
56///    octet Protocol Field, a 1 octet Algorithm Field, and the Public Key
57///    Field.
58///
59///                         1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 3 3
60///     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
61///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
62///    |              Flags            |    Protocol   |   Algorithm   |
63///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64///    /                                                               /
65///    /                            Public Key                         /
66///    /                                                               /
67///    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68///
69/// 2.1.5.  Notes on DNSKEY RDATA Design
70///
71///    Although the Protocol Field always has value 3, it is retained for
72///    backward compatibility with early versions of the KEY record.
73///
74/// ```
75#[cfg_attr(feature = "serde-config", derive(Deserialize, Serialize))]
76#[derive(Debug, PartialEq, Eq, Hash, Clone)]
77pub struct DNSKEY {
78    zone_key: bool,
79    secure_entry_point: bool,
80    revoke: bool,
81    algorithm: Algorithm,
82    public_key: Vec<u8>,
83}
84
85impl DNSKEY {
86    /// Construct a new DNSKey RData
87    ///
88    /// # Arguments
89    ///
90    /// * `zone_key` - this key is used to sign Zone resource records
91    /// * `secure_entry_point` - this key is used to sign DNSKeys that sign the Zone records
92    /// * `revoke` - this key has been revoked
93    /// * `algorithm` - specifies the algorithm which this Key uses to sign records
94    /// * `public_key` - the public key material, in native endian, the emitter will perform any necessary conversion
95    ///
96    /// # Return
97    ///
98    /// A new DNSKEY RData for use in a Resource Record
99    pub fn new(
100        zone_key: bool,
101        secure_entry_point: bool,
102        revoke: bool,
103        algorithm: Algorithm,
104        public_key: Vec<u8>,
105    ) -> Self {
106        Self {
107            zone_key,
108            secure_entry_point,
109            revoke,
110            algorithm,
111            public_key,
112        }
113    }
114
115    /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.1)
116    ///
117    /// ```text
118    /// 2.1.1.  The Flags Field
119    ///
120    ///    Bit 7 of the Flags field is the Zone Key flag.  If bit 7 has value 1,
121    ///    then the DNSKEY record holds a DNS zone key, and the DNSKEY RR's
122    ///    owner name MUST be the name of a zone.  If bit 7 has value 0, then
123    ///    the DNSKEY record holds some other type of DNS public key and MUST
124    ///    NOT be used to verify RRSIGs that cover RRsets.
125    ///
126    ///
127    ///    Bits 0-6 and 8-14 are reserved: these bits MUST have value 0 upon
128    ///    creation of the DNSKEY RR and MUST be ignored upon receipt.
129    /// ```
130    pub fn zone_key(&self) -> bool {
131        self.zone_key
132    }
133
134    /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.1)
135    ///
136    /// ```text
137    /// 2.1.1.  The Flags Field
138    ///
139    ///    Bit 15 of the Flags field is the Secure Entry Point flag, described
140    ///    in [RFC3757].  If bit 15 has value 1, then the DNSKEY record holds a
141    ///    key intended for use as a secure entry point.  This flag is only
142    ///    intended to be a hint to zone signing or debugging software as to the
143    ///    intended use of this DNSKEY record; validators MUST NOT alter their
144    ///    behavior during the signature validation process in any way based on
145    ///    the setting of this bit.  This also means that a DNSKEY RR with the
146    ///    SEP bit set would also need the Zone Key flag set in order to be able
147    ///    to generate signatures legally.  A DNSKEY RR with the SEP set and the
148    ///    Zone Key flag not set MUST NOT be used to verify RRSIGs that cover
149    ///    RRsets.
150    /// ```
151    pub fn secure_entry_point(&self) -> bool {
152        self.secure_entry_point
153    }
154
155    /// [RFC 5011, Trust Anchor Update, September 2007](https://tools.ietf.org/html/rfc5011#section-3)
156    ///
157    /// ```text
158    /// RFC 5011                  Trust Anchor Update             September 2007
159    ///
160    /// 7.  IANA Considerations
161    ///
162    ///   The IANA has assigned a bit in the DNSKEY flags field (see Section 7
163    ///   of [RFC4034]) for the REVOKE bit (8).
164    /// ```
165    pub fn revoke(&self) -> bool {
166        self.revoke
167    }
168
169    /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.3)
170    ///
171    /// ```text
172    /// 2.1.3.  The Algorithm Field
173    ///
174    ///    The Algorithm field identifies the public key's cryptographic
175    ///    algorithm and determines the format of the Public Key field.  A list
176    ///    of DNSSEC algorithm types can be found in Appendix A.1
177    /// ```
178    pub fn algorithm(&self) -> Algorithm {
179        self.algorithm
180    }
181
182    /// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.4)
183    ///
184    /// ```text
185    /// 2.1.4.  The Public Key Field
186    ///
187    ///    The Public Key Field holds the public key material.  The format
188    ///    depends on the algorithm of the key being stored and is described in
189    ///    separate documents.
190    /// ```
191    pub fn public_key(&self) -> &[u8] {
192        &self.public_key
193    }
194
195    /// Output the encoded form of the flags
196    pub fn flags(&self) -> u16 {
197        let mut flags: u16 = 0;
198        if self.zone_key() {
199            flags |= 0b0000_0001_0000_0000
200        }
201        if self.secure_entry_point() {
202            flags |= 0b0000_0000_0000_0001
203        }
204        if self.revoke() {
205            flags |= 0b0000_0000_1000_0000
206        }
207
208        flags
209    }
210
211    /// Creates a message digest for this DNSKEY record.
212    ///
213    /// ```text
214    /// 5.1.4.  The Digest Field
215    ///
216    ///    The DS record refers to a DNSKEY RR by including a digest of that
217    ///    DNSKEY RR.
218    ///
219    ///    The digest is calculated by concatenating the canonical form of the
220    ///    fully qualified owner name of the DNSKEY RR with the DNSKEY RDATA,
221    ///    and then applying the digest algorithm.
222    ///
223    ///      digest = digest_algorithm( DNSKEY owner name | DNSKEY RDATA);
224    ///
225    ///       "|" denotes concatenation
226    ///
227    ///      DNSKEY RDATA = Flags | Protocol | Algorithm | Public Key.
228    ///
229    ///    The size of the digest may vary depending on the digest algorithm and
230    ///    DNSKEY RR size.  As of the time of this writing, the only defined
231    ///    digest algorithm is SHA-1, which produces a 20 octet digest.
232    /// ```
233    ///
234    /// # Arguments
235    ///
236    /// * `name` - the label of of the DNSKEY record.
237    /// * `digest_type` - the `DigestType` with which to create the message digest.
238    #[cfg(any(feature = "openssl", feature = "ring"))]
239    #[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
240    pub fn to_digest(&self, name: &Name, digest_type: DigestType) -> ProtoResult<Digest> {
241        let mut buf: Vec<u8> = Vec::new();
242        {
243            let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut buf);
244            encoder.set_canonical_names(true);
245            if let Err(e) = name
246                .emit(&mut encoder)
247                .and_then(|_| self.emit(&mut encoder))
248            {
249                tracing::warn!("error serializing dnskey: {e}");
250                return Err(format!("error serializing dnskey: {e}").into());
251            }
252        }
253
254        digest_type.hash(&buf)
255    }
256
257    /// This will always return an error unless the Ring or OpenSSL features are enabled
258    #[cfg(not(any(feature = "openssl", feature = "ring")))]
259    #[cfg_attr(docsrs, doc(cfg(not(any(feature = "openssl", feature = "ring")))))]
260    pub fn to_digest(&self, _: &Name, _: DigestType) -> ProtoResult<Digest> {
261        Err("Ring or OpenSSL must be enabled for this feature".into())
262    }
263
264    /// The key tag is calculated as a hash to more quickly lookup a DNSKEY.
265    ///
266    /// [RFC 2535](https://tools.ietf.org/html/rfc2535), Domain Name System Security Extensions, March 1999
267    ///
268    /// ```text
269    /// RFC 2535                DNS Security Extensions               March 1999
270    ///
271    /// 4.1.6 Key Tag Field
272    ///
273    ///  The "key Tag" is a two octet quantity that is used to efficiently
274    ///  select between multiple keys which may be applicable and thus check
275    ///  that a public key about to be used for the computationally expensive
276    ///  effort to check the signature is possibly valid.  For algorithm 1
277    ///  (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
278    ///  octets of the public key modulus needed to decode the signature
279    ///  field.  That is to say, the most significant 16 of the least
280    ///  significant 24 bits of the modulus in network (big endian) order. For
281    ///  all other algorithms, including private algorithms, it is calculated
282    ///  as a simple checksum of the KEY RR as described in Appendix C.
283    ///
284    /// Appendix C: Key Tag Calculation
285    ///
286    ///  The key tag field in the SIG RR is just a means of more efficiently
287    ///  selecting the correct KEY RR to use when there is more than one KEY
288    ///  RR candidate available, for example, in verifying a signature.  It is
289    ///  possible for more than one candidate key to have the same tag, in
290    ///  which case each must be tried until one works or all fail.  The
291    ///  following reference implementation of how to calculate the Key Tag,
292    ///  for all algorithms other than algorithm 1, is in ANSI C.  It is coded
293    ///  for clarity, not efficiency.  (See section 4.1.6 for how to determine
294    ///  the Key Tag of an algorithm 1 key.)
295    ///
296    ///  /* assumes int is at least 16 bits
297    ///     first byte of the key tag is the most significant byte of return
298    ///     value
299    ///     second byte of the key tag is the least significant byte of
300    ///     return value
301    ///     */
302    ///
303    ///  int keytag (
304    ///
305    ///          unsigned char key[],  /* the RDATA part of the KEY RR */
306    ///          unsigned int keysize, /* the RDLENGTH */
307    ///          )
308    ///  {
309    ///  long int    ac;    /* assumed to be 32 bits or larger */
310    ///
311    ///  for ( ac = 0, i = 0; i < keysize; ++i )
312    ///      ac += (i&1) ? key[i] : key[i]<<8;
313    ///  ac += (ac>>16) & 0xFFFF;
314    ///  return ac & 0xFFFF;
315    ///  }
316    /// ```
317    pub fn calculate_key_tag(&self) -> ProtoResult<u16> {
318        // TODO:
319        let mut bytes: Vec<u8> = Vec::with_capacity(512);
320        {
321            let mut e = BinEncoder::new(&mut bytes);
322            self.emit(&mut e)?;
323        }
324        Ok(Self::calculate_key_tag_internal(&bytes))
325    }
326
327    /// Internal checksum function (used for non-RSAMD5 hashes only,
328    /// however, RSAMD5 is considered deprecated and not implemented in
329    /// hickory-dns, anyways).
330    pub fn calculate_key_tag_internal(bytes: &[u8]) -> u16 {
331        let mut ac: u32 = 0;
332        for (i, k) in bytes.iter().enumerate() {
333            ac += u32::from(*k) << if i & 0x01 != 0 { 0 } else { 8 };
334        }
335        ac += ac >> 16;
336        (ac & 0xFFFF) as u16
337    }
338}
339
340impl From<DNSKEY> for RData {
341    fn from(key: DNSKEY) -> Self {
342        Self::DNSSEC(super::DNSSECRData::DNSKEY(key))
343    }
344}
345
346impl BinEncodable for DNSKEY {
347    fn emit(&self, encoder: &mut BinEncoder<'_>) -> ProtoResult<()> {
348        encoder.emit_u16(self.flags())?;
349        encoder.emit(3)?; // always 3 for now
350        self.algorithm().emit(encoder)?;
351        encoder.emit_vec(self.public_key())?;
352
353        Ok(())
354    }
355}
356
357impl<'r> RecordDataDecodable<'r> for DNSKEY {
358    fn read_data(decoder: &mut BinDecoder<'r>, length: Restrict<u16>) -> ProtoResult<Self> {
359        let flags: u16 = decoder.read_u16()?.unverified(/*used as a bitfield, this is safe*/);
360
361        //    Bits 0-6 and 8-14 are reserved: these bits MUST have value 0 upon
362        //    creation of the DNSKEY RR and MUST be ignored upon receipt.
363        let zone_key: bool = flags & 0b0000_0001_0000_0000 == 0b0000_0001_0000_0000;
364        let secure_entry_point: bool = flags & 0b0000_0000_0000_0001 == 0b0000_0000_0000_0001;
365        let revoke: bool = flags & 0b0000_0000_1000_0000 == 0b0000_0000_1000_0000;
366        let _protocol: u8 = decoder
367            .read_u8()?
368            .verify_unwrap(|protocol| {
369                // RFC 4034                DNSSEC Resource Records               March 2005
370                //
371                // 2.1.2.  The Protocol Field
372                //
373                //    The Protocol Field MUST have value 3, and the DNSKEY RR MUST be
374                //    treated as invalid during signature verification if it is found to be
375                //    some value other than 3.
376                //
377                // protocol is defined to only be '3' right now
378
379                *protocol == 3
380            })
381            .map_err(|protocol| ProtoError::from(ProtoErrorKind::DnsKeyProtocolNot3(protocol)))?;
382
383        let algorithm: Algorithm = Algorithm::read(decoder)?;
384
385        // the public key is the left-over bytes minus 4 for the first fields
386        //   this sub is safe, as the first 4 fields must have been in the rdata, otherwise there would have been
387        //   an earlier return.
388        let key_len = length
389        .map(|u| u as usize)
390        .checked_sub(4)
391        .map_err(|_| ProtoError::from("invalid rdata length in DNSKEY"))?
392        .unverified(/*used only as length safely*/);
393        let public_key: Vec<u8> =
394            decoder.read_vec(key_len)?.unverified(/*the byte array will fail in usage if invalid*/);
395
396        Ok(Self::new(
397            zone_key,
398            secure_entry_point,
399            revoke,
400            algorithm,
401            public_key,
402        ))
403    }
404}
405
406impl RecordData for DNSKEY {
407    fn try_from_rdata(data: RData) -> Result<Self, RData> {
408        match data {
409            RData::DNSSEC(DNSSECRData::DNSKEY(csync)) => Ok(csync),
410            _ => Err(data),
411        }
412    }
413
414    fn try_borrow(data: &RData) -> Option<&Self> {
415        match data {
416            RData::DNSSEC(DNSSECRData::DNSKEY(csync)) => Some(csync),
417            _ => None,
418        }
419    }
420
421    fn record_type(&self) -> RecordType {
422        RecordType::DNSKEY
423    }
424
425    fn into_rdata(self) -> RData {
426        RData::DNSSEC(DNSSECRData::DNSKEY(self))
427    }
428}
429
430/// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.2)
431///
432/// ```text
433/// 2.2.  The DNSKEY RR Presentation Format
434///
435///    The presentation format of the RDATA portion is as follows:
436///
437///    The Flag field MUST be represented as an unsigned decimal integer.
438///    Given the currently defined flags, the possible values are: 0, 256,
439///    and 257.
440///
441///    The Protocol Field MUST be represented as an unsigned decimal integer
442///    with a value of 3.
443///
444///    The Algorithm field MUST be represented either as an unsigned decimal
445///    integer or as an algorithm mnemonic as specified in Appendix A.1.
446///
447///    The Public Key field MUST be represented as a Base64 encoding of the
448///    Public Key.  Whitespace is allowed within the Base64 text.  For a
449///    definition of Base64 encoding, see [RFC3548].
450///
451/// 2.3.  DNSKEY RR Example
452///
453///    The following DNSKEY RR stores a DNS zone key for example.com.
454///
455///    example.com. 86400 IN DNSKEY 256 3 5 ( AQPSKmynfzW4kyBv015MUG2DeIQ3
456///                                           Cbl+BBZH4b/0PY1kxkmvHjcZc8no
457///                                           kfzj31GajIQKY+5CptLr3buXA10h
458///                                           WqTkF7H6RfoRqXQeogmMHfpftf6z
459///                                           Mv1LyBUgia7za6ZEzOJBOztyvhjL
460///                                           742iU/TpPSEDhm2SNKLijfUppn1U
461///                                           aNvv4w==  )
462///
463///    The first four text fields specify the owner name, TTL, Class, and RR
464///    type (DNSKEY).  Value 256 indicates that the Zone Key bit (bit 7) in
465///    the Flags field has value 1.  Value 3 is the fixed Protocol value.
466///    Value 5 indicates the public key algorithm.  Appendix A.1 identifies
467///    algorithm type 5 as RSA/SHA1 and indicates that the format of the
468///    RSA/SHA1 public key field is defined in [RFC3110].  The remaining
469///    text is a Base64 encoding of the public key.
470/// ```
471impl fmt::Display for DNSKEY {
472    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
473        write!(
474            f,
475            "{flags} 3 {alg} {key}",
476            flags = self.flags(),
477            alg = u8::from(self.algorithm),
478            key = data_encoding::BASE64.encode(&self.public_key)
479        )
480    }
481}
482
483#[cfg(test)]
484mod tests {
485    #![allow(clippy::dbg_macro, clippy::print_stdout)]
486
487    use super::*;
488
489    #[test]
490    #[cfg(any(feature = "openssl", feature = "ring"))]
491    fn test() {
492        let rdata = DNSKEY::new(
493            true,
494            true,
495            false,
496            Algorithm::RSASHA256,
497            vec![0, 1, 2, 3, 4, 5, 6, 7],
498        );
499
500        let mut bytes = Vec::new();
501        let mut encoder: BinEncoder<'_> = BinEncoder::new(&mut bytes);
502        assert!(rdata.emit(&mut encoder).is_ok());
503        let bytes = encoder.into_bytes();
504
505        println!("bytes: {bytes:?}");
506
507        let mut decoder: BinDecoder<'_> = BinDecoder::new(bytes);
508        let read_rdata = DNSKEY::read_data(&mut decoder, Restrict::new(bytes.len() as u16));
509        let read_rdata = read_rdata.expect("error decoding");
510
511        assert_eq!(rdata, read_rdata);
512        assert!(rdata
513            .to_digest(
514                &Name::parse("www.example.com.", None).unwrap(),
515                DigestType::SHA256
516            )
517            .is_ok());
518    }
519
520    #[test]
521    fn test_calculate_key_tag_checksum() {
522        let test_text = "The quick brown fox jumps over the lazy dog";
523        let test_vectors = vec![
524            (vec![], 0),
525            (vec![0, 0, 0, 0], 0),
526            (vec![0xff, 0xff, 0xff, 0xff], 0xffff),
527            (vec![1, 0, 0, 0], 0x0100),
528            (vec![0, 1, 0, 0], 0x0001),
529            (vec![0, 0, 1, 0], 0x0100),
530            (test_text.as_bytes().to_vec(), 0x8d5b),
531        ];
532
533        for &(ref input_data, exp_result) in test_vectors.iter() {
534            let result = DNSKEY::calculate_key_tag_internal(input_data);
535            assert_eq!(result, exp_result);
536        }
537    }
538}