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}