hickory_proto/rr/dnssec/
keypair.rs

1// Copyright 2015-2016 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#[cfg(not(feature = "openssl"))]
9use std::marker::PhantomData;
10
11#[cfg(feature = "openssl")]
12use openssl::bn::BigNumContext;
13#[cfg(feature = "openssl")]
14use openssl::ec::{EcGroup, EcKey, PointConversionForm};
15#[cfg(feature = "openssl")]
16use openssl::nid::Nid;
17#[cfg(feature = "openssl")]
18use openssl::pkey::PKey;
19#[cfg(feature = "openssl")]
20use openssl::rsa::Rsa as OpenSslRsa;
21#[cfg(feature = "openssl")]
22use openssl::sign::Signer;
23
24#[allow(deprecated)]
25use crate::rr::dnssec::rdata::key::{KeyTrust, Protocol, UpdateScope};
26#[cfg(feature = "ring")]
27use ring::{
28    rand,
29    signature::{
30        EcdsaKeyPair, Ed25519KeyPair, KeyPair as RingKeyPair, ECDSA_P256_SHA256_FIXED_SIGNING,
31        ECDSA_P384_SHA384_FIXED_SIGNING,
32    },
33};
34
35use crate::error::*;
36use crate::rr::dnssec::rdata::key::KeyUsage;
37#[cfg(any(feature = "openssl", feature = "ring"))]
38use crate::rr::dnssec::rdata::DS;
39use crate::rr::dnssec::rdata::{DNSKEY, KEY};
40#[cfg(any(feature = "openssl", feature = "ring"))]
41use crate::rr::dnssec::DigestType;
42use crate::rr::dnssec::{Algorithm, PublicKeyBuf};
43use crate::rr::dnssec::{HasPrivate, HasPublic, Private, TBS};
44#[cfg(any(feature = "openssl", feature = "ring"))]
45use crate::rr::Name;
46
47/// A public and private key pair, the private portion is not required.
48///
49/// This supports all the various public/private keys which Hickory DNS is capable of using. Given
50///  differing features, some key types may not be available. The `openssl` feature will enable RSA and EC
51///  (P256 and P384). The `ring` feature enables ED25519, in the future, Ring will also be used for other keys.
52#[allow(clippy::large_enum_variant)]
53pub enum KeyPair<K> {
54    /// RSA keypair, supported by OpenSSL
55    #[cfg(feature = "openssl")]
56    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
57    RSA(PKey<K>),
58    /// Elliptic curve keypair, supported by OpenSSL
59    #[cfg(feature = "openssl")]
60    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
61    EC(PKey<K>),
62    #[cfg(not(feature = "openssl"))]
63    #[doc(hidden)]
64    Phantom(PhantomData<K>),
65    /// *ring* ECDSA keypair
66    #[cfg(feature = "ring")]
67    #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
68    ECDSA(EcdsaKeyPair),
69    /// ED25519 encryption and hash defined keypair
70    #[cfg(feature = "ring")]
71    #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
72    ED25519(Ed25519KeyPair),
73}
74
75impl<K> KeyPair<K> {
76    /// Creates an RSA type keypair.
77    #[cfg(feature = "openssl")]
78    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
79    pub fn from_rsa(rsa: OpenSslRsa<K>) -> DnsSecResult<Self> {
80        PKey::from_rsa(rsa).map(Self::RSA).map_err(Into::into)
81    }
82
83    /// Given a known pkey of an RSA key, return the wrapped keypair
84    #[cfg(feature = "openssl")]
85    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
86    pub fn from_rsa_pkey(pkey: PKey<K>) -> Self {
87        Self::RSA(pkey)
88    }
89
90    /// Creates an EC, elliptic curve, type keypair, only P256 or P384 are supported.
91    #[cfg(feature = "openssl")]
92    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
93    pub fn from_ec_key(ec_key: EcKey<K>) -> DnsSecResult<Self> {
94        PKey::from_ec_key(ec_key).map(Self::EC).map_err(Into::into)
95    }
96
97    /// Given a known pkey of an EC key, return the wrapped keypair
98    #[cfg(feature = "openssl")]
99    #[cfg_attr(docsrs, doc(cfg(feature = "openssl")))]
100    pub fn from_ec_pkey(pkey: PKey<K>) -> Self {
101        Self::EC(pkey)
102    }
103
104    /// Creates an ECDSA keypair with ring.
105    #[cfg(feature = "ring")]
106    #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
107    pub fn from_ecdsa(ec_key: EcdsaKeyPair) -> Self {
108        Self::ECDSA(ec_key)
109    }
110
111    /// Creates an ED25519 keypair.
112    #[cfg(feature = "ring")]
113    #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
114    pub fn from_ed25519(ed_key: Ed25519KeyPair) -> Self {
115        Self::ED25519(ed_key)
116    }
117}
118
119impl<K: HasPublic> KeyPair<K> {
120    /// Converts this keypair to the DNS binary form of the public_key.
121    ///
122    /// If there is a private key associated with this keypair, it will not be included in this
123    ///  format. Only the public key material will be included.
124    pub fn to_public_bytes(&self) -> DnsSecResult<Vec<u8>> {
125        #[allow(unreachable_patterns)]
126        match *self {
127            // see from_vec() RSA sections for reference
128            #[cfg(feature = "openssl")]
129            Self::RSA(ref pkey) => {
130                let mut bytes: Vec<u8> = Vec::new();
131                // TODO: make these expects a try! and Err()
132                let rsa: OpenSslRsa<K> = pkey
133                    .rsa()
134                    .expect("pkey should have been initialized with RSA");
135
136                // this is to get us access to the exponent and the modulus
137                let e: Vec<u8> = rsa.e().to_vec();
138                let n: Vec<u8> = rsa.n().to_vec();
139
140                if e.len() > 255 {
141                    bytes.push(0);
142                    bytes.push((e.len() >> 8) as u8);
143                }
144
145                bytes.push(e.len() as u8);
146                bytes.extend_from_slice(&e);
147                bytes.extend_from_slice(&n);
148
149                Ok(bytes)
150            }
151            // see from_vec() ECDSA sections for reference
152            #[cfg(feature = "openssl")]
153            Self::EC(ref pkey) => {
154                // TODO: make these expects a try! and Err()
155                let ec_key: EcKey<K> = pkey
156                    .ec_key()
157                    .expect("pkey should have been initialized with EC");
158                let group = ec_key.group();
159                let point = ec_key.public_key();
160
161                let mut bytes = BigNumContext::new()
162                    .and_then(|mut ctx| {
163                        point.to_bytes(group, PointConversionForm::UNCOMPRESSED, &mut ctx)
164                    })
165                    .map_err(DnsSecError::from)?;
166
167                // Remove OpenSSL header byte
168                bytes.remove(0);
169                Ok(bytes)
170            }
171            #[cfg(feature = "ring")]
172            Self::ECDSA(ref ec_key) => {
173                let mut bytes: Vec<u8> = ec_key.public_key().as_ref().to_vec();
174                bytes.remove(0);
175                Ok(bytes)
176            }
177            #[cfg(feature = "ring")]
178            Self::ED25519(ref ed_key) => Ok(ed_key.public_key().as_ref().to_vec()),
179            #[cfg(not(feature = "openssl"))]
180            Self::Phantom(..) => panic!("Phantom disallowed"),
181            #[cfg(not(any(feature = "openssl", feature = "ring")))]
182            _ => Err(DnsSecErrorKind::Message("openssl or ring feature(s) not enabled").into()),
183        }
184    }
185
186    /// Returns a PublicKeyBuf of the KeyPair
187    pub fn to_public_key(&self) -> DnsSecResult<PublicKeyBuf> {
188        Ok(PublicKeyBuf::new(self.to_public_bytes()?))
189    }
190
191    /// The key tag is calculated as a hash to more quickly lookup a DNSKEY.
192    ///
193    /// [RFC 1035](https://tools.ietf.org/html/rfc1035), DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987
194    ///
195    /// ```text
196    /// RFC 2535                DNS Security Extensions               March 1999
197    ///
198    /// 4.1.6 Key Tag Field
199    ///
200    ///  The "key Tag" is a two octet quantity that is used to efficiently
201    ///  select between multiple keys which may be applicable and thus check
202    ///  that a public key about to be used for the computationally expensive
203    ///  effort to check the signature is possibly valid.  For algorithm 1
204    ///  (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
205    ///  octets of the public key modulus needed to decode the signature
206    ///  field.  That is to say, the most significant 16 of the least
207    ///  significant 24 bits of the modulus in network (big endian) order. For
208    ///  all other algorithms, including private algorithms, it is calculated
209    ///  as a simple checksum of the KEY RR as described in Appendix C.
210    ///
211    /// Appendix C: Key Tag Calculation
212    ///
213    ///  The key tag field in the SIG RR is just a means of more efficiently
214    ///  selecting the correct KEY RR to use when there is more than one KEY
215    ///  RR candidate available, for example, in verifying a signature.  It is
216    ///  possible for more than one candidate key to have the same tag, in
217    ///  which case each must be tried until one works or all fail.  The
218    ///  following reference implementation of how to calculate the Key Tag,
219    ///  for all algorithms other than algorithm 1, is in ANSI C.  It is coded
220    ///  for clarity, not efficiency.  (See section 4.1.6 for how to determine
221    ///  the Key Tag of an algorithm 1 key.)
222    ///
223    ///  /* assumes int is at least 16 bits
224    ///     first byte of the key tag is the most significant byte of return
225    ///     value
226    ///     second byte of the key tag is the least significant byte of
227    ///     return value
228    ///     */
229    ///
230    ///  int keytag (
231    ///
232    ///          unsigned char key[],  /* the RDATA part of the KEY RR */
233    ///          unsigned int keysize, /* the RDLENGTH */
234    ///          )
235    ///  {
236    ///  long int    ac;    /* assumed to be 32 bits or larger */
237    ///
238    ///  for ( ac = 0, i = 0; i < keysize; ++i )
239    ///      ac += (i&1) ? key[i] : key[i]<<8;
240    ///  ac += (ac>>16) & 0xFFFF;
241    ///  return ac & 0xFFFF;
242    ///  }
243    /// ```
244    pub fn key_tag(&self) -> DnsSecResult<u16> {
245        let mut ac: usize = 0;
246
247        for (i, k) in self.to_public_bytes()?.iter().enumerate() {
248            ac += if i & 0x0001 == 0x0001 {
249                *k as usize
250            } else {
251                (*k as usize) << 8
252            };
253        }
254
255        ac += (ac >> 16) & 0xFFFF;
256        Ok((ac & 0xFFFF) as u16) // this is unnecessary, no?
257    }
258
259    /// Creates a Record that represents the public key for this Signer
260    ///
261    /// # Arguments
262    ///
263    /// * `algorithm` - algorithm of the DNSKEY
264    ///
265    /// # Return
266    ///
267    /// the DNSKEY record data
268    pub fn to_dnskey(&self, algorithm: Algorithm) -> DnsSecResult<DNSKEY> {
269        self.to_public_bytes()
270            .map(|bytes| DNSKEY::new(true, true, false, algorithm, bytes))
271    }
272
273    /// Convert this keypair into a KEY record type for usage with SIG0
274    /// with key type entity (`KeyUsage::Entity`).
275    ///
276    /// # Arguments
277    ///
278    /// * `algorithm` - algorithm of the KEY
279    ///
280    /// # Return
281    ///
282    /// the KEY record data
283    pub fn to_sig0key(&self, algorithm: Algorithm) -> DnsSecResult<KEY> {
284        self.to_sig0key_with_usage(algorithm, KeyUsage::default())
285    }
286
287    /// Convert this keypair into a KEY record type for usage with SIG0
288    /// with a given key (usage) type.
289    ///
290    /// # Arguments
291    ///
292    /// * `algorithm` - algorithm of the KEY
293    /// * `usage`     - the key type
294    ///
295    /// # Return
296    ///
297    /// the KEY record data
298    pub fn to_sig0key_with_usage(
299        &self,
300        algorithm: Algorithm,
301        usage: KeyUsage,
302    ) -> DnsSecResult<KEY> {
303        self.to_public_bytes().map(|bytes| {
304            KEY::new(
305                KeyTrust::default(),
306                usage,
307                #[allow(deprecated)]
308                UpdateScope::default(),
309                Protocol::default(),
310                algorithm,
311                bytes,
312            )
313        })
314    }
315
316    /// Creates a DS record for this KeyPair associated to the given name
317    ///
318    /// # Arguments
319    ///
320    /// * `name` - name of the DNSKEY record covered by the new DS record
321    /// * `algorithm` - the algorithm of the DNSKEY
322    /// * `digest_type` - the digest_type used to
323    #[cfg(any(feature = "openssl", feature = "ring"))]
324    #[cfg_attr(docsrs, doc(cfg(any(feature = "openssl", feature = "ring"))))]
325    pub fn to_ds(
326        &self,
327        name: &Name,
328        algorithm: Algorithm,
329        digest_type: DigestType,
330    ) -> DnsSecResult<DS> {
331        self.to_dnskey(algorithm)
332            .and_then(|dnskey| self.key_tag().map(|key_tag| (key_tag, dnskey)))
333            .and_then(|(key_tag, dnskey)| {
334                dnskey
335                    .to_digest(name, digest_type)
336                    .map(|digest| (key_tag, digest))
337                    .map_err(Into::into)
338            })
339            .map(|(key_tag, digest)| {
340                DS::new(key_tag, algorithm, digest_type, digest.as_ref().to_owned())
341            })
342    }
343}
344
345impl<K: HasPrivate> KeyPair<K> {
346    /// Signs a hash.
347    ///
348    /// This will panic if the `key` is not a private key and can be used for signing.
349    ///
350    /// # Arguments
351    ///
352    /// * `message` - the message bytes to be signed, see `rrset_tbs`.
353    ///
354    /// # Return value
355    ///
356    /// The signature, ready to be stored in an `RData::RRSIG`.
357    #[allow(unused)]
358    pub fn sign(&self, algorithm: Algorithm, tbs: &TBS) -> DnsSecResult<Vec<u8>> {
359        use std::iter;
360
361        match *self {
362            #[cfg(feature = "openssl")]
363            Self::RSA(ref pkey) | Self::EC(ref pkey) => {
364                let digest_type = DigestType::from(algorithm).to_openssl_digest()?;
365                let mut signer = Signer::new(digest_type, pkey)?;
366                signer.update(tbs.as_ref())?;
367                signer.sign_to_vec().map_err(Into::into).and_then(|bytes| {
368                    if let Self::RSA(_) = *self {
369                        return Ok(bytes);
370                    }
371
372                    // Convert DER signature to raw signature (see RFC 6605 Section 4)
373                    if bytes.len() < 8 {
374                        return Err("unexpected signature format (length too short)".into());
375                    }
376                    let expect = |pos: usize, expected: u8| -> DnsSecResult<()> {
377                        if bytes[pos] != expected {
378                            return Err(format!(
379                                "unexpected signature format ({pos}, {expected}))"
380                            )
381                            .into());
382                        }
383                        Ok(())
384                    };
385                    // Sanity checks
386                    expect(0, 0x30)?;
387                    expect(1, (bytes.len() - 2) as u8)?;
388                    expect(2, 0x02)?;
389                    let p1_len = bytes[3] as usize;
390                    let p2_pos = 4 + p1_len;
391                    expect(p2_pos, 0x02)?;
392                    let p2_len = bytes[p2_pos + 1] as usize;
393                    if p2_pos + 2 + p2_len > bytes.len() {
394                        return Err("unexpected signature format (invalid length)".into());
395                    }
396
397                    let p1 = &bytes[4..p2_pos];
398                    let p2 = &bytes[p2_pos + 2..p2_pos + 2 + p2_len];
399
400                    // For P-256, each integer MUST be encoded as 32 octets;
401                    // for P-384, each integer MUST be encoded as 48 octets.
402                    let part_len = match algorithm {
403                        Algorithm::ECDSAP256SHA256 => 32,
404                        Algorithm::ECDSAP384SHA384 => 48,
405                        _ => return Err("unexpected algorithm".into()),
406                    };
407                    let mut ret = Vec::<u8>::new();
408                    {
409                        let mut write_part = |mut part: &[u8]| -> DnsSecResult<()> {
410                            // We need to pad or trim the octet string to expected length
411                            if part.len() > part_len + 1 {
412                                return Err("invalid signature data".into());
413                            }
414                            if part.len() == part_len + 1 {
415                                // Trim leading zero
416                                if part[0] != 0x00 {
417                                    return Err("invalid signature data".into());
418                                }
419                                part = &part[1..];
420                            }
421
422                            // Pad with zeros. All numbers are big-endian here.
423                            ret.extend(iter::repeat(0x00).take(part_len - part.len()));
424                            ret.extend(part);
425                            Ok(())
426                        };
427                        write_part(p1)?;
428                        write_part(p2)?;
429                    }
430                    assert_eq!(ret.len(), part_len * 2);
431                    Ok(ret)
432                })
433            }
434            #[cfg(feature = "ring")]
435            Self::ECDSA(ref ec_key) => {
436                let rng = rand::SystemRandom::new();
437                Ok(ec_key.sign(&rng, tbs.as_ref())?.as_ref().to_vec())
438            }
439            #[cfg(feature = "ring")]
440            Self::ED25519(ref ed_key) => Ok(ed_key.sign(tbs.as_ref()).as_ref().to_vec()),
441            #[cfg(not(feature = "openssl"))]
442            Self::Phantom(..) => panic!("Phantom disallowed"),
443            #[cfg(not(any(feature = "openssl", feature = "ring")))]
444            _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
445        }
446    }
447}
448
449impl KeyPair<Private> {
450    /// Generates a new private and public key pair for the specified algorithm.
451    ///
452    /// RSA keys are hardcoded to 2048bits at the moment. Other keys have predefined sizes.
453    pub fn generate(algorithm: Algorithm) -> DnsSecResult<Self> {
454        #[allow(deprecated)]
455        match algorithm {
456            Algorithm::Unknown(_) => Err(DnsSecErrorKind::Message("unknown algorithm").into()),
457            #[cfg(feature = "openssl")]
458            Algorithm::RSASHA1
459            | Algorithm::RSASHA1NSEC3SHA1
460            | Algorithm::RSASHA256
461            | Algorithm::RSASHA512 => {
462                // TODO: the only keysize right now, would be better for people to use other algorithms...
463                OpenSslRsa::generate(2048)
464                    .map_err(Into::into)
465                    .and_then(Self::from_rsa)
466            }
467            #[cfg(feature = "openssl")]
468            Algorithm::ECDSAP256SHA256 => EcGroup::from_curve_name(Nid::X9_62_PRIME256V1)
469                .and_then(|group| EcKey::generate(&group))
470                .map_err(Into::into)
471                .and_then(Self::from_ec_key),
472            #[cfg(feature = "openssl")]
473            Algorithm::ECDSAP384SHA384 => EcGroup::from_curve_name(Nid::SECP384R1)
474                .and_then(|group| EcKey::generate(&group))
475                .map_err(Into::into)
476                .and_then(Self::from_ec_key),
477            #[cfg(feature = "ring")]
478            Algorithm::ED25519 => Err(DnsSecErrorKind::Message(
479                "use generate_pkcs8 for generating private key and encoding",
480            )
481            .into()),
482            _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
483        }
484    }
485
486    /// Generates a key, securing it with pkcs8
487    #[cfg(feature = "ring")]
488    #[cfg_attr(docsrs, doc(cfg(feature = "ring")))]
489    pub fn generate_pkcs8(algorithm: Algorithm) -> DnsSecResult<Vec<u8>> {
490        #[allow(deprecated)]
491        match algorithm {
492            Algorithm::Unknown(_) => Err(DnsSecErrorKind::Message("unknown algorithm").into()),
493            #[cfg(feature = "openssl")]
494            Algorithm::RSASHA1
495            | Algorithm::RSASHA1NSEC3SHA1
496            | Algorithm::RSASHA256
497            | Algorithm::RSASHA512 => {
498                Err(DnsSecErrorKind::Message("openssl does not yet support pkcs8").into())
499            }
500            #[cfg(feature = "ring")]
501            Algorithm::ECDSAP256SHA256 => {
502                let rng = rand::SystemRandom::new();
503                EcdsaKeyPair::generate_pkcs8(&ECDSA_P256_SHA256_FIXED_SIGNING, &rng)
504                    .map_err(Into::into)
505                    .map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
506            }
507            #[cfg(feature = "ring")]
508            Algorithm::ECDSAP384SHA384 => {
509                let rng = rand::SystemRandom::new();
510                EcdsaKeyPair::generate_pkcs8(&ECDSA_P384_SHA384_FIXED_SIGNING, &rng)
511                    .map_err(Into::into)
512                    .map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
513            }
514            #[cfg(feature = "ring")]
515            Algorithm::ED25519 => {
516                let rng = rand::SystemRandom::new();
517                Ed25519KeyPair::generate_pkcs8(&rng)
518                    .map_err(Into::into)
519                    .map(|pkcs8_bytes| pkcs8_bytes.as_ref().to_vec())
520            }
521            _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
522        }
523    }
524}
525
526#[cfg(any(feature = "openssl", feature = "ring"))]
527#[cfg(test)]
528mod tests {
529    use crate::rr::dnssec::TBS;
530    use crate::rr::dnssec::*;
531
532    #[cfg(feature = "openssl")]
533    #[test]
534    fn test_rsa() {
535        public_key_test(Algorithm::RSASHA256, KeyFormat::Der);
536        hash_test(Algorithm::RSASHA256, KeyFormat::Der);
537    }
538
539    #[cfg(feature = "openssl")]
540    #[test]
541    fn test_ec_p256() {
542        public_key_test(Algorithm::ECDSAP256SHA256, KeyFormat::Der);
543        hash_test(Algorithm::ECDSAP256SHA256, KeyFormat::Der);
544    }
545
546    #[cfg(feature = "ring")]
547    #[test]
548    fn test_ec_p256_pkcs8() {
549        public_key_test(Algorithm::ECDSAP256SHA256, KeyFormat::Pkcs8);
550        hash_test(Algorithm::ECDSAP256SHA256, KeyFormat::Pkcs8);
551    }
552
553    #[cfg(feature = "openssl")]
554    #[test]
555    fn test_ec_p384() {
556        public_key_test(Algorithm::ECDSAP384SHA384, KeyFormat::Der);
557        hash_test(Algorithm::ECDSAP384SHA384, KeyFormat::Der);
558    }
559
560    #[cfg(feature = "ring")]
561    #[test]
562    fn test_ec_p384_pkcs8() {
563        public_key_test(Algorithm::ECDSAP384SHA384, KeyFormat::Pkcs8);
564        hash_test(Algorithm::ECDSAP384SHA384, KeyFormat::Pkcs8);
565    }
566
567    #[cfg(feature = "ring")]
568    #[test]
569    fn test_ed25519() {
570        public_key_test(Algorithm::ED25519, KeyFormat::Pkcs8);
571        hash_test(Algorithm::ED25519, KeyFormat::Pkcs8);
572    }
573
574    #[allow(clippy::uninlined_format_args)]
575    fn public_key_test(algorithm: Algorithm, key_format: KeyFormat) {
576        let key = key_format
577            .decode_key(
578                &key_format.generate_and_encode(algorithm, None).unwrap(),
579                None,
580                algorithm,
581            )
582            .unwrap();
583        let pk = key.to_public_key().unwrap();
584
585        let tbs = TBS::from(&b"www.example.com"[..]);
586        let mut sig = key.sign(algorithm, &tbs).unwrap();
587        assert!(
588            pk.verify(algorithm, tbs.as_ref(), &sig).is_ok(),
589            "algorithm: {:?} (public key)",
590            algorithm
591        );
592        sig[10] = !sig[10];
593        assert!(
594            pk.verify(algorithm, tbs.as_ref(), &sig).is_err(),
595            "algorithm: {:?} (public key, neg)",
596            algorithm
597        );
598    }
599
600    #[allow(clippy::uninlined_format_args)]
601    fn hash_test(algorithm: Algorithm, key_format: KeyFormat) {
602        let tbs = TBS::from(&b"www.example.com"[..]);
603
604        // TODO: convert to stored keys...
605        let key = key_format
606            .decode_key(
607                &key_format.generate_and_encode(algorithm, None).unwrap(),
608                None,
609                algorithm,
610            )
611            .unwrap();
612        let pub_key = key.to_public_key().unwrap();
613
614        let neg = key_format
615            .decode_key(
616                &key_format.generate_and_encode(algorithm, None).unwrap(),
617                None,
618                algorithm,
619            )
620            .unwrap();
621        let neg_pub_key = neg.to_public_key().unwrap();
622
623        let sig = key.sign(algorithm, &tbs).unwrap();
624        assert!(
625            pub_key.verify(algorithm, tbs.as_ref(), &sig).is_ok(),
626            "algorithm: {:?}",
627            algorithm
628        );
629        assert!(
630            key.to_dnskey(algorithm)
631                .unwrap()
632                .verify(tbs.as_ref(), &sig)
633                .is_ok(),
634            "algorithm: {:?} (dnskey)",
635            algorithm
636        );
637        assert!(
638            neg_pub_key.verify(algorithm, tbs.as_ref(), &sig).is_err(),
639            "algorithm: {:?} (neg)",
640            algorithm
641        );
642        assert!(
643            neg.to_dnskey(algorithm)
644                .unwrap()
645                .verify(tbs.as_ref(), &sig)
646                .is_err(),
647            "algorithm: {:?} (dnskey, neg)",
648            algorithm
649        );
650    }
651}