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

Complete Curve25519 implementation

parent c44b63c3
......@@ -10,10 +10,10 @@ Currently implemented features, ordered by priority:
- [ ] SSH Client
- [ ] Key Exchange algorithms
- [ ] `diffie-hellman-group-exchange-sha1`
- [ ] `curve25519-sha256`
- [x] `curve25519-sha256`
- [ ] Public Key algorithms
- [ ] `ssh-rsa`
- [ ] `ssh-ed25519`
- [x] `ssh-ed25519`
- [ ] Encryption algorithms
- [ ] `aes256-ctr`
- [ ] `aes256-gcm`
......
use std::io::{self, BufRead, BufReader, Read, Write};
use std::sync::Arc;
use error::{ConnectionError, ConnectionResult};
use key_exchange::{self, KeyExchange, KeyExchangeResult};
use key_exchange::{self, KexResult, KeyExchange};
use message::MessageType;
use packet::{Packet, ReadPacketExt, WritePacketExt};
use server::ServerConfig;
#[derive(PartialEq)]
enum ConnectionState {
......@@ -12,55 +14,72 @@ enum ConnectionState {
Established,
}
#[derive(PartialEq)]
#[derive(Clone)]
pub enum ConnectionType {
Server,
Server(Arc<ServerConfig>),
Client,
}
pub struct Connection<W: Write> {
ctype: ConnectionType,
#[derive(Default, Debug)]
pub struct HashData {
pub client_id: Option<String>,
pub server_id: Option<String>,
pub client_kexinit: Option<Vec<u8>>,
pub server_kexinit: Option<Vec<u8>>,
}
pub struct Connection {
pub conn_type: ConnectionType,
pub hash_data: HashData,
state: ConnectionState,
key_exchange: Option<Box<KeyExchange>>,
stream: W,
my_id: String,
peer_id: Option<String>,
stream: Box<Write>,
session_id: Option<Vec<u8>>,
}
impl<W: Write> Connection<W> {
pub fn new(ctype: ConnectionType, stream: W) -> Connection<W> {
impl<'a> Connection {
pub fn new(conn_type: ConnectionType, stream: Box<Write>) -> Connection {
Connection {
ctype: ctype,
conn_type: conn_type,
hash_data: HashData::default(),
state: ConnectionState::Initial,
key_exchange: None,
stream: stream,
my_id: format!(
"SSH-2.0-RedoxSSH_{}\r\n",
env!("CARGO_PKG_VERSION")
),
peer_id: None,
stream: Box::new(stream),
session_id: None,
}
}
pub fn run(&mut self, stream: &mut Read) -> ConnectionResult<()> {
self.stream.write(self.my_id.as_bytes())?;
self.stream.flush()?;
let mut stream = BufReader::new(stream);
self.peer_id = Some(self.read_id(&mut stream)?);
let mut reader = BufReader::new(stream);
if let Some(ref peer_id) = self.peer_id {
println!("Identifies as {:?}", peer_id);
}
self.send_id()?;
self.read_id(&mut reader)?;
loop {
let packet = Packet::read_from(&mut stream)?;
println!("packet: {:?}", packet);
self.process(&packet)?;
let packet = Packet::read_from(&mut reader)?;
trace!("Packet received: {:?}", packet);
self.process(packet)?;
}
}
fn read_id(&mut self, mut reader: &mut BufRead) -> io::Result<String> {
pub fn send(&mut self, packet: &Packet) -> io::Result<()> {
packet.write_to(&mut self.stream)
}
fn send_id(&mut self) -> io::Result<()> {
let id = format!("SSH-2.0-RedoxSSH_{}", env!("CARGO_PKG_VERSION"));
info!("Identifying as {:?}", id);
self.stream.write(id.as_bytes())?;
self.stream.write(b"\r\n")?;
self.stream.flush()?;
self.hash_data.server_id = Some(id);
Ok(())
}
fn read_id(&mut self, mut reader: &mut BufRead) -> io::Result<()> {
// The identification string has a maximum length of 255 bytes
// TODO: Make sure to stop reading if the client sends too much
......@@ -70,38 +89,88 @@ impl<W: Write> Connection<W> {
reader.read_line(&mut id)?;
}
Ok(id.trim_right().to_owned())
let peer_id = id.trim_right().to_owned();
info!("Peer identifies as {:?}", peer_id);
self.hash_data.client_id = Some(peer_id);
Ok(())
}
pub fn process(&mut self, packet: &Packet) -> ConnectionResult<()> {
fn generate_key(
&mut self,
id: &[u8],
len: usize,
) -> ConnectionResult<Vec<u8>> {
use self::ConnectionError::KeyGenerationError;
let kex = self.key_exchange.take().ok_or(KeyGenerationError)?;
let key = kex.hash(
&[
kex.shared_secret().ok_or(KeyGenerationError)?,
kex.exchange_hash().ok_or(KeyGenerationError)?,
id,
self.session_id
.as_ref()
.ok_or(KeyGenerationError)?
.as_slice(),
],
);
self.key_exchange = Some(kex);
Ok(key)
}
pub fn process(&mut self, packet: Packet) -> ConnectionResult<()> {
match packet.msg_type()
{
MessageType::KexInit => {
println!("Starting Key Exchange!");
self.kex_init(packet)
}
MessageType::NewKeys => {
println!("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)?;
println!("c2s enc key: {:?}", enc_c2s);
Ok(())
}
MessageType::KeyExchange(_) => {
let ref mut kex = self.key_exchange.as_mut().ok_or(
let mut kex = self.key_exchange.take().ok_or(
ConnectionError::KeyExchangeError,
)?;
match kex.process(packet)
match kex.process(self, packet)
{
KeyExchangeResult::Ok(Some(packet)) => {
packet.write_to(&mut self.stream)?;
}
KeyExchangeResult::Error(Some(packet)) => {
packet.write_to(&mut self.stream)?;
}
KeyExchangeResult::Done(packet) => {
if let Some(packet) = packet {
packet.write_to(&mut self.stream)?;
}
KexResult::Done(packet) => {
self.state = ConnectionState::Established;
self.send(&packet)?;
if self.session_id.is_none() {
self.session_id =
kex.exchange_hash().map(|h| h.to_vec());
}
let packet = Packet::new(MessageType::NewKeys);
self.send(&packet)?;
Ok(())
}
KeyExchangeResult::Ok(None) |
KeyExchangeResult::Error(None) => {}
};
KexResult::Ok(packet) => {
self.send(&packet)?;
Ok(())
}
KexResult::Error => Err(ConnectionError::KeyExchangeError),
}?;
self.key_exchange = Some(kex);
Ok(())
}
_ => {
......@@ -111,35 +180,43 @@ impl<W: Write> Connection<W> {
}
}
pub fn kex_init(&mut self, packet: &Packet) -> ConnectionResult<()> {
pub fn kex_init(&mut self, packet: Packet) -> ConnectionResult<()> {
use algorithm::*;
let mut reader = packet.reader();
let _ = reader.read_bytes(16)?; // Cookie. Throw it away.
let kex_algos = reader.read_enum_list::<KeyExchangeAlgorithm>()?;
let srv_host_key_algos = reader.read_enum_list::<PublicKeyAlgorithm>()?;
let enc_algos_c2s = reader.read_enum_list::<EncryptionAlgorithm>()?;
let enc_algos_s2c = reader.read_enum_list::<EncryptionAlgorithm>()?;
let mac_algos_c2s = reader.read_enum_list::<MacAlgorithm>()?;
let mac_algos_s2c = reader.read_enum_list::<MacAlgorithm>()?;
let comp_algos_c2s = reader.read_enum_list::<CompressionAlgorithm>()?;
let comp_algos_s2c = reader.read_enum_list::<CompressionAlgorithm>()?;
let kex_algo = negotiate(KEY_EXCHANGE, kex_algos.as_slice())?;
let srv_host_key_algo =
negotiate(HOST_KEY, srv_host_key_algos.as_slice())?;
let enc_algo = negotiate(ENCRYPTION, enc_algos_s2c.as_slice())?;
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);
use rand::{OsRng, Rng};
let mut rng = OsRng::new()?;
{
let mut reader = packet.reader();
let _ = reader.read_bytes(16)?; // Cookie. Throw it away.
let kex_algos = reader.read_enum_list::<KeyExchangeAlgorithm>()?;
let srv_host_key_algos =
reader.read_enum_list::<PublicKeyAlgorithm>()?;
let enc_algos_c2s = reader.read_enum_list::<EncryptionAlgorithm>()?;
let enc_algos_s2c = reader.read_enum_list::<EncryptionAlgorithm>()?;
let mac_algos_c2s = reader.read_enum_list::<MacAlgorithm>()?;
let mac_algos_s2c = reader.read_enum_list::<MacAlgorithm>()?;
let comp_algos_c2s = reader
.read_enum_list::<CompressionAlgorithm>()?;
let comp_algos_s2c = reader
.read_enum_list::<CompressionAlgorithm>()?;
let kex_algo = negotiate(KEY_EXCHANGE, kex_algos.as_slice())?;
let srv_host_key_algo =
negotiate(HOST_KEY, srv_host_key_algos.as_slice())?;
let enc_algo = negotiate(ENCRYPTION, enc_algos_s2c.as_slice())?;
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);
}
// Save payload for hash generation
self.hash_data.client_kexinit = Some(packet.payload());
// Create a random 16 byte cookie
use rand::{self, Rng};
let mut rng = rand::thread_rng();
let cookie: Vec<u8> = rng.gen_iter::<u8>().take(16).collect();
let mut packet = Packet::new(MessageType::KexInit);
......@@ -162,7 +239,12 @@ impl<W: Write> Connection<W> {
self.state = ConnectionState::KeyExchange;
self.key_exchange = Some(Box::new(key_exchange::Curve25519::new()));
packet.write_to(&mut self.stream)?;
// Save payload for hash generation
self.hash_data.server_kexinit = Some(packet.payload());
Ok(())
}
}
......@@ -11,6 +11,7 @@ pub enum ConnectionError {
ProtocolError,
NegotiationError,
KeyExchangeError,
KeyGenerationError,
}
impl fmt::Display for ConnectionError {
......@@ -28,6 +29,7 @@ impl Error for ConnectionError {
&ProtocolError => "protocol error",
&NegotiationError => "negotiation error",
&KeyExchangeError => "key exchange error",
&KeyGenerationError => "key generation error",
}
}
}
......
use crypto::curve25519::curve25519;
use key_exchange::{KeyExchange, KeyExchangeResult};
use connection::{Connection, ConnectionType};
use crypto::curve25519;
use crypto::digest::Digest;
use crypto::sha2::Sha256;
use key_exchange::{KexResult, KeyExchange};
use message::MessageType;
use num_bigint::{BigInt, Sign};
use packet::{Packet, ReadPacketExt, WritePacketExt};
use public_key::ED25519;
use rand::{self, Rng};
const ECDH_KEX_INIT: u8 = 30;
const ECDH_KEX_REPLY: u8 = 31;
pub struct Curve25519 {}
pub struct Curve25519 {
shared_secret: Option<[u8; 32]>,
exchange_hash: Option<Vec<u8>>,
}
impl Curve25519 {
pub fn new() -> Curve25519 {
Curve25519 {}
Curve25519 {
shared_secret: None,
exchange_hash: None,
}
}
}
impl KeyExchange for Curve25519 {
fn process(&mut self, packet: &Packet) -> KeyExchangeResult {
fn shared_secret<'a>(&'a self) -> Option<&'a [u8]> {
self.shared_secret.as_ref().map(|x| x as &[u8])
}
fn exchange_hash<'a>(&'a self) -> Option<&'a [u8]> {
self.exchange_hash.as_ref().map(|x| x.as_slice())
}
fn hash(&self, data: &[&[u8]]) -> Vec<u8> {
let mut hash = [0; 32];
let mut hasher = Sha256::new();
for item in data {
hasher.input(item);
}
hasher.result(&mut hash);
hash.to_vec()
}
fn process(&mut self, conn: &mut Connection, packet: Packet) -> KexResult {
match packet.msg_type()
{
MessageType::KeyExchange(ECDH_KEX_INIT) => {
let mut reader = packet.reader();
let qc = reader.read_string().unwrap();
let client_public = reader.read_string().unwrap();
let config = match &conn.conn_type
{
&ConnectionType::Server(ref config) => config.clone(),
_ => return KexResult::Error,
};
let keypair = (ED25519.generate_key_pair)(None);
let mut public_key = Vec::new();
keypair.write_public(&mut public_key);
let public_key = {
let mut key = Vec::new();
config.as_ref().key.write_public(&mut key).unwrap();
key
};
println!("Received qc: {:?}", qc);
println!("Received qc: {:?}", client_public);
let mut packet =
Packet::new(MessageType::KeyExchange(ECDH_KEX_REPLY));
packet.with_writer(&|w| {
w.write_bytes(public_key.as_slice())?;
w.write_bytes(qc.as_slice())?;
w.write_bytes(&[0; 256])?;
Ok(())
});
let server_secret = {
let mut secret = [0; 32];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut secret);
secret[0] &= 248;
secret[31] &= 127;
secret[31] |= 64;
secret
};
let server_public = curve25519::curve25519_base(&server_secret);
let shared_secret =
curve25519::curve25519(&server_secret, &client_public);
let hash_data = {
let mut buf = Vec::new();
let data = &conn.hash_data;
let items =
[
data.client_id.as_ref().unwrap().as_bytes(),
data.server_id.as_ref().unwrap().as_bytes(),
data.client_kexinit.as_ref().unwrap().as_slice(),
data.server_kexinit.as_ref().unwrap().as_slice(),
public_key.as_slice(),
client_public.as_slice(),
&server_public,
];
for item in items.iter() {
buf.write_bytes(item);
}
buf.write_mpint(
BigInt::from_bytes_be(Sign::Plus, &shared_secret),
);
buf
};
// Calculate hash
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())?;
w.write_bytes(&server_public)?;
w.write_bytes(signature.as_slice())?; // Signature
Ok(())
})
.unwrap();
self.exchange_hash = Some(hash);
self.shared_secret = Some(shared_secret);
KeyExchangeResult::Ok(Some(packet))
KexResult::Done(packet)
}
_ => {
debug!("Unhandled key exchange packet: {:?}", packet);
KeyExchangeResult::Error(None)
KexResult::Error
}
}
}
......
use key_exchange::{KeyExchange, KeyExchangeResult};
use connection::Connection;
use key_exchange::{KexResult, KeyExchange};
use message::MessageType;
use num_bigint::{BigInt, RandBigInt, ToBigInt};
use packet::{Packet, ReadPacketExt, WritePacketExt};
......@@ -38,7 +39,19 @@ impl DhGroupSha1 {
}
impl KeyExchange for DhGroupSha1 {
fn process(&mut self, packet: &Packet) -> KeyExchangeResult {
fn shared_secret<'a>(&'a self) -> Option<&'a [u8]> {
Some(&[])
}
fn exchange_hash<'a>(&'a self) -> Option<&'a [u8]> {
Some(&[])
}
fn hash(&self, data: &[&[u8]]) -> Vec<u8> {
vec![]
}
fn process(&mut self, conn: &mut Connection, packet: Packet) -> KexResult {
match packet.msg_type()
{
MessageType::KeyExchange(DH_GEX_REQUEST) => {
......@@ -64,7 +77,7 @@ impl KeyExchange for DhGroupSha1 {
self.g = Some(g);
self.p = Some(p);
KeyExchangeResult::Ok(Some(packet))
KexResult::Ok(packet)
}
MessageType::KeyExchange(DH_GEX_INIT) => {
let mut reader = packet.reader();
......@@ -83,11 +96,11 @@ impl KeyExchange for DhGroupSha1 {
self.e = Some(e);
KeyExchangeResult::Ok(Some(packet))
KexResult::Done(packet)
}
_ => {
debug!("Unhandled key exchange packet: {:?}", packet);
KeyExchangeResult::Error(None)
KexResult::Error
}
}
}
......
......@@ -4,14 +4,18 @@ mod dh_group_sha1;
pub use self::curve25519::Curve25519;
pub use self::dh_group_sha1::DhGroupSha1;
use connection::Connection;
use packet::Packet;
pub enum KeyExchangeResult {
Ok(Option<Packet>),
Done(Option<Packet>),
Error(Option<Packet>),
pub enum KexResult {
Ok(Packet),
Done(Packet),
Error,
}
pub trait KeyExchange {
fn process(&mut self, packet: &Packet) -> KeyExchangeResult;
fn process(&mut self, conn: &mut Connection, packet: Packet) -> KexResult;
fn shared_secret<'a>(&'a self) -> Option<&'a [u8]>;
fn exchange_hash<'a>(&'a self) -> Option<&'a [u8]>;
fn hash(&self, data: &[&[u8]]) -> Vec<u8>;
}
......@@ -21,6 +21,10 @@ impl Packet {
self.payload[0].into()
}
pub fn payload(self) -> Vec<u8> {
self.payload
}
pub fn read_from<R: io::Read>(stream: &mut R) -> Result<Packet> {
let mac_len = 0;
......
......@@ -84,13 +84,28 @@ impl KeyPair for Ed25519KeyPair {
}
fn verify(&self, data: &[u8], signature: &[u8]) -> Result<bool, ()> {
Ok(ed25519::verify(data, &self.public, signature))
use packet::ReadPacketExt;
use std::io::Cursor;
let mut reader = Cursor::new(signature);
let id = reader.read_string().unwrap_or(vec![]);
if id == b"ssh-ed25519" {
if let Ok(sig) = reader.read_string() {
return Ok(ed25519::verify(data, &self.public, sig.as_slice()));
}
}
Err(())
}
fn sign(&self, data: &[u8]) -> Result<Vec<u8>, ()> {
use packet::WritePacketExt;
if let Some(private_key) = self.private {
let signature = ed25519::signature(data, &private_key);
Ok(signature.to_vec())
let mut result = Vec::new();
let sig = ed25519::signature(data, &pri