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}