mod groups; use std::marker::PhantomData; use byteorder::{BigEndian, ByteOrder}; use digest::Digest; use groups::DH; use log::debug; use num_bigint::BigUint; use sha1::Sha1; use sha2::{Sha256, Sha512}; use self::groups::{DhGroup, DH_GROUP1, DH_GROUP14, DH_GROUP16}; use super::{compute_keys, KexAlgorithm, KexType}; use crate::keys::encoding::Encoding; use crate::session::Exchange; use crate::{cipher, mac, msg, CryptoVec}; pub struct DhGroup1Sha1KexType {} impl KexType for DhGroup1Sha1KexType { fn make(&self) -> Box { Box::new(DhGroupKex::::new(&DH_GROUP1)) as Box } } pub struct DhGroup14Sha1KexType {} impl KexType for DhGroup14Sha1KexType { fn make(&self) -> Box { Box::new(DhGroupKex::::new(&DH_GROUP14)) as Box } } pub struct DhGroup14Sha256KexType {} impl KexType for DhGroup14Sha256KexType { fn make(&self) -> Box { Box::new(DhGroupKex::::new(&DH_GROUP14)) as Box } } pub struct DhGroup16Sha512KexType {} impl KexType for DhGroup16Sha512KexType { fn make(&self) -> Box { Box::new(DhGroupKex::::new(&DH_GROUP16)) as Box } } #[doc(hidden)] pub struct DhGroupKex { dh: DH, shared_secret: Option>, _digest: PhantomData, } impl DhGroupKex { pub fn new(group: &DhGroup) -> DhGroupKex { let dh = DH::new(group); DhGroupKex { dh, shared_secret: None, _digest: PhantomData, } } } impl std::fmt::Debug for DhGroupKex { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!( f, "Algorithm {{ local_secret: [hidden], shared_secret: [hidden] }}", ) } } fn biguint_to_mpint(biguint: &BigUint) -> Vec { let mut mpint = Vec::new(); let bytes = biguint.to_bytes_be(); if let Some(b) = bytes.first() { if b > &0x7f { mpint.push(0); } } mpint.extend(&bytes); mpint } impl KexAlgorithm for DhGroupKex { fn skip_exchange(&self) -> bool { false } #[doc(hidden)] fn server_dh(&mut self, exchange: &mut Exchange, payload: &[u8]) -> Result<(), crate::Error> { debug!("server_dh"); let client_pubkey = { if payload.first() != Some(&msg::KEX_ECDH_INIT) { return Err(crate::Error::Inconsistent); } #[allow(clippy::indexing_slicing)] // length checked let pubkey_len = BigEndian::read_u32(&payload[1..]) as usize; if payload.len() < 5 + pubkey_len { return Err(crate::Error::Inconsistent); } &payload .get(5..(5 + pubkey_len)) .ok_or(crate::Error::Inconsistent)? }; debug!("client_pubkey: {:?}", client_pubkey); self.dh.generate_private_key(true); let server_pubkey = &self.dh.generate_public_key(); if !self.dh.validate_public_key(server_pubkey) { return Err(crate::Error::Inconsistent); } let encoded_server_pubkey = biguint_to_mpint(server_pubkey); // fill exchange. exchange.server_ephemeral.clear(); exchange.server_ephemeral.extend(&encoded_server_pubkey); let decoded_client_pubkey = DH::decode_public_key(client_pubkey); if !self.dh.validate_public_key(&decoded_client_pubkey) { return Err(crate::Error::Inconsistent); } let shared = self.dh.compute_shared_secret(decoded_client_pubkey); if !self.dh.validate_shared_secret(&shared) { return Err(crate::Error::Inconsistent); } self.shared_secret = Some(biguint_to_mpint(&shared)); Ok(()) } #[doc(hidden)] fn client_dh( &mut self, client_ephemeral: &mut CryptoVec, buf: &mut CryptoVec, ) -> Result<(), crate::Error> { self.dh.generate_private_key(false); let client_pubkey = &self.dh.generate_public_key(); if !self.dh.validate_public_key(client_pubkey) { return Err(crate::Error::Inconsistent); } // fill exchange. let encoded_pubkey = biguint_to_mpint(client_pubkey); client_ephemeral.clear(); client_ephemeral.extend(&encoded_pubkey); buf.push(msg::KEX_ECDH_INIT); buf.extend_ssh_string(&encoded_pubkey); Ok(()) } fn compute_shared_secret(&mut self, remote_pubkey_: &[u8]) -> Result<(), crate::Error> { let remote_pubkey = DH::decode_public_key(remote_pubkey_); if !self.dh.validate_public_key(&remote_pubkey) { return Err(crate::Error::Inconsistent); } let shared = self.dh.compute_shared_secret(remote_pubkey); if !self.dh.validate_shared_secret(&shared) { return Err(crate::Error::Inconsistent); } self.shared_secret = Some(biguint_to_mpint(&shared)); Ok(()) } fn compute_exchange_hash( &self, key: &CryptoVec, exchange: &Exchange, buffer: &mut CryptoVec, ) -> Result { // Computing the exchange hash, see page 7 of RFC 5656. buffer.clear(); buffer.extend_ssh_string(&exchange.client_id); buffer.extend_ssh_string(&exchange.server_id); buffer.extend_ssh_string(&exchange.client_kex_init); buffer.extend_ssh_string(&exchange.server_kex_init); buffer.extend(key); buffer.extend_ssh_string(&exchange.client_ephemeral); buffer.extend_ssh_string(&exchange.server_ephemeral); if let Some(ref shared) = self.shared_secret { buffer.extend_ssh_mpint(shared); } let mut hasher = D::new(); hasher.update(&buffer); let mut res = CryptoVec::new(); res.extend(hasher.finalize().as_slice()); Ok(res) } fn compute_keys( &self, session_id: &CryptoVec, exchange_hash: &CryptoVec, cipher: cipher::Name, remote_to_local_mac: mac::Name, local_to_remote_mac: mac::Name, is_server: bool, ) -> Result { compute_keys::( self.shared_secret.as_deref(), session_id, exchange_hash, cipher, remote_to_local_mac, local_to_remote_mac, is_server, ) } }