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}