ssh_agent_lib/proto/message/
request.rs

1//! SSH agent protocol request messages.
2
3use ssh_encoding::{CheckedSum, Decode, Encode, Reader, Writer};
4
5use super::{
6    AddIdentity, AddIdentityConstrained, AddSmartcardKeyConstrained, Extension, RemoveIdentity,
7    SignRequest, SmartcardKey,
8};
9use crate::proto::{Error, Result};
10
11/// SSH agent protocol request messages.
12///
13/// These message types are sent from a client *to* an agent.
14///
15/// Described in [draft-miller-ssh-agent-14 § 3](https://www.ietf.org/archive/id/draft-miller-ssh-agent-14.html#section-3).
16#[derive(Clone, PartialEq, Debug)]
17pub enum Request {
18    /// Request a list of all identities (public key/certificate & comment)
19    /// from an agent
20    RequestIdentities,
21
22    /// Perform a private key signature operation using a key
23    /// stored in the agent
24    SignRequest(SignRequest),
25
26    /// Add an identity (private key/certificate & comment) to an agent
27    AddIdentity(AddIdentity),
28
29    /// Remove an identity from an agent
30    RemoveIdentity(RemoveIdentity),
31
32    /// Remove all identities from an agent
33    RemoveAllIdentities,
34
35    /// Add an identity (private key/certificate & comment) to an agent
36    /// where the private key is stored on a hardware token
37    AddSmartcardKey(SmartcardKey),
38
39    /// Remove a key stored on a hardware token from an agent
40    RemoveSmartcardKey(SmartcardKey),
41
42    /// Temporarily lock an agent with a pass-phrase
43    Lock(String),
44
45    /// Unlock a locked agaent with a pass-phrase
46    Unlock(String),
47
48    /// Add an identity (private key/certificate & comment) to an agent,
49    /// with constraints on it's usage
50    AddIdConstrained(AddIdentityConstrained),
51
52    /// Add an identity (private key/certificate & comment) to an agent
53    /// where the private key is stored on a hardware token,
54    /// with constraints on it's usage
55    AddSmartcardKeyConstrained(AddSmartcardKeyConstrained),
56
57    /// Send a vendor-specific message via the agent protocol,
58    /// identified by an *extension type*.
59    Extension(Extension),
60}
61
62impl Request {
63    /// The protocol message identifier for a given [`Request`] message type.
64    ///
65    /// Described in [draft-miller-ssh-agent-14 § 6.1](https://www.ietf.org/archive/id/draft-miller-ssh-agent-14.html#section-6.1).
66    pub fn message_id(&self) -> u8 {
67        match self {
68            Self::RequestIdentities => 11,
69            Self::SignRequest(_) => 13,
70            Self::AddIdentity(_) => 17,
71            Self::RemoveIdentity(_) => 18,
72            Self::RemoveAllIdentities => 19,
73            Self::AddSmartcardKey(_) => 20,
74            Self::RemoveSmartcardKey(_) => 21,
75            Self::Lock(_) => 22,
76            Self::Unlock(_) => 23,
77            Self::AddIdConstrained(_) => 25,
78            Self::AddSmartcardKeyConstrained(_) => 26,
79            Self::Extension(_) => 27,
80        }
81    }
82}
83
84impl Decode for Request {
85    type Error = Error;
86
87    fn decode(reader: &mut impl Reader) -> Result<Self> {
88        let message_type = u8::decode(reader)?;
89
90        match message_type {
91            11 => Ok(Self::RequestIdentities),
92            13 => SignRequest::decode(reader).map(Self::SignRequest),
93            17 => AddIdentity::decode(reader).map(Self::AddIdentity),
94            18 => RemoveIdentity::decode(reader).map(Self::RemoveIdentity),
95            19 => Ok(Self::RemoveAllIdentities),
96            20 => SmartcardKey::decode(reader).map(Self::AddSmartcardKey),
97            21 => SmartcardKey::decode(reader).map(Self::RemoveSmartcardKey),
98            22 => Ok(String::decode(reader).map(Self::Lock)?),
99            23 => Ok(String::decode(reader).map(Self::Unlock)?),
100            25 => AddIdentityConstrained::decode(reader).map(Self::AddIdConstrained),
101            26 => AddSmartcardKeyConstrained::decode(reader).map(Self::AddSmartcardKeyConstrained),
102            27 => Extension::decode(reader).map(Self::Extension),
103            command => Err(Error::UnsupportedCommand { command }),
104        }
105    }
106}
107
108impl Encode for Request {
109    fn encoded_len(&self) -> ssh_encoding::Result<usize> {
110        let message_id_len = 1;
111        let payload_len = match self {
112            Self::RequestIdentities => 0,
113            Self::SignRequest(request) => request.encoded_len()?,
114            Self::AddIdentity(identity) => identity.encoded_len()?,
115            Self::RemoveIdentity(identity) => identity.encoded_len()?,
116            Self::RemoveAllIdentities => 0,
117            Self::AddSmartcardKey(key) => key.encoded_len()?,
118            Self::RemoveSmartcardKey(key) => key.encoded_len()?,
119            Self::Lock(passphrase) => passphrase.encoded_len()?,
120            Self::Unlock(passphrase) => passphrase.encoded_len()?,
121            Self::AddIdConstrained(key) => key.encoded_len()?,
122            Self::AddSmartcardKeyConstrained(key) => key.encoded_len()?,
123            Self::Extension(extension) => extension.encoded_len()?,
124        };
125
126        [message_id_len, payload_len].checked_sum()
127    }
128
129    fn encode(&self, writer: &mut impl Writer) -> ssh_encoding::Result<()> {
130        let message_id: u8 = self.message_id();
131        message_id.encode(writer)?;
132
133        match self {
134            Self::RequestIdentities => {}
135            Self::SignRequest(request) => request.encode(writer)?,
136            Self::AddIdentity(identity) => identity.encode(writer)?,
137            Self::RemoveIdentity(identity) => identity.encode(writer)?,
138            Self::RemoveAllIdentities => {}
139            Self::AddSmartcardKey(key) => key.encode(writer)?,
140            Self::RemoveSmartcardKey(key) => key.encode(writer)?,
141            Self::Lock(passphrase) => passphrase.encode(writer)?,
142            Self::Unlock(passphrase) => passphrase.encode(writer)?,
143            Self::AddIdConstrained(identity) => identity.encode(writer)?,
144            Self::AddSmartcardKeyConstrained(key) => key.encode(writer)?,
145            Self::Extension(extension) => extension.encode(writer)?,
146        };
147
148        Ok(())
149    }
150}