Commit fe447bf4 authored by Thomas Gatzweiler's avatar Thomas Gatzweiler
Browse files

Implement hmac-sha2-256

parent 64e44de1
......@@ -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
......
......@@ -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] =
......
......@@ -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<Write>,
session_id: Option<Vec<u8>>,
encryption: Option<(Box<Encryption>, Box<Encryption>)>,
mac: Option<(Box<MacAlgorithm>, Box<MacAlgorithm>)>,
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
......
......@@ -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);
}
}
......@@ -27,8 +27,11 @@ impl<'a> Decryptor<'a> {
impl<'a> Read for Decryptor<'a> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
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)
}
}
......@@ -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",
}
}
}
......
......@@ -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<Vec<u8>>,
exchange_hash: Option<Vec<u8>>,
}
......@@ -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())?;
......
......@@ -12,6 +12,7 @@ mod message;
mod connection;
mod key_exchange;
mod encryption;
mod mac;
pub mod public_key;
pub mod server;
......
use crypto::hmac::Hmac as rcHmac;
use crypto::mac::Mac;
use crypto::sha2::Sha256;
use mac::MacAlgorithm;
pub struct Hmac {
hmac: Box<rcHmac<Sha256>>,
}
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();
}
}
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]);
}
......@@ -8,85 +8,137 @@ use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use message::MessageType;
use num_bigint::BigInt;
pub struct Packet {
payload: Vec<u8>,
pub enum Packet {
Raw(Vec<u8>, usize),
Payload(Vec<u8>),
}
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<u8> {
self.payload
match self
{
&Packet::Raw(ref data, _) => data[5],
&Packet::Payload(ref data) => data[0],
}.into()
}
pub fn read_from<R: io::Read>(stream: &mut R) -> Result<Packet> {
let mac_len = 0;
let packet_len = stream.read_uint32()? as usize;
trace!("Reading incoming packet ({} bytes)", packet_len);
trace!("Waiting for incoming packet...");
let packet_len = stream.read_u32::<BigEndian>()? as usize;
trace!("Read incoming packet ({} bytes)", packet_len);
// TODO: Prevent packets that are too large
let padding_len = stream.read_u8()? as usize;
let payload_len = packet_len - padding_len - 1;
trace!("Padding: {} bytes", padding_len);
let mut raw = Vec::with_capacity(packet_len + 4);
raw.write_uint32(packet_len as u32)?;
// TODO: Prevent packets that are too large
let count = stream.take(packet_len as u64).read_to_end(&mut raw)?;
let mut payload = Vec::with_capacity(payload_len);
let mut padding = Vec::with_capacity(padding_len);
// let mut mac = Vec::with_capacity(mac_len);
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"))
}
}
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)?;
pub fn write_to<W: io::Write>(&self, stream: &mut W) -> Result<()> {
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;
// if mac_len > 0 {
// stream.take(mac_len as u64).read_to_end(&mut mac);
// }
stream.write_u32::<BigEndian>(packet_len as u32)?;
stream.write_u8(padding_len as u8)?;
stream.write_all(&payload)?;
stream.write_all(&[0u8; 255][..padding_len])?;
Ok(Packet { payload: payload })
stream.flush()
}
}
}
pub fn write_to<W: io::Write>(&self, stream: &mut W) -> Result<()> {
let padding_len = self.padding_len();
let packet_len = self.payload.len() + padding_len + 1;
pub fn payload(self) -> Vec<u8> {
match self
{
Packet::Raw(data, payload_len) => data[5..payload_len + 5].to_vec(),
Packet::Payload(payload) => payload,
}
}
stream.write_u32::<BigEndian>(packet_len as u32)?;
stream.write_u8(padding_len as u8)?;
stream.write(&self.payload)?;
stream.write(&[0u8; 255][..padding_len])?;
stream.flush()?;
pub fn data<'a>(&'a self) -> &'a [u8] {
match self
{
&Packet::Raw(ref data, _) => &data,
&Packet::Payload(ref payload) => &payload,
}
}
Ok(())
pub fn to_raw(self) -> Result<Packet> {
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()
)
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment