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(¶ms, 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(¶ms, 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}