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