ssh_agent_lib/proto/message/
request.rs1use 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#[derive(Clone, PartialEq, Debug)]
17pub enum Request {
18 RequestIdentities,
21
22 SignRequest(SignRequest),
25
26 AddIdentity(AddIdentity),
28
29 RemoveIdentity(RemoveIdentity),
31
32 RemoveAllIdentities,
34
35 AddSmartcardKey(SmartcardKey),
38
39 RemoveSmartcardKey(SmartcardKey),
41
42 Lock(String),
44
45 Unlock(String),
47
48 AddIdConstrained(AddIdentityConstrained),
51
52 AddSmartcardKeyConstrained(AddSmartcardKeyConstrained),
56
57 Extension(Extension),
60}
61
62impl Request {
63 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}
151
152#[cfg(test)]
153mod tests {
154 use ssh_key::Algorithm;
155 use testresult::TestResult;
156
157 use super::*;
158 use crate::proto::{PublicCredential, Response};
159
160 #[test]
161 fn sign_with_cert() -> TestResult {
162 let req =
163 Request::decode(&mut &std::fs::read("tests/messages/req-sign-with-cert.bin")?[..])?;
164 let Request::SignRequest(req) = req else {
165 panic!("expected SignRequest");
166 };
167 let PublicCredential::Cert(cert) = req.credential else {
168 panic!("expected certificate");
169 };
170 assert_eq!(cert.algorithm(), Algorithm::Rsa { hash: None });
171 Ok(())
172 }
173
174 #[test]
175 fn sign_with_pubkey() -> TestResult {
176 let req = Request::decode(&mut &std::fs::read("tests/messages/req-sign-request.bin")?[..])?;
177 let Request::SignRequest(req) = req else {
178 panic!("expected SignRequest");
179 };
180 let PublicCredential::Key(cert) = req.credential else {
181 panic!("expected key");
182 };
183 assert_eq!(cert.algorithm(), Algorithm::Rsa { hash: None });
184 Ok(())
185 }
186
187 #[test]
188 fn resp_identities_with_cert() -> TestResult {
189 let resp = Response::decode(
190 &mut &std::fs::read("tests/messages/resp-identities-with-cert.bin")?[..],
191 )?;
192 let Response::IdentitiesAnswer(resp) = resp else {
193 panic!("expected IdentitiesAnswer");
194 };
195 assert_eq!(resp.len(), 4);
196 let PublicCredential::Cert(cert) = &resp[3].credential else {
197 panic!("expected certificate");
198 };
199 assert_eq!(cert.algorithm(), Algorithm::Rsa { hash: None });
200 Ok(())
201 }
202
203 #[test]
204 fn resp_identities_with_key() -> TestResult {
205 let resp = Response::decode(
206 &mut &std::fs::read("tests/messages/resp-identities-answer.bin")?[..],
207 )?;
208 let Response::IdentitiesAnswer(resp) = resp else {
209 panic!("expected IdentitiesAnswer");
210 };
211 assert_eq!(resp.len(), 1);
212 let PublicCredential::Key(key) = &resp[0].credential else {
213 panic!("expected key");
214 };
215 assert_eq!(key.algorithm(), Algorithm::Rsa { hash: None });
216 Ok(())
217 }
218}