bc_components/signing/
signing_private_key.rs

1use std::{cell::RefCell, rc::Rc};
2
3use bc_rand::RandomNumberGenerator;
4#[cfg(feature = "secp256k1")]
5use bc_rand::SecureRandomNumberGenerator;
6use bc_ur::prelude::*;
7#[cfg(feature = "ssh")]
8use ssh_key::{HashAlg, LineEnding, private::PrivateKey as SSHPrivateKey};
9
10use super::Verifier;
11#[cfg(feature = "ed25519")]
12use crate::Ed25519PrivateKey;
13#[cfg(any(feature = "secp256k1", feature = "ssh", feature = "pqcrypto"))]
14use crate::Error;
15#[cfg(feature = "pqcrypto")]
16use crate::MLDSAPrivateKey;
17use crate::{
18    Digest, Reference, ReferenceProvider, Result, Signature, Signer,
19    SigningPublicKey, tags,
20};
21#[cfg(feature = "secp256k1")]
22use crate::{ECKey, ECPrivateKey};
23
24/// Options for configuring signature creation.
25///
26/// Different signature schemes may require specific options:
27///
28/// - `Schnorr`: Requires a random number generator for signature creation
29/// - `Ssh`: Requires a namespace and hash algorithm
30///
31/// Other signature types like ECDSA, Ed25519, and ML-DSA don't require options.
32///
33/// # Examples
34///
35/// Creating Schnorr signing options:
36///
37/// ```
38/// use std::{cell::RefCell, rc::Rc};
39///
40/// use bc_components::SigningOptions;
41/// use bc_rand::SecureRandomNumberGenerator;
42///
43/// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
44/// let options = SigningOptions::Schnorr { rng };
45/// ```
46///
47/// Creating SSH signing options:
48///
49/// ```ignore
50/// use bc_components::SigningOptions;
51/// use ssh_key::HashAlg;
52///
53/// let options = SigningOptions::Ssh {
54///     namespace: "ssh".to_string(),
55///     hash_alg: HashAlg::Sha512,
56/// };
57/// ```
58#[derive(Clone)]
59pub enum SigningOptions {
60    /// Options for Schnorr signatures
61    Schnorr {
62        /// Non-default random number generator used for signature creation
63        rng: Rc<RefCell<dyn RandomNumberGenerator>>,
64    },
65
66    /// Options for SSH signatures
67    #[cfg(feature = "ssh")]
68    Ssh {
69        /// The namespace used for SSH signatures
70        namespace: String,
71
72        /// The hash algorithm used for SSH signatures
73        hash_alg: HashAlg,
74    },
75}
76
77/// A private key used for creating digital signatures.
78///
79/// `SigningPrivateKey` is an enum representing different types of signing
80/// private keys, including elliptic curve schemes (ECDSA, Schnorr), Edwards
81/// curve schemes (Ed25519), post-quantum schemes (ML-DSA), and SSH keys.
82///
83/// This type implements the `Signer` trait, allowing it to create signatures of
84/// the appropriate type.
85///
86/// # Examples
87///
88/// Creating a new Schnorr signing key and using it to sign a message:
89///
90/// ```ignore
91/// # // Requires secp256k1 feature (enabled by default)
92/// use bc_components::{ECPrivateKey, Signer, SigningPrivateKey, Verifier};
93///
94/// // Create a new Schnorr signing key
95/// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
96///
97/// // Get the corresponding public key
98/// let public_key = private_key.public_key().unwrap();
99///
100/// // Sign a message
101/// let message = b"Hello, world!";
102/// let signature = private_key.sign(&message).unwrap();
103///
104/// // Verify the signature
105/// assert!(public_key.verify(&signature, &message));
106/// ```
107///
108/// # CBOR Serialization
109///
110/// `SigningPrivateKey` can be serialized to and from CBOR with appropriate
111/// tags:
112///
113/// ```ignore
114/// # // Requires secp256k1 feature (enabled by default)
115/// use bc_components::{ECPrivateKey, SigningPrivateKey};
116/// use dcbor::prelude::*;
117///
118/// // Create a key
119/// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
120///
121/// // Convert to CBOR
122/// let cbor: CBOR = private_key.clone().into();
123/// let data = cbor.to_cbor_data();
124///
125/// // Convert back from CBOR
126/// let recovered = SigningPrivateKey::from_tagged_cbor_data(&data).unwrap();
127///
128/// // The keys should be equal
129/// assert_eq!(private_key, recovered);
130/// ```
131#[derive(Clone, PartialEq)]
132pub enum SigningPrivateKey {
133    /// A Schnorr private key based on the secp256k1 curve
134    #[cfg(feature = "secp256k1")]
135    Schnorr(ECPrivateKey),
136
137    /// An ECDSA private key based on the secp256k1 curve
138    #[cfg(feature = "secp256k1")]
139    ECDSA(ECPrivateKey),
140
141    /// An Ed25519 private key
142    #[cfg(feature = "ed25519")]
143    Ed25519(Ed25519PrivateKey),
144
145    /// An SSH private key
146    #[cfg(feature = "ssh")]
147    SSH(Box<SSHPrivateKey>),
148
149    /// A post-quantum ML-DSA private key
150    #[cfg(feature = "pqcrypto")]
151    MLDSA(MLDSAPrivateKey),
152}
153
154/// Implementation of hashing for SigningPrivateKey
155impl std::hash::Hash for SigningPrivateKey {
156    /// Hashes the key's data.
157    ///
158    /// This is used for collections that require hash support.
159    #[allow(unreachable_patterns)]
160    fn hash<H: std::hash::Hasher>(&self, _state: &mut H) {
161        match self {
162            #[cfg(feature = "secp256k1")]
163            Self::Schnorr(key) => key.hash(_state),
164            #[cfg(feature = "secp256k1")]
165            Self::ECDSA(key) => key.hash(_state),
166            #[cfg(feature = "ed25519")]
167            Self::Ed25519(key) => key.hash(_state),
168            #[cfg(feature = "ssh")]
169            Self::SSH(key) => key.to_bytes().unwrap().hash(_state),
170            #[cfg(feature = "pqcrypto")]
171            Self::MLDSA(key) => key.as_bytes().hash(_state),
172            #[cfg(not(any(
173                feature = "secp256k1",
174                feature = "ed25519",
175                feature = "ssh",
176                feature = "pqcrypto"
177            )))]
178            _ => unreachable!(),
179        }
180    }
181}
182
183impl Eq for SigningPrivateKey {}
184
185impl SigningPrivateKey {
186    /// Creates a new Schnorr signing private key from an `ECPrivateKey`.
187    ///
188    /// # Arguments
189    ///
190    /// * `key` - The elliptic curve private key to use
191    ///
192    /// # Returns
193    ///
194    /// A new Schnorr signing private key
195    ///
196    /// # Examples
197    ///
198    /// ```
199    /// # #[cfg(feature = "secp256k1")]
200    /// # {
201    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
202    ///
203    /// // Create a new EC private key
204    /// let ec_key = ECPrivateKey::new();
205    ///
206    /// // Create a Schnorr signing key from it
207    /// let signing_key = SigningPrivateKey::new_schnorr(ec_key);
208    /// # }
209    /// ```
210    #[cfg(feature = "secp256k1")]
211    pub const fn new_schnorr(key: ECPrivateKey) -> Self { Self::Schnorr(key) }
212
213    /// Creates a new ECDSA signing private key from an `ECPrivateKey`.
214    ///
215    /// # Arguments
216    ///
217    /// * `key` - The elliptic curve private key to use
218    ///
219    /// # Returns
220    ///
221    /// A new ECDSA signing private key
222    ///
223    /// # Examples
224    ///
225    /// ```
226    /// # #[cfg(feature = "secp256k1")]
227    /// # {
228    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
229    ///
230    /// // Create a new EC private key
231    /// let ec_key = ECPrivateKey::new();
232    ///
233    /// // Create an ECDSA signing key from it
234    /// let signing_key = SigningPrivateKey::new_ecdsa(ec_key);
235    /// # }
236    /// ```
237    #[cfg(feature = "secp256k1")]
238    pub const fn new_ecdsa(key: ECPrivateKey) -> Self { Self::ECDSA(key) }
239
240    /// Creates a new Ed25519 signing private key from an `Ed25519PrivateKey`.
241    ///
242    /// # Arguments
243    ///
244    /// * `key` - The Ed25519 private key to use
245    ///
246    /// # Returns
247    ///
248    /// A new Ed25519 signing private key
249    ///
250    /// # Examples
251    ///
252    /// ```
253    /// # #[cfg(feature = "ed25519")]
254    /// # {
255    /// use bc_components::{Ed25519PrivateKey, SigningPrivateKey};
256    ///
257    /// // Create a new Ed25519 private key
258    /// let ed_key = Ed25519PrivateKey::new();
259    ///
260    /// // Create an Ed25519 signing key from it
261    /// let signing_key = SigningPrivateKey::new_ed25519(ed_key);
262    /// # }
263    /// ```
264    #[cfg(feature = "ed25519")]
265    pub const fn new_ed25519(key: Ed25519PrivateKey) -> Self {
266        Self::Ed25519(key)
267    }
268
269    /// Creates a new SSH signing private key from an `SSHPrivateKey`.
270    ///
271    /// # Arguments
272    ///
273    /// * `key` - The SSH private key to use
274    ///
275    /// # Returns
276    ///
277    /// A new SSH signing private key
278    #[cfg(feature = "ssh")]
279    pub fn new_ssh(key: SSHPrivateKey) -> Self { Self::SSH(Box::new(key)) }
280
281    /// Returns the underlying Schnorr private key if this is a Schnorr key.
282    ///
283    /// # Returns
284    ///
285    /// Some reference to the EC private key if this is a Schnorr key,
286    /// or None if it's a different key type.
287    ///
288    /// # Examples
289    ///
290    /// ```
291    /// # #[cfg(feature = "secp256k1")]
292    /// # {
293    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
294    ///
295    /// // Create a Schnorr key
296    /// let schnorr_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
297    /// assert!(schnorr_key.to_schnorr().is_some());
298    ///
299    /// // Create an ECDSA key
300    /// let ecdsa_key = SigningPrivateKey::new_ecdsa(ECPrivateKey::new());
301    /// assert!(ecdsa_key.to_schnorr().is_none());
302    /// # }
303    /// ```
304    #[cfg(feature = "secp256k1")]
305    pub fn to_schnorr(&self) -> Option<&ECPrivateKey> {
306        match self {
307            Self::Schnorr(key) => Some(key),
308            _ => None,
309        }
310    }
311
312    /// Checks if this is a Schnorr signing key.
313    ///
314    /// # Returns
315    ///
316    /// `true` if this is a Schnorr key, `false` otherwise
317    #[cfg(feature = "secp256k1")]
318    pub fn is_schnorr(&self) -> bool { self.to_schnorr().is_some() }
319
320    /// Returns the underlying ECDSA private key if this is an ECDSA key.
321    ///
322    /// # Returns
323    ///
324    /// Some reference to the EC private key if this is an ECDSA key,
325    /// or None if it's a different key type.
326    #[cfg(feature = "secp256k1")]
327    pub fn to_ecdsa(&self) -> Option<&ECPrivateKey> {
328        match self {
329            Self::ECDSA(key) => Some(key),
330            _ => None,
331        }
332    }
333
334    /// Checks if this is an ECDSA signing key.
335    ///
336    /// # Returns
337    ///
338    /// `true` if this is an ECDSA key, `false` otherwise
339    #[cfg(feature = "secp256k1")]
340    pub fn is_ecdsa(&self) -> bool { self.to_ecdsa().is_some() }
341
342    /// Returns the underlying SSH private key if this is an SSH key.
343    ///
344    /// # Returns
345    ///
346    /// Some reference to the SSH private key if this is an SSH key,
347    /// or None if it's a different key type.
348    #[cfg(feature = "ssh")]
349    pub fn to_ssh(&self) -> Option<&SSHPrivateKey> {
350        match self {
351            Self::SSH(key) => Some(key),
352            #[cfg(any(
353                feature = "secp256k1",
354                feature = "ed25519",
355                feature = "pqcrypto"
356            ))]
357            _ => None,
358        }
359    }
360
361    /// Checks if this is an SSH signing key.
362    ///
363    /// # Returns
364    ///
365    /// `true` if this is an SSH key, `false` otherwise
366    #[cfg(feature = "ssh")]
367    pub fn is_ssh(&self) -> bool { self.to_ssh().is_some() }
368
369    /// Derives the corresponding public key for this private key.
370    ///
371    /// # Returns
372    ///
373    /// A `Result` containing the public key, or an error if the public key
374    /// cannot be derived (e.g., for MLDSA keys).
375    ///
376    /// # Examples
377    ///
378    /// ```
379    /// # #[cfg(feature = "secp256k1")]
380    /// # {
381    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
382    ///
383    /// // Create a Schnorr signing key
384    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
385    ///
386    /// // Derive the public key
387    /// let public_key = private_key.public_key().unwrap();
388    /// # }
389    /// ```
390    #[allow(unreachable_patterns)]
391    pub fn public_key(&self) -> Result<SigningPublicKey> {
392        match self {
393            #[cfg(feature = "secp256k1")]
394            Self::Schnorr(key) => {
395                Ok(SigningPublicKey::from_schnorr(key.schnorr_public_key()))
396            }
397            #[cfg(feature = "secp256k1")]
398            Self::ECDSA(key) => {
399                Ok(SigningPublicKey::from_ecdsa(key.public_key()))
400            }
401            #[cfg(feature = "ed25519")]
402            Self::Ed25519(key) => {
403                Ok(SigningPublicKey::from_ed25519(key.public_key()))
404            }
405            #[cfg(feature = "ssh")]
406            Self::SSH(key) => {
407                Ok(SigningPublicKey::from_ssh(key.public_key().clone()))
408            }
409            #[cfg(feature = "pqcrypto")]
410            Self::MLDSA(_) => {
411                Err(Error::general("Deriving ML-DSA public key not supported"))
412            }
413            #[cfg(not(any(
414                feature = "secp256k1",
415                feature = "ed25519",
416                feature = "ssh",
417                feature = "pqcrypto"
418            )))]
419            _ => unreachable!(),
420        }
421    }
422}
423
424impl SigningPrivateKey {
425    /// Signs a message using ECDSA.
426    ///
427    /// This method is only valid for ECDSA keys.
428    ///
429    /// # Arguments
430    ///
431    /// * `message` - The message to sign
432    ///
433    /// # Returns
434    ///
435    /// A `Result` containing the ECDSA signature, or an error if the key is not
436    /// an ECDSA key.
437    ///
438    /// # Examples
439    ///
440    /// ```
441    /// # #[cfg(feature = "secp256k1")]
442    /// # {
443    /// use bc_components::{ECPrivateKey, Signer, SigningPrivateKey};
444    ///
445    /// // Create an ECDSA key
446    /// let private_key = SigningPrivateKey::new_ecdsa(ECPrivateKey::new());
447    ///
448    /// // Sign a message
449    /// let message = b"Hello, world!";
450    /// let signature = private_key.sign(&message).unwrap();
451    /// # }
452    /// ```
453    #[cfg(feature = "secp256k1")]
454    fn ecdsa_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
455        if let Some(private_key) = self.to_ecdsa() {
456            let sig = private_key.ecdsa_sign(message);
457            Ok(Signature::ecdsa_from_data(sig))
458        } else {
459            Err(Error::crypto("Invalid key type for ECDSA signing"))
460        }
461    }
462
463    /// Signs a message using Schnorr with the provided random number generator.
464    ///
465    /// This method is only valid for Schnorr keys.
466    ///
467    /// # Arguments
468    ///
469    /// * `message` - The message to sign
470    /// * `rng` - The random number generator to use for signature creation
471    ///
472    /// # Returns
473    ///
474    /// A `Result` containing the Schnorr signature, or an error if the key is
475    /// not a Schnorr key.
476    ///
477    /// # Examples
478    ///
479    /// ```
480    /// # #[cfg(feature = "secp256k1")]
481    /// # {
482    /// use std::{cell::RefCell, rc::Rc};
483    ///
484    /// use bc_components::{ECPrivateKey, SigningPrivateKey};
485    /// use bc_rand::SecureRandomNumberGenerator;
486    ///
487    /// // Create a Schnorr key
488    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
489    ///
490    /// // Create an RNG
491    /// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
492    ///
493    /// // Sign a message
494    /// let message = b"Hello, world!";
495    /// let signature = private_key.schnorr_sign(&message, rng).unwrap();
496    /// # }
497    /// ```
498    #[cfg(feature = "secp256k1")]
499    pub fn schnorr_sign(
500        &self,
501        message: impl AsRef<[u8]>,
502        rng: Rc<RefCell<dyn RandomNumberGenerator>>,
503    ) -> Result<Signature> {
504        if let Some(private_key) = self.to_schnorr() {
505            let sig =
506                private_key.schnorr_sign_using(message, &mut *rng.borrow_mut());
507            Ok(Signature::schnorr_from_data(sig))
508        } else {
509            Err(Error::crypto("Invalid key type for Schnorr signing"))
510        }
511    }
512
513    /// Signs a message using Ed25519.
514    ///
515    /// This method is only valid for Ed25519 keys.
516    ///
517    /// # Arguments
518    ///
519    /// * `message` - The message to sign
520    ///
521    /// # Returns
522    ///
523    /// A `Result` containing the Ed25519 signature, or an error if the key is
524    /// not an Ed25519 key.
525    ///
526    /// # Examples
527    ///
528    /// ```
529    /// # #[cfg(feature = "ed25519")]
530    /// # {
531    /// use bc_components::{Ed25519PrivateKey, Signer, SigningPrivateKey};
532    ///
533    /// // Create an Ed25519 key
534    /// let private_key = SigningPrivateKey::new_ed25519(Ed25519PrivateKey::new());
535    ///
536    /// // Sign a message
537    /// let message = b"Hello, world!";
538    /// let signature = private_key.sign(&message).unwrap();
539    /// # }
540    /// ```
541    #[cfg(feature = "ed25519")]
542    pub fn ed25519_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
543        #[cfg(any(
544            feature = "secp256k1",
545            feature = "ssh",
546            feature = "pqcrypto"
547        ))]
548        if let Self::Ed25519(key) = self {
549            let sig = key.sign(message.as_ref());
550            Ok(Signature::ed25519_from_data(sig))
551        } else {
552            Err(Error::crypto("Invalid key type for Ed25519 signing"))
553        }
554        #[cfg(not(any(
555            feature = "secp256k1",
556            feature = "ssh",
557            feature = "pqcrypto"
558        )))]
559        {
560            let Self::Ed25519(key) = self;
561            let sig = key.sign(message.as_ref());
562            Ok(Signature::ed25519_from_data(sig))
563        }
564    }
565
566    /// Signs a message using SSH.
567    ///
568    /// This method is only valid for SSH keys.
569    ///
570    /// # Arguments
571    ///
572    /// * `message` - The message to sign
573    /// * `namespace` - The SSH namespace string
574    /// * `hash_alg` - The hash algorithm to use
575    ///
576    /// # Returns
577    ///
578    /// A `Result` containing the SSH signature, or an error if the key is not
579    /// an SSH key.
580    #[cfg(feature = "ssh")]
581    fn ssh_sign(
582        &self,
583        message: impl AsRef<[u8]>,
584        namespace: impl AsRef<str>,
585        hash_alg: HashAlg,
586    ) -> Result<Signature> {
587        if let Some(private) = self.to_ssh() {
588            let sig =
589                private.sign(namespace.as_ref(), hash_alg, message.as_ref())?;
590            Ok(Signature::from_ssh(sig))
591        } else {
592            Err(Error::ssh("Invalid key type for SSH signing"))
593        }
594    }
595
596    /// Signs a message using ML-DSA.
597    ///
598    /// This method is only valid for ML-DSA keys.
599    ///
600    /// # Arguments
601    ///
602    /// * `message` - The message to sign
603    ///
604    /// # Returns
605    ///
606    /// A `Result` containing the ML-DSA signature, or an error if the key is
607    /// not an ML-DSA key.
608    #[cfg(feature = "pqcrypto")]
609    #[allow(irrefutable_let_patterns)]
610    fn mldsa_sign(&self, message: impl AsRef<[u8]>) -> Result<Signature> {
611        if let Self::MLDSA(key) = self {
612            let sig = key.sign(message.as_ref());
613            Ok(Signature::MLDSA(sig))
614        } else {
615            Err(Error::post_quantum("Invalid key type for MLDSA signing"))
616        }
617    }
618}
619
620/// Implementation of the Signer trait for SigningPrivateKey
621impl Signer for SigningPrivateKey {
622    /// Signs a message with the appropriate algorithm based on the key type.
623    ///
624    /// This method dispatches to the appropriate signing method based on the
625    /// key type and provided options.
626    ///
627    /// # Arguments
628    ///
629    /// * `message` - The message to sign
630    /// * `options` - Optional signing options (algorithm-specific parameters)
631    ///
632    /// # Returns
633    ///
634    /// A `Result` containing the signature, or an error if signing fails
635    ///
636    /// # Examples
637    ///
638    /// ```ignore
639    /// # // Requires secp256k1 feature (enabled by default)
640    /// use std::{cell::RefCell, rc::Rc};
641    ///
642    /// use bc_components::{
643    ///     ECPrivateKey, Signer, SigningOptions, SigningPrivateKey,
644    /// };
645    /// use bc_rand::SecureRandomNumberGenerator;
646    ///
647    /// // Create a Schnorr key
648    /// let private_key = SigningPrivateKey::new_schnorr(ECPrivateKey::new());
649    ///
650    /// // Create Schnorr signing options
651    /// let rng = Rc::new(RefCell::new(SecureRandomNumberGenerator));
652    /// let options = SigningOptions::Schnorr { rng };
653    ///
654    /// // Sign a message with options
655    /// let message = b"Hello, world!";
656    /// let signature = private_key
657    ///     .sign_with_options(&message, Some(options))
658    ///     .unwrap();
659    /// ```
660    #[allow(unreachable_patterns)]
661    fn sign_with_options(
662        &self,
663        _message: &dyn AsRef<[u8]>,
664        #[cfg_attr(
665            not(any(feature = "secp256k1", feature = "ssh")),
666            allow(unused_variables)
667        )]
668        options: Option<SigningOptions>,
669    ) -> Result<Signature> {
670        match self {
671            #[cfg(feature = "secp256k1")]
672            Self::Schnorr(_) => {
673                if let Some(SigningOptions::Schnorr { rng }) = options {
674                    self.schnorr_sign(_message, rng)
675                } else {
676                    self.schnorr_sign(
677                        _message,
678                        Rc::new(RefCell::new(SecureRandomNumberGenerator)),
679                    )
680                }
681            }
682            #[cfg(feature = "secp256k1")]
683            Self::ECDSA(_) => self.ecdsa_sign(_message),
684            #[cfg(feature = "ed25519")]
685            Self::Ed25519(_) => self.ed25519_sign(_message),
686            #[cfg(feature = "ssh")]
687            Self::SSH(_) => {
688                if let Some(SigningOptions::Ssh { namespace, hash_alg }) =
689                    options
690                {
691                    self.ssh_sign(_message, namespace, hash_alg)
692                } else {
693                    Err(Error::ssh(
694                        "Missing namespace and hash algorithm for SSH signing",
695                    ))
696                }
697            }
698            #[cfg(feature = "pqcrypto")]
699            Self::MLDSA(_) => self.mldsa_sign(_message),
700            #[cfg(not(any(
701                feature = "secp256k1",
702                feature = "ed25519",
703                feature = "ssh",
704                feature = "pqcrypto"
705            )))]
706            _ => unreachable!(),
707        }
708    }
709}
710
711/// Implementation of the Verifier trait for SigningPrivateKey
712impl Verifier for SigningPrivateKey {
713    /// Verifies a signature against a message.
714    ///
715    /// This method is only implemented for Schnorr keys, where it derives the
716    /// public key and uses it to verify the signature. For other key types,
717    /// this method always returns `false`.
718    ///
719    /// # Arguments
720    ///
721    /// * `signature` - The signature to verify
722    /// * `message` - The message that was allegedly signed
723    ///
724    /// # Returns
725    ///
726    /// `true` if the signature is valid for the message, `false` otherwise
727    fn verify(
728        &self,
729        _signature: &Signature,
730        _message: &dyn AsRef<[u8]>,
731    ) -> bool {
732        match self {
733            #[cfg(feature = "secp256k1")]
734            Self::Schnorr(key) => {
735                if let Signature::Schnorr(sig) = _signature {
736                    key.schnorr_public_key().schnorr_verify(sig, _message)
737                } else {
738                    false
739                }
740            }
741            _ => false,
742        }
743    }
744}
745
746/// Implementation of the CBORTagged trait for SigningPrivateKey
747impl CBORTagged for SigningPrivateKey {
748    /// Returns the CBOR tags used for this type.
749    ///
750    /// For SigningPrivateKey, the tag is 40021.
751    fn cbor_tags() -> Vec<Tag> {
752        tags_for_values(&[tags::TAG_SIGNING_PRIVATE_KEY])
753    }
754}
755
756/// Conversion from SigningPrivateKey to CBOR
757impl From<SigningPrivateKey> for CBOR {
758    /// Converts a SigningPrivateKey to a tagged CBOR value.
759    fn from(value: SigningPrivateKey) -> Self { value.tagged_cbor() }
760}
761
762/// Implementation of the CBORTaggedEncodable trait for SigningPrivateKey
763impl CBORTaggedEncodable for SigningPrivateKey {
764    /// Converts the SigningPrivateKey to an untagged CBOR value.
765    ///
766    /// The CBOR encoding depends on the key type:
767    ///
768    /// - Schnorr: A byte string containing the 32-byte private key
769    /// - ECDSA: An array containing the discriminator 1 and the 32-byte private
770    ///   key
771    /// - Ed25519: An array containing the discriminator 2 and the 32-byte
772    ///   private key
773    /// - SSH: A tagged text string containing the OpenSSH-encoded private key
774    /// - ML-DSA: Delegates to the MLDSAPrivateKey implementation
775    fn untagged_cbor(&self) -> CBOR {
776        match self {
777            #[cfg(feature = "secp256k1")]
778            SigningPrivateKey::Schnorr(key) => CBOR::to_byte_string(key.data()),
779            #[cfg(feature = "secp256k1")]
780            SigningPrivateKey::ECDSA(key) => {
781                vec![(1).into(), CBOR::to_byte_string(key.data())].into()
782            }
783            #[cfg(feature = "ed25519")]
784            SigningPrivateKey::Ed25519(key) => {
785                vec![(2).into(), CBOR::to_byte_string(key.data())].into()
786            }
787            #[cfg(feature = "ssh")]
788            SigningPrivateKey::SSH(key) => {
789                let string = key.to_openssh(LineEnding::LF).unwrap();
790                CBOR::to_tagged_value(
791                    tags::TAG_SSH_TEXT_PRIVATE_KEY,
792                    (*string).clone(),
793                )
794            }
795            #[cfg(feature = "pqcrypto")]
796            SigningPrivateKey::MLDSA(key) => key.clone().into(),
797            #[cfg(not(any(
798                feature = "secp256k1",
799                feature = "ed25519",
800                feature = "ssh",
801                feature = "pqcrypto"
802            )))]
803            _ => unreachable!(),
804        }
805    }
806}
807
808/// TryFrom implementation for converting CBOR to SigningPrivateKey
809impl TryFrom<CBOR> for SigningPrivateKey {
810    type Error = dcbor::Error;
811
812    /// Tries to convert a CBOR value to a SigningPrivateKey.
813    ///
814    /// This is a convenience method that calls from_tagged_cbor.
815    fn try_from(cbor: CBOR) -> dcbor::Result<Self> {
816        Self::from_tagged_cbor(cbor)
817    }
818}
819
820/// Implementation of the CBORTaggedDecodable trait for SigningPrivateKey
821impl CBORTaggedDecodable for SigningPrivateKey {
822    /// Creates a SigningPrivateKey from an untagged CBOR value.
823    ///
824    /// # Arguments
825    ///
826    /// * `untagged_cbor` - The CBOR value to decode
827    ///
828    /// # Returns
829    ///
830    /// A Result containing the decoded SigningPrivateKey or an error if
831    /// decoding fails.
832    ///
833    /// # Format
834    ///
835    /// The CBOR value must be one of:
836    /// - A byte string (interpreted as a Schnorr private key)
837    /// - An array where the first element is a discriminator (1 for ECDSA, 2
838    ///   for Ed25519) and the second element is a byte string containing the
839    ///   key data
840    /// - A tagged value with a tag for ML-DSA or SSH keys
841    fn from_untagged_cbor(untagged_cbor: CBOR) -> dcbor::Result<Self> {
842        match untagged_cbor.into_case() {
843            CBORCase::ByteString(data) => {
844                #[cfg(feature = "secp256k1")]
845                {
846                    Ok(Self::Schnorr(ECPrivateKey::from_data_ref(data)?))
847                }
848                #[cfg(not(feature = "secp256k1"))]
849                {
850                    let _ = data;
851                    Err("Schnorr private key not available without secp256k1 feature".into())
852                }
853            }
854            CBORCase::Array(mut elements) => {
855                let discriminator = usize::try_from(elements.remove(0))?;
856                match discriminator {
857                    #[cfg(feature = "secp256k1")]
858                    1 => {
859                        let data = elements.remove(0).try_into_byte_string()?;
860                        let key = ECPrivateKey::from_data_ref(data)?;
861                        Ok(Self::ECDSA(key))
862                    }
863                    #[cfg(feature = "ed25519")]
864                    2 => {
865                        let data = elements.remove(0).try_into_byte_string()?;
866                        let key = Ed25519PrivateKey::from_data_ref(data)?;
867                        Ok(Self::Ed25519(key))
868                    }
869                    _ => Err(format!(
870                        "Invalid discriminator for SigningPrivateKey: {}",
871                        discriminator
872                    )
873                    .into()),
874                }
875            }
876            #[cfg_attr(
877                not(any(feature = "ssh", feature = "pqcrypto")),
878                allow(unused_variables)
879            )]
880            CBORCase::Tagged(tag, item) => {
881                let value = tag.value();
882                match value {
883                    #[cfg(feature = "ssh")]
884                    tags::TAG_SSH_TEXT_PRIVATE_KEY => {
885                        let string = item.try_into_text()?;
886                        let key = SSHPrivateKey::from_openssh(string).map_err(
887                            |_| dcbor::Error::msg("Invalid SSH private key"),
888                        )?;
889                        Ok(Self::SSH(Box::new(key)))
890                    }
891                    #[cfg(feature = "pqcrypto")]
892                    tags::TAG_MLDSA_PRIVATE_KEY => {
893                        let key = MLDSAPrivateKey::from_untagged_cbor(item)?;
894                        Ok(Self::MLDSA(key))
895                    }
896                    _ => Err(format!(
897                        "Invalid CBOR tag for SigningPrivateKey: {value}"
898                    )
899                    .into()),
900                }
901            }
902            _ => Err("Invalid CBOR case for SigningPrivateKey".into()),
903        }
904    }
905}
906
907/// Debug implementation for SigningPrivateKey
908impl std::fmt::Debug for SigningPrivateKey {
909    /// Formats the SigningPrivateKey for display.
910    ///
911    /// For security reasons, the private key data is not displayed.
912    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
913        write!(f, "SigningPrivateKey")
914    }
915}
916
917/// Implementation of the From trait for reference to SigningPrivateKey
918impl From<&SigningPrivateKey> for SigningPrivateKey {
919    /// Clones a SigningPrivateKey from a reference.
920    fn from(key: &SigningPrivateKey) -> Self { key.clone() }
921}
922
923#[cfg(feature = "ssh")]
924impl ReferenceProvider for SSHPrivateKey {
925    fn reference(&self) -> Reference {
926        let string = self.to_openssh(LineEnding::default()).unwrap();
927        let bytes = string.as_bytes();
928        let digest = Digest::from_image(bytes);
929        Reference::from_digest(digest)
930    }
931}
932
933impl ReferenceProvider for SigningPrivateKey {
934    fn reference(&self) -> Reference {
935        Reference::from_digest(Digest::from_image(
936            self.tagged_cbor().to_cbor_data(),
937        ))
938    }
939}
940
941impl std::fmt::Display for SigningPrivateKey {
942    #[allow(unreachable_patterns)]
943    fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
944        #[cfg(any(
945            feature = "secp256k1",
946            feature = "ed25519",
947            feature = "ssh",
948            feature = "pqcrypto"
949        ))]
950        {
951            let display_key = match self {
952                #[cfg(feature = "secp256k1")]
953                SigningPrivateKey::Schnorr(key) => {
954                    format!("SchnorrPrivateKey({})", key.ref_hex_short())
955                }
956                #[cfg(feature = "secp256k1")]
957                SigningPrivateKey::ECDSA(key) => {
958                    format!("ECDSAPrivateKey({})", key.ref_hex_short())
959                }
960                #[cfg(feature = "ed25519")]
961                SigningPrivateKey::Ed25519(key) => key.to_string(),
962                #[cfg(feature = "ssh")]
963                SigningPrivateKey::SSH(key) => {
964                    format!("SSHPrivateKey({})", key.ref_hex_short())
965                }
966                #[cfg(feature = "pqcrypto")]
967                SigningPrivateKey::MLDSA(key) => key.to_string(),
968                _ => unreachable!(),
969            };
970            write!(
971                _f,
972                "SigningPrivateKey({}, {})",
973                self.ref_hex_short(),
974                display_key
975            )
976        }
977        #[cfg(not(any(
978            feature = "secp256k1",
979            feature = "ed25519",
980            feature = "ssh",
981            feature = "pqcrypto"
982        )))]
983        {
984            match *self {}
985        }
986    }
987}