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