1use std::{
2 fmt::{Debug, Display},
3 ops::{Deref, DerefMut},
4 str::FromStr,
5};
6use tracing::*;
7
8use crate::{
9 algorithm::{Compress, Enc, Kex, Mac, PubKey},
10 client::Client,
11 constant::ssh_transport_code,
12 error::{SshError, SshResult},
13 model::{Data, Packet, SecPacket},
14 util,
15};
16
17macro_rules! create_wrapped_type {
18 ($name: ident, $value_type: ty) => {
19 #[derive(Clone, Default)]
20 pub(crate) struct $name(Vec<$value_type>);
21 impl Deref for $name {
22 type Target = Vec<$value_type>;
23 fn deref(&self) -> &Self::Target {
24 &self.0
25 }
26 }
27
28 impl DerefMut for $name {
29 fn deref_mut(&mut self) -> &mut Self::Target {
30 &mut self.0
31 }
32 }
33
34 impl Display for $name {
35 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36 write!(
37 f,
38 "{}",
39 self.iter()
40 .map(|&x| x.as_ref().to_owned())
41 .collect::<Vec<String>>()
42 .join(",")
43 )
44 }
45 }
46
47 impl TryFrom<Vec<String>> for $name {
48 type Error = SshError;
49 fn try_from(v: Vec<String>) -> Result<Self, Self::Error> {
50 let v = v
51 .iter()
52 .filter_map(|x| <$value_type>::from_str(x.as_str()).ok())
53 .collect::<Vec<$value_type>>();
54 Ok(Self(v))
55 }
56 }
57
58 impl From<Vec<$value_type>> for $name {
59 fn from(v: Vec<$value_type>) -> Self {
60 Self(v)
61 }
62 }
63 };
64}
65
66create_wrapped_type!(Kexs, Kex);
67create_wrapped_type!(PubKeys, PubKey);
68create_wrapped_type!(Encs, Enc);
69create_wrapped_type!(Macs, Mac);
70create_wrapped_type!(Compresses, Compress);
71
72#[derive(Clone, Default)]
73pub(crate) struct AlgList {
74 pub key_exchange: Kexs,
75 pub public_key: PubKeys,
76 pub c_encryption: Encs,
77 pub s_encryption: Encs,
78 pub c_mac: Macs,
79 pub s_mac: Macs,
80 pub c_compress: Compresses,
81 pub s_compress: Compresses,
82}
83
84impl Debug for AlgList {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 write!(f, "kex: \"{}\", ", self.key_exchange)?;
87 write!(f, "pubkey: \"{}\", ", self.public_key)?;
88 write!(f, "c_enc: \"{}\", ", self.c_encryption)?;
89 write!(f, "s_enc: \"{}\", ", self.s_encryption)?;
90 write!(f, "c_mac: \"{}\", ", self.c_mac)?;
91 write!(f, "s_mac: \"{}\", ", self.s_mac)?;
92 write!(f, "c_compress: \"{}\", ", self.c_compress)?;
93 write!(f, "s_compress: \"{}\"", self.s_compress)
94 }
95}
96
97impl AlgList {
98 pub fn new() -> Self {
99 AlgList {
100 ..Default::default()
101 }
102 }
103
104 pub fn client_default() -> Self {
105 AlgList {
106 key_exchange: vec![
107 Kex::Curve25519Sha256,
108 Kex::EcdhSha2Nistrp256,
109 Kex::DiffieHellmanGroup14Sha256,
110 Kex::DiffieHellmanGroup14Sha1,
111 ]
112 .into(),
113 public_key: vec![PubKey::RsaSha2_512, PubKey::RsaSha2_256].into(),
114 c_encryption: vec![
115 Enc::Chacha20Poly1305Openssh,
116 Enc::Aes128Ctr,
117 Enc::Aes192Ctr,
118 Enc::Aes256Ctr,
119 ]
120 .into(),
121 s_encryption: vec![
122 Enc::Chacha20Poly1305Openssh,
123 Enc::Aes128Ctr,
124 Enc::Aes192Ctr,
125 Enc::Aes256Ctr,
126 ]
127 .into(),
128 c_mac: vec![Mac::HmacSha2_256, Mac::HmacSha2_512, Mac::HmacSha1].into(),
129 s_mac: vec![Mac::HmacSha2_256, Mac::HmacSha2_512, Mac::HmacSha1].into(),
130 c_compress: vec![Compress::None, Compress::ZlibOpenSsh].into(),
131 s_compress: vec![Compress::None, Compress::ZlibOpenSsh].into(),
132 }
133 }
134
135 fn from(mut data: Data) -> SshResult<Self> {
136 data.get_u8();
137 data.skip(16);
139 let mut server_algorithm = Self::new();
140
141 macro_rules! try_convert {
142 ($hint: literal, $field: ident) => {
143 let alg_string = util::vec_u8_to_string(data.get_u8s(), ",")?;
144 info!("server {}: {:?}", $hint, alg_string);
145 server_algorithm.$field = alg_string.try_into()?;
146 };
147 }
148 try_convert!("key exchange", key_exchange);
149 try_convert!("public key", public_key);
150 try_convert!("c2s encryption", c_encryption);
151 try_convert!("s2c encryption", s_encryption);
152 try_convert!("c2s mac", c_mac);
153 try_convert!("s2c mac", s_mac);
154 try_convert!("c2s compression", c_compress);
155 try_convert!("s2c compression", s_compress);
156 debug!("converted server algorithms: [{:?}]", server_algorithm);
157 Ok(server_algorithm)
158 }
159
160 pub fn match_with(&self, other: &Self) -> SshResult<Self> {
161 macro_rules! match_field {
162 ($our: expr, $their:expr, $field: ident, $err_hint: literal) => {
163 $our.$field
164 .iter()
165 .find_map(|k| {
166 if $their.$field.contains(k) {
167 Some(k)
168 } else {
169 None
170 }
171 })
172 .ok_or_else(|| {
173 let err_msg = format!(
174 "Key_agreement: the {} fails to match, \
175 algorithms supported by the server: {},\
176 algorithms supported by the client: {}",
177 $err_hint, $their.$field, $our.$field
178 );
179 error!(err_msg);
180 SshError::KexError(err_msg)
181 })
182 };
183 }
184
185 let kex = match_field!(self, other, key_exchange, "DH algorithm")?;
187 let pubkey = match_field!(self, other, public_key, "signature algorithm")?;
189 let c_enc = match_field!(self, other, c_encryption, "client encryption algorithm")?;
191 let s_enc = match_field!(self, other, s_encryption, "server encryption algorithm")?;
192
193 let c_mac = match_field!(self, other, c_mac, "client mac algorithm")?;
195 let s_mac = match_field!(self, other, s_mac, "server mac algorithm")?;
196
197 let c_compress = match_field!(self, other, c_compress, "client compression algorithm")?;
199 let s_compress = match_field!(self, other, s_compress, "server compression algorithm")?;
200
201 let negotiated = Self {
202 key_exchange: vec![*kex].into(),
203 public_key: vec![*pubkey].into(),
204 c_encryption: vec![*c_enc].into(),
205 s_encryption: vec![*s_enc].into(),
206 c_mac: vec![*c_mac].into(),
207 s_mac: vec![*s_mac].into(),
208 c_compress: vec![*c_compress].into(),
209 s_compress: vec![*s_compress].into(),
210 };
211
212 info!("matched algorithms [{:?}]", negotiated);
213
214 Ok(negotiated)
215 }
216
217 fn as_i(&self) -> Vec<u8> {
218 let mut data = Data::new();
219 data.put_str(&self.key_exchange.to_string());
220 data.put_str(&self.public_key.to_string());
221 data.put_str(&self.c_encryption.to_string());
222 data.put_str(&self.s_encryption.to_string());
223 data.put_str(&self.c_mac.to_string());
224 data.put_str(&self.s_mac.to_string());
225 data.put_str(&self.c_compress.to_string());
226 data.put_str(&self.s_compress.to_string());
227 data.to_vec()
228 }
229}
230
231impl<'a> Packet<'a> for AlgList {
232 fn pack(self, client: &'a mut Client) -> crate::model::SecPacket<'a> {
233 info!("client algorithms: [{:?}]", self);
234 let mut data = Data::new();
235 data.put_u8(ssh_transport_code::KEXINIT);
236 data.extend(util::cookie());
237 data.extend(self.as_i());
238 data.put_str("")
239 .put_str("")
240 .put_u8(false as u8)
241 .put_u32(0_u32);
242
243 (data, client).into()
244 }
245
246 fn unpack(pkt: SecPacket) -> SshResult<Self>
247 where
248 Self: Sized,
249 {
250 let data = pkt.into_inner();
251 assert_eq!(data[0], ssh_transport_code::KEXINIT);
252 AlgList::from(data)
253 }
254}