entrust-core 0.6.0

En-/decryption and password store management for entrust
Documentation
use crate::age::identity::read_identity;
use anyhow::anyhow;
use entrust_agent::NO_AGENT_ERROR_KIND;
use entrust_agent::server::GetAgeIdentityResponse;
use std::io::Error;
use std::process::{Command, Stdio};
use std::time::Duration;
use std::{io, thread};

pub fn get_identity() -> anyhow::Result<Vec<u8>> {
    let result = entrust_agent::client::get_age_identity(entrust_agent::env::agent_pin().ok());
    match result {
        Err(e) if e.kind() == NO_AGENT_ERROR_KIND => start_agent()?,
        Err(e) => return Err(anyhow!(e)),
        Ok(response) => match response {
            GetAgeIdentityResponse::Ok { identity } => return Ok(identity.into_bytes()),
            GetAgeIdentityResponse::NotSet => start_agent()?,
            GetAgeIdentityResponse::WrongPin => entrust_agent::client::shutdown_server()?,
        },
    }

    let id = read_identity()?;
    set_identity(&id)?;
    Ok(id)
}

fn start_agent() -> io::Result<()> {
    let seconds = entrust_agent::env::agent_seconds();
    let ent_agent_bin = entrust_agent::env::agent_bin();
    let mut command = if cfg!(windows) {
        let mut command = Command::new("pwsh");
        command.args(["-C", "Start-Process", ent_agent_bin.as_ref()]);
        command
    } else {
        Command::new(ent_agent_bin.as_ref())
    };
    command
        .arg(seconds.as_ref())
        .stdin(Stdio::null())
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn()
        .map(|_| ())
}

fn set_identity(id: &[u8]) -> anyhow::Result<()> {
    let string = String::from_utf8(id.to_vec())?;
    with_retry(
        10,
        Duration::from_millis(100),
        |e: &Error| e.kind() == NO_AGENT_ERROR_KIND,
        || {
            entrust_agent::client::set_age_identity(
                string.clone(),
                entrust_agent::env::agent_pin().ok(),
            )
        },
    )?;
    Ok(())
}

fn with_retry<T, E, N, F>(num: usize, backoff: Duration, on_error: N, action: F) -> Result<T, E>
where
    N: Fn(&E) -> bool,
    F: Fn() -> Result<T, E>,
{
    let mut result = action();
    for _ in 0..num {
        match result {
            Ok(value) => return Ok(value),
            Err(e) if on_error(&e) => {
                thread::sleep(backoff);
                result = action();
            }
            Err(e) => return Err(e),
        }
    }
    result
}