bc_components/signing/
signing_private_key.rs

1use std::{cell::RefCell, rc::Rc};
2
3use crate::{
4    tags, ECKey, ECPrivateKey, Ed25519PrivateKey, MLDSAPrivateKey, Signature, Signer,
5    SigningPublicKey,
6};
7use anyhow::{bail, Error, Result};
8use bc_rand::{RandomNumberGenerator, SecureRandomNumberGenerator};
9use bc_ur::prelude::*;
10use ssh_key::{private::PrivateKey as SSHPrivateKey, HashAlg, LineEnding};
11
12use super::Verifier;
13
14/// Options for configuring signature creation.
15///
16/// Different signature schemes may require specific options:
17///
18/// - `Schnorr`: Requires a random number generator for signature creation
19/// - `Ssh`: Requires a namespace and hash algorithm
20///
21/// Other signature types like ECDSA, Ed25519, and ML-DSA don't require options.
22///
23/// # Examples
24///
25/// Creating Schnorr signing options:
26///
27/// ```
28/// use std::{cell::RefCell, rc::Rc};
29/// use bc_components::SigningOptions;
30/// use bc_rand::SecureRandomNumberGenerator;
31///
32/// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
33/// let options = SigningOptions::Schnorr { rng };
34/// ```
35///
36/// Creating SSH signing options:
37///
38/// ```
39/// use bc_components::SigningOptions;
40/// use ssh_key::HashAlg;
41///
42/// let options = SigningOptions::Ssh {
43///     namespace: "ssh".to_string(),
44///     hash_alg: HashAlg::Sha512,
45/// };
46/// ```
47#[derive(Clone)]
48pub enum SigningOptions {
49    /// Options for Schnorr signatures
50    Schnorr {
51        /// Non-default random number generator used for signature creation
52        rng: Rc<RefCell<dyn RandomNumberGenerator>>,
53    },
54
55    /// Options for SSH signatures
56    Ssh {
57        /// The namespace used for SSH signatures
58        namespace: String,
59
60        /// The hash algorithm used for SSH signatures
61        hash_alg: HashAlg,
62    },
63}
64
65/// A private key used for creating digital signatures.
66///
67/// `SigningPrivateKey` is an enum representing different types of signing private keys,
68/// including elliptic curve schemes (ECDSA, Schnorr), Edwards curve schemes (Ed25519),
69/// post-quantum schemes (ML-DSA), and SSH keys.
70///
71/// This type implements the `Signer` trait, allowing it to create signatures of
72/// the appropriate type.
73///
74/// # Examples
75///
76/// Creating a new Schnorr signing key and using it to sign a message:
77///
78/// ```
79/// use bc_components::{ECPrivateKey, SigningPrivateKey, Signer, Verifier};
80///
81/// // Create a new Schnorr signing key
82/// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
83///
84/// // Get the corresponding public key
85/// let public_key = private_key.public_key().unwrap();
86///
87/// // Sign a message
88/// let message = b"Hello, world!";
89/// let signature = private_key.sign(&message).unwrap();
90///
91/// // Verify the signature
92/// assert!(public_key.verify(&signature, &message));
93/// ```
94///
95/// # CBOR Serialization
96///
97/// `SigningPrivateKey` can be serialized to and from CBOR with appropriate tags:
98///
99/// ```
100/// use bc_components::{ECPrivateKey, SigningPrivateKey};
101/// use dcbor::prelude::*;
102///
103/// // Create a key
104/// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
105///
106/// // Convert to CBOR
107/// let cbor: CBOR = private_key.clone().into();
108/// let data = cbor.to_cbor_data();
109///
110/// // Convert back from CBOR
111/// let recovered = SigningPrivateKey::from_tagged_cbor_data(&data).unwrap();
112///
113/// // The keys should be equal
114/// assert_eq!(private_key, recovered);
115/// ```
116#[derive(Clone, PartialEq)]
117pub enum SigningPrivateKey {
118    /// A Schnorr private key based on the secp256k1 curve
119    Schnorr(ECPrivateKey),
120
121    /// An ECDSA private key based on the secp256k1 curve
122    ECDSA(ECPrivateKey),
123
124    /// An Ed25519 private key
125    Ed25519(Ed25519PrivateKey),
126
127    /// An SSH private key
128    SSH(Box<SSHPrivateKey>),
129
130    /// A post-quantum ML-DSA private key
131    MLDSA(MLDSAPrivateKey),
132}
133
134/// Implementation of hashing for SigningPrivateKey
135impl std::hash::Hash for SigningPrivateKey {
136    /// Hashes the key's data.
137    ///
138    /// This is used for collections that require hash support.
139    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
140        match self {
141            Self::Schnorr(key) => key.hash(state),
142            Self::ECDSA(key) => key.hash(state),
143            Self::Ed25519(key) => key.hash(state),
144            Self::SSH(key) => key.to_bytes().unwrap().hash(state),
145            Self::MLDSA(key) => key.as_bytes().hash(state),
146        }
147    }
148}
149
150impl Eq for SigningPrivateKey {}
151
152impl SigningPrivateKey {
153    /// Creates a new Schnorr signing private key from an `ECPrivateKey`.
154    ///
155    /// # Arguments
156    ///
157    /// * `key` - The elliptic curve private key to use
158    ///
159    /// # Returns
160    ///
161    /// A new Schnorr signing private key
162    ///
163    /// # Examples
164    ///
165    /// ```
166    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
167    ///
168    /// // Create a new EC private key
169    /// let ec_key = ECPrivateKey::new();
170    ///
171    /// // Create a Schnorr signing key from it
172    /// let signing_key = SigningPrivateKey::new_schnorr(ec_key);
173    /// ```
174    pub const fn new_schnorr(key: ECPrivateKey) -> Self {
175        Self::Schnorr(key)
176    }
177
178    /// Creates a new ECDSA signing private key from an `ECPrivateKey`.
179    ///
180    /// # Arguments
181    ///
182    /// * `key` - The elliptic curve private key to use
183    ///
184    /// # Returns
185    ///
186    /// A new ECDSA signing private key
187    ///
188    /// # Examples
189    ///
190    /// ```
191    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
192    ///
193    /// // Create a new EC private key
194    /// let ec_key = ECPrivateKey::new();
195    ///
196    /// // Create an ECDSA signing key from it
197    /// let signing_key = SigningPrivateKey::new_ecdsa(ec_key);
198    /// ```
199    pub const fn new_ecdsa(key: ECPrivateKey) -> Self {
200        Self::ECDSA(key)
201    }
202
203    /// Creates a new Ed25519 signing private key from an `Ed25519PrivateKey`.
204    ///
205    /// # Arguments
206    ///
207    /// * `key` - The Ed25519 private key to use
208    ///
209    /// # Returns
210    ///
211    /// A new Ed25519 signing private key
212    ///
213    /// # Examples
214    ///
215    /// ```
216    /// use bc_components::{Ed25519PrivateKey, SigningPrivateKey};
217    ///
218    /// // Create a new Ed25519 private key
219    /// let ed_key = Ed25519PrivateKey::new();
220    ///
221    /// // Create an Ed25519 signing key from it
222    /// let signing_key = SigningPrivateKey::new_ed25519(ed_key);
223    /// ```
224    pub const fn new_ed25519(key: Ed25519PrivateKey) -> Self {
225        Self::Ed25519(key)
226    }
227
228    /// Creates a new SSH signing private key from an `SSHPrivateKey`.
229    ///
230    /// # Arguments
231    ///
232    /// * `key` - The SSH private key to use
233    ///
234    /// # Returns
235    ///
236    /// A new SSH signing private key
237    pub fn new_ssh(key: SSHPrivateKey) -> Self {
238        Self::SSH(Box::new(key))
239    }
240
241    /// Returns the underlying Schnorr private key if this is a Schnorr key.
242    ///
243    /// # Returns
244    ///
245    /// Some reference to the EC private key if this is a Schnorr key,
246    /// or None if it's a different key type.
247    ///
248    /// # Examples
249    ///
250    /// ```
251    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
252    ///
253    /// // Create a Schnorr key
254    /// let schnorr_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
255    /// assert!(schnorr_key.to_schnorr().is_some());
256    ///
257    /// // Create an ECDSA key
258    /// let ecdsa_key = SigningPrivateKey::new_ecdsa(ECPrivateKey::new());
259    /// assert!(ecdsa_key.to_schnorr().is_none());
260    /// ```
261    pub fn to_schnorr(&self) -> Option<&ECPrivateKey> {
262        match self {
263            Self::Schnorr(key) => Some(key),
264            _ => None,
265        }
266    }
267
268    /// Checks if this is a Schnorr signing key.
269    ///
270    /// # Returns
271    ///
272    /// `true` if this is a Schnorr key, `false` otherwise
273    pub fn is_schnorr(&self) -> bool {
274        self.to_schnorr().is_some()
275    }
276
277    /// Returns the underlying ECDSA private key if this is an ECDSA key.
278    ///
279    /// # Returns
280    ///
281    /// Some reference to the EC private key if this is an ECDSA key,
282    /// or None if it's a different key type.
283    pub fn to_ecdsa(&self) -> Option<&ECPrivateKey> {
284        match self {
285            Self::ECDSA(key) => Some(key),
286            _ => None,
287        }
288    }
289
290    /// Checks if this is an ECDSA signing key.
291    ///
292    /// # Returns
293    ///
294    /// `true` if this is an ECDSA key, `false` otherwise
295    pub fn is_ecdsa(&self) -> bool {
296        self.to_ecdsa().is_some()
297    }
298
299    /// Returns the underlying SSH private key if this is an SSH key.
300    ///
301    /// # Returns
302    ///
303    /// Some reference to the SSH private key if this is an SSH key,
304    /// or None if it's a different key type.
305    pub fn to_ssh(&self) -> Option<&SSHPrivateKey> {
306        match self {
307            Self::SSH(key) => Some(key),
308            _ => None,
309        }
310    }
311
312    /// Checks if this is an SSH signing key.
313    ///
314    /// # Returns
315    ///
316    /// `true` if this is an SSH key, `false` otherwise
317    pub fn is_ssh(&self) -> bool {
318        self.to_ssh().is_some()
319    }
320
321    /// Derives the corresponding public key for this private key.
322    ///
323    /// # Returns
324    ///
325    /// A `Result` containing the public key, or an error if the public key
326    /// cannot be derived (e.g., for MLDSA keys).
327    ///
328    /// # Examples
329    ///
330    /// ```
331    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
332    ///
333    /// // Create a Schnorr signing key
334    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
335    ///
336    /// // Derive the public key
337    /// let public_key = private_key.public_key().unwrap();
338    /// ```
339    pub fn public_key(&self) -> Result<SigningPublicKey> {
340        match self {
341            Self::Schnorr(key) => Ok(SigningPublicKey::from_schnorr(key.schnorr_public_key())),
342            Self::ECDSA(key) => Ok(SigningPublicKey::from_ecdsa(key.public_key())),
343            Self::Ed25519(key) => Ok(SigningPublicKey::from_ed25519(key.public_key())),
344            Self::SSH(key) => Ok(SigningPublicKey::from_ssh(key.public_key().clone())),
345            Self::MLDSA(_) => bail!("Deriving MLDSA public key not supported"),
346        }
347    }
348}
349
350impl SigningPrivateKey {
351    /// Signs a message using ECDSA.
352    ///
353    /// This method is only valid for ECDSA keys.
354    ///
355    /// # Arguments
356    ///
357    /// * `message` - The message to sign
358    ///
359    /// # Returns
360    ///
361    /// A `Result` containing the ECDSA signature, or an error if the key is not an ECDSA key.
362    ///
363    /// # Examples
364    ///
365    /// ```
366    /// use bc_components::{ECPrivateKey, SigningPrivateKey, Signer};
367    ///
368    /// // Create an ECDSA key
369    /// let private_key = SigningPrivateKey::new_ecdsa(ECPrivateKey::new());
370    ///
371    /// // Sign a message
372    /// let message = b"Hello, world!";
373    /// let signature = private_key.sign(&message).unwrap();
374    /// ```
375    fn ecdsa_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
376        if let Some(private_key) = self.to_ecdsa() {
377            let sig = private_key.ecdsa_sign(message);
378            Ok(Signature::ecdsa_from_data(sig))
379        } else {
380            bail!("Invalid key type for ECDSA signing");
381        }
382    }
383
384    /// Signs a message using Schnorr with the provided random number generator.
385    ///
386    /// This method is only valid for Schnorr keys.
387    ///
388    /// # Arguments
389    ///
390    /// * `message` - The message to sign
391    /// * `rng` - The random number generator to use for signature creation
392    ///
393    /// # Returns
394    ///
395    /// A `Result` containing the Schnorr signature, or an error if the key is not a Schnorr key.
396    ///
397    /// # Examples
398    ///
399    /// ```
400    /// use std::{cell::RefCell, rc::Rc};
401    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
402    /// use bc_rand::SecureRandomNumberGenerator;
403    ///
404    /// // Create a Schnorr key
405    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
406    ///
407    /// // Create an RNG
408    /// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
409    ///
410    /// // Sign a message
411    /// let message = b"Hello, world!";
412    /// let signature = private_key.schnorr_sign(&message, rng).unwrap();
413    /// ```
414    pub fn schnorr_sign(
415        &self,
416        message: impl AsRef<[u8]>,
417        rng: Rc<RefCell<dyn RandomNumberGenerator>>,
418    ) -> Result<Signature> {
419        if let Some(private_key) = self.to_schnorr() {
420            let sig = private_key.schnorr_sign_using(message, &mut *rng.borrow_mut());
421            Ok(Signature::schnorr_from_data(sig))
422        } else {
423            bail!("Invalid key type for Schnorr signing");
424        }
425    }
426
427    /// Signs a message using Ed25519.
428    ///
429    /// This method is only valid for Ed25519 keys.
430    ///
431    /// # Arguments
432    ///
433    /// * `message` - The message to sign
434    ///
435    /// # Returns
436    ///
437    /// A `Result` containing the Ed25519 signature, or an error if the key is not an Ed25519 key.
438    ///
439    /// # Examples
440    ///
441    /// ```
442    /// use bc_components::{Ed25519PrivateKey, SigningPrivateKey, Signer};
443    ///
444    /// // Create an Ed25519 key
445    /// let private_key = SigningPrivateKey::new_ed25519(Ed25519PrivateKey::new());
446    ///
447    /// // Sign a message
448    /// let message = b"Hello, world!";
449    /// let signature = private_key.sign(&message).unwrap();
450    /// ```
451    pub fn ed25519_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
452        if let Self::Ed25519(key) = self {
453            let sig = key.sign(message.as_ref());
454            Ok(Signature::ed25519_from_data(sig))
455        } else {
456            bail!("Invalid key type for Ed25519 signing");
457        }
458    }
459
460    /// Signs a message using SSH.
461    ///
462    /// This method is only valid for SSH keys.
463    ///
464    /// # Arguments
465    ///
466    /// * `message` - The message to sign
467    /// * `namespace` - The SSH namespace string
468    /// * `hash_alg` - The hash algorithm to use
469    ///
470    /// # Returns
471    ///
472    /// A `Result` containing the SSH signature, or an error if the key is not an SSH key.
473    fn ssh_sign(
474        &self,
475        message: impl AsRef<[u8]>,
476        namespace: impl AsRef<str>,
477        hash_alg: HashAlg,
478    ) -> Result<Signature> {
479        if let Some(private) = self.to_ssh() {
480            let sig = private.sign(namespace.as_ref(), hash_alg, message.as_ref())?;
481            Ok(Signature::from_ssh(sig))
482        } else {
483            bail!("Invalid key type for SSH signing");
484        }
485    }
486
487    /// Signs a message using ML-DSA.
488    ///
489    /// This method is only valid for ML-DSA keys.
490    ///
491    /// # Arguments
492    ///
493    /// * `message` - The message to sign
494    ///
495    /// # Returns
496    ///
497    /// A `Result` containing the ML-DSA signature, or an error if the key is not an ML-DSA key.
498    fn mldsa_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
499        if let Self::MLDSA(key) = self {
500            let sig = key.sign(message.as_ref());
501            Ok(Signature::MLDSA(sig))
502        } else {
503            bail!("Invalid key type for MLDSA signing");
504        }
505    }
506}
507
508/// Implementation of the Signer trait for SigningPrivateKey
509impl Signer for SigningPrivateKey {
510    /// Signs a message with the appropriate algorithm based on the key type.
511    ///
512    /// This method dispatches to the appropriate signing method based on the key type
513    /// and provided options.
514    ///
515    /// # Arguments
516    ///
517    /// * `message` - The message to sign
518    /// * `options` - Optional signing options (algorithm-specific parameters)
519    ///
520    /// # Returns
521    ///
522    /// A `Result` containing the signature, or an error if signing fails
523    ///
524    /// # Examples
525    ///
526    /// ```
527    /// use std::{cell::RefCell, rc::Rc};
528    /// use bc_components::{ECPrivateKey, SigningOptions, SigningPrivateKey, Signer};
529    /// use bc_rand::SecureRandomNumberGenerator;
530    ///
531    /// // Create a Schnorr key
532    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
533    ///
534    /// // Create Schnorr signing options
535    /// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
536    /// let options = SigningOptions::Schnorr { rng };
537    ///
538    /// // Sign a message with options
539    /// let message = b"Hello, world!";
540    /// let signature = private_key.sign_with_options(&message, Some(options)).unwrap();
541    /// ```
542    fn sign_with_options(
543        &self,
544        message: &dyn AsRef<[u8]>,
545        options: Option<SigningOptions>,
546    ) -> Result<Signature> {
547        match self {
548            Self::Schnorr(_) => {
549                if let Some(SigningOptions::Schnorr { rng }) = options {
550                    self.schnorr_sign(message, rng)
551                } else {
552                    self.schnorr_sign(message, Rc::new(RefCell::new(SecureRandomNumberGenerator)))
553                }
554            }
555            Self::ECDSA(_) => self.ecdsa_sign(message),
556            Self::Ed25519(_) => self.ed25519_sign(message),
557            Self::SSH(_) => {
558                if let Some(SigningOptions::Ssh { namespace, hash_alg }) = options {
559                    self.ssh_sign(message, namespace, hash_alg)
560                } else {
561                    bail!("Missing namespace and hash algorithm for SSH signing");
562                }
563            }
564            Self::MLDSA(_) => self.mldsa_sign(message),
565        }
566    }
567}
568
569/// Implementation of the Verifier trait for SigningPrivateKey
570impl Verifier for SigningPrivateKey {
571    /// Verifies a signature against a message.
572    ///
573    /// This method is only implemented for Schnorr keys, where it derives the
574    /// public key and uses it to verify the signature. For other key types,
575    /// this method always returns `false`.
576    ///
577    /// # Arguments
578    ///
579    /// * `signature` - The signature to verify
580    /// * `message` - The message that was allegedly signed
581    ///
582    /// # Returns
583    ///
584    /// `true` if the signature is valid for the message, `false` otherwise
585    fn verify(&self, signature: &Signature, message: &dyn AsRef<[u8]>) -> bool {
586        match self {
587            Self::Schnorr(key) => {
588                if let Signature::Schnorr(sig) = signature {
589                    key.schnorr_public_key().schnorr_verify(sig, message)
590                } else {
591                    false
592                }
593            }
594            _ => false,
595        }
596    }
597}
598
599/// Implementation of the CBORTagged trait for SigningPrivateKey
600impl CBORTagged for SigningPrivateKey {
601    /// Returns the CBOR tags used for this type.
602    ///
603    /// For SigningPrivateKey, the tag is 40021.
604    fn cbor_tags() -> Vec<Tag> {
605        tags_for_values(&[tags::TAG_SIGNING_PRIVATE_KEY])
606    }
607}
608
609/// Conversion from SigningPrivateKey to CBOR
610impl From<SigningPrivateKey> for CBOR {
611    /// Converts a SigningPrivateKey to a tagged CBOR value.
612    fn from(value: SigningPrivateKey) -> Self {
613        value.tagged_cbor()
614    }
615}
616
617/// Implementation of the CBORTaggedEncodable trait for SigningPrivateKey
618impl CBORTaggedEncodable for SigningPrivateKey {
619    /// Converts the SigningPrivateKey to an untagged CBOR value.
620    ///
621    /// The CBOR encoding depends on the key type:
622    ///
623    /// - Schnorr: A byte string containing the 32-byte private key
624    /// - ECDSA: An array containing the discriminator 1 and the 32-byte private key
625    /// - Ed25519: An array containing the discriminator 2 and the 32-byte private key
626    /// - SSH: A tagged text string containing the OpenSSH-encoded private key
627    /// - ML-DSA: Delegates to the MLDSAPrivateKey implementation
628    fn untagged_cbor(&self) -> CBOR {
629        match self {
630            SigningPrivateKey::Schnorr(key) => CBOR::to_byte_string(key.data()),
631            SigningPrivateKey::ECDSA(key) => {
632                vec![(1).into(), CBOR::to_byte_string(key.data())].into()
633            }
634            SigningPrivateKey::Ed25519(key) => {
635                vec![(2).into(), CBOR::to_byte_string(key.data())].into()
636            }
637            SigningPrivateKey::SSH(key) => {
638                let string = key.to_openssh(LineEnding::LF).unwrap();
639                CBOR::to_tagged_value(tags::TAG_SSH_TEXT_PRIVATE_KEY, (*string).clone())
640            }
641            SigningPrivateKey::MLDSA(key) => key.clone().into(),
642        }
643    }
644}
645
646/// TryFrom implementation for converting CBOR to SigningPrivateKey
647impl TryFrom<CBOR> for SigningPrivateKey {
648    type Error = Error;
649
650    /// Tries to convert a CBOR value to a SigningPrivateKey.
651    ///
652    /// This is a convenience method that calls from_tagged_cbor.
653    fn try_from(cbor: CBOR) -> Result<Self, Self::Error> {
654        Self::from_tagged_cbor(cbor)
655    }
656}
657
658/// Implementation of the CBORTaggedDecodable trait for SigningPrivateKey
659impl CBORTaggedDecodable for SigningPrivateKey {
660    /// Creates a SigningPrivateKey from an untagged CBOR value.
661    ///
662    /// # Arguments
663    ///
664    /// * `untagged_cbor` - The CBOR value to decode
665    ///
666    /// # Returns
667    ///
668    /// A Result containing the decoded SigningPrivateKey or an error if decoding fails.
669    ///
670    /// # Format
671    ///
672    /// The CBOR value must be one of:
673    /// - A byte string (interpreted as a Schnorr private key)
674    /// - An array where the first element is a discriminator (1 for ECDSA, 2 for Ed25519)
675    ///   and the second element is a byte string containing the key data
676    /// - A tagged value with a tag for ML-DSA or SSH keys
677    fn from_untagged_cbor(untagged_cbor: CBOR) -> Result<Self> {
678        match untagged_cbor.into_case() {
679            CBORCase::ByteString(data) => Ok(Self::Schnorr(ECPrivateKey::from_data_ref(data)?)),
680            CBORCase::Array(mut elements) => {
681                let discriminator = usize::try_from(elements.remove(0))?;
682                match discriminator {
683                    1 => {
684                        let data = elements.remove(0).try_into_byte_string()?;
685                        let key = ECPrivateKey::from_data_ref(data)?;
686                        Ok(Self::ECDSA(key))
687                    }
688                    2 => {
689                        let data = elements.remove(0).try_into_byte_string()?;
690                        let key = Ed25519PrivateKey::from_data_ref(data)?;
691                        Ok(Self::Ed25519(key))
692                    }
693                    _ => bail!(
694                        "Invalid discriminator for SigningPrivateKey: {}",
695                        discriminator
696                    ),
697                }
698            }
699            CBORCase::Tagged(tag, item) => {
700                let value = tag.value();
701                match value {
702                    tags::TAG_SSH_TEXT_PRIVATE_KEY => {
703                        let string = item.try_into_text()?;
704                        let key = SSHPrivateKey::from_openssh(string)?;
705                        Ok(Self::SSH(Box::new(key)))
706                    }
707                    tags::TAG_MLDSA_PRIVATE_KEY => {
708                        let key = MLDSAPrivateKey::from_untagged_cbor(item)?;
709                        Ok(Self::MLDSA(key))
710                    }
711                    _ => bail!("Invalid CBOR tag for SigningPrivateKey: {}", value),
712                }
713            }
714            _ => {
715                bail!("Invalid CBOR case for SigningPrivateKey");
716            }
717        }
718    }
719}
720
721/// Debug implementation for SigningPrivateKey
722impl std::fmt::Debug for SigningPrivateKey {
723    /// Formats the SigningPrivateKey for display.
724    ///
725    /// For security reasons, the private key data is not displayed.
726    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
727        write!(f, "SigningPrivateKey")
728    }
729}
730
731/// Implementation of the From trait for reference to SigningPrivateKey
732impl From<&SigningPrivateKey> for SigningPrivateKey {
733    /// Clones a SigningPrivateKey from a reference.
734    fn from(key: &SigningPrivateKey) -> Self {
735        key.clone()
736    }
737}