use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD as base64engine};
use orion::aead::{self, SecretKey};
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum KeyLoadError {
#[error("io error: {0}")]
Io(#[from] std::io::Error),
#[error("base64 decode error: {0}")]
Base64Decode(#[from] base64::DecodeError),
#[error("cryptographic error: {0}")]
Crypto(#[from] orion::errors::UnknownCryptoError),
}
fn key_path() -> PathBuf {
std::env::home_dir()
.unwrap_or_default()
.join(".ssh/workgroup")
}
pub fn key_load() -> Result<SecretKey, KeyLoadError> {
let encoded_key = std::fs::read_to_string(key_path())?;
let key = base64engine.decode(encoded_key)?;
Ok(SecretKey::from_slice(&key)?)
}
pub fn key_create() -> std::io::Result<()> {
let key = SecretKey::default();
let encoded_key = base64engine.encode(key.unprotected_as_bytes());
std::fs::write(key_path(), encoded_key)
}
pub fn chain_open(key: Option<&SecretKey>) -> Vec<String> {
if let Some(key) = key
&& let Ok(chain) = std::env::var("WORKGROUP_CHAIN")
&& let Ok(chain) = base64engine.decode(chain)
&& let Ok(chain) = aead::open(key, &chain)
&& let Ok(chain) = String::from_utf8(chain)
&& !chain.is_empty()
{
return chain.split(' ').map(ToOwned::to_owned).collect();
}
if let Ok(conn) = std::env::var("SSH_CONNECTION")
&& let Some(host) = conn.split_whitespace().next()
{
return vec![host.to_owned()];
}
vec![]
}
#[must_use]
pub fn chain_seal(chain: &[String], key: &SecretKey) -> String {
base64engine.encode(aead::seal(key, chain.join(" ").as_bytes()).unwrap())
}