bc_components/signing/
signature_scheme.rs

1use bc_rand::RandomNumberGenerator;
2use ssh_key::Algorithm;
3
4use super::{SigningPrivateKey, SigningPublicKey};
5use crate::{ECPrivateKey, Ed25519PrivateKey, Error, PrivateKeyBase, Result};
6
7/// Supported digital signature schemes.
8///
9/// This enum represents the various signature schemes supported in this crate,
10/// including elliptic curve schemes (ECDSA, Schnorr), Edwards curve schemes
11/// (Ed25519), post-quantum schemes (ML-DSA), and SSH-specific algorithms.
12///
13/// # Examples
14///
15/// ```
16/// use bc_components::SignatureScheme;
17///
18/// // Use the default signature scheme (Schnorr)
19/// let scheme = SignatureScheme::default();
20/// let (private_key, public_key) = scheme.keypair();
21///
22/// // Create a key pair using a specific signature scheme
23/// let (mldsa_private, mldsa_public) = SignatureScheme::MLDSA65.keypair();
24/// ```
25#[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
26pub enum SignatureScheme {
27    /// BIP-340 Schnorr signature scheme, used in Bitcoin Taproot (default)
28    #[default]
29    Schnorr,
30
31    /// ECDSA signature scheme using the secp256k1 curve
32    Ecdsa,
33
34    /// Ed25519 signature scheme (RFC 8032)
35    Ed25519,
36
37    /// ML-DSA44 post-quantum signature scheme (NIST level 2)
38    MLDSA44,
39
40    /// ML-DSA65 post-quantum signature scheme (NIST level 3)
41    MLDSA65,
42
43    /// ML-DSA87 post-quantum signature scheme (NIST level 5)
44    MLDSA87,
45
46    /// Ed25519 signature scheme for SSH
47    SshEd25519,
48
49    // Disabled due to tests not working correctly for undiagnosed reasons.
50    // SshRsaSha256,
51    // SshRsaSha512,
52    /// DSA signature scheme for SSH
53    SshDsa,
54
55    /// ECDSA signature scheme with NIST P-256 curve for SSH
56    SshEcdsaP256,
57
58    /// ECDSA signature scheme with NIST P-384 curve for SSH
59    SshEcdsaP384,
60    // Disabled due to a bug in the ssh-key crate.
61    // See: https://github.com/RustCrypto/SSH/issues/232
62
63    // SSH-ECDSA NIST P-521
64    // SshEcdsaP521,
65}
66
67impl SignatureScheme {
68    /// Creates a new key pair for the signature scheme using the system's
69    /// secure random number generator.
70    ///
71    /// This is a convenience method that calls `keypair_opt` with an empty
72    /// comment.
73    ///
74    /// # Returns
75    ///
76    /// A tuple containing a signing private key and its corresponding public
77    /// key.
78    ///
79    /// # Examples
80    ///
81    /// ```
82    /// use bc_components::SignatureScheme;
83    ///
84    /// // Generate a Schnorr key pair
85    /// let (private_key, public_key) = SignatureScheme::Schnorr.keypair();
86    ///
87    /// // Use the default scheme (also Schnorr)
88    /// let (default_private, default_public) =
89    ///     SignatureScheme::default().keypair();
90    /// ```
91    pub fn keypair(&self) -> (SigningPrivateKey, SigningPublicKey) {
92        self.keypair_opt("")
93    }
94
95    /// Creates a new key pair for the signature scheme with an optional
96    /// comment.
97    ///
98    /// The comment is only used for SSH keys and is ignored for other schemes.
99    ///
100    /// # Arguments
101    ///
102    /// * `comment` - A string comment to include with SSH keys
103    ///
104    /// # Returns
105    ///
106    /// A tuple containing a signing private key and its corresponding public
107    /// key.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use bc_components::SignatureScheme;
113    ///
114    /// // Generate an SSH Ed25519 key pair with a comment
115    /// let (ssh_private, ssh_public) =
116    ///     SignatureScheme::SshEd25519.keypair_opt("user@example.com");
117    /// ```
118    pub fn keypair_opt(
119        &self,
120        comment: impl Into<String>,
121    ) -> (SigningPrivateKey, SigningPublicKey) {
122        match self {
123            Self::Schnorr => {
124                let private_key =
125                    SigningPrivateKey::new_schnorr(ECPrivateKey::new());
126                let public_key = private_key.public_key().unwrap();
127                (private_key, public_key)
128            }
129            Self::Ecdsa => {
130                let private_key =
131                    SigningPrivateKey::new_ecdsa(ECPrivateKey::new());
132                let public_key = private_key.public_key().unwrap();
133                (private_key, public_key)
134            }
135            Self::Ed25519 => {
136                let private_key =
137                    SigningPrivateKey::new_ed25519(Ed25519PrivateKey::new());
138                let public_key = private_key.public_key().unwrap();
139                (private_key, public_key)
140            }
141            Self::MLDSA44 => {
142                let (private_key, public_key) = crate::MLDSA::MLDSA44.keypair();
143                let private_key = SigningPrivateKey::MLDSA(private_key);
144                let public_key = SigningPublicKey::MLDSA(public_key);
145                (private_key, public_key)
146            }
147            Self::MLDSA65 => {
148                let (private_key, public_key) = crate::MLDSA::MLDSA65.keypair();
149                let private_key = SigningPrivateKey::MLDSA(private_key);
150                let public_key = SigningPublicKey::MLDSA(public_key);
151                (private_key, public_key)
152            }
153            Self::MLDSA87 => {
154                let (private_key, public_key) = crate::MLDSA::MLDSA87.keypair();
155                let private_key = SigningPrivateKey::MLDSA(private_key);
156                let public_key = SigningPublicKey::MLDSA(public_key);
157                (private_key, public_key)
158            }
159            Self::SshEd25519 => {
160                let private_key_base = PrivateKeyBase::new();
161                let private_key = private_key_base
162                    .ssh_signing_private_key(Algorithm::Ed25519, comment)
163                    .unwrap();
164                let public_key = private_key.public_key().unwrap();
165                (private_key, public_key)
166            }
167            // Self::SshRsaSha256 => {
168            //     let private_key_base = PrivateKeyBase::new();
169            //     let private_key = private_key_base
170            //         .ssh_signing_private_key(
171            //             Algorithm::Rsa { hash: Some(HashAlg::Sha256) },
172            //             comment
173            //         )
174            //         .unwrap();
175            //     let public_key = private_key.public_key().unwrap();
176            //     (private_key, public_key)
177            // }
178            // Self::SshRsaSha512 => {
179            //     let private_key_base = PrivateKeyBase::new();
180            //     let private_key = private_key_base
181            //         .ssh_signing_private_key(
182            //             Algorithm::Rsa { hash: Some(HashAlg::Sha512) },
183            //             comment
184            //         )
185            //         .unwrap();
186            //     let public_key = private_key.public_key().unwrap();
187            //     (private_key, public_key)
188            // }
189            Self::SshDsa => {
190                let private_key_base = PrivateKeyBase::new();
191                let private_key = private_key_base
192                    .ssh_signing_private_key(Algorithm::Dsa, comment)
193                    .unwrap();
194                let public_key = private_key.public_key().unwrap();
195                (private_key, public_key)
196            }
197            Self::SshEcdsaP256 => {
198                let private_key_base = PrivateKeyBase::new();
199                let private_key = private_key_base
200                    .ssh_signing_private_key(
201                        Algorithm::Ecdsa {
202                            curve: ssh_key::EcdsaCurve::NistP256,
203                        },
204                        comment,
205                    )
206                    .unwrap();
207                let public_key = private_key.public_key().unwrap();
208                (private_key, public_key)
209            }
210            Self::SshEcdsaP384 => {
211                let private_key_base = PrivateKeyBase::new();
212                let private_key = private_key_base
213                    .ssh_signing_private_key(
214                        Algorithm::Ecdsa {
215                            curve: ssh_key::EcdsaCurve::NistP384,
216                        },
217                        comment,
218                    )
219                    .unwrap();
220                let public_key = private_key.public_key().unwrap();
221                (private_key, public_key)
222            }
223        }
224    }
225
226    /// Creates a key pair for the signature scheme using a provided random
227    /// number generator.
228    ///
229    /// This allows for deterministic key generation when using a seeded RNG.
230    /// Note that not all signature schemes support deterministic generation.
231    ///
232    /// # Arguments
233    ///
234    /// * `rng` - A mutable reference to a random number generator
235    /// * `comment` - A string comment to include with SSH keys
236    ///
237    /// # Returns
238    ///
239    /// A `Result` containing a tuple of signing private key and public key, or
240    /// an error if the signature scheme doesn't support deterministic
241    /// generation.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use bc_components::SignatureScheme;
247    /// use bc_rand::SecureRandomNumberGenerator;
248    ///
249    /// let mut rng = SecureRandomNumberGenerator;
250    ///
251    /// // Generate an ECDSA key pair with a specific RNG
252    /// let result = SignatureScheme::Ecdsa.keypair_using(&mut rng, "");
253    /// assert!(result.is_ok());
254    /// ```
255    pub fn keypair_using(
256        &self,
257        rng: &mut impl RandomNumberGenerator,
258        comment: impl Into<String>,
259    ) -> Result<(SigningPrivateKey, SigningPublicKey)> {
260        match self {
261            Self::Schnorr => {
262                let private_key = SigningPrivateKey::new_schnorr(
263                    ECPrivateKey::new_using(rng),
264                );
265                let public_key = private_key.public_key().unwrap();
266                Ok((private_key, public_key))
267            }
268            Self::Ecdsa => {
269                let private_key =
270                    SigningPrivateKey::new_ecdsa(ECPrivateKey::new_using(rng));
271                let public_key = private_key.public_key().unwrap();
272                Ok((private_key, public_key))
273            }
274            Self::Ed25519 => {
275                let private_key = SigningPrivateKey::new_ed25519(
276                    Ed25519PrivateKey::new_using(rng),
277                );
278                let public_key = private_key.public_key().unwrap();
279                Ok((private_key, public_key))
280            }
281            Self::SshEd25519 => {
282                let private_key_base = PrivateKeyBase::new_using(rng);
283                let private_key = private_key_base
284                    .ssh_signing_private_key(Algorithm::Ed25519, comment)
285                    .unwrap();
286                let public_key = private_key.public_key().unwrap();
287                Ok((private_key, public_key))
288            }
289            Self::SshDsa => {
290                let private_key_base = PrivateKeyBase::new_using(rng);
291                let private_key = private_key_base
292                    .ssh_signing_private_key(Algorithm::Dsa, comment)
293                    .unwrap();
294                let public_key = private_key.public_key().unwrap();
295                Ok((private_key, public_key))
296            }
297            Self::SshEcdsaP256 => {
298                let private_key_base = PrivateKeyBase::new_using(rng);
299                let private_key = private_key_base
300                    .ssh_signing_private_key(
301                        Algorithm::Ecdsa {
302                            curve: ssh_key::EcdsaCurve::NistP256,
303                        },
304                        comment,
305                    )
306                    .unwrap();
307                let public_key = private_key.public_key().unwrap();
308                Ok((private_key, public_key))
309            }
310            Self::SshEcdsaP384 => {
311                let private_key_base = PrivateKeyBase::new_using(rng);
312                let private_key = private_key_base
313                    .ssh_signing_private_key(
314                        Algorithm::Ecdsa {
315                            curve: ssh_key::EcdsaCurve::NistP384,
316                        },
317                        comment,
318                    )
319                    .unwrap();
320                let public_key = private_key.public_key().unwrap();
321                Ok((private_key, public_key))
322            }
323            _ => Err(Error::general(
324                "Deterministic keypair generation not supported for this signature scheme",
325            )),
326        }
327    }
328}