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}