bc_components/signing/
signing_private_key.rs

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