vynil-core 0.7.0

A Rust toolbox for bootstrapping projects combining a Rhai scripting engine and a Handlebars templating engine, with optional Kubernetes / OCI / S3 handlers.
Documentation
use crate::{Error, Result};
use openssl::{pkey::PKey, rsa::Rsa};
use rhai::Engine;

pub const DEFAULT_RSA_BITS: u32 = 4096;

pub fn gen_private_key(algo: &str, bits: u32) -> Result<String> {
    let pem = match algo.to_ascii_lowercase().as_str() {
        "ed25519" => PKey::generate_ed25519()?.private_key_to_pem_pkcs8()?,
        "rsa" => PKey::from_rsa(Rsa::generate(bits)?)?.private_key_to_pem_pkcs8()?,
        other => return Err(Error::UnsupportedKeyAlgorithm(other.to_string())),
    };
    String::from_utf8(pem).map_err(Error::UTF8)
}

pub fn key_rhai_register(engine: &mut Engine) {
    engine
        .register_fn("gen_private_key", |algo: &str| -> crate::RhaiRes<String> {
            gen_private_key(algo, DEFAULT_RSA_BITS).map_err(|e| format!("{e}").into())
        })
        .register_fn(
            "gen_private_key",
            |algo: &str, bits: i64| -> crate::RhaiRes<String> {
                gen_private_key(algo, bits as u32).map_err(|e| format!("{e}").into())
            },
        );
}

#[cfg(test)]
mod tests {
    use super::*;
    use openssl::pkey::Id;

    #[test]
    fn ed25519_produces_valid_pkcs8_pem() {
        let pem = gen_private_key("ed25519", 0).unwrap();
        assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----"));
        let key = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
        assert_eq!(key.id(), Id::ED25519);
    }

    #[test]
    fn rsa_produces_valid_pkcs8_pem() {
        let pem = gen_private_key("rsa", 2048).unwrap();
        assert!(pem.starts_with("-----BEGIN PRIVATE KEY-----"));
        let key = PKey::private_key_from_pem(pem.as_bytes()).unwrap();
        assert_eq!(key.id(), Id::RSA);
        assert_eq!(key.bits(), 2048);
    }

    #[test]
    fn algorithm_is_case_insensitive() {
        gen_private_key("ED25519", 0).unwrap();
    }

    #[test]
    fn unknown_algorithm_is_an_error() {
        assert!(gen_private_key("dsa", 0).is_err());
    }

    #[test]
    fn two_generations_differ() {
        let a = gen_private_key("ed25519", 0).unwrap();
        let b = gen_private_key("ed25519", 0).unwrap();
        assert_ne!(a, b);
    }
}