Skip to main content

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