diff --git a/README.md b/README.md index 3821fd8..becdc3c 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,19 @@ A ssh client and server written entirely on rust, primarily targeted at [Redox O Currently implemented features, ordered by priority: - - [ ] SSH Server + - [x] SSH Server - [ ] SSH Client - - [ ] Key Exchange algorithms + - Key Exchange algorithms - [ ] `diffie-hellman-group-exchange-sha1` - [x] `curve25519-sha256` - - [ ] Public Key algorithms + - Public Key algorithms - [ ] `ssh-rsa` - [x] `ssh-ed25519` - - [ ] Encryption algorithms - - [ ] `aes256-ctr` + - Encryption algorithms + - [x] `aes256-ctr` - [ ] `aes256-gcm` + - MAC algorithms + - [x] `hmac-sha2-256` - [ ] Port forwarding - [ ] SCP File Transfers diff --git a/src/algorithm.rs b/src/algorithm.rs index 870434d..4e9996b 100644 --- a/src/algorithm.rs +++ b/src/algorithm.rs @@ -21,7 +21,7 @@ pub static ENCRYPTION: &[EncryptionAlgorithm] = &[EncryptionAlgorithm::AES256_CTR]; /// Slice of implemented MAC algorithms, ordered by preference -pub static MAC: &[MacAlgorithm] = &[MacAlgorithm::HMAC_SHA2_512]; +pub static MAC: &[MacAlgorithm] = &[MacAlgorithm::HMAC_SHA2_256]; /// Slice of implemented compression algorithms, ordered by preference pub static COMPRESSION: &[CompressionAlgorithm] = diff --git a/src/connection.rs b/src/connection.rs index 355899e..90ddee8 100644 --- a/src/connection.rs +++ b/src/connection.rs @@ -5,6 +5,7 @@ use std::sync::Arc; use encryption::{AesCtr, Decryptor, Encryption}; use error::{ConnectionError, ConnectionResult}; use key_exchange::{self, KexResult, KeyExchange}; +use mac::{Hmac, MacAlgorithm}; use message::MessageType; use packet::{Packet, ReadPacketExt, WritePacketExt}; use server::ServerConfig; @@ -38,6 +39,8 @@ pub struct Connection { stream: Box, session_id: Option>, encryption: Option<(Box, Box)>, + mac: Option<(Box, Box)>, + seq: (u32, u32), } impl<'a> Connection { @@ -50,6 +53,8 @@ impl<'a> Connection { stream: Box::new(stream), session_id: None, encryption: None, + mac: None, + seq: (0, 0), } } @@ -61,20 +66,58 @@ impl<'a> Connection { loop { let packet = if let Some((ref mut c2s, _)) = self.encryption { - println!("decrypting!!!"); let mut decryptor = Decryptor::new(&mut **c2s, &mut reader); Packet::read_from(&mut decryptor)? } else { Packet::read_from(&mut reader)? }; - trace!("Packet received: {:?}", packet); + + if let Some((ref mut mac, _)) = self.mac { + let mut sig = vec![0; mac.size()]; + reader.read_exact(&mut sig)?; + + let mut sig_cmp = vec![0; mac.size()]; + mac.sign(packet.data(), self.seq.0, sig_cmp.as_mut_slice()); + + if sig != sig_cmp { + return Err(ConnectionError::IntegrityError); + } + } + + trace!("Packet {} received: {:?}", self.seq.0, packet); self.process(packet)?; + + self.seq.0 += 1; } } - pub fn send(&mut self, packet: &Packet) -> io::Result<()> { - packet.write_to(&mut self.stream) + pub fn send(&mut self, packet: Packet) -> io::Result<()> { + trace!("Sending packet {}: {:?}", self.seq.1, packet); + + let packet = packet.to_raw()?; + + if let Some((_, ref mut s2c)) = self.encryption { + + let mut encrypted = vec![0; packet.data().len()]; + s2c.encrypt(packet.data(), encrypted.as_mut_slice()); + + // Sending encrypted packet + self.stream.write_all(encrypted.as_slice())?; + } + else { + packet.write_to(&mut self.stream)?; + } + + self.seq.1 += 1; + + if let Some((_, ref mut mac)) = self.mac { + let mut sig = vec![0; mac.size()]; + mac.sign(packet.data(), self.seq.1, sig.as_mut_slice()); + self.stream.write_all(sig.as_slice())?; + } + + Ok(()) } fn send_id(&mut self) -> io::Result<()> { @@ -134,18 +177,18 @@ impl<'a> Connection { match packet.msg_type() { MessageType::KexInit => { - println!("Starting Key Exchange!"); + debug!("Starting key exchange"); self.kex_init(packet) } MessageType::NewKeys => { - println!("Switching to new Keys"); + debug!("Switching to new keys"); let iv_c2s = self.generate_key(b"A", 256)?; let iv_s2c = self.generate_key(b"B", 256)?; let enc_c2s = self.generate_key(b"C", 256)?; let enc_s2c = self.generate_key(b"D", 256)?; - let int_c2s = self.generate_key(b"E", 256)?; - let int_s2c = self.generate_key(b"F", 256)?; + let mac_c2s = self.generate_key(b"E", 256)?; + let mac_s2c = self.generate_key(b"F", 256)?; self.encryption = Some(( @@ -157,6 +200,29 @@ impl<'a> Connection { ), )); + self.mac = Some(( + Box::new(Hmac::new(mac_c2s.as_slice())), + Box::new(Hmac::new(mac_s2c.as_slice())), + )); + + Ok(()) + } + MessageType::ServiceRequest => { + let mut reader = packet.reader(); + let name = reader.read_string()?; + + trace!( + "{:?}", + ::std::str::from_utf8(&name.as_slice()).unwrap() + ); + + let mut res = Packet::new(MessageType::ServiceAccept); + res.with_writer(&|w| { + w.write_bytes(name.as_slice())?; + Ok(()) + })?; + + self.send(res)?; Ok(()) } MessageType::KeyExchange(_) => { @@ -168,7 +234,7 @@ impl<'a> Connection { { KexResult::Done(packet) => { self.state = ConnectionState::Established; - self.send(&packet)?; + self.send(packet)?; if self.session_id.is_none() { self.session_id = @@ -176,11 +242,11 @@ impl<'a> Connection { } let packet = Packet::new(MessageType::NewKeys); - self.send(&packet)?; + self.send(packet)?; Ok(()) } KexResult::Ok(packet) => { - self.send(&packet)?; + self.send(packet)?; Ok(()) } KexResult::Error => Err(ConnectionError::KeyExchangeError), @@ -190,8 +256,8 @@ impl<'a> Connection { Ok(()) } _ => { - println!("Unhandled packet: {:?}", packet); - Err(ConnectionError::KeyExchangeError) + error!("Unhandled packet: {:?}", packet); + Err(ConnectionError::ProtocolError) } } } @@ -220,11 +286,11 @@ impl<'a> Connection { let mac_algo = negotiate(MAC, mac_algos_s2c.as_slice())?; let comp_algo = negotiate(COMPRESSION, comp_algos_s2c.as_slice())?; - println!("Negotiated Kex Algorithm: {:?}", kex_algo); - println!("Negotiated Host Key Algorithm: {:?}", srv_host_key_algo); - println!("Negotiated Encryption Algorithm: {:?}", enc_algo); - println!("Negotiated Mac Algorithm: {:?}", mac_algo); - println!("Negotiated Comp Algorithm: {:?}", comp_algo); + debug!("Negotiated Kex Algorithm: {:?}", kex_algo); + debug!("Negotiated Host Key Algorithm: {:?}", srv_host_key_algo); + debug!("Negotiated Encryption Algorithm: {:?}", enc_algo); + debug!("Negotiated Mac Algorithm: {:?}", mac_algo); + debug!("Negotiated Comp Algorithm: {:?}", comp_algo); } // Save payload for hash generation diff --git a/src/encryption/aes_ctr.rs b/src/encryption/aes_ctr.rs index 17f6bb2..75df3dc 100644 --- a/src/encryption/aes_ctr.rs +++ b/src/encryption/aes_ctr.rs @@ -9,16 +9,18 @@ pub struct AesCtr { impl AesCtr { pub fn new(key: &[u8], iv: &[u8]) -> AesCtr { - AesCtr { cipher: ctr(KeySize::KeySize256, key, iv) } + AesCtr { cipher: ctr(KeySize::KeySize256, key, &iv[0..16]) } } } impl Encryption for AesCtr { fn encrypt(&mut self, data: &[u8], buf: &mut [u8]) { + trace!("Encrypting {} -> {}", data.len(), buf.len()); self.cipher.process(data, buf); } fn decrypt(&mut self, data: &[u8], buf: &mut [u8]) { - self.encrypt(data, buf); + trace!("Decrypting {} -> {}", data.len(), buf.len()); + self.cipher.process(data, buf); } } diff --git a/src/encryption/mod.rs b/src/encryption/mod.rs index 66f921e..e5892ca 100644 --- a/src/encryption/mod.rs +++ b/src/encryption/mod.rs @@ -27,8 +27,11 @@ impl<'a> Decryptor<'a> { impl<'a> Read for Decryptor<'a> { fn read(&mut self, buf: &mut [u8]) -> io::Result { let mut tmp = vec![0; buf.len()]; - self.stream.read(tmp.as_mut_slice())?; - self.encryption.decrypt(tmp.as_slice(), buf); - Ok(buf.len()) + let count = self.stream.read(tmp.as_mut_slice())?; + self.encryption.decrypt( + &tmp.as_slice()[0..count], + &mut buf[0..count], + ); + Ok(count) } } diff --git a/src/error.rs b/src/error.rs index 9ee5d45..3f7f67a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -12,6 +12,7 @@ pub enum ConnectionError { NegotiationError, KeyExchangeError, KeyGenerationError, + IntegrityError, } impl fmt::Display for ConnectionError { @@ -30,6 +31,7 @@ impl Error for ConnectionError { &NegotiationError => "negotiation error", &KeyExchangeError => "key exchange error", &KeyGenerationError => "key generation error", + &IntegrityError => "integrity error", } } } diff --git a/src/key_exchange/curve25519.rs b/src/key_exchange/curve25519.rs index 3decc81..8e3457b 100644 --- a/src/key_exchange/curve25519.rs +++ b/src/key_exchange/curve25519.rs @@ -12,7 +12,7 @@ const ECDH_KEX_INIT: u8 = 30; const ECDH_KEX_REPLY: u8 = 31; pub struct Curve25519 { - shared_secret: Option<[u8; 32]>, + shared_secret: Option>, exchange_hash: Option>, } @@ -65,7 +65,6 @@ impl KeyExchange for Curve25519 { key }; - println!("Received qc: {:?}", client_public); let mut packet = Packet::new(MessageType::KeyExchange(ECDH_KEX_REPLY)); @@ -82,8 +81,14 @@ impl KeyExchange for Curve25519 { }; let server_public = curve25519::curve25519_base(&server_secret); - let shared_secret = - curve25519::curve25519(&server_secret, &client_public); + let shared_secret = { + let mut buf = Vec::new(); + buf.write_mpint(BigInt::from_bytes_be( + Sign::Plus, + &curve25519::curve25519(&server_secret, &client_public), + )); + buf + }; let hash_data = { let mut buf = Vec::new(); @@ -104,9 +109,7 @@ impl KeyExchange for Curve25519 { buf.write_bytes(item); } - buf.write_mpint( - BigInt::from_bytes_be(Sign::Plus, &shared_secret), - ); + buf.write_raw_bytes(&shared_secret); buf }; @@ -115,10 +118,6 @@ impl KeyExchange for Curve25519 { let hash = self.hash(&[hash_data.as_slice()]); let signature = config.as_ref().key.sign(&hash).unwrap(); - println!("Hash: {:?}", hash); - println!("Public Key: {:?}", public_key); - println!("Signature: {:?}", signature); - packet .with_writer(&|w| { w.write_bytes(public_key.as_slice())?; diff --git a/src/lib.rs b/src/lib.rs index ad00053..1785fd9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -12,6 +12,7 @@ mod message; mod connection; mod key_exchange; mod encryption; +mod mac; pub mod public_key; pub mod server; diff --git a/src/mac/hmac.rs b/src/mac/hmac.rs new file mode 100644 index 0000000..3a0292a --- /dev/null +++ b/src/mac/hmac.rs @@ -0,0 +1,35 @@ +use crypto::hmac::Hmac as rcHmac; +use crypto::mac::Mac; +use crypto::sha2::Sha256; +use mac::MacAlgorithm; + +pub struct Hmac { + hmac: Box>, +} + +impl Hmac { + pub fn new(key: &[u8]) -> Hmac { + let digest = Sha256::new(); + Hmac { hmac: Box::new(rcHmac::new(digest, key)) } + } +} + +impl MacAlgorithm for Hmac { + fn size(&self) -> usize { + 32 + } + + fn sign(&mut self, data: &[u8], seq: u32, buf: &mut [u8]) { + let sequence = &[ + ((seq & 0xff000000) >> 24) as u8, + ((seq & 0x00ff0000) >> 16) as u8, + ((seq & 0x0000ff00) >> 8) as u8, + ((seq & 0x000000ff)) as u8, + ]; + + self.hmac.input(sequence); + self.hmac.input(data); + self.hmac.raw_result(buf); + self.hmac.reset(); + } +} diff --git a/src/mac/mod.rs b/src/mac/mod.rs new file mode 100644 index 0000000..e2d492a --- /dev/null +++ b/src/mac/mod.rs @@ -0,0 +1,8 @@ +mod hmac; + +pub use self::hmac::Hmac; + +pub trait MacAlgorithm { + fn size(&self) -> usize; + fn sign(&mut self, data: &[u8], seq: u32, buf: &mut [u8]); +} diff --git a/src/packet.rs b/src/packet.rs index 8ebf036..83233ed 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -8,85 +8,137 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; use message::MessageType; use num_bigint::BigInt; -pub struct Packet { - payload: Vec, +pub enum Packet { + Raw(Vec, usize), + Payload(Vec), } impl Packet { pub fn new(msg_type: MessageType) -> Packet { - Packet { payload: (&[msg_type.into()]).to_vec() } + Packet::Payload([msg_type.into()].to_vec()) } pub fn msg_type(&self) -> MessageType { - self.payload[0].into() - } - - pub fn payload(self) -> Vec { - self.payload + match self + { + &Packet::Raw(ref data, _) => data[5], + &Packet::Payload(ref data) => data[0], + }.into() } pub fn read_from(stream: &mut R) -> Result { - let mac_len = 0; - - trace!("Waiting for incoming packet..."); - let packet_len = stream.read_u32::()? as usize; - trace!("Read incoming packet ({} bytes)", packet_len); - - let padding_len = stream.read_u8()? as usize; - let payload_len = packet_len - padding_len - 1; - trace!("Padding: {} bytes", padding_len); + let packet_len = stream.read_uint32()? as usize; + trace!("Reading incoming packet ({} bytes)", packet_len); // TODO: Prevent packets that are too large - let mut payload = Vec::with_capacity(payload_len); - let mut padding = Vec::with_capacity(padding_len); - // let mut mac = Vec::with_capacity(mac_len); + let mut raw = Vec::with_capacity(packet_len + 4); + raw.write_uint32(packet_len as u32)?; - trace!("Reading packet..."); - stream.take(payload_len as u64).read_to_end(&mut payload)?; - trace!("Reading payload..."); - stream.take(padding_len as u64).read_to_end(&mut padding)?; + let count = stream.take(packet_len as u64).read_to_end(&mut raw)?; - // if mac_len > 0 { - // stream.take(mac_len as u64).read_to_end(&mut mac); - // } - - Ok(Packet { payload: payload }) + if count == packet_len { + let padding_len = raw[4] as usize; + let payload_len = packet_len - padding_len - 1; + // TODO: Verify packet size (mod 8) + Ok(Packet::Raw(raw, payload_len)) + } + else { + Err(io::Error::new(io::ErrorKind::BrokenPipe, "broken stream")) + } } pub fn write_to(&self, stream: &mut W) -> Result<()> { - let padding_len = self.padding_len(); - let packet_len = self.payload.len() + padding_len + 1; + match self + { + &Packet::Raw(ref data, _) => { + stream.write_all(data)?; + stream.flush() + } + &Packet::Payload(ref payload) => { + let padding_len = self.padding_len(); + let packet_len = payload.len() + padding_len + 1; - stream.write_u32::(packet_len as u32)?; - stream.write_u8(padding_len as u8)?; - stream.write(&self.payload)?; - stream.write(&[0u8; 255][..padding_len])?; - stream.flush()?; + stream.write_u32::(packet_len as u32)?; + stream.write_u8(padding_len as u8)?; + stream.write_all(&payload)?; + stream.write_all(&[0u8; 255][..padding_len])?; - Ok(()) + stream.flush() + } + } + } + + pub fn payload(self) -> Vec { + match self + { + Packet::Raw(data, payload_len) => data[5..payload_len + 5].to_vec(), + Packet::Payload(payload) => payload, + } + } + + pub fn data<'a>(&'a self) -> &'a [u8] { + match self + { + &Packet::Raw(ref data, _) => &data, + &Packet::Payload(ref payload) => &payload, + } + } + + pub fn to_raw(self) -> Result { + match self + { + Packet::Raw(_, _) => Ok(self), + Packet::Payload(ref payload) => { + let mut buf = Vec::with_capacity(payload.len()); + self.write_to(&mut buf)?; + Ok(Packet::Raw(buf, payload.len())) + } + } } pub fn writer<'a>(&'a mut self) -> &'a mut Write { - &mut self.payload + match self + { + &mut Packet::Raw(ref mut data, _) => data, + &mut Packet::Payload(ref mut payload) => payload, + } } pub fn with_writer(&mut self, f: &Fn(&mut Write) -> Result<()>) -> Result<()> { - f(&mut self.payload) + f(self.writer()) } pub fn reader<'a>(&'a self) -> BufReader<&'a [u8]> { - BufReader::new(&self.payload.as_slice()[1..]) + match self + { + &Packet::Raw(ref data, payload_len) => { + BufReader::new(&data.as_slice()[6..payload_len + 5]) + } + &Packet::Payload(ref payload) => { + BufReader::new(&payload.as_slice()[1..]) + } + } + } + + pub fn payload_len(&self) -> usize { + match self + { + &Packet::Raw(_, payload_len) => payload_len, + &Packet::Payload(ref payload) => payload.len(), + } } pub fn padding_len(&self) -> usize { + let align = 32; + // Calculate the padding to reach a multiple of 8 bytes - let padding_len = 8 - ((self.payload.len() + 5) % 8); + let padding_len = align - ((self.payload_len() + 5) % align); // The padding has to be at least 4 bytes long if padding_len < 4 { - padding_len + 8 + padding_len + align } else { padding_len @@ -200,7 +252,7 @@ impl fmt::Debug for Packet { f, "Packet({:?}, {} bytes)", self.msg_type(), - self.payload.len() + self.payload_len() ) } }