1use crate::{Error, Result};
6use openssl::{pkey::PKey, rsa::Rsa};
7#[cfg(feature = "rhai")] use rhai::Engine;
8
9pub const DEFAULT_RSA_BITS: u32 = 4096;
11
12pub fn gen_private_key(algo: &str, bits: u32) -> Result<String> {
15 let pem = match algo.to_ascii_lowercase().as_str() {
16 "ed25519" => PKey::generate_ed25519()?.private_key_to_pem_pkcs8()?,
17 "rsa" => PKey::from_rsa(Rsa::generate(bits)?)?.private_key_to_pem_pkcs8()?,
18 other => return Err(Error::UnsupportedKeyAlgorithm(other.to_string())),
19 };
20 String::from_utf8(pem).map_err(Error::UTF8)
21}
22
23#[cfg(feature = "rhai")]
24pub fn key_rhai_register(engine: &mut Engine) {
25 engine
26 .register_fn("gen_private_key", |algo: &str| -> crate::RhaiRes<String> {
27 gen_private_key(algo, DEFAULT_RSA_BITS).map_err(|e| format!("{e}").into())
28 })
29 .register_fn(
30 "gen_private_key",
31 |algo: &str, bits: i64| -> crate::RhaiRes<String> {
32 gen_private_key(algo, bits as u32).map_err(|e| format!("{e}").into())
33 },
34 );
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use openssl::pkey::Id;
41
42 #[test]
43 fn ed25519_produces_valid_pkcs8_pem() {
44 let pem = gen_private_key("ed25519", 0).unwrap();
45 assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----"));
46 let key = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
47 assert_eq!(key.id(), Id::ED25519);
48 }
49
50 #[test]
51 fn rsa_produces_valid_pkcs8_pem() {
52 let pem = gen_private_key("rsa", 2048).unwrap();
53 assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----"));
54 let key = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
55 assert_eq!(key.id(), Id::RSA);
56 assert_eq!(key.bits(), 2048);
57 }
58
59 #[test]
60 fn algorithm_is_case_insensitive() {
61 gen_private_key("ED25519", 0).unwrap();
62 }
63
64 #[test]
65 fn unknown_algorithm_is_an_error() {
66 assert!(gen_private_key("dsa", 0).is_err());
67 }
68
69 #[test]
70 fn two_generations_differ() {
71 let a = gen_private_key("ed25519", 0).unwrap();
72 let b = gen_private_key("ed25519", 0).unwrap();
73 assert_ne!(a, b);
74 }
75}