@@ -55,6 +55,18 @@ pub struct Preferred {
5555 pub compression : & ' static [ & ' static str ] ,
5656}
5757
58+ impl Preferred {
59+ pub ( crate ) fn possible_host_key_algos_for_keys (
60+ & self ,
61+ available_host_keys : & [ KeyPair ] ,
62+ ) -> Vec < & ' static key:: Name > {
63+ self . key
64+ . iter ( )
65+ . filter ( |n| available_host_keys. iter ( ) . any ( |k| k. name ( ) == n. 0 ) )
66+ . collect :: < Vec < _ > > ( )
67+ }
68+ }
69+
5870const SAFE_KEX_ORDER : & [ kex:: Name ] = & [
5971 kex:: CURVE25519 ,
6072 kex:: CURVE25519_PRE_RFC_8731 ,
@@ -158,19 +170,28 @@ pub(crate) trait Select {
158170
159171 fn select < S : AsRef < str > + Copy > ( a : & [ S ] , b : & [ u8 ] ) -> Option < ( bool , S ) > ;
160172
161- fn read_kex ( buffer : & [ u8 ] , pref : & Preferred ) -> Result < Names , Error > {
173+ /// `available_host_keys`, if present, is used to limit the host key algorithms to the ones we have keys for.
174+ fn read_kex (
175+ buffer : & [ u8 ] ,
176+ pref : & Preferred ,
177+ available_host_keys : Option < & [ KeyPair ] > ,
178+ ) -> Result < Names , Error > {
162179 let mut r = buffer. reader ( 17 ) ;
180+
181+ // Key exchange
182+
163183 let kex_string = r. read_string ( ) ?;
164- let ( kex_both_first, kex_algorithm) = if let Some ( x) = Self :: select ( pref. kex , kex_string) {
165- x
166- } else {
184+ let ( kex_both_first, kex_algorithm) = Self :: select ( pref. kex , kex_string) . ok_or_else ( ||
185+ {
167186 debug ! (
168187 "Could not find common kex algorithm, other side only supports {:?}, we only support {:?}" ,
169188 from_utf8( kex_string) ,
170189 pref. kex
171190 ) ;
172- return Err ( Error :: NoCommonKexAlgo ) ;
173- } ;
191+ Error :: NoCommonKexAlgo
192+ } ) ?;
193+
194+ // Strict kex detection
174195
175196 let strict_kex_requested = pref. kex . contains ( if Self :: is_server ( ) {
176197 & EXTENSION_OPENSSH_STRICT_KEX_AS_SERVER
@@ -190,35 +211,42 @@ pub(crate) trait Select {
190211 debug ! ( "strict kex enabled" )
191212 }
192213
193- let key_string = r. read_string ( ) ?;
194- let ( key_both_first, key_algorithm) = if let Some ( x) = Self :: select ( pref. key , key_string) {
195- x
196- } else {
197- debug ! (
198- "Could not find common key algorithm, other side only supports {:?}, we only support {:?}" ,
199- from_utf8( key_string) ,
200- pref. key
201- ) ;
202- return Err ( Error :: NoCommonKeyAlgo ) ;
214+ // Host key
215+
216+ let key_string: & [ u8 ] = r. read_string ( ) ?;
217+ let possible_host_key_algos = match available_host_keys {
218+ Some ( available_host_keys) => pref. possible_host_key_algos_for_keys ( available_host_keys) ,
219+ None => pref. key . iter ( ) . collect :: < Vec < _ > > ( ) ,
203220 } ;
204221
222+ let ( key_both_first, key_algorithm) =
223+ Self :: select ( & possible_host_key_algos[ ..] , key_string) . ok_or_else ( || {
224+ debug ! (
225+ "Could not find common key algorithm, other side only supports {:?}, we only support {:?}" ,
226+ from_utf8( key_string) ,
227+ pref. key
228+ ) ;
229+ Error :: NoCommonKeyAlgo
230+ } ) ?;
231+
232+ // Cipher
233+
205234 let cipher_string = r. read_string ( ) ?;
206- let cipher = Self :: select ( pref . cipher , cipher_string ) ;
207- if cipher. is_none ( ) {
208- debug ! (
235+ let ( _cipher_both_first , cipher ) =
236+ Self :: select ( pref . cipher , cipher_string ) . ok_or_else ( || {
237+ debug ! (
209238 "Could not find common cipher, other side only supports {:?}, we only support {:?}" ,
210239 from_utf8( cipher_string) ,
211240 pref. cipher
212241 ) ;
213- return Err ( Error :: NoCommonCipher ) ;
214- }
242+ Error :: NoCommonCipher
243+ } ) ? ;
215244 r. read_string ( ) ?; // cipher server-to-client.
216245 debug ! ( "kex {}" , line!( ) ) ;
217246
218- let need_mac = cipher
219- . and_then ( |x| CIPHERS . get ( & x. 1 ) )
220- . map ( |x| x. needs_mac ( ) )
221- . unwrap_or ( false ) ;
247+ // MAC
248+
249+ let need_mac = CIPHERS . get ( & cipher) . map ( |x| x. needs_mac ( ) ) . unwrap_or ( false ) ;
222250
223251 let client_mac = if let Some ( ( _, m) ) = Self :: select ( pref. mac , r. read_string ( ) ?) {
224252 m
@@ -235,6 +263,8 @@ pub(crate) trait Select {
235263 mac:: NONE
236264 } ;
237265
266+ // Compression
267+
238268 debug ! ( "kex {}" , line!( ) ) ;
239269 // client-to-server compression.
240270 let client_compression =
@@ -256,23 +286,18 @@ pub(crate) trait Select {
256286 r. read_string ( ) ?; // languages server-to-client
257287
258288 let follows = r. read_byte ( ) ? != 0 ;
259- match ( cipher, follows) {
260- ( Some ( ( _, cipher) ) , fol) => {
261- Ok ( Names {
262- kex : kex_algorithm,
263- key : key_algorithm,
264- cipher,
265- client_mac,
266- server_mac,
267- client_compression,
268- server_compression,
269- // Ignore the next packet if (1) it follows and (2) it's not the correct guess.
270- ignore_guessed : fol && !( kex_both_first && key_both_first) ,
271- strict_kex : strict_kex_requested && strict_kex_provided,
272- } )
273- }
274- _ => Err ( Error :: KexInit ) ,
275- }
289+ Ok ( Names {
290+ kex : kex_algorithm,
291+ key : * key_algorithm,
292+ cipher,
293+ client_mac,
294+ server_mac,
295+ client_compression,
296+ server_compression,
297+ // Ignore the next packet if (1) it follows and (2) it's not the correct guess.
298+ ignore_guessed : follows && !( kex_both_first && key_both_first) ,
299+ strict_kex : strict_kex_requested && strict_kex_provided,
300+ } )
276301 }
277302}
278303
0 commit comments