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