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    /// ```text
346    /// RFC 2535                DNS Security Extensions               March 1999
347    ///
348    /// 4.1.6 Key Tag Field
349    ///
350    ///  The "key Tag" is a two octet quantity that is used to efficiently
351    ///  select between multiple keys which may be applicable and thus check
352    ///  that a public key about to be used for the computationally expensive
353    ///  effort to check the signature is possibly valid.  For algorithm 1
354    ///  (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
355    ///  octets of the public key modulus needed to decode the signature
356    ///  field.  That is to say, the most significant 16 of the least
357    ///  significant 24 bits of the modulus in network (big endian) order. For
358    ///  all other algorithms, including private algorithms, it is calculated
359    ///  as a simple checksum of the KEY RR as described in Appendix C.
360    ///
361    /// Appendix C: Key Tag Calculation
362    ///
363    ///  The key tag field in the SIG RR is just a means of more efficiently
364    ///  selecting the correct KEY RR to use when there is more than one KEY
365    ///  RR candidate available, for example, in verifying a signature.  It is
366    ///  possible for more than one candidate key to have the same tag, in
367    ///  which case each must be tried until one works or all fail.  The
368    ///  following reference implementation of how to calculate the Key Tag,
369    ///  for all algorithms other than algorithm 1, is in ANSI C.  It is coded
370    ///  for clarity, not efficiency.  (See section 4.1.6 for how to determine
371    ///  the Key Tag of an algorithm 1 key.)
372    ///
373    ///  /* assumes int is at least 16 bits
374    ///     first byte of the key tag is the most significant byte of return
375    ///     value
376    ///     second byte of the key tag is the least significant byte of
377    ///     return value
378    ///     */
379    ///
380    ///  int keytag (
381    ///
382    ///          unsigned char key[],  /* the RDATA part of the KEY RR */
383    ///          unsigned int keysize, /* the RDLENGTH */
384    ///          )
385    ///  {
386    ///  long int    ac;    /* assumed to be 32 bits or larger */
387    ///
388    ///  for ( ac = 0, i = 0; i < keysize; ++i )
389    ///      ac += (i&1) ? key[i] : key[i]<<8;
390    ///  ac += (ac>>16) & 0xFFFF;
391    ///  return ac & 0xFFFF;
392    ///  }
393    /// ```
394    pub fn calculate_key_tag(&self) -> ProtoResult<u16> {
395        let mut bytes: Vec<u8> = Vec::with_capacity(512);
396        {
397            let mut e = BinEncoder::new(&mut bytes);
398            self.key_rdata.emit(&mut e)?;
399        }
400        Ok(DNSKEY::calculate_key_tag_internal(&bytes))
401    }
402
403    /// Signs the given message, returning the signature bytes.
404    ///
405    /// # Arguments
406    ///
407    /// * `message` - the message to sign
408    ///
409    /// [rfc2535](https://tools.ietf.org/html/rfc2535#section-4.1.8.1), Domain Name System Security Extensions, 1999
410    ///
411    /// ```text
412    /// 4.1.8.1 Calculating Transaction and Request SIGs
413    ///
414    ///  A response message from a security aware server may optionally
415    ///  contain a special SIG at the end of the additional information
416    ///  section to authenticate the transaction.
417    ///
418    ///  This SIG has a "type covered" field of zero, which is not a valid RR
419    ///  type.  It is calculated by using a "data" (see Section 4.1.8) of the
420    ///  entire preceding DNS reply message, including DNS header but not the
421    ///  IP header and before the reply RR counts have been adjusted for the
422    ///  inclusion of any transaction SIG, concatenated with the entire DNS
423    ///  query message that produced this response, including the query's DNS
424    ///  header and any request SIGs but not its IP header.  That is
425    ///
426    ///  data = full response (less transaction SIG) | full query
427    ///
428    ///  Verification of the transaction SIG (which is signed by the server
429    ///  host key, not the zone key) by the requesting resolver shows that the
430    ///  query and response were not tampered with in transit, that the
431    ///  response corresponds to the intended query, and that the response
432    ///  comes from the queried server.
433    ///
434    ///  A DNS request may be optionally signed by including one or more SIGs
435    ///  at the end of the query. Such SIGs are identified by having a "type
436    ///  covered" field of zero. They sign the preceding DNS request message
437    ///  including DNS header but not including the IP header or any request
438    ///  SIGs at the end and before the request RR counts have been adjusted
439    ///  for the inclusions of any request SIG(s).
440    ///
441    ///  WARNING: Request SIGs are unnecessary for any currently defined
442    ///  request other than update [RFC 2136, 2137] and will cause some old
443    ///  DNS servers to give an error return or ignore a query.  However, such
444    ///  SIGs may in the future be needed for other requests.
445    ///
446    ///  Except where needed to authenticate an update or similar privileged
447    ///  request, servers are not required to check request SIGs.
448    /// ```
449    ///  ---
450    ///
451    /// NOTE: In classic RFC style, this is unclear, it implies that each SIG record is not included in
452    ///  the Additional record count, but this makes it more difficult to process and calculate more
453    ///  than one SIG0 record. Annoyingly, it means that the Header is signed with different material
454    ///  (i.e. additional record count - #SIG0 records), so the exact header sent is NOT the header
455    ///  being verified.
456    ///
457    ///  ---
458    pub fn sign_message(&self, message: &Message, pre_sig0: &SIG) -> ProtoResult<Vec<u8>> {
459        tbs::message_tbs(message, pre_sig0).and_then(|tbs| self.sign(&tbs))
460    }
461
462    /// Extracts a public KEY from this Signer
463    pub fn to_dnskey(&self) -> DnsSecResult<DNSKEY> {
464        // TODO: this interface should allow for setting if this is a secure entry point vs. ZSK
465        let pub_key = self.key.to_public_key()?;
466        Ok(DNSKEY::new(self.is_zone_signing_key, true, false, pub_key))
467    }
468
469    /// Test that this key is capable of signing and verifying data
470    pub fn test_key(&self) -> DnsSecResult<()> {
471        // use proto::rr::dnssec::PublicKey;
472
473        // // TODO: why doesn't this work for ecdsa_256 and 384?
474        // let test_data = TBS::from(b"DEADBEEF" as &[u8]);
475
476        // let signature = self.sign(&test_data).map_err(|e| {println!("failed to sign, {:?}", e); e})?;
477        // let pk = self.key.to_public_key()?;
478        // pk.verify(self.algorithm, test_data.as_ref(), &signature).map_err(|e| {println!("failed to verify, {:?}", e); e})?;
479
480        Ok(())
481    }
482}
483
484impl MessageFinalizer for SigSigner {
485    fn finalize_message(
486        &self,
487        message: &Message,
488        current_time: u32,
489    ) -> ProtoResult<(Vec<Record>, Option<MessageVerifier>)> {
490        debug!("signing message: {message:?}");
491        let key_tag: u16 = self.calculate_key_tag()?;
492
493        // this is based on RFCs 2535, 2931 and 3007
494
495        // The owner name SHOULD be root (a single zero octet).
496        let name = Name::root();
497        // The TTL fields SHOULD be zero
498        let ttl = 0;
499
500        let num_labels = name.num_labels();
501
502        let expiration_time: u32 = current_time + (5 * 60); // +5 minutes in seconds
503
504        let pre_sig0 = SIG::new(
505            // type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
506            RecordType::ZERO,
507            self.key.algorithm(),
508            num_labels,
509            // see above, original_ttl is meaningless, The TTL fields SHOULD be zero
510            0,
511            // recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
512            expiration_time,
513            // current time, this should be UTC
514            // unsigned numbers of seconds since the start of 1 January 1970, GMT
515            current_time,
516            key_tag,
517            // can probably get rid of this clone if the ownership is correct
518            self.signer_name().clone(),
519            Vec::new(),
520        );
521        let signature: Vec<u8> = self.sign_message(message, &pre_sig0)?;
522        let rdata = RData::DNSSEC(DNSSECRData::SIG(pre_sig0.set_sig(signature)));
523
524        // 'For all SIG(0) RRs, the owner name, class, TTL, and original TTL, are
525        //  meaningless.' - 2931
526        let mut sig0 = Record::from_rdata(name, ttl, rdata);
527
528        // The CLASS field SHOULD be ANY
529        sig0.set_dns_class(DNSClass::ANY);
530
531        Ok((vec![sig0], None))
532    }
533}
534
535#[cfg(test)]
536mod tests {
537    #![allow(clippy::dbg_macro, clippy::print_stdout)]
538
539    #[cfg(feature = "std")]
540    use std::println;
541
542    use rustls_pki_types::PrivatePkcs8KeyDer;
543
544    use super::*;
545    use crate::dnssec::{
546        Algorithm, PublicKey, SigningKey, TBS, Verifier,
547        crypto::RsaSigningKey,
548        rdata::{DNSSECRData, KEY, RRSIG, SIG, key::KeyUsage},
549    };
550    use crate::op::{Message, Query};
551    use crate::rr::rdata::{CNAME, NS};
552    use crate::rr::{DNSClass, Name, RData, Record, RecordType};
553
554    fn assert_send_and_sync<T: Send + Sync>() {}
555
556    #[test]
557    fn test_send_and_sync() {
558        assert_send_and_sync::<SigSigner>();
559    }
560
561    fn pre_sig0(signer: &SigSigner, inception_time: u32, expiration_time: u32) -> SIG {
562        SIG::new(
563            // type covered in SIG(0) is 0 which is what makes this SIG0 vs a standard SIG
564            RecordType::ZERO,
565            signer.key().algorithm(),
566            0,
567            // see above, original_ttl is meaningless, The TTL fields SHOULD be zero
568            0,
569            // recommended time is +5 minutes from now, to prevent timing attacks, 2 is probably good
570            expiration_time,
571            // current time, this should be UTC
572            // unsigned numbers of seconds since the start of 1 January 1970, GMT
573            inception_time,
574            signer.calculate_key_tag().unwrap(),
575            // can probably get rid of this clone if the ownership is correct
576            signer.signer_name().clone(),
577            Vec::new(),
578        )
579    }
580
581    #[test]
582    fn test_sign_and_verify_message_sig0() {
583        let origin: Name = Name::parse("example.com.", None).unwrap();
584        let mut question: Message = Message::new();
585        let mut query: Query = Query::new();
586        query.set_name(origin);
587        question.add_query(query);
588
589        let key =
590            RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
591                .unwrap();
592        let pub_key = key.to_public_key().unwrap();
593        let sig0key = KEY::new_sig0key(&pub_key);
594        let signer = SigSigner::sig0(sig0key.clone(), Box::new(key), Name::root());
595
596        let pre_sig0 = pre_sig0(&signer, 0, 300);
597        let sig = signer.sign_message(&question, &pre_sig0).unwrap();
598        #[cfg(feature = "std")]
599        println!("sig: {sig:?}");
600
601        assert!(!sig.is_empty());
602
603        assert!(sig0key.verify_message(&question, &sig, &pre_sig0).is_ok());
604
605        // now test that the sig0 record works correctly.
606        assert!(question.sig0().is_empty());
607        question.finalize(&signer, 0).expect("should have signed");
608        assert!(!question.sig0().is_empty());
609
610        let sig = signer.sign_message(&question, &pre_sig0);
611        #[cfg(feature = "std")]
612        println!("sig after sign: {sig:?}");
613
614        if let RData::DNSSEC(DNSSECRData::SIG(sig)) = question.sig0()[0].data() {
615            assert!(sig0key.verify_message(&question, sig.sig(), sig).is_ok());
616        }
617    }
618
619    #[test]
620    #[allow(deprecated)]
621    fn test_sign_and_verify_rrset() {
622        let key =
623            RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
624                .unwrap();
625        let pub_key = key.to_public_key().unwrap();
626        let sig0key = KEY::new_sig0key_with_usage(&pub_key, KeyUsage::Zone);
627        let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
628
629        let origin: Name = Name::parse("example.com.", None).unwrap();
630        let rrsig = Record::from_rdata(
631            origin.clone(),
632            86400,
633            RRSIG::new(
634                RecordType::NS,
635                Algorithm::RSASHA256,
636                origin.num_labels(),
637                86400,
638                5,
639                0,
640                signer.calculate_key_tag().unwrap(),
641                origin.clone(),
642                vec![],
643            ),
644        );
645
646        let rrset = vec![
647            Record::from_rdata(
648                origin.clone(),
649                86400,
650                RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
651            )
652            .set_dns_class(DNSClass::IN)
653            .clone(),
654            Record::from_rdata(
655                origin,
656                86400,
657                RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
658            )
659            .set_dns_class(DNSClass::IN)
660            .clone(),
661        ];
662
663        let tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
664        let sig = signer.sign(&tbs).unwrap();
665
666        let pub_key = signer.key().to_public_key().unwrap();
667        assert!(pub_key.verify(tbs.as_ref(), &sig).is_ok());
668    }
669
670    #[test]
671    #[allow(deprecated)]
672    fn test_calculate_key_tag_pem() {
673        let key =
674            RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
675                .unwrap();
676        let pub_key = key.to_public_key().unwrap();
677        let sig0key = KEY::new_sig0key_with_usage(&pub_key, KeyUsage::Zone);
678        let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
679        let key_tag = signer.calculate_key_tag().unwrap();
680
681        assert_eq!(key_tag, 3256);
682    }
683
684    #[test]
685    fn test_rrset_tbs() {
686        let key =
687            RsaSigningKey::from_pkcs8(&PrivatePkcs8KeyDer::from(RSA_KEY), Algorithm::RSASHA256)
688                .unwrap();
689        let pub_key = key.to_public_key().unwrap();
690        let sig0key = KEY::new_sig0key(&pub_key);
691        let signer = SigSigner::sig0(sig0key, Box::new(key), Name::root());
692
693        let origin = Name::parse("example.com.", None).unwrap();
694        let rrsig = Record::from_rdata(
695            origin.clone(),
696            86400,
697            RRSIG::new(
698                RecordType::NS,
699                Algorithm::RSASHA256,
700                origin.num_labels(),
701                86400,
702                5,
703                0,
704                signer.calculate_key_tag().unwrap(),
705                origin.clone(),
706                vec![],
707            ),
708        );
709        let rrset = vec![
710            Record::from_rdata(
711                origin.clone(),
712                86400,
713                RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
714            )
715            .set_dns_class(DNSClass::IN)
716            .clone(),
717            Record::from_rdata(
718                origin.clone(),
719                86400,
720                RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
721            )
722            .set_dns_class(DNSClass::IN)
723            .clone(),
724        ];
725
726        let tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
727        assert!(!tbs.as_ref().is_empty());
728
729        let rrset = vec![
730            Record::from_rdata(
731                origin.clone(),
732                86400,
733                RData::CNAME(CNAME(Name::parse("a.iana-servers.net.", None).unwrap())),
734            )
735            .set_dns_class(DNSClass::IN)
736            .clone(), // different type
737            Record::from_rdata(
738                Name::parse("www.example.com.", None).unwrap(),
739                86400,
740                RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
741            )
742            .set_dns_class(DNSClass::IN)
743            .clone(), // different name
744            Record::from_rdata(
745                origin.clone(),
746                86400,
747                RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
748            )
749            .set_dns_class(DNSClass::CH)
750            .clone(), // different class
751            Record::from_rdata(
752                origin.clone(),
753                86400,
754                RData::NS(NS(Name::parse("a.iana-servers.net.", None).unwrap())),
755            )
756            .set_dns_class(DNSClass::IN)
757            .clone(),
758            Record::from_rdata(
759                origin,
760                86400,
761                RData::NS(NS(Name::parse("b.iana-servers.net.", None).unwrap())),
762            )
763            .set_dns_class(DNSClass::IN)
764            .clone(),
765        ];
766
767        let filtered_tbs = TBS::from_rrsig(&rrsig, rrset.iter()).unwrap();
768        assert!(!filtered_tbs.as_ref().is_empty());
769        assert_eq!(tbs.as_ref(), filtered_tbs.as_ref());
770    }
771
772    const RSA_KEY: &[u8] = include_bytes!("../../tests/test-data/rsa-2048-private-key-1.pk8");
773}