hickory_proto/dnssec/signer.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//! signer is a structure for performing many of the signing processes of the DNSSEC specification
9use alloc::{boxed::Box, vec::Vec};
10use core::time::Duration;
11
12use tracing::debug;
13
14use super::{DnsSecResult, SigningKey};
15use crate::{
16 dnssec::{
17 TBS,
18 rdata::{DNSKEY, DNSSECRData, KEY, SIG},
19 tbs,
20 },
21 error::{ProtoErrorKind, ProtoResult},
22 op::{Message, MessageFinalizer, MessageVerifier},
23 rr::{
24 Record, {DNSClass, Name, RData, RecordType},
25 },
26 serialize::binary::{BinEncodable, BinEncoder},
27};
28
29/// Use for performing signing and validation of DNSSEC based components. The SigSigner can be used for singing requests and responses with SIG0, or DNSSEC RRSIG records. The format is based on the SIG record type.
30///
31/// TODO: warning this struct and it's impl are under high volatility, expect breaking changes
32///
33/// [RFC 4035](https://tools.ietf.org/html/rfc4035), DNSSEC Protocol Modifications, March 2005
34///
35/// ```text
36/// 5.3. Authenticating an RRset with an RRSIG RR
37///
38/// A validator can use an RRSIG RR and its corresponding DNSKEY RR to
39/// attempt to authenticate RRsets. The validator first checks the RRSIG
40/// RR to verify that it covers the RRset, has a valid time interval, and
41/// identifies a valid DNSKEY RR. The validator then constructs the
42/// canonical form of the signed data by appending the RRSIG RDATA
43/// (excluding the Signature Field) with the canonical form of the
44/// covered RRset. Finally, the validator uses the public key and
45/// signature to authenticate the signed data. Sections 5.3.1, 5.3.2,
46/// and 5.3.3 describe each step in detail.
47///
48/// 5.3.1. Checking the RRSIG RR Validity
49///
50/// A security-aware resolver can use an RRSIG RR to authenticate an
51/// RRset if all of the following conditions hold:
52///
53/// o The RRSIG RR and the RRset MUST have the same owner name and the
54/// same class.
55///
56/// o The RRSIG RR's Signer's Name field MUST be the name of the zone
57/// that contains the RRset.
58///
59/// o The RRSIG RR's Type Covered field MUST equal the RRset's type.
60///
61/// o The number of labels in the RRset owner name MUST be greater than
62/// or equal to the value in the RRSIG RR's Labels field.
63///
64/// o The validator's notion of the current time MUST be less than or
65/// equal to the time listed in the RRSIG RR's Expiration field.
66///
67/// o The validator's notion of the current time MUST be greater than or
68/// equal to the time listed in the RRSIG RR's Inception field.
69///
70/// o The RRSIG RR's Signer's Name, Algorithm, and Key Tag fields MUST
71/// match the owner name, algorithm, and key tag for some DNSKEY RR in
72/// the zone's apex DNSKEY RRset.
73///
74/// o The matching DNSKEY RR MUST be present in the zone's apex DNSKEY
75/// RRset, and MUST have the Zone Flag bit (DNSKEY RDATA Flag bit 7)
76/// set.
77///
78/// It is possible for more than one DNSKEY RR to match the conditions
79/// above. In this case, the validator cannot predetermine which DNSKEY
80/// RR to use to authenticate the signature, and it MUST try each
81/// matching DNSKEY RR until either the signature is validated or the
82/// validator has run out of matching public keys to try.
83///
84/// Note that this authentication process is only meaningful if the
85/// validator authenticates the DNSKEY RR before using it to validate
86/// signatures. The matching DNSKEY RR is considered to be authentic if:
87///
88/// o the apex DNSKEY RRset containing the DNSKEY RR is considered
89/// authentic; or
90///
91/// o the RRset covered by the RRSIG RR is the apex DNSKEY RRset itself,
92/// and the DNSKEY RR either matches an authenticated DS RR from the
93/// parent zone or matches a trust anchor.
94///
95/// 5.3.2. Reconstructing the Signed Data
96///
97/// Once the RRSIG RR has met the validity requirements described in
98/// Section 5.3.1, the validator has to reconstruct the original signed
99/// data. The original signed data includes RRSIG RDATA (excluding the
100/// Signature field) and the canonical form of the RRset. Aside from
101/// being ordered, the canonical form of the RRset might also differ from
102/// the received RRset due to DNS name compression, decremented TTLs, or
103/// wildcard expansion. The validator should use the following to
104/// reconstruct the original signed data:
105///
106/// signed_data = RRSIG_RDATA | RR(1) | RR(2)... where
107///
108/// "|" denotes concatenation
109///
110/// RRSIG_RDATA is the wire format of the RRSIG RDATA fields
111/// with the Signature field excluded and the Signer's Name
112/// in canonical form.
113///
114/// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
115///
116/// name is calculated according to the function below
117///
118/// class is the RRset's class
119///
120/// type is the RRset type and all RRs in the class
121///
122/// OrigTTL is the value from the RRSIG Original TTL field
123///
124/// All names in the RDATA field are in canonical form
125///
126/// The set of all RR(i) is sorted into canonical order.
127///
128/// To calculate the name:
129/// let rrsig_labels = the value of the RRSIG Labels field
130///
131/// let fqdn = RRset's fully qualified domain name in
132/// canonical form
133///
134/// let fqdn_labels = Label count of the fqdn above.
135///
136/// if rrsig_labels = fqdn_labels,
137/// name = fqdn
138///
139/// if rrsig_labels < fqdn_labels,
140/// name = "*." | the rightmost rrsig_label labels of the
141/// fqdn
142///
143/// if rrsig_labels > fqdn_labels
144/// the RRSIG RR did not pass the necessary validation
145/// checks and MUST NOT be used to authenticate this
146/// RRset.
147///
148/// The canonical forms for names and RRsets are defined in [RFC4034].
149///
150/// NSEC RRsets at a delegation boundary require special processing.
151/// There are two distinct NSEC RRsets associated with a signed delegated
152/// name. One NSEC RRset resides in the parent zone, and specifies which
153/// RRsets are present at the parent zone. The second NSEC RRset resides
154/// at the child zone and identifies which RRsets are present at the apex
155/// in the child zone. The parent NSEC RRset and child NSEC RRset can
156/// always be distinguished as only a child NSEC RR will indicate that an
157/// SOA RRset exists at the name. When reconstructing the original NSEC
158/// RRset for the delegation from the parent zone, the NSEC RRs MUST NOT
159/// be combined with NSEC RRs from the child zone. When reconstructing
160/// the original NSEC RRset for the apex of the child zone, the NSEC RRs
161/// MUST NOT be combined with NSEC RRs from the parent zone.
162///
163/// Note that each of the two NSEC RRsets at a delegation point has a
164/// corresponding RRSIG RR with an owner name matching the delegated
165/// name, and each of these RRSIG RRs is authoritative data associated
166/// with the same zone that contains the corresponding NSEC RRset. If
167/// necessary, a resolver can tell these RRSIG RRs apart by checking the
168/// Signer's Name field.
169///
170/// 5.3.3. Checking the Signature
171///
172/// Once the resolver has validated the RRSIG RR as described in Section
173/// 5.3.1 and reconstructed the original signed data as described in
174/// Section 5.3.2, the validator can attempt to use the cryptographic
175/// signature to authenticate the signed data, and thus (finally!)
176/// authenticate the RRset.
177///
178/// The Algorithm field in the RRSIG RR identifies the cryptographic
179/// algorithm used to generate the signature. The signature itself is
180/// contained in the Signature field of the RRSIG RDATA, and the public
181/// key used to verify the signature is contained in the Public Key field
182/// of the matching DNSKEY RR(s) (found in Section 5.3.1). [RFC4034]
183/// provides a list of algorithm types and provides pointers to the
184/// documents that define each algorithm's use.
185///
186/// Note that it is possible for more than one DNSKEY RR to match the
187/// conditions in Section 5.3.1. In this case, the validator can only
188/// determine which DNSKEY RR is correct by trying each matching public
189/// key until the validator either succeeds in validating the signature
190/// or runs out of keys to try.
191///
192/// If the Labels field of the RRSIG RR is not equal to the number of
193/// labels in the RRset's fully qualified owner name, then the RRset is
194/// either invalid or the result of wildcard expansion. The resolver
195/// MUST verify that wildcard expansion was applied properly before
196/// considering the RRset to be authentic. Section 5.3.4 describes how
197/// to determine whether a wildcard was applied properly.
198///
199/// If other RRSIG RRs also cover this RRset, the local resolver security
200/// policy determines whether the resolver also has to test these RRSIG
201/// RRs and how to resolve conflicts if these RRSIG RRs lead to differing
202/// results.
203///
204/// If the resolver accepts the RRset as authentic, the validator MUST
205/// set the TTL of the RRSIG RR and each RR in the authenticated RRset to
206/// a value no greater than the minimum of:
207///
208/// o the RRset's TTL as received in the response;
209///
210/// o the RRSIG RR's TTL as received in the response;
211///
212/// o the value in the RRSIG RR's Original TTL field; and
213///
214/// o the difference of the RRSIG RR's Signature Expiration time and the
215/// current time.
216///
217/// 5.3.4. Authenticating a Wildcard Expanded RRset Positive Response
218///
219/// If the number of labels in an RRset's owner name is greater than the
220/// Labels field of the covering RRSIG RR, then the RRset and its
221/// covering RRSIG RR were created as a result of wildcard expansion.
222/// Once the validator has verified the signature, as described in
223/// Section 5.3, it must take additional steps to verify the non-
224/// existence of an exact match or closer wildcard match for the query.
225/// Section 5.4 discusses these steps.
226///
227/// Note that the response received by the resolver should include all
228/// NSEC RRs needed to authenticate the response (see Section 3.1.3).
229/// ```
230pub struct SigSigner {
231 // TODO: this should really be a trait and generic struct over KEY and DNSKEY
232 key_rdata: RData,
233 key: Box<dyn SigningKey>,
234 signer_name: Name,
235 sig_duration: Duration,
236 is_zone_signing_key: bool,
237}
238
239impl SigSigner {
240 /// Version of Signer for verifying RRSIGs and SIG0 records.
241 ///
242 /// # Arguments
243 ///
244 /// * `key_rdata` - the DNSKEY and public key material
245 /// * `key` - the private key for signing, unless validating, where just the public key is necessary
246 /// * `signer_name` - name in the zone to which this DNSKEY is bound
247 /// * `sig_duration` - time period for which this key is valid, 0 when verifying
248 /// * `is_zone_update_auth` - this key may be used for updating the zone
249 pub fn dnssec(
250 key_rdata: DNSKEY,
251 key: Box<dyn SigningKey>,
252 signer_name: Name,
253 sig_duration: Duration,
254 ) -> Self {
255 Self {
256 is_zone_signing_key: key_rdata.zone_key(),
257 key_rdata: key_rdata.into(),
258 key,
259 signer_name,
260 sig_duration,
261 }
262 }
263
264 /// Version of Signer for verifying RRSIGs and SIG0 records.
265 ///
266 /// # Arguments
267 ///
268 /// * `key_rdata` - the KEY and public key material
269 /// * `key` - the private key for signing, unless validating, where just the public key is necessary
270 /// * `signer_name` - name in the zone to which this DNSKEY is bound
271 /// * `is_zone_update_auth` - this key may be used for updating the zone
272 pub fn sig0(key_rdata: KEY, key: Box<dyn SigningKey>, signer_name: Name) -> Self {
273 Self {
274 key_rdata: key_rdata.into(),
275 key,
276 signer_name,
277 sig_duration: Duration::ZERO,
278 is_zone_signing_key: false,
279 }
280 }
281
282 /// Version of Signer for signing RRSIGs and SIG0 records.
283 #[deprecated(note = "use SIG0 or DNSSEC constructors")]
284 pub fn new(
285 key: Box<dyn SigningKey>,
286 signer_name: Name,
287 sig_duration: Duration,
288 is_zone_signing_key: bool,
289 _: bool,
290 ) -> Self {
291 let pub_key = key.to_public_key().expect("key is not a private key");
292 let dnskey = DNSKEY::from_key(&pub_key);
293
294 Self {
295 key_rdata: dnskey.into(),
296 key,
297 signer_name,
298 sig_duration,
299 is_zone_signing_key,
300 }
301 }
302
303 /// Return the key used for validation/signing
304 pub fn key(&self) -> &dyn SigningKey {
305 &*self.key
306 }
307
308 /// Returns the duration that this signature is valid for
309 pub fn sig_duration(&self) -> Duration {
310 self.sig_duration
311 }
312
313 /// A hint to the DNSKey associated with this Signer can be used to sign/validate records in the zone
314 pub fn is_zone_signing_key(&self) -> bool {
315 self.is_zone_signing_key
316 }
317
318 /// Signs a hash.
319 ///
320 /// This will panic if the `key` is not a private key and can be used for signing.
321 ///
322 /// # Arguments
323 ///
324 /// * `hash` - the hashed resource record set, see `rrset_tbs`.
325 ///
326 /// # Return value
327 ///
328 /// The signature, ready to be stored in an `RData::RRSIG`.
329 pub fn sign(&self, tbs: &TBS) -> ProtoResult<Vec<u8>> {
330 self.key
331 .sign(tbs)
332 .map_err(|e| ProtoErrorKind::Msg(format!("signing error: {e}")).into())
333 }
334
335 /// The name of the signing entity, e.g. the DNS server name.
336 ///
337 /// This should match the name on key in the zone.
338 pub fn signer_name(&self) -> &Name {
339 &self.signer_name
340 }
341
342 // TODO: move this to DNSKEY/KEY?
343 /// The key tag is calculated as a hash to more quickly lookup a DNSKEY.
344 ///
345 /// [RFC 1035](https://tools.ietf.org/html/rfc1035), DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987
346 ///
347 /// ```text
348 /// RFC 2535 DNS Security Extensions March 1999
349 ///
350 /// 4.1.6 Key Tag Field
351 ///
352 /// The "key Tag" is a two octet quantity that is used to efficiently
353 /// select between multiple keys which may be applicable and thus check
354 /// that a public key about to be used for the computationally expensive
355 /// effort to check the signature is possibly valid. For algorithm 1
356 /// (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
357 /// octets of the public key modulus needed to decode the signature
358 /// field. That is to say, the most significant 16 of the least
359 /// significant 24 bits of the modulus in network (big endian) order. For
360 /// all other algorithms, including private algorithms, it is calculated
361 /// as a simple checksum of the KEY RR as described in Appendix C.
362 ///
363 /// Appendix C: Key Tag Calculation
364 ///
365 /// The key tag field in the SIG RR is just a means of more efficiently
366 /// selecting the correct KEY RR to use when there is more than one KEY
367 /// RR candidate available, for example, in verifying a signature. It is
368 /// possible for more than one candidate key to have the same tag, in
369 /// which case each must be tried until one works or all fail. The
370 /// following reference implementation of how to calculate the Key Tag,
371 /// for all algorithms other than algorithm 1, is in ANSI C. It is coded
372 /// for clarity, not efficiency. (See section 4.1.6 for how to determine
373 /// the Key Tag of an algorithm 1 key.)
374 ///
375 /// /* assumes int is at least 16 bits
376 /// first byte of the key tag is the most significant byte of return
377 /// value
378 /// second byte of the key tag is the least significant byte of
379 /// return value
380 /// */
381 ///
382 /// int keytag (
383 ///
384 /// unsigned char key[], /* the RDATA part of the KEY RR */
385 /// unsigned int keysize, /* the RDLENGTH */
386 /// )
387 /// {
388 /// long int ac; /* assumed to be 32 bits or larger */
389 ///
390 /// for ( ac = 0, i = 0; i < keysize; ++i )
391 /// ac += (i&1) ? key[i] : key[i]<<8;
392 /// ac += (ac>>16) & 0xFFFF;
393 /// return ac & 0xFFFF;
394 /// }
395 /// ```
396 pub fn calculate_key_tag(&self) -> ProtoResult<u16> {
397 let mut bytes: Vec<u8> = Vec::with_capacity(512);
398 {
399 let mut e = BinEncoder::new(&mut bytes);
400 self.key_rdata.emit(&mut e)?;
401 }
402 Ok(DNSKEY::calculate_key_tag_internal(&bytes))
403 }
404
405 /// Signs the given message, returning the signature bytes.
406 ///
407 /// # Arguments
408 ///
409 /// * `message` - the message to sign
410 ///
411 /// [rfc2535](https://tools.ietf.org/html/rfc2535#section-4.1.8.1), Domain Name System Security Extensions, 1999
412 ///
413 /// ```text
414 /// 4.1.8.1 Calculating Transaction and Request SIGs
415 ///
416 /// A response message from a security aware server may optionally
417 /// contain a special SIG at the end of the additional information
418 /// section to authenticate the transaction.
419 ///
420 /// This SIG has a "type covered" field of zero, which is not a valid RR
421 /// type. It is calculated by using a "data" (see Section 4.1.8) of the
422 /// entire preceding DNS reply message, including DNS header but not the
423 /// IP header and before the reply RR counts have been adjusted for the
424 /// inclusion of any transaction SIG, concatenated with the entire DNS
425 /// query message that produced this response, including the query's DNS
426 /// header and any request SIGs but not its IP header. That is
427 ///
428 /// data = full response (less transaction SIG) | full query
429 ///
430 /// Verification of the transaction SIG (which is signed by the server
431 /// host key, not the zone key) by the requesting resolver shows that the
432 /// query and response were not tampered with in transit, that the
433 /// response corresponds to the intended query, and that the response
434 /// comes from the queried server.
435 ///
436 /// A DNS request may be optionally signed by including one or more SIGs
437 /// at the end of the query. Such SIGs are identified by having a "type
438 /// covered" field of zero. They sign the preceding DNS request message
439 /// including DNS header but not including the IP header or any request
440 /// SIGs at the end and before the request RR counts have been adjusted
441 /// for the inclusions of any request SIG(s).
442 ///
443 /// WARNING: Request SIGs are unnecessary for any currently defined
444 /// request other than update [RFC 2136, 2137] and will cause some old
445 /// DNS servers to give an error return or ignore a query. However, such
446 /// SIGs may in the future be needed for other requests.
447 ///
448 /// Except where needed to authenticate an update or similar privileged
449 /// request, servers are not required to check request SIGs.
450 /// ```
451 /// ---
452 ///
453 /// NOTE: In classic RFC style, this is unclear, it implies that each SIG record is not included in
454 /// the Additional record count, but this makes it more difficult to process and calculate more
455 /// than one SIG0 record. Annoyingly, it means that the Header is signed with different material
456 /// (i.e. additional record count - #SIG0 records), so the exact header sent is NOT the header
457 /// being verified.
458 ///
459 /// ---
460 pub fn sign_message(&self, message: &Message, pre_sig0: &SIG) -> ProtoResult<Vec<u8>> {
461 tbs::message_tbs(message, pre_sig0).and_then(|tbs| self.sign(&tbs))
462 }
463
464 /// Extracts a public KEY from this Signer
465 pub fn to_dnskey(&self) -> DnsSecResult<DNSKEY> {
466 // TODO: this interface should allow for setting if this is a secure entry point vs. ZSK
467 let pub_key = self.key.to_public_key()?;
468 Ok(DNSKEY::new(self.is_zone_signing_key, true, false, pub_key))
469 }
470
471 /// Test that this key is capable of signing and verifying data
472 pub fn test_key(&self) -> DnsSecResult<()> {
473 // use proto::rr::dnssec::PublicKey;
474
475 // // TODO: why doesn't this work for ecdsa_256 and 384?
476 // let test_data = TBS::from(b"DEADBEEF" as &[u8]);
477
478 // let signature = self.sign(&test_data).map_err(|e| {println!("failed to sign, {:?}", e); e})?;
479 // let pk = self.key.to_public_key()?;
480 // pk.verify(self.algorithm, test_data.as_ref(), &signature).map_err(|e| {println!("failed to verify, {:?}", e); e})?;
481
482 Ok(())
483 }
484}
485
486impl MessageFinalizer for SigSigner {
487 fn finalize_message(
488 &self,
489 message: &Message,
490 current_time: u32,
491 ) -> ProtoResult<(Vec<Record>, Option<MessageVerifier>)> {
492 debug!("signing message: {message:?}");
493 let key_tag: u16 = self.calculate_key_tag()?;
494
495 // this is based on RFCs 2535, 2931 and 3007
496
497 // The owner name SHOULD be root (a single zero octet).
498 let name = Name::root();
499 // The TTL fields SHOULD be zero
500 let ttl = 0;
501
502 let num_labels = name.num_labels();
503
504 let expiration_time: u32 = current_time + (5 * 60); // +5 minutes in seconds
505
506 let pre_sig0 = SIG::new(
507 // type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
508 RecordType::ZERO,
509 self.key.algorithm(),
510 num_labels,
511 // see above, original_ttl is meaningless, The TTL fields SHOULD be zero
512 0,
513 // recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
514 expiration_time,
515 // current time, this should be UTC
516 // unsigned numbers of seconds since the start of 1 January 1970, GMT
517 current_time,
518 key_tag,
519 // can probably get rid of this clone if the ownership is correct
520 self.signer_name().clone(),
521 Vec::new(),
522 );
523 let signature: Vec<u8> = self.sign_message(message, &pre_sig0)?;
524 let rdata = RData::DNSSEC(DNSSECRData::SIG(pre_sig0.set_sig(signature)));
525
526 // 'For all SIG(0) RRs, the owner name, class, TTL, and original TTL, are
527 // meaningless.' - 2931
528 let mut sig0 = Record::from_rdata(name, ttl, rdata);
529
530 // The CLASS field SHOULD be ANY
531 sig0.set_dns_class(DNSClass::ANY);
532
533 Ok((vec![sig0], None))
534 }
535}
536
537#[cfg(test)]
538mod tests {
539 #![allow(clippy::dbg_macro, clippy::print_stdout)]
540
541 #[cfg(feature = "std")]
542 use std::println;
543
544 use rustls_pki_types::PrivatePkcs8KeyDer;
545
546 use super::*;
547 use crate::dnssec::{
548 Algorithm, PublicKey, SigningKey, TBS, Verifier,
549 crypto::RsaSigningKey,
550 rdata::{DNSSECRData, KEY, RRSIG, SIG, key::KeyUsage},
551 };
552 use crate::op::{Message, Query};
553 use crate::rr::rdata::{CNAME, NS};
554 use crate::rr::{DNSClass, Name, RData, Record, RecordType};
555
556 fn assert_send_and_sync<T: Send + Sync>() {}
557
558 #[test]
559 fn test_send_and_sync() {
560 assert_send_and_sync::<SigSigner>();
561 }
562
563 fn pre_sig0(signer: &SigSigner, inception_time: u32, expiration_time: u32) -> SIG {
564 SIG::new(
565 // type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
566 RecordType::ZERO,
567 signer.key().algorithm(),
568 0,
569 // see above, original_ttl is meaningless, The TTL fields SHOULD be zero
570 0,
571 // recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
572 expiration_time,
573 // current time, this should be UTC
574 // unsigned numbers of seconds since the start of 1 January 1970, GMT
575 inception_time,
576 signer.calculate_key_tag().unwrap(),
577 // can probably get rid of this clone if the ownership is correct
578 signer.signer_name().clone(),
579 Vec::new(),
580 )
581 }
582
583 #[test]
584 fn test_sign_and_verify_message_sig0() {
585 let origin: Name = Name::parse("example.com.", None).unwrap();
586 let mut question: Message = Message::new();
587 let mut query: Query = Query::new();
588 query.set_name(origin);
589 question.add_query(query);
590
591 let key =
592 RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
593 .unwrap();
594 let pub_key = key.to_public_key().unwrap();
595 let sig0key = KEY::new_sig0key(&pub_key);
596 let signer = SigSigner::sig0(sig0key.clone(), Box::new(key), Name::root());
597
598 let pre_sig0 = pre_sig0(&signer, 0, 300);
599 let sig = signer.sign_message(&question, &pre_sig0).unwrap();
600 #[cfg(feature = "std")]
601 println!("sig: {sig:?}");
602
603 assert!(!sig.is_empty());
604
605 assert!(sig0key.verify_message(&question, &sig, &pre_sig0).is_ok());
606
607 // now test that the sig0 record works correctly.
608 assert!(question.sig0().is_empty());
609 question.finalize(&signer, 0).expect("should have signed");
610 assert!(!question.sig0().is_empty());
611
612 let sig = signer.sign_message(&question, &pre_sig0);
613 #[cfg(feature = "std")]
614 println!("sig after sign: {sig:?}");
615
616 if let RData::DNSSEC(DNSSECRData::SIG(sig)) = question.sig0()[0].data() {
617 assert!(sig0key.verify_message(&question, sig.sig(), sig).is_ok());
618 }
619 }
620
621 #[test]
622 #[allow(deprecated)]
623 fn test_sign_and_verify_rrset() {
624 let key =
625 RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
626 .unwrap();
627 let pub_key = key.to_public_key().unwrap();
628 let sig0key = KEY::new_sig0key_with_usage(&pub_key, KeyUsage::Zone);
629 let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
630
631 let origin: Name = Name::parse("example.com.", None).unwrap();
632 let rrsig = Record::from_rdata(
633 origin.clone(),
634 86400,
635 RRSIG::new(
636 RecordType::NS,
637 Algorithm::RSASHA256,
638 origin.num_labels(),
639 86400,
640 5,
641 0,
642 signer.calculate_key_tag().unwrap(),
643 origin.clone(),
644 vec![],
645 ),
646 );
647
648 let rrset = vec![
649 Record::from_rdata(
650 origin.clone(),
651 86400,
652 RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
653 )
654 .set_dns_class(DNSClass::IN)
655 .clone(),
656 Record::from_rdata(
657 origin,
658 86400,
659 RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
660 )
661 .set_dns_class(DNSClass::IN)
662 .clone(),
663 ];
664
665 let tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
666 let sig = signer.sign(&tbs).unwrap();
667
668 let pub_key = signer.key().to_public_key().unwrap();
669 assert!(pub_key.verify(tbs.as_ref(), &sig).is_ok());
670 }
671
672 #[test]
673 #[allow(deprecated)]
674 fn test_calculate_key_tag_pem() {
675 let key =
676 RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
677 .unwrap();
678 let pub_key = key.to_public_key().unwrap();
679 let sig0key = KEY::new_sig0key_with_usage(&pub_key, KeyUsage::Zone);
680 let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
681 let key_tag = signer.calculate_key_tag().unwrap();
682
683 assert_eq!(key_tag, 3256);
684 }
685
686 #[test]
687 fn test_rrset_tbs() {
688 let key =
689 RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
690 .unwrap();
691 let pub_key = key.to_public_key().unwrap();
692 let sig0key = KEY::new_sig0key(&pub_key);
693 let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
694
695 let origin = Name::parse("example.com.", None).unwrap();
696 let rrsig = Record::from_rdata(
697 origin.clone(),
698 86400,
699 RRSIG::new(
700 RecordType::NS,
701 Algorithm::RSASHA256,
702 origin.num_labels(),
703 86400,
704 5,
705 0,
706 signer.calculate_key_tag().unwrap(),
707 origin.clone(),
708 vec![],
709 ),
710 );
711 let rrset = vec![
712 Record::from_rdata(
713 origin.clone(),
714 86400,
715 RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
716 )
717 .set_dns_class(DNSClass::IN)
718 .clone(),
719 Record::from_rdata(
720 origin.clone(),
721 86400,
722 RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
723 )
724 .set_dns_class(DNSClass::IN)
725 .clone(),
726 ];
727
728 let tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
729 assert!(!tbs.as_ref().is_empty());
730
731 let rrset = vec![
732 Record::from_rdata(
733 origin.clone(),
734 86400,
735 RData::CNAME(CNAME(Name::parse("a.iana-servers.net.", None).unwrap())),
736 )
737 .set_dns_class(DNSClass::IN)
738 .clone(), // different type
739 Record::from_rdata(
740 Name::parse("www.example.com.", None).unwrap(),
741 86400,
742 RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
743 )
744 .set_dns_class(DNSClass::IN)
745 .clone(), // different name
746 Record::from_rdata(
747 origin.clone(),
748 86400,
749 RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
750 )
751 .set_dns_class(DNSClass::CH)
752 .clone(), // different class
753 Record::from_rdata(
754 origin.clone(),
755 86400,
756 RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
757 )
758 .set_dns_class(DNSClass::IN)
759 .clone(),
760 Record::from_rdata(
761 origin,
762 86400,
763 RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
764 )
765 .set_dns_class(DNSClass::IN)
766 .clone(),
767 ];
768
769 let filtered_tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
770 assert!(!filtered_tbs.as_ref().is_empty());
771 assert_eq!(tbs.as_ref(), filtered_tbs.as_ref());
772 }
773
774 const RSA_KEY: &[u8] = include_bytes!("../../tests/test-data/rsa-2048-private-key-1.pk8");
775}