ssh/client/
client_kex.rs

1#[cfg(feature = "deprecated-zlib")]
2use crate::algorithm::{compression, Compress};
3use crate::{
4    algorithm::{
5        encryption,
6        hash::{self, HashCtx},
7        key_exchange::{self, KeyExchange},
8        mac,
9        public_key::{self, PublicKey},
10        Digest,
11    },
12    client::Client,
13    config::algorithm::AlgList,
14    constant::ssh_transport_code,
15    error::{SshError, SshResult},
16    model::{Data, Packet, SecPacket},
17};
18use std::io::{Read, Write};
19use tracing::*;
20
21impl Client {
22    pub fn key_agreement<S>(
23        &mut self,
24        stream: &mut S,
25        server_algs: AlgList,
26        digest: &mut Digest,
27    ) -> SshResult<()>
28    where
29        S: Read + Write,
30    {
31        // initialize the hash context
32        digest.hash_ctx.set_v_c(&self.config.ver.client_ver);
33        digest.hash_ctx.set_v_s(&self.config.ver.server_ver);
34
35        info!("start for key negotiation.");
36        info!("send client algorithm list.");
37
38        let algs = self.config.algs.clone();
39        let client_algs = algs.pack(self);
40        digest.hash_ctx.set_i_c(client_algs.get_inner());
41        client_algs.write_stream(stream)?;
42
43        let negotiated = self.config.algs.match_with(&server_algs)?;
44
45        // key exchange algorithm
46        let mut key_exchange = key_exchange::from(&negotiated.key_exchange[0])?;
47        self.send_qc(stream, key_exchange.get_public_key())?;
48
49        // host key algorithm
50        let mut public_key = public_key::from(&negotiated.public_key[0]);
51
52        // generate session id
53        let session_id = {
54            let session_id = self.verify_signature_and_new_keys(
55                stream,
56                &mut public_key,
57                &mut key_exchange,
58                &mut digest.hash_ctx,
59            )?;
60
61            if self.session_id.is_empty() {
62                session_id
63            } else {
64                self.session_id.clone()
65            }
66        };
67
68        let hash = hash::Hash::new(
69            digest.hash_ctx.clone(),
70            &session_id,
71            key_exchange.get_hash_type(),
72        );
73
74        // mac algorithm
75        let mac = mac::from(&negotiated.c_mac[0]);
76
77        // encryption algorithm
78        let encryption = encryption::from(&negotiated.c_encryption[0], hash, mac);
79
80        self.session_id = session_id;
81        self.negotiated = negotiated;
82        self.encryptor = encryption;
83
84        #[cfg(feature = "deprecated-zlib")]
85        {
86            if let Compress::Zlib = self.negotiated.c_compress[0] {
87                let comp = compression::from(&Compress::Zlib);
88                self.compressor = comp;
89            }
90        }
91
92        digest.key_exchange = Some(key_exchange);
93
94        info!("key negotiation successful.");
95
96        Ok(())
97    }
98
99    /// Send the public key
100    fn send_qc<S>(&mut self, stream: &mut S, public_key: &[u8]) -> SshResult<()>
101    where
102        S: Read + Write,
103    {
104        let mut data = Data::new();
105        data.put_u8(ssh_transport_code::KEXDH_INIT)
106            .put_u8s(public_key);
107        data.pack(self).write_stream(stream)
108    }
109
110    fn verify_signature_and_new_keys<S>(
111        &mut self,
112        stream: &mut S,
113        public_key: &mut Box<dyn PublicKey>,
114        key_exchange: &mut Box<dyn KeyExchange>,
115        h: &mut HashCtx,
116    ) -> SshResult<Vec<u8>>
117    where
118        S: Read + Write,
119    {
120        let mut session_id = vec![];
121        loop {
122            let mut data = Data::unpack(SecPacket::from_stream(stream, self)?)?;
123            let message_code = data.get_u8();
124            match message_code {
125                ssh_transport_code::KEXDH_REPLY => {
126                    // Generate the session id, get the signature
127                    let sig = self.generate_signature(data, h, key_exchange)?;
128                    // verify the signature
129                    session_id = hash::digest(&h.as_bytes(), key_exchange.get_hash_type());
130                    let flag = public_key.verify_signature(&h.k_s, &session_id, &sig)?;
131                    if !flag {
132                        let err_msg = "signature verification failure.".to_owned();
133                        error!(err_msg);
134                        return Err(SshError::KexError(err_msg));
135                    }
136                    info!("signature verification success.");
137                }
138                ssh_transport_code::NEWKEYS => {
139                    self.new_keys(stream)?;
140                    return Ok(session_id);
141                }
142                _ => unreachable!(),
143            }
144        }
145    }
146
147    /// get the signature
148    fn generate_signature(
149        &mut self,
150        mut data: Data,
151        h: &mut HashCtx,
152        key_exchange: &mut Box<dyn KeyExchange>,
153    ) -> SshResult<Vec<u8>> {
154        let ks = data.get_u8s();
155        h.set_k_s(&ks);
156        // TODO:
157        //   No fingerprint verification
158        let qs = data.get_u8s();
159        h.set_e(key_exchange.get_public_key());
160        h.set_f(&qs);
161        let vec = key_exchange.get_shared_secret(qs)?;
162        h.set_k(&vec);
163        let h = data.get_u8s();
164        let mut hd = Data::from(h);
165        hd.get_u8s();
166        let signature = hd.get_u8s();
167        Ok(signature)
168    }
169
170    /// NEWKEYS indicates that kex is done
171    fn new_keys<S>(&mut self, stream: &mut S) -> SshResult<()>
172    where
173        S: Write,
174    {
175        let mut data = Data::new();
176        data.put_u8(ssh_transport_code::NEWKEYS);
177        info!("send new keys");
178        data.pack(self).write_stream(stream)
179    }
180}