volli-manager 0.1.12

Manager for volli
Documentation
use ed25519_dalek::{SigningKey, VerifyingKey};
use eyre::{Report, eyre};
use getrandom;
use hex;
use rand_core::OsRng;
use std::fs;
use std::path::{Path, PathBuf};
use volli_core::profile as core_profile;

/// Return the default directory used for storing manager secrets.
pub fn default_secret_dir() -> PathBuf {
    core_profile::role_dir("manager")
}

/// Resolve the directory that holds secrets for the given profile.
pub fn secret_dir(profile: Option<&str>) -> PathBuf {
    match profile {
        Some(p) => core_profile::profile_dir("manager", p),
        None => core_profile::role_dir("manager"),
    }
}

/// Generate a new keypair and cluster shared key in the provided directory.
pub fn bootstrap_keypair(dir: Option<&Path>) -> Result<(), Report> {
    let dir = dir.map(PathBuf::from).unwrap_or_else(default_secret_dir);
    fs::create_dir_all(&dir)?;
    let sk_path = dir.join("manager_sk");
    let pk_path = dir.join("manager_pk");
    if sk_path.exists() || pk_path.exists() {
        return Err(eyre!("keypair already exists"));
    }
    let mut rng = OsRng;
    let signing = SigningKey::generate(&mut rng);
    let verifying: VerifyingKey = signing.verifying_key();
    fs::write(&sk_path, hex::encode(signing.to_bytes()))?;
    fs::write(&pk_path, hex::encode(verifying.to_bytes()))?;
    let mut csk = [0u8; 32];
    getrandom::fill(&mut csk)?;
    fs::write(dir.join("csk"), hex::encode(csk))?;
    fs::write(dir.join("csk_ver"), "1")?;
    tracing::debug!("Generated manager keypair at {}", dir.display());
    Ok(())
}

pub fn load_signing_key(dir: Option<&Path>) -> Result<SigningKey, Report> {
    let dir = dir.map(PathBuf::from).unwrap_or_else(default_secret_dir);
    let data = fs::read(dir.join("manager_sk"))?;
    let bytes = hex::decode(data)?;
    let arr: [u8; 32] = bytes.as_slice().try_into().map_err(|_| eyre!("bad sk"))?;
    Ok(SigningKey::from_bytes(&arr))
}

pub fn load_verifying_key(dir: Option<&Path>) -> Result<VerifyingKey, Report> {
    let dir = dir.map(PathBuf::from).unwrap_or_else(default_secret_dir);
    let data = fs::read(dir.join("manager_pk"))?;
    let bytes = hex::decode(data)?;
    let arr: [u8; 32] = bytes.as_slice().try_into().map_err(|_| eyre!("bad pk"))?;
    Ok(VerifyingKey::from_bytes(&arr)?)
}

pub fn save_csk(profile: &str, csk: &[u8; 32], ver: u32) -> Result<(), Report> {
    let dir = secret_dir(Some(profile));
    fs::create_dir_all(&dir)?;
    fs::write(dir.join("csk"), hex::encode(csk))?;
    fs::write(dir.join("csk_ver"), ver.to_string())?;
    Ok(())
}

pub fn load_csk(profile: &str) -> Result<Option<([u8; 32], u32)>, Report> {
    let dir = secret_dir(Some(profile));
    let key_path = dir.join("csk");
    let ver_path = dir.join("csk_ver");
    if key_path.exists() && ver_path.exists() {
        let data = fs::read_to_string(key_path)?;
        let bytes = hex::decode(data)?;
        let arr: [u8; 32] = bytes.as_slice().try_into().map_err(|_| eyre!("bad csk"))?;
        let ver: u32 = fs::read_to_string(ver_path)?.trim().parse()?;
        Ok(Some((arr, ver)))
    } else {
        Ok(None)
    }
}