use std::path::PathBuf;
#[derive(Debug, Clone)]
pub enum RecipientKey {
Ssh(String),
Age(String),
}
impl RecipientKey {
pub fn from_ssh_file(path: &PathBuf) -> crate::error::Result<Self> {
let content = std::fs::read_to_string(path).map_err(crate::error::MnemeError::Io)?;
Ok(RecipientKey::Ssh(content.trim().to_string()))
}
pub fn from_string(s: &str) -> crate::error::Result<Self> {
let s = s.trim();
if s.starts_with("age1") {
Ok(RecipientKey::Age(s.to_string()))
} else {
Ok(RecipientKey::Ssh(s.to_string()))
}
}
pub fn key_type(&self) -> &str {
match self {
RecipientKey::Ssh(s) => {
if s.contains("ssh-ed25519") {
"ssh-ed25519"
} else if s.contains("ssh-rsa") {
"ssh-rsa"
} else {
"ssh"
}
}
RecipientKey::Age(_) => "age",
}
}
pub fn public_key_string(&self) -> String {
match self {
RecipientKey::Ssh(s) | RecipientKey::Age(s) => s.clone(),
}
}
}
#[derive(Debug)]
pub enum IdentityKey {
Ssh(PathBuf),
Age(PathBuf),
}
impl IdentityKey {
pub fn detect() -> crate::error::Result<Self> {
if let Ok(val) = std::env::var("MNEME_IDENTITY") {
return Self::from_path(&PathBuf::from(val));
}
if let Some(mut home) = dirs::home_dir() {
home.push(".ssh");
let ed25519 = home.join("id_ed25519");
if ed25519.exists() {
return Ok(IdentityKey::Ssh(ed25519));
}
let rsa = home.join("id_rsa");
if rsa.exists() {
return Ok(IdentityKey::Ssh(rsa));
}
}
if let Some(mut home) = dirs::home_dir() {
home.push(".age");
let key = home.join("key.txt");
if key.exists() {
return Ok(IdentityKey::Age(key));
}
}
Err(crate::error::MnemeError::IdentityNotLoaded)
}
pub fn from_path(path: &PathBuf) -> crate::error::Result<Self> {
if !path.exists() {
return Err(crate::error::MnemeError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("identity file not found: {}", path.display()),
)));
}
if path.extension().and_then(|e| e.to_str()) == Some("txt") {
return Ok(IdentityKey::Age(path.clone()));
}
let filename = path.file_name().and_then(|n| n.to_str()).unwrap_or("");
if filename.starts_with("id_") {
Ok(IdentityKey::Ssh(path.clone()))
} else {
let content = std::fs::read_to_string(path)?;
if content.contains("AGE-SECRET-KEY") {
Ok(IdentityKey::Age(path.clone()))
} else {
Ok(IdentityKey::Ssh(path.clone()))
}
}
}
pub fn path(&self) -> &PathBuf {
match self {
IdentityKey::Ssh(p) | IdentityKey::Age(p) => p,
}
}
}