@@ -6,7 +6,7 @@ use std::sync::Arc;
66use bytes:: Bytes ;
77use log:: { debug, error, warn} ;
88use ssh_encoding:: { Decode , Encode } ;
9- use ssh_key:: { Mpint , PublicKey , Signature } ;
9+ use ssh_key:: { Certificate , Mpint , PublicKey , Signature } ;
1010
1111use super :: IncomingSshPacket ;
1212use crate :: client:: { Config , NewKeys } ;
@@ -38,6 +38,7 @@ enum ClientKexState {
3838 } ,
3939 WaitingForNewKeys {
4040 server_host_key : PublicKey ,
41+ server_host_certificate : Option < Certificate > ,
4142 newkeys : NewKeys ,
4243 } ,
4344}
@@ -152,6 +153,7 @@ impl ClientKex {
152153 } ) ?;
153154
154155 return Ok ( KexProgress :: Done {
156+ server_host_certificate : None ,
155157 newkeys,
156158 server_host_key : None ,
157159 } ) ;
@@ -263,11 +265,31 @@ impl ClientKex {
263265 #[ allow( clippy:: indexing_slicing) ] // length checked
264266 let r = & mut & input. buffer [ 1 ..] ;
265267
266- let server_host_key = Bytes :: decode ( r) ?; // server public key.
267- let server_host_key = parse_public_key ( & server_host_key) ?;
268+ // The raw blob is kept as well as the parsed key. It is what
269+ // goes into the exchange hash below: for a certificate the
270+ // parsed form is only the key *inside* it, and re-encoding that
271+ // would hash something the server never sent — a failure that
272+ // looks like a bad signature and is computed entirely locally,
273+ // so there is nothing on the wire to compare against.
274+ let server_host_key_blob = Bytes :: decode ( r) ?;
275+ let server_host_certificate = if names. host_key_is_certificate {
276+ Some ( Certificate :: from_bytes ( & server_host_key_blob) ?)
277+ } else {
278+ None
279+ } ;
280+ let server_host_key = match & server_host_certificate {
281+ // The certificate's own signature is checked by the client
282+ // against its trusted authorities, not here; what the key
283+ // exchange is signed with is the key the certificate
284+ // contains. The two are separate proofs and collapsing them
285+ // would accept a certificate nobody vouched for.
286+ Some ( certificate) => PublicKey :: new ( certificate. public_key ( ) . clone ( ) , "" ) ,
287+ None => parse_public_key ( & server_host_key_blob) ?,
288+ } ;
268289 debug ! (
269- "received server host key: {:?}" ,
270- server_host_key. to_openssh( )
290+ "received server host key: {:?} (certificate: {})" ,
291+ server_host_key. to_openssh( ) ,
292+ server_host_certificate. is_some( )
271293 ) ;
272294
273295 let server_ephemeral = Bytes :: decode ( r) ?;
@@ -277,7 +299,7 @@ impl ClientKex {
277299 kex. compute_shared_secret ( & self . exchange . server_ephemeral ) ?;
278300
279301 let mut pubkey_vec = Vec :: new ( ) ;
280- server_host_key . to_bytes ( ) ? . encode ( & mut pubkey_vec) ?;
302+ server_host_key_blob . encode ( & mut pubkey_vec) ?;
281303
282304 let exchange = & self . exchange ;
283305 let hash = HASH_BUFFER . with ( {
@@ -318,6 +340,7 @@ impl ClientKex {
318340
319341 self . state = ClientKexState :: WaitingForNewKeys {
320342 server_host_key,
343+ server_host_certificate,
321344 newkeys,
322345 } ;
323346
@@ -328,6 +351,7 @@ impl ClientKex {
328351 }
329352 ClientKexState :: WaitingForNewKeys {
330353 server_host_key,
354+ server_host_certificate,
331355 newkeys,
332356 } => {
333357 // At this point the exchange is complete
@@ -349,6 +373,7 @@ impl ClientKex {
349373 ensure_end ( & r) ?;
350374
351375 Ok ( KexProgress :: Done {
376+ server_host_certificate,
352377 newkeys,
353378 server_host_key : Some ( server_host_key) ,
354379 } )
0 commit comments