agent.rs
1 //! CLI adapter for agent-based signing operations. 2 //! 3 //! Wraps the Unix-only agent client from `auths-core` behind the 4 //! `AgentSigningPort` trait, producing SSHSIG PEM output compatible 5 //! with `sign_with_seed()`. 6 7 #[cfg(unix)] 8 use auths_core::agent::{AgentStatus, add_identity, agent_sign, check_agent_status}; 9 #[cfg(unix)] 10 use auths_core::crypto::ssh::{construct_sshsig_pem, construct_sshsig_signed_data}; 11 use auths_sdk::ports::agent::{AgentSigningError, AgentSigningPort}; 12 13 #[cfg(unix)] 14 use crate::commands::agent::{ensure_agent_running, get_default_socket_path}; 15 16 /// CLI adapter that delegates signing to the Unix SSH agent. 17 /// 18 /// On non-Unix platforms this struct is not compiled; the CLI wires 19 /// `NoopAgentProvider` instead. 20 /// 21 /// Usage: 22 /// ```ignore 23 /// let adapter = CliAgentAdapter; 24 /// let pem = adapter.try_sign("git", &pubkey, &data)?; 25 /// ``` 26 #[cfg(unix)] 27 pub struct CliAgentAdapter; 28 29 #[cfg(unix)] 30 impl AgentSigningPort for CliAgentAdapter { 31 fn try_sign( 32 &self, 33 namespace: &str, 34 pubkey: &[u8], 35 data: &[u8], 36 ) -> Result<String, AgentSigningError> { 37 if pubkey.len() != 32 { 38 return Err(AgentSigningError::Unavailable( 39 "no public key available for agent signing".into(), 40 )); 41 } 42 43 let socket_path = 44 get_default_socket_path().map_err(|e| AgentSigningError::Unavailable(e.to_string()))?; 45 46 match check_agent_status(&socket_path) { 47 AgentStatus::Running { key_count } if key_count > 0 => {} 48 AgentStatus::Running { .. } => { 49 return Err(AgentSigningError::Unavailable( 50 "agent running but no keys loaded".into(), 51 )); 52 } 53 AgentStatus::ConnectionFailed => { 54 return Err(AgentSigningError::ConnectionFailed( 55 "agent socket unreachable".into(), 56 )); 57 } 58 AgentStatus::NotRunning => { 59 return Err(AgentSigningError::Unavailable("agent not running".into())); 60 } 61 } 62 63 let sig_data = construct_sshsig_signed_data(data, namespace) 64 .map_err(|e| AgentSigningError::SigningFailed(e.to_string()))?; 65 66 let raw_sig = agent_sign(&socket_path, pubkey, &sig_data) 67 .map_err(|e| AgentSigningError::SigningFailed(e.to_string()))?; 68 69 construct_sshsig_pem(pubkey, &raw_sig, namespace) 70 .map_err(|e| AgentSigningError::SigningFailed(e.to_string())) 71 } 72 73 fn ensure_running(&self) -> Result<(), AgentSigningError> { 74 ensure_agent_running(true) 75 .map(|_| ()) 76 .map_err(|e| AgentSigningError::StartupFailed(e.to_string())) 77 } 78 79 fn add_identity(&self, _namespace: &str, pkcs8_der: &[u8]) -> Result<(), AgentSigningError> { 80 let socket_path = get_default_socket_path() 81 .map_err(|e| AgentSigningError::ConnectionFailed(e.to_string()))?; 82 83 add_identity(&socket_path, pkcs8_der) 84 .map(|_| ()) 85 .map_err(|e| AgentSigningError::SigningFailed(e.to_string())) 86 } 87 }