bc_components/signing/
signing_private_key.rs

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