statusline/
workgroup.rs

1use anyhow::{Context as _, Result, anyhow};
2use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD as base64engine};
3use orion::aead::{self, SecretKey};
4use std::{
5    env,
6    fs::{self, File},
7    io::{BufRead as _, BufReader},
8};
9
10pub struct WorkgroupKey(SecretKey);
11
12impl WorkgroupKey {
13    pub fn load() -> Result<Self> {
14        Ok(WorkgroupKey(SecretKey::from_slice(
15            &base64engine.decode(
16                &BufReader::new(File::open(format!(
17                    "{}/.ssh/workgroup",
18                    env::var("HOME").unwrap_or_default()
19                ))?)
20                .lines()
21                .next()
22                .context("Workgroup key file is corrupted")??,
23            )?,
24        )?))
25    }
26
27    pub fn create() -> Result<()> {
28        Ok(fs::write(
29            format!("{}/.ssh/workgroup", env::var("HOME").unwrap_or_default()),
30            base64engine.encode(SecretKey::default().unprotected_as_bytes()),
31        )?)
32    }
33}
34
35pub struct SshChain(pub Vec<String>);
36
37impl SshChain {
38    fn open_impl(key: &WorkgroupKey) -> Result<Vec<String>> {
39        Ok(String::from_utf8(aead::open(
40            &key.0,
41            &base64engine.decode(env::var("WORKGROUP_CHAIN")?)?,
42        )?)?
43        .split_whitespace()
44        .map(ToOwned::to_owned)
45        .collect::<Vec<_>>())
46    }
47
48    pub fn open(key: Option<&WorkgroupKey>) -> SshChain {
49        let ssh_chain = key
50            .context("No workgroup key passed")
51            .and_then(Self::open_impl)
52            .and_then(|chain| {
53                if chain.is_empty() {
54                    Err(anyhow!("Empty ssh chain, but decoded"))
55                } else {
56                    Ok(chain)
57                }
58            });
59
60        SshChain(match (ssh_chain, env::var("SSH_CONNECTION")) {
61            (Err(_), Err(_)) => vec![],
62            (Err(_), Ok(conn)) => vec![conn.split_whitespace().next().unwrap_or("?").to_owned()],
63            (Ok(ch), _) => ch,
64        })
65    }
66
67    fn seal_impl(&self, key: &WorkgroupKey) -> Result<String> {
68        Ok(base64engine.encode(aead::seal(&key.0, self.0.join(" ").as_bytes())?))
69    }
70
71    #[must_use]
72    pub fn seal(&self, key: &WorkgroupKey) -> String {
73        self.seal_impl(key).unwrap_or_default()
74    }
75}