signstar-client 0.1.0

Library and executable for creating, reading and writing of signing requests for files
Documentation
//! A simple SSH agent implementation which contains one, randomly generated RSA key.

use std::path::Path;
use std::sync::{Arc, Mutex};

use rand_0_8_for_ssh_agent_lib::thread_rng;
use rsa_0_9_for_ssh_agent_lib::{
    RsaPrivateKey,
    RsaPublicKey,
    pkcs1v15::SigningKey,
    sha2::{Sha256, Sha512},
    signature::{RandomizedSigner, SignatureEncoding},
};
use ssh_agent_lib::agent::{Session as AgentSession, listen};
use ssh_agent_lib::error::AgentError;
use ssh_agent_lib::proto::{Identity, SignRequest, signature};
use ssh_agent_lib::ssh_key::HashAlg;
use ssh_agent_lib::ssh_key::public::KeyData;
use ssh_agent_lib::ssh_key::{Algorithm, Signature};
use tokio::net::UnixListener;

#[derive(Clone)]
struct RandomKey {
    private_key: Arc<Mutex<RsaPrivateKey>>,
}

impl RandomKey {
    pub fn new() -> Result<Self, AgentError> {
        let private_key = RsaPrivateKey::new(&mut thread_rng(), 2048).map_err(AgentError::other)?;
        Ok(Self {
            private_key: Arc::new(Mutex::new(private_key)),
        })
    }
}

#[ssh_agent_lib::async_trait]
impl AgentSession for RandomKey {
    async fn sign(&mut self, sign_request: SignRequest) -> Result<Signature, AgentError> {
        let private_key = self.private_key.lock().unwrap();
        let data = &sign_request.data;

        Ok(if sign_request.flags & signature::RSA_SHA2_512 != 0 {
            Signature::new(
                Algorithm::Rsa {
                    hash: Some(HashAlg::Sha512),
                },
                SigningKey::<Sha512>::new(private_key.clone())
                    .sign_with_rng(&mut thread_rng(), data)
                    .to_bytes(),
            )
        } else if sign_request.flags & signature::RSA_SHA2_256 != 0 {
            Signature::new(
                Algorithm::Rsa {
                    hash: Some(HashAlg::Sha256),
                },
                SigningKey::<Sha256>::new(private_key.clone())
                    .sign_with_rng(&mut thread_rng(), data)
                    .to_bytes(),
            )
        } else {
            Err(std::io::Error::other("Signature for signature type not implemented").into())
        }
        .map_err(AgentError::other)?)
    }

    async fn request_identities(&mut self) -> Result<Vec<Identity>, AgentError> {
        use ssh_agent_lib::ssh_key::public::RsaPublicKey as SshPK;
        let identity = self.private_key.lock().unwrap();
        Ok(vec![Identity {
            credential: KeyData::from(SshPK::try_from(RsaPublicKey::from(&*identity)).unwrap())
                .into(),
            comment: "randomly generated RSA key".into(),
        }])
    }
}

/// Starts the SSH agent listening on given path.
///
/// # Errors
///
/// Returns an error if:
/// - generating RSA key fails
/// - binding to the given path as a Unix Domain Socket fails
pub async fn listen_on_socket(socket: impl AsRef<Path>) -> Result<(), AgentError> {
    listen(UnixListener::bind(socket)?, RandomKey::new()?).await?;
    Ok(())
}