Skip to main content

domain/crypto/
sign.rs

1//! DNSSEC signing using built-in backends.
2//!
3//! This backend supports all the algorithms supported by Ring and OpenSSL,
4//! depending on whether the respective crate features are enabled.  See the
5//! documentation for each backend for more information.
6//!
7//! The [`SecretKeyBytes`] type is a generic representation of a secret key as
8//! a byte slice.  While it does not offer any cryptographic functionality, it
9//! is useful to transfer secret keys stored in memory, independent of any
10//! cryptographic backend.
11//!
12//! [`SecretKeyBytes`] also supports importing and exporting keys from and to
13//! the conventional private-key format popularized by BIND.  This format is
14//! used by a variety of tools for storing DNSSEC keys on disk.  See the
15//! type-level documentation for a specification of the format.
16//!
17//! # Importing keys
18//!
19//! Keys can be imported from files stored on disk in the conventional BIND
20//! format.
21//!
22//! ```
23//! # use domain::base::iana::SecurityAlgorithm;
24//! # use domain::crypto::sign::{KeyPair, self, SecretKeyBytes, SignRaw};
25//! # use domain::dnssec::common::parse_from_bind;
26//! // Load an Ed25519 key named 'Ktest.+015+56037'.
27//! let base = "test-data/dnssec-keys/Ktest.+015+56037";
28//! let sec_text = std::fs::read_to_string(format!("{base}.private")).unwrap();
29//! let sec_bytes = SecretKeyBytes::parse_from_bind(&sec_text).unwrap();
30//! let pub_text = std::fs::read_to_string(format!("{base}.key")).unwrap();
31//! let pub_key = parse_from_bind::<Vec<u8>>(&pub_text).unwrap();
32//!
33//! // Parse the key into Ring or OpenSSL.
34//! let key_pair = KeyPair::from_bytes(&sec_bytes, pub_key.data())
35//!     .unwrap();
36//!
37//! // Check that the owner, algorithm, and key tag matched expectations.
38//! assert_eq!(key_pair.algorithm(), SecurityAlgorithm::ED25519);
39//! assert_eq!(key_pair.dnskey().key_tag(), 56037);
40//! ```
41//!
42//! # Generating keys
43//!
44//! Keys can also be generated.
45//!
46//! ```
47//! # use domain::base::Name;
48//! # use domain::crypto::common;
49//! # use domain::crypto::sign::{generate, GenerateParams, KeyPair};
50//! // Generate a new Ed25519 key.
51//! let params = GenerateParams::Ed25519;
52//! let (sec_bytes, pub_key) = generate(&params, 257).unwrap();
53//!
54//! // Parse the key into Ring or OpenSSL.
55//! let key_pair = KeyPair::from_bytes(&sec_bytes, &pub_key).unwrap();
56//!
57//! // Access the public key (with metadata).
58//! println!("{:?}", pub_key);
59//! ```
60//!
61//! # Signing data
62//!
63//! Given some data and a key, the data can be signed with the key.
64//!
65//! ```
66//! # use domain::base::Name;
67//! # use domain::crypto::common;
68//! # use domain::crypto::sign::{generate, GenerateParams, KeyPair, SignRaw};
69//! # let (sec_bytes, pub_bytes) = generate(
70//!        &GenerateParams::Ed25519,
71//!        256).unwrap();
72//! # let key_pair = KeyPair::from_bytes(&sec_bytes, &pub_bytes).unwrap();
73//! // Sign arbitrary byte sequences with the key.
74//! let sig = key_pair.sign_raw(b"Hello, World!").unwrap();
75//! println!("{:?}", sig);
76//! ```
77//!
78
79#![cfg(feature = "unstable-crypto-sign")]
80#![cfg_attr(docsrs, doc(cfg(feature = "unstable-crypto-sign")))]
81
82use alloc::boxed::Box;
83use alloc::fmt;
84use alloc::vec::Vec;
85
86use secrecy::{ExposeSecret, SecretBox};
87
88use crate::base::iana::SecurityAlgorithm;
89use crate::rdata::Dnskey;
90use crate::utils::base64;
91
92#[cfg(feature = "openssl")]
93use super::openssl;
94
95#[cfg(feature = "ring")]
96use super::ring;
97
98//----------- GenerateParams -------------------------------------------------
99
100/// Parameters for generating a secret key.
101#[derive(Clone, Debug, PartialEq, Eq)]
102pub enum GenerateParams {
103    /// Generate an RSA/SHA-256 keypair.
104    RsaSha256 {
105        /// The number of bits in the public modulus.
106        ///
107        /// A ~3000-bit key corresponds to a 128-bit security level.  However,
108        /// RSA is mostly used with 2048-bit keys.  Some backends (like Ring)
109        /// do not support smaller key sizes than that.
110        ///
111        /// For more information about security levels, see [NIST SP 800-57
112        /// part 1 revision 5], page 54, table 2.
113        ///
114        /// [NIST SP 800-57 part 1 revision 5]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r5.pdf
115        bits: u32,
116    },
117
118    /// Generate an RSA/SHA-512 keypair.
119    RsaSha512 {
120        /// The number of bits in the public modulus.
121        bits: u32,
122    },
123
124    /// Generate an ECDSA P-256/SHA-256 keypair.
125    EcdsaP256Sha256,
126
127    /// Generate an ECDSA P-384/SHA-384 keypair.
128    EcdsaP384Sha384,
129
130    /// Generate an Ed25519 keypair.
131    Ed25519,
132
133    /// An Ed448 keypair.
134    Ed448,
135}
136
137//--- Inspection
138
139impl GenerateParams {
140    /// The algorithm of the generated key.
141    pub fn algorithm(&self) -> SecurityAlgorithm {
142        match self {
143            Self::RsaSha256 { .. } => SecurityAlgorithm::RSASHA256,
144            Self::RsaSha512 { .. } => SecurityAlgorithm::RSASHA512,
145            Self::EcdsaP256Sha256 => SecurityAlgorithm::ECDSAP256SHA256,
146            Self::EcdsaP384Sha384 => SecurityAlgorithm::ECDSAP384SHA384,
147            Self::Ed25519 => SecurityAlgorithm::ED25519,
148            Self::Ed448 => SecurityAlgorithm::ED448,
149        }
150    }
151}
152
153//----------- SignRaw --------------------------------------------------------
154
155/// Low-level signing functionality.
156///
157/// Types that implement this trait own a private key and can sign arbitrary
158/// information (in the form of slices of bytes).
159///
160/// Implementing types should validate keys during construction, so that
161/// signing does not fail due to invalid keys.  If the implementing type
162/// allows [`sign_raw()`] to be called on unvalidated keys, it will have to
163/// check the validity of the key for every signature; this is unnecessary
164/// overhead when many signatures have to be generated.
165///
166/// [`sign_raw()`]: SignRaw::sign_raw()
167pub trait SignRaw {
168    /// The signature algorithm used.
169    ///
170    /// See [RFC 8624, section 3.1] for IETF implementation recommendations.
171    ///
172    /// [RFC 8624, section 3.1]: https://datatracker.ietf.org/doc/html/rfc8624#section-3.1
173    fn algorithm(&self) -> SecurityAlgorithm;
174
175    /// The public key.
176    ///
177    /// This can be used to verify produced signatures.  It must use the same
178    /// algorithm as returned by [`Self::algorithm()`].
179    fn dnskey(&self) -> Dnskey<Vec<u8>>;
180
181    /// Sign the given bytes.
182    ///
183    /// # Errors
184    ///
185    /// See [`SignError`] for a discussion of possible failure cases.  To the
186    /// greatest extent possible, the implementation should check for failure
187    /// cases beforehand and prevent them (e.g. when the keypair is created).
188    fn sign_raw(&self, data: &[u8]) -> Result<Signature, SignError>;
189}
190
191//----------- Signature ------------------------------------------------------
192
193/// A cryptographic signature.
194///
195/// The format of the signature varies depending on the underlying algorithm:
196///
197/// - RSA: the signature is a single integer `s`, which is less than the key's
198///   public modulus `n`.  `s` is encoded as bytes and ordered from most
199///   significant to least significant digits.  It must be at least 64 bytes
200///   long and at most 512 bytes long.  Leading zero bytes can be inserted for
201///   padding.
202///
203///   See [RFC 3110](https://datatracker.ietf.org/doc/html/rfc3110).
204///
205/// - ECDSA: the signature has a fixed length (64 bytes for P-256, 96 for
206///   P-384).  It is the concatenation of two fixed-length integers (`r` and
207///   `s`, each of equal size).
208///
209///   See [RFC 6605](https://datatracker.ietf.org/doc/html/rfc6605) and [SEC 1
210///   v2.0](https://www.secg.org/sec1-v2.pdf).
211///
212/// - EdDSA: the signature has a fixed length (64 bytes for ED25519, 114 bytes
213///   for ED448).  It is the concatenation of two curve points (`R` and `S`)
214///   that are encoded into bytes.
215///
216/// Signatures are too big to pass by value, so they are placed on the heap.
217#[derive(Clone, Debug, PartialEq, Eq)]
218pub enum Signature {
219    /// Signature using RSA and SHA-1.
220    RsaSha1(Box<[u8]>),
221
222    /// Signature using RSA and SHA-1. This also signals support for NSEC3.
223    RsaSha1Nsec3Sha1(Box<[u8]>),
224
225    /// Signature using RSA and SHA-256.
226    RsaSha256(Box<[u8]>),
227
228    /// Signature using RSA and SHA-512.
229    RsaSha512(Box<[u8]>),
230
231    /// Signature using ECDSA and SHA-256.
232    EcdsaP256Sha256(Box<[u8; 64]>),
233
234    /// Signature using ECDSA and SHA-384.
235    EcdsaP384Sha384(Box<[u8; 96]>),
236
237    /// Signature using Ed25519.
238    Ed25519(Box<[u8; 64]>),
239
240    /// Signature using Ed448.
241    Ed448(Box<[u8; 114]>),
242}
243
244impl Signature {
245    /// The algorithm used to make the signature.
246    pub fn algorithm(&self) -> SecurityAlgorithm {
247        match self {
248            Self::RsaSha1(_) => SecurityAlgorithm::RSASHA1,
249            Self::RsaSha1Nsec3Sha1(_) => {
250                SecurityAlgorithm::RSASHA1_NSEC3_SHA1
251            }
252            Self::RsaSha256(_) => SecurityAlgorithm::RSASHA256,
253            Self::RsaSha512(_) => SecurityAlgorithm::RSASHA512,
254            Self::EcdsaP256Sha256(_) => SecurityAlgorithm::ECDSAP256SHA256,
255            Self::EcdsaP384Sha384(_) => SecurityAlgorithm::ECDSAP384SHA384,
256            Self::Ed25519(_) => SecurityAlgorithm::ED25519,
257            Self::Ed448(_) => SecurityAlgorithm::ED448,
258        }
259    }
260}
261
262impl AsRef<[u8]> for Signature {
263    fn as_ref(&self) -> &[u8] {
264        match self {
265            Self::RsaSha1(s)
266            | Self::RsaSha1Nsec3Sha1(s)
267            | Self::RsaSha256(s)
268            | Self::RsaSha512(s) => s,
269            Self::EcdsaP256Sha256(s) => &**s,
270            Self::EcdsaP384Sha384(s) => &**s,
271            Self::Ed25519(s) => &**s,
272            Self::Ed448(s) => &**s,
273        }
274    }
275}
276
277impl From<Signature> for Box<[u8]> {
278    fn from(value: Signature) -> Self {
279        match value {
280            Signature::RsaSha1(s)
281            | Signature::RsaSha1Nsec3Sha1(s)
282            | Signature::RsaSha256(s)
283            | Signature::RsaSha512(s) => s,
284            Signature::EcdsaP256Sha256(s) => s as _,
285            Signature::EcdsaP384Sha384(s) => s as _,
286            Signature::Ed25519(s) => s as _,
287            Signature::Ed448(s) => s as _,
288        }
289    }
290}
291
292//----------- KeyPair --------------------------------------------------------
293
294/// A key pair based on a built-in backend.
295///
296/// This supports any built-in backend (currently, that is OpenSSL and Ring,
297/// if their respective feature flags are enabled).  Wherever possible, it
298/// will prefer the Ring backend over OpenSSL -- but for more uncommon or
299/// insecure algorithms, that Ring does not support, OpenSSL must be used.
300#[derive(Debug)]
301// Note: ring does not implement Clone for KeyPair.
302#[allow(clippy::large_enum_variant)] // TODO
303pub enum KeyPair {
304    /// A key backed by Ring.
305    #[cfg(feature = "ring")]
306    Ring(ring::sign::KeyPair),
307
308    /// A key backed by OpenSSL.
309    #[cfg(feature = "openssl")]
310    OpenSSL(openssl::sign::KeyPair),
311}
312
313//--- Conversion to and from bytes
314
315impl KeyPair {
316    /// Import a key pair from bytes.
317    pub fn from_bytes<Octs>(
318        secret: &SecretKeyBytes,
319        public: &Dnskey<Octs>,
320    ) -> Result<Self, FromBytesError>
321    where
322        Octs: AsRef<[u8]>,
323    {
324        // The error generated by the last-attempted backend.
325        //
326        // NOTE: At least one backend is required to be available, so there is
327        // no chance that 'error' will be returned directly. Thus, we don't
328        // need to specify a default error.
329        #[allow(unused_mut)] // occurs if a single backend is enabled
330        let mut error;
331
332        // Prefer Ring if it is available.
333        #[cfg(feature = "ring")]
334        match ring::sign::KeyPair::from_bytes(secret, public) {
335            Ok(key) => return Ok(Self::Ring(key)),
336            #[allow(unused_assignments)] // 'error' might be ignored
337            Err(
338                impl_error @ (ring::FromBytesError::UnsupportedAlgorithm
339                | ring::FromBytesError::WeakKey),
340            ) => {
341                error = impl_error.into();
342                // Try the next available implementation.
343            }
344            Err(error) => return Err(error.into()),
345        }
346
347        // Fall back to OpenSSL.
348        #[cfg(feature = "openssl")]
349        match openssl::sign::KeyPair::from_bytes(secret, public) {
350            Ok(key) => return Ok(Self::OpenSSL(key)),
351            #[allow(unused_assignments)] // 'error' might be ignored
352            Err(
353                impl_error @ openssl::FromBytesError::UnsupportedAlgorithm,
354            ) => {
355                error = impl_error.into();
356                // Try the next available implementation.
357            }
358            Err(error) => return Err(error.into()),
359        }
360
361        // If no implementation worked, fail.
362        #[allow(unreachable_code)]
363        Err(error)
364    }
365}
366
367//--- SignRaw
368
369impl SignRaw for KeyPair {
370    fn algorithm(&self) -> SecurityAlgorithm {
371        match self {
372            #[cfg(feature = "ring")]
373            Self::Ring(key) => key.algorithm(),
374            #[cfg(feature = "openssl")]
375            Self::OpenSSL(key) => key.algorithm(),
376        }
377    }
378
379    fn dnskey(&self) -> Dnskey<Vec<u8>> {
380        match self {
381            #[cfg(feature = "ring")]
382            Self::Ring(key) => key.dnskey(),
383            #[cfg(feature = "openssl")]
384            Self::OpenSSL(key) => key.dnskey(),
385        }
386    }
387
388    fn sign_raw(&self, data: &[u8]) -> Result<Signature, SignError> {
389        match self {
390            #[cfg(feature = "ring")]
391            Self::Ring(key) => key.sign_raw(data),
392            #[cfg(feature = "openssl")]
393            Self::OpenSSL(key) => key.sign_raw(data),
394        }
395    }
396}
397
398//----------- generate() -----------------------------------------------------
399
400/// Generate a new secret key for the given algorithm.
401pub fn generate(
402    params: &GenerateParams,
403    flags: u16,
404) -> Result<(SecretKeyBytes, Dnskey<Vec<u8>>), GenerateError> {
405    // The error generated by the last-attempted backend.
406    //
407    // NOTE: At least one backend is required to be available, so there is
408    // no chance that 'error' will be returned directly. Thus, we don't
409    // need to specify a default error.
410    #[allow(unused_mut)] // occurs if a single backend is enabled
411    let mut error;
412
413    // Prefer Ring if it is available.
414    #[cfg(feature = "ring")]
415    {
416        let rng = ::ring::rand::SystemRandom::new();
417        match ring::sign::generate(params, flags, &rng) {
418            Ok(key) => return Ok(key),
419            #[allow(unused_assignments)] // 'error' might be ignored
420            Err(impl_error @ ring::GenerateError::UnsupportedAlgorithm) => {
421                error = impl_error.into();
422                // Try the next available implementation.
423            }
424            Err(error) => return Err(error.into()),
425        }
426    }
427
428    // Fall back to OpenSSL.
429    #[cfg(feature = "openssl")]
430    match openssl::sign::generate(params, flags) {
431        Ok(key) => return Ok((key.to_bytes(), key.dnskey())),
432        #[allow(unused_assignments)] // 'error' might be ignored
433        Err(impl_error @ openssl::GenerateError::UnsupportedAlgorithm) => {
434            error = impl_error.into();
435            // Try the next available implementation.
436        }
437        Err(error) => return Err(error.into()),
438    }
439
440    // If no implementation worked, fail.
441    #[allow(unreachable_code)]
442    Err(error)
443}
444
445//----------- SecretKeyBytes -------------------------------------------------
446
447/// A secret key expressed as raw bytes.
448///
449/// This is a low-level generic representation of a secret key from any one of
450/// the commonly supported signature algorithms.  It is useful for abstracting
451/// over most cryptographic implementations, and it provides functionality for
452/// importing and exporting keys from and to the disk.
453///
454/// # Serialization
455///
456/// This type can be used to interact with private keys stored in the format
457/// popularized by BIND.  The format is rather under-specified, but examples
458/// of it are available in [RFC 5702], [RFC 6605], and [RFC 8080].
459///
460/// [RFC 5702]: https://www.rfc-editor.org/rfc/rfc5702
461/// [RFC 6605]: https://www.rfc-editor.org/rfc/rfc6605
462/// [RFC 8080]: https://www.rfc-editor.org/rfc/rfc8080
463///
464/// In this format, a private key is a line-oriented text file.  Each line is
465/// either blank (having only whitespace) or a key-value entry.  Entries have
466/// three components: a key, an ASCII colon, and a value.  Keys contain ASCII
467/// text (except for colons) and values contain any data up to the end of the
468/// line.  Whitespace at either end of the key and the value will be ignored.
469///
470/// Every file begins with two entries:
471///
472/// - `Private-key-format` specifies the format of the file.  The RFC examples
473///   above use version 1.2 (serialised `v1.2`), but recent versions of BIND
474///   have defined a new version 1.3 (serialized `v1.3`).
475///
476///   This value should be treated akin to Semantic Versioning principles.  If
477///   the major version (the first number) is unknown to a parser, it should
478///   fail, since it does not know the layout of the following fields.  If the
479///   minor version is greater than what a parser is expecting, it should
480///   ignore any following fields it did not expect.
481///
482/// - `Algorithm` specifies the signing algorithm used by the private key.
483///   This can affect the format of later fields.  The value consists of two
484///   whitespace-separated words: the first is the ASCII decimal number of the
485///   algorithm (see [`SecurityAlgorithm`]); the second is the name of the algorithm in
486///   ASCII parentheses (with no whitespace inside).  Valid combinations are:
487///
488///   - `8 (RSASHA256)`: RSA with the SHA-256 digest.
489///   - `10 (RSASHA512)`: RSA with the SHA-512 digest.
490///   - `13 (ECDSAP256SHA256)`: ECDSA with the P-256 curve and SHA-256 digest.
491///   - `14 (ECDSAP384SHA384)`: ECDSA with the P-384 curve and SHA-384 digest.
492///   - `15 (ED25519)`: Ed25519.
493///   - `16 (ED448)`: Ed448.
494///
495/// The value of every following entry is a Base64-encoded string of variable
496/// length, using the RFC 4648 variant (i.e. with `+` and `/`, and `=` for
497/// padding).  It is unclear whether padding is required or optional.
498///
499/// In the case of RSA, the following fields are defined (their conventional
500/// symbolic names are also provided):
501///
502/// - `Modulus` (n)
503/// - `PublicExponent` (e)
504/// - `PrivateExponent` (d)
505/// - `Prime1` (p)
506/// - `Prime2` (q)
507/// - `Exponent1` (d_p)
508/// - `Exponent2` (d_q)
509/// - `Coefficient` (q_inv)
510///
511/// For all other algorithms, there is a single `PrivateKey` field, whose
512/// contents should be interpreted as:
513///
514/// - For ECDSA, the private scalar of the key, as a fixed-width byte string
515///   interpreted as a big-endian integer.
516///
517/// - For EdDSA, the private scalar of the key, as a fixed-width byte string.
518#[derive(Debug)]
519pub enum SecretKeyBytes {
520    /// An RSA/SHA-256 keypair.
521    RsaSha256(RsaSecretKeyBytes),
522
523    /// An RSA/SHA-256 keypair.
524    RsaSha512(RsaSecretKeyBytes),
525
526    /// An ECDSA P-256/SHA-256 keypair.
527    ///
528    /// The private key is a single 32-byte big-endian integer.
529    EcdsaP256Sha256(SecretBox<[u8; 32]>),
530
531    /// An ECDSA P-384/SHA-384 keypair.
532    ///
533    /// The private key is a single 48-byte big-endian integer.
534    EcdsaP384Sha384(SecretBox<[u8; 48]>),
535
536    /// An Ed25519 keypair.
537    ///
538    /// The private key is a single 32-byte string.
539    Ed25519(SecretBox<[u8; 32]>),
540
541    /// An Ed448 keypair.
542    ///
543    /// The private key is a single 57-byte string.
544    Ed448(SecretBox<[u8; 57]>),
545}
546
547//--- Inspection
548
549impl SecretKeyBytes {
550    /// The algorithm used by this key.
551    pub fn algorithm(&self) -> SecurityAlgorithm {
552        match self {
553            Self::RsaSha256(_) => SecurityAlgorithm::RSASHA256,
554            Self::RsaSha512(_) => SecurityAlgorithm::RSASHA512,
555            Self::EcdsaP256Sha256(_) => SecurityAlgorithm::ECDSAP256SHA256,
556            Self::EcdsaP384Sha384(_) => SecurityAlgorithm::ECDSAP384SHA384,
557            Self::Ed25519(_) => SecurityAlgorithm::ED25519,
558            Self::Ed448(_) => SecurityAlgorithm::ED448,
559        }
560    }
561}
562
563//--- Converting to and from the BIND format
564
565impl SecretKeyBytes {
566    /// Serialize this secret key in the conventional format used by BIND.
567    ///
568    /// The key is formatted in the private key v1.2 format and written to the
569    /// given formatter.  See the type-level documentation for a description
570    /// of this format.
571    pub fn format_as_bind(&self, mut w: impl fmt::Write) -> fmt::Result {
572        writeln!(w, "Private-key-format: v1.2")?;
573        match self {
574            Self::RsaSha256(k) => {
575                writeln!(w, "Algorithm: 8 (RSASHA256)")?;
576                k.format_as_bind(w)
577            }
578
579            Self::RsaSha512(k) => {
580                writeln!(w, "Algorithm: 10 (RSASHA512)")?;
581                k.format_as_bind(w)
582            }
583
584            Self::EcdsaP256Sha256(s) => {
585                let s = s.expose_secret();
586                writeln!(w, "Algorithm: 13 (ECDSAP256SHA256)")?;
587                writeln!(w, "PrivateKey: {}", base64::encode_display(s))
588            }
589
590            Self::EcdsaP384Sha384(s) => {
591                let s = s.expose_secret();
592                writeln!(w, "Algorithm: 14 (ECDSAP384SHA384)")?;
593                writeln!(w, "PrivateKey: {}", base64::encode_display(s))
594            }
595
596            Self::Ed25519(s) => {
597                let s = s.expose_secret();
598                writeln!(w, "Algorithm: 15 (ED25519)")?;
599                writeln!(w, "PrivateKey: {}", base64::encode_display(s))
600            }
601
602            Self::Ed448(s) => {
603                let s = s.expose_secret();
604                writeln!(w, "Algorithm: 16 (ED448)")?;
605                writeln!(w, "PrivateKey: {}", base64::encode_display(s))
606            }
607        }
608    }
609
610    /// Display this secret key in the conventional format used by BIND.
611    ///
612    /// This is a simple wrapper around [`Self::format_as_bind()`].
613    pub fn display_as_bind(&self) -> impl fmt::Display + '_ {
614        /// Display type to return from this function.
615        struct Display<'a>(&'a SecretKeyBytes);
616        impl fmt::Display for Display<'_> {
617            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618                self.0.format_as_bind(f)
619            }
620        }
621        Display(self)
622    }
623
624    /// Parse a secret key from the conventional format used by BIND.
625    ///
626    /// This parser supports the private key v1.2 format, but it should be
627    /// compatible with any future v1.x key.  See the type-level documentation
628    /// for a description of this format.
629    pub fn parse_from_bind(data: &str) -> Result<Self, BindFormatError> {
630        /// Parse private keys for most algorithms (except RSA).
631        fn parse_pkey<const N: usize>(
632            mut data: &str,
633        ) -> Result<SecretBox<[u8; N]>, BindFormatError> {
634            // Look for the 'PrivateKey' field.
635            while let Some((key, val, rest)) = parse_bind_entry(data)? {
636                data = rest;
637
638                if key != "PrivateKey" {
639                    continue;
640                }
641
642                // TODO: Evaluate security of 'base64::decode()'.
643                let val: Vec<u8> = base64::decode(val)
644                    .map_err(|_| BindFormatError::Misformatted)?;
645                let val: Box<[u8]> = val.into_boxed_slice();
646                let val: Box<[u8; N]> = val
647                    .try_into()
648                    .map_err(|_| BindFormatError::Misformatted)?;
649
650                return Ok(val.into());
651            }
652
653            // The 'PrivateKey' field was not found.
654            Err(BindFormatError::Misformatted)
655        }
656
657        // The first line should specify the key format.
658        let (_, _, data) = parse_bind_entry(data)?
659            .filter(|&(k, v, _)| {
660                k == "Private-key-format"
661                    && v.strip_prefix("v1.")
662                        .and_then(|minor| minor.parse::<u8>().ok())
663                        .is_some_and(|minor| minor >= 2)
664            })
665            .ok_or(BindFormatError::UnsupportedFormat)?;
666
667        // The second line should specify the algorithm.
668        let (_, val, data) = parse_bind_entry(data)?
669            .filter(|&(k, _, _)| k == "Algorithm")
670            .ok_or(BindFormatError::Misformatted)?;
671
672        // Parse the algorithm.
673        let mut words = val.split_whitespace();
674        let code = words
675            .next()
676            .and_then(|code| code.parse::<u8>().ok())
677            .ok_or(BindFormatError::Misformatted)?;
678        let name = words.next().ok_or(BindFormatError::Misformatted)?;
679        if words.next().is_some() {
680            return Err(BindFormatError::Misformatted);
681        }
682
683        match (code, name) {
684            (8, "(RSASHA256)") => {
685                RsaSecretKeyBytes::parse_from_bind(data).map(Self::RsaSha256)
686            }
687            (10, "(RSASHA512)") => {
688                RsaSecretKeyBytes::parse_from_bind(data).map(Self::RsaSha512)
689            }
690            (13, "(ECDSAP256SHA256)") => {
691                parse_pkey(data).map(Self::EcdsaP256Sha256)
692            }
693            (14, "(ECDSAP384SHA384)") => {
694                parse_pkey(data).map(Self::EcdsaP384Sha384)
695            }
696            (15, "(ED25519)") => parse_pkey(data).map(Self::Ed25519),
697            (16, "(ED448)") => parse_pkey(data).map(Self::Ed448),
698            _ => Err(BindFormatError::UnsupportedAlgorithm),
699        }
700    }
701}
702
703//----------- Helpers for parsing the BIND format ----------------------------
704
705/// Extract the next key-value pair in a BIND-format private key file.
706pub(crate) fn parse_bind_entry(
707    data: &str,
708) -> Result<Option<(&str, &str, &str)>, BindFormatError> {
709    // TODO: Use 'trim_ascii_start()' etc. once they pass the MSRV.
710
711    // Trim any pending newlines.
712    let data = data.trim_start();
713
714    // Stop if there's no more data.
715    if data.is_empty() {
716        return Ok(None);
717    }
718
719    // Get the first line (NOTE: CR LF is handled later).
720    let (line, rest) = data.split_once('\n').unwrap_or((data, ""));
721
722    // Split the line by a colon.
723    let (key, val) =
724        line.split_once(':').ok_or(BindFormatError::Misformatted)?;
725
726    // Trim the key and value (incl. for CR LFs).
727    Ok(Some((key.trim(), val.trim(), rest)))
728}
729
730//----------- RsaSecretKeyBytes ----------------------------------------------
731
732/// An RSA secret key expressed as raw bytes.
733///
734/// All fields here are arbitrary-precision integers in big-endian format.
735/// The public values, `n` and `e`, must not have leading zeros; the remaining
736/// values may be padded with leading zeros.
737#[derive(Debug)]
738pub struct RsaSecretKeyBytes {
739    /// The public modulus.
740    pub n: Box<[u8]>,
741
742    /// The public exponent.
743    pub e: Box<[u8]>,
744
745    /// The private exponent.
746    pub d: SecretBox<[u8]>,
747
748    /// The first prime factor of `d`.
749    pub p: SecretBox<[u8]>,
750
751    /// The second prime factor of `d`.
752    pub q: SecretBox<[u8]>,
753
754    /// The exponent corresponding to the first prime factor of `d`.
755    pub d_p: SecretBox<[u8]>,
756
757    /// The exponent corresponding to the second prime factor of `d`.
758    pub d_q: SecretBox<[u8]>,
759
760    /// The inverse of the second prime factor modulo the first.
761    pub q_i: SecretBox<[u8]>,
762}
763
764//--- Conversion to and from the BIND format
765
766impl RsaSecretKeyBytes {
767    /// Serialize this secret key in the conventional format used by BIND.
768    ///
769    /// The key is formatted in the private key v1.2 format and written to the
770    /// given formatter.  Note that the header and algorithm lines are not
771    /// written.  See the type-level documentation of [`SecretKeyBytes`] for a
772    /// description of this format.
773    pub fn format_as_bind(&self, mut w: impl fmt::Write) -> fmt::Result {
774        w.write_str("Modulus: ")?;
775        writeln!(w, "{}", base64::encode_display(&self.n))?;
776        w.write_str("PublicExponent: ")?;
777        writeln!(w, "{}", base64::encode_display(&self.e))?;
778        w.write_str("PrivateExponent: ")?;
779        writeln!(w, "{}", base64::encode_display(&self.d.expose_secret()))?;
780        w.write_str("Prime1: ")?;
781        writeln!(w, "{}", base64::encode_display(&self.p.expose_secret()))?;
782        w.write_str("Prime2: ")?;
783        writeln!(w, "{}", base64::encode_display(&self.q.expose_secret()))?;
784        w.write_str("Exponent1: ")?;
785        writeln!(w, "{}", base64::encode_display(&self.d_p.expose_secret()))?;
786        w.write_str("Exponent2: ")?;
787        writeln!(w, "{}", base64::encode_display(&self.d_q.expose_secret()))?;
788        w.write_str("Coefficient: ")?;
789        writeln!(w, "{}", base64::encode_display(&self.q_i.expose_secret()))?;
790        Ok(())
791    }
792
793    /// Display this secret key in the conventional format used by BIND.
794    ///
795    /// This is a simple wrapper around [`Self::format_as_bind()`].
796    pub fn display_as_bind(&self) -> impl fmt::Display + '_ {
797        /// Display type to return from this function.
798        struct Display<'a>(&'a RsaSecretKeyBytes);
799        impl fmt::Display for Display<'_> {
800            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
801                self.0.format_as_bind(f)
802            }
803        }
804        Display(self)
805    }
806
807    /// Parse a secret key from the conventional format used by BIND.
808    ///
809    /// This parser supports the private key v1.2 format, but it should be
810    /// compatible with any future v1.x key.  Note that the header and
811    /// algorithm lines are ignored.  See the type-level documentation of
812    /// [`SecretKeyBytes`] for a description of this format.
813    pub fn parse_from_bind(mut data: &str) -> Result<Self, BindFormatError> {
814        let mut n = None;
815        let mut e = None;
816        let mut d = None;
817        let mut p = None;
818        let mut q = None;
819        let mut d_p = None;
820        let mut d_q = None;
821        let mut q_i = None;
822
823        while let Some((key, val, rest)) = parse_bind_entry(data)? {
824            let field = match key {
825                "Modulus" => &mut n,
826                "PublicExponent" => &mut e,
827                "PrivateExponent" => &mut d,
828                "Prime1" => &mut p,
829                "Prime2" => &mut q,
830                "Exponent1" => &mut d_p,
831                "Exponent2" => &mut d_q,
832                "Coefficient" => &mut q_i,
833                _ => {
834                    data = rest;
835                    continue;
836                }
837            };
838
839            if field.is_some() {
840                // This field has already been filled.
841                return Err(BindFormatError::Misformatted);
842            }
843
844            let buffer: Vec<u8> = base64::decode(val)
845                .map_err(|_| BindFormatError::Misformatted)?;
846
847            *field = Some(buffer.into_boxed_slice());
848            data = rest;
849        }
850
851        for field in [&n, &e, &d, &p, &q, &d_p, &d_q, &q_i] {
852            if field.is_none() {
853                // A field was missing.
854                return Err(BindFormatError::Misformatted);
855            }
856        }
857
858        Ok(Self {
859            n: n.unwrap(),
860            e: e.unwrap(),
861            d: d.unwrap().into(),
862            p: p.unwrap().into(),
863            q: q.unwrap().into(),
864            d_p: d_p.unwrap().into(),
865            d_q: d_q.unwrap().into(),
866            q_i: q_i.unwrap().into(),
867        })
868    }
869}
870
871//============ Error Types ===================================================
872
873//----------- FromBytesError -----------------------------------------------
874
875/// An error in importing a key pair from bytes.
876#[derive(Clone, Debug)]
877pub enum FromBytesError {
878    /// The requested algorithm was not supported.
879    UnsupportedAlgorithm,
880
881    /// The key's parameters were invalid.
882    InvalidKey,
883
884    /// The implementation does not allow such weak keys.
885    WeakKey,
886
887    /// An implementation failure occurred.
888    ///
889    /// This includes memory allocation failures.
890    Implementation,
891}
892
893//--- Conversions
894
895#[cfg(feature = "ring")]
896impl From<ring::FromBytesError> for FromBytesError {
897    fn from(value: ring::FromBytesError) -> Self {
898        match value {
899            ring::FromBytesError::UnsupportedAlgorithm => {
900                Self::UnsupportedAlgorithm
901            }
902            ring::FromBytesError::InvalidKey => Self::InvalidKey,
903            ring::FromBytesError::WeakKey => Self::WeakKey,
904        }
905    }
906}
907
908#[cfg(feature = "openssl")]
909impl From<openssl::FromBytesError> for FromBytesError {
910    fn from(value: openssl::FromBytesError) -> Self {
911        match value {
912            openssl::FromBytesError::UnsupportedAlgorithm => {
913                Self::UnsupportedAlgorithm
914            }
915            openssl::FromBytesError::InvalidKey => Self::InvalidKey,
916            openssl::FromBytesError::Implementation => Self::Implementation,
917        }
918    }
919}
920
921//--- Formatting
922
923impl fmt::Display for FromBytesError {
924    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
925        f.write_str(match self {
926            Self::UnsupportedAlgorithm => "algorithm not supported",
927            Self::InvalidKey => "malformed or insecure private key",
928            Self::WeakKey => "key too weak to be supported",
929            Self::Implementation => "an internal error occurred",
930        })
931    }
932}
933
934//--- Error
935
936impl core::error::Error for FromBytesError {}
937
938//----------- GenerateError --------------------------------------------------
939
940/// An error in generating a key pair.
941#[derive(Clone, Debug)]
942pub enum GenerateError {
943    /// The requested algorithm was not supported.
944    UnsupportedAlgorithm,
945
946    /// An implementation failure occurred.
947    ///
948    /// This includes memory allocation failures.
949    Implementation,
950}
951
952//--- Conversion
953
954#[cfg(feature = "ring")]
955impl From<ring::GenerateError> for GenerateError {
956    fn from(value: ring::GenerateError) -> Self {
957        match value {
958            ring::GenerateError::UnsupportedAlgorithm => {
959                Self::UnsupportedAlgorithm
960            }
961            ring::GenerateError::Implementation => Self::Implementation,
962        }
963    }
964}
965
966#[cfg(feature = "openssl")]
967impl From<openssl::GenerateError> for GenerateError {
968    fn from(value: openssl::GenerateError) -> Self {
969        match value {
970            openssl::GenerateError::UnsupportedAlgorithm => {
971                Self::UnsupportedAlgorithm
972            }
973            openssl::GenerateError::Implementation => Self::Implementation,
974        }
975    }
976}
977
978//--- Formatting
979
980impl fmt::Display for GenerateError {
981    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
982        f.write_str(match self {
983            Self::UnsupportedAlgorithm => "algorithm not supported",
984            Self::Implementation => "an internal error occurred",
985        })
986    }
987}
988
989//--- Error
990
991impl core::error::Error for GenerateError {}
992
993//----------- SignError ------------------------------------------------------
994
995/// A signature failure.
996///
997/// In case such an error occurs, callers should stop using the key pair they
998/// attempted to sign with.  If such an error occurs with every key pair they
999/// have available, or if such an error occurs with a freshly-generated key
1000/// pair, they should use a different cryptographic implementation.  If that
1001/// is not possible, they must forego signing entirely.
1002///
1003/// # Failure Cases
1004///
1005/// Signing should be an infallible process.  There are three considerable
1006/// failure cases for it:
1007///
1008/// - The secret key was invalid (e.g. its parameters were inconsistent).
1009///
1010///   Such a failure would mean that all future signing (with this key) will
1011///   also fail.  In any case, the implementations provided by this crate try
1012///   to verify the key (e.g. by checking the consistency of the private and
1013///   public components) before any signing occurs, largely ruling this class
1014///   of errors out.
1015///
1016/// - Not enough randomness could be obtained.  This applies to signature
1017///   algorithms which use randomization (e.g. RSA and ECDSA).
1018///
1019///   On the vast majority of platforms, randomness can always be obtained.
1020///   The [`getrandom` crate documentation][getrandom] notes:
1021///
1022///   > If an error does occur, then it is likely that it will occur on every
1023///   > call to getrandom, hence after the first successful call one can be
1024///   > reasonably confident that no errors will occur.
1025///
1026///   [getrandom]: https://docs.rs/getrandom
1027///
1028///   Thus, in case such a failure occurs, all future signing will probably
1029///   also fail.
1030///
1031/// - Not enough memory could be allocated.
1032///
1033///   Signature algorithms have a small memory overhead, so an out-of-memory
1034///   condition means that the program is nearly out of allocatable space.
1035///
1036///   Callers who do not expect allocations to fail (i.e. who are using the
1037///   standard memory allocation routines, not their `try_` variants) will
1038///   likely panic shortly after such an error.
1039///
1040///   Callers who are aware of their memory usage will likely restrict it far
1041///   before they get to this point.  Systems running at near-maximum load
1042///   tend to quickly become unresponsive and staggeringly slow.  If memory
1043///   usage is an important consideration, programs will likely cap it before
1044///   the system reaches e.g. 90% memory use.
1045///
1046///   As such, memory allocation failure should never really occur.  It is far
1047///   more likely that one of the other errors has occurred.
1048///
1049/// It may be reasonable to panic in any such situation, since each kind of
1050/// error is essentially unrecoverable.  However, applications where signing
1051/// is an optional step, or where crashing is prohibited, may wish to recover
1052/// from such an error differently (e.g. by foregoing signatures or informing
1053/// an operator).
1054#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1055pub struct SignError;
1056
1057impl fmt::Display for SignError {
1058    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1059        f.write_str("could not create a cryptographic signature")
1060    }
1061}
1062
1063impl core::error::Error for SignError {}
1064
1065//----------- BindFormatError ------------------------------------------------
1066
1067/// An error in loading a [`SecretKeyBytes`] from the conventional DNS format.
1068#[derive(Clone, Debug, PartialEq, Eq, Hash)]
1069pub enum BindFormatError {
1070    /// The key file uses an unsupported version of the format.
1071    UnsupportedFormat,
1072
1073    /// The key file did not follow the DNS format correctly.
1074    Misformatted,
1075
1076    /// The key file used an unsupported algorithm.
1077    UnsupportedAlgorithm,
1078}
1079
1080//--- Display
1081
1082impl fmt::Display for BindFormatError {
1083    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1084        f.write_str(match self {
1085            Self::UnsupportedFormat => "unsupported format",
1086            Self::Misformatted => "misformatted key file",
1087            Self::UnsupportedAlgorithm => "unsupported algorithm",
1088        })
1089    }
1090}
1091
1092//--- Error
1093
1094impl core::error::Error for BindFormatError {}
1095
1096//============ Tests =========================================================
1097
1098#[cfg(test)]
1099mod tests {
1100    use alloc::format;
1101    use alloc::string::ToString;
1102    use alloc::vec::Vec;
1103
1104    use crate::base::iana::SecurityAlgorithm;
1105    use crate::crypto::sign::{
1106        GenerateParams, KeyPair, SecretKeyBytes, generate,
1107    };
1108    const KEYS: &[(SecurityAlgorithm, u16)] = &[
1109        (SecurityAlgorithm::RSASHA256, 60616),
1110        (SecurityAlgorithm::RSASHA512, 46731),
1111        (SecurityAlgorithm::ECDSAP256SHA256, 42253),
1112        (SecurityAlgorithm::ECDSAP384SHA384, 33566),
1113        (SecurityAlgorithm::ED25519, 56037),
1114        (SecurityAlgorithm::ED448, 7379),
1115    ];
1116
1117    #[test]
1118    fn secret_from_dns() {
1119        for &(algorithm, key_tag) in KEYS {
1120            let name =
1121                format!("test.+{:03}+{:05}", algorithm.to_int(), key_tag);
1122            let path = format!("test-data/dnssec-keys/K{}.private", name);
1123            let data = std::fs::read_to_string(path).unwrap();
1124            let key = SecretKeyBytes::parse_from_bind(&data).unwrap();
1125            assert_eq!(key.algorithm(), algorithm);
1126        }
1127    }
1128
1129    #[test]
1130    fn secret_roundtrip() {
1131        for &(algorithm, key_tag) in KEYS {
1132            let name =
1133                format!("test.+{:03}+{:05}", algorithm.to_int(), key_tag);
1134            let path = format!("test-data/dnssec-keys/K{}.private", name);
1135            let data = std::fs::read_to_string(path).unwrap();
1136            let key = SecretKeyBytes::parse_from_bind(&data).unwrap();
1137            let same = key.display_as_bind().to_string();
1138            let data = data.lines().collect::<Vec<_>>();
1139            let same = same.lines().collect::<Vec<_>>();
1140            assert_eq!(data, same);
1141        }
1142    }
1143
1144    #[test]
1145    fn keypair_from_bytes() {
1146        for &(algorithm, _) in KEYS {
1147            let params = match algorithm {
1148                SecurityAlgorithm::RSASHA256 => {
1149                    if cfg!(feature = "openssl") {
1150                        GenerateParams::RsaSha256 { bits: 2048 }
1151                    } else {
1152                        // No support for RSASHA256 in Ring.
1153                        continue;
1154                    }
1155                }
1156                SecurityAlgorithm::RSASHA512 => {
1157                    if cfg!(feature = "openssl") {
1158                        GenerateParams::RsaSha512 { bits: 2048 }
1159                    } else {
1160                        // No support for RSASHA256 in Ring.
1161                        continue;
1162                    }
1163                }
1164                SecurityAlgorithm::ECDSAP256SHA256 => {
1165                    GenerateParams::EcdsaP256Sha256
1166                }
1167                SecurityAlgorithm::ECDSAP384SHA384 => {
1168                    GenerateParams::EcdsaP384Sha384
1169                }
1170                SecurityAlgorithm::ED25519 => GenerateParams::Ed25519,
1171                SecurityAlgorithm::ED448 => {
1172                    if cfg!(feature = "openssl") {
1173                        GenerateParams::Ed448
1174                    } else {
1175                        // No support for ED448 in Ring.
1176                        continue;
1177                    }
1178                }
1179                _ => unreachable!(),
1180            };
1181            let (sec_bytes, pub_key) = generate(&params, 257)
1182                .map_err(|e| format!("generate failed for {params:?}: {e}"))
1183                .unwrap();
1184            let _key_pair = KeyPair::from_bytes(&sec_bytes, &pub_key)
1185                .map_err(|e| {
1186                    format!("KeyPair::from_bytes failed for {params:?}: {e}")
1187                })
1188                .unwrap();
1189        }
1190    }
1191}