bc_components/signing/
signing_private_key.rs

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