pub mod ed25519;
pub mod rsa;
use anyhow::Result;
use std::path::Path;
#[derive(Debug, Clone)]
pub struct GeneratedKey {
pub private_key_pem: String,
pub public_key_openssh: String,
pub fingerprint: String,
pub key_type: String,
}
pub fn generate_ed25519(output_path: &Path, comment: Option<&str>) -> Result<GeneratedKey> {
ed25519::generate(output_path, comment)
}
pub fn generate_rsa(output_path: &Path, bits: u32, comment: Option<&str>) -> Result<GeneratedKey> {
rsa::generate(output_path, bits, comment)
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_generate_ed25519() {
let temp_dir = tempdir().unwrap();
let key_path = temp_dir.path().join("id_ed25519");
let result = generate_ed25519(&key_path, Some("test@example.com"));
assert!(result.is_ok());
let key = result.unwrap();
assert!(key
.private_key_pem
.contains("-----BEGIN OPENSSH PRIVATE KEY-----"));
assert!(key.public_key_openssh.starts_with("ssh-ed25519 "));
assert!(key.public_key_openssh.contains("test@example.com"));
assert!(key.fingerprint.starts_with("SHA256:"));
assert_eq!(key.key_type, "ed25519");
assert!(key_path.exists());
let pub_path = temp_dir.path().join("id_ed25519.pub");
assert!(pub_path.exists());
}
#[test]
fn test_generate_rsa() {
let temp_dir = tempdir().unwrap();
let key_path = temp_dir.path().join("id_rsa");
let result = generate_rsa(&key_path, 2048, Some("test@example.com"));
assert!(result.is_ok());
let key = result.unwrap();
assert!(key
.private_key_pem
.contains("-----BEGIN OPENSSH PRIVATE KEY-----"));
assert!(key.public_key_openssh.starts_with("ssh-rsa "));
assert!(key.public_key_openssh.contains("test@example.com"));
assert!(key.fingerprint.starts_with("SHA256:"));
assert_eq!(key.key_type, "rsa-2048");
assert!(key_path.exists());
let pub_path = temp_dir.path().join("id_rsa.pub");
assert!(pub_path.exists());
}
#[test]
fn test_generate_rsa_invalid_bits() {
let temp_dir = tempdir().unwrap();
let key_path = temp_dir.path().join("id_rsa");
let result = generate_rsa(&key_path, 1024, None);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("2048"));
let result = generate_rsa(&key_path, 32768, None);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("16384"));
}
#[test]
fn test_default_comment() {
let temp_dir = tempdir().unwrap();
let key_path = temp_dir.path().join("id_ed25519");
let result = generate_ed25519(&key_path, None);
assert!(result.is_ok());
let key = result.unwrap();
assert!(key.public_key_openssh.contains("bssh-keygen"));
}
#[test]
#[cfg(unix)]
fn test_file_permissions() {
use std::os::unix::fs::PermissionsExt;
let temp_dir = tempdir().unwrap();
let key_path = temp_dir.path().join("id_ed25519");
let result = generate_ed25519(&key_path, None);
assert!(result.is_ok());
let metadata = fs::metadata(&key_path).unwrap();
let permissions = metadata.permissions();
assert_eq!(permissions.mode() & 0o777, 0o600);
}
}