Skip to content

SSH agent client support #32

Closed
Closed
@jcspencer

Description

Given the work occurring in #26 (with encode/decode support now supported for the entire spec), the project is now relatively close to being able to implement support for acting as an SSH agent client.

I imagine this would entail:

  • Splitting MessageCodec into it's own pub(crate) module, as it would be shared between the agent and client.
  • Implementing some kind of generic ClientSocket trait to support Unix Sockets / TCP / Named Pipes as transports.
  • Implementing a Client trait which provides helper methods for sending messages over the a ClientSocket.

It's probably worth noting that from a client perspective, one connection == one session, so the Agent/Session-style split would not be required.

I'm also unsure whether the interface needs to be async, or whether a synchronous interface would be sufficient?

Interested to hear your thoughts!


A sample trait for a client could look something like:

pub trait Client {
    fn request_identities(&self) -> Result<Message, AgentError> {
        let message = Message::RequestIdentities;
        todo!()
    }

    fn sign(
        &self,
        public_key: KeyData,
        data: Vec<u8>,
        flags: u32
    ) -> Result<Message, AgentError> {
        let message = Message::SignRequest(SignRequest {
            pubkey: public_key,
            data,
            flags
        });
        todo!()
    }

    fn add_identity(
        &self,
        private_key: KeypairData,
        comment: String,
        constraints: Option<Vec<KeyConstraint>>
    ) -> Result<Message, AgentError> {
        let identity = AddIdentity {
           privkey: private_key,
           comment 
        };

        let message = match constraints {
            Some(constraints) => {
                Message::AddIdConstrained(AddIdentityConstrained {
                    identity,
                    constraints
                })
            },
            None => {
                Message::AddIdentity(identity)
            }
        };
        todo!()
    }

    fn remove_identity(&self, public_key: KeyData) -> Result<Message, AgentError> {
        let message = Message::RemoveIdentity(RemoveIdentity {
            pubkey: public_key
        });
        todo!()
    }

    fn remove_all_identities(&self) -> Result<Message, AgentError> {
        let message = Message::RemoveAllIdentities;
        todo!()   
    }

    fn add_smartcard_key(
        &self,
        id: String,
        pin: String,
        constraints: Option<Vec<KeyConstraint>>
    ) -> Result<Message, AgentError> {
        let key = SmartcardKey { id, pin };
        let message = match constraints {
            Some(constraints) => {
                Message::AddSmartcardKeyConstrained(AddSmartcardKeyConstrained {
                    key,
                    constraints
                })
            },
            None => {
                Message::AddSmartcardKey(key)
            }
        };
        todo!()
    }

    fn remove_smartcard_key(&self, id: String, pin: String) -> Result<Message, AgentError> {
        let key = SmartcardKey { id, pin };
        let message = Message::RemoveSmartcardKey(key);
        todo!()
    }

    fn lock(&self, passphrase: Passphrase) -> Result<Message, AgentError> {
        let message = Message::Lock(passphrase);
        todo!()
    }
    
    fn unlock(&self, passphrase: Passphrase) -> Result<Message, AgentError> {
        let message = Message::Unlock(passphrase);
        todo!()
    }


    fn extension<T>(&self, name: String, data: T) -> Result<Message, AgentError>
    where
        T: Encode
    {
        let mut buffer: Vec<u8> = vec![];
        data.encode(&mut buffer)?;

        let extention = Extension {
            name,
            details: Unparsed(buffer)
        };

        let message = Message::Extension(extension);
        todo!()
    }
}

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions