diff --git a/src/bin/ssh-keygen.rs b/src/bin/ssh-keygen.rs index 714fee2..14349aa 100644 --- a/src/bin/ssh-keygen.rs +++ b/src/bin/ssh-keygen.rs @@ -3,7 +3,7 @@ use std::io::prelude::*; use std::fs::File; pub fn main() { - let keys = (ssh::key::RSA.generate_key_pair)(1024); + let keypair = (ssh::key::ED25519.generate_key_pair)(None); let mut buffer = File::create("key.pub").unwrap(); - keys.0.write(&mut buffer); + keypair.export(&mut buffer); } diff --git a/src/key/ed25519.rs b/src/key/ed25519.rs new file mode 100644 index 0000000..8612e44 --- /dev/null +++ b/src/key/ed25519.rs @@ -0,0 +1,106 @@ +use key::{KeyPair, CryptoSystem}; +use std::io::{self, Read, Write}; +use rand::{self, Rng}; +use crypto::ed25519; + +pub static ED25519: CryptoSystem = CryptoSystem { + id: "ed25519", + generate_key_pair: Ed25519KeyPair::generate, + import: Ed25519KeyPair::import, + read_public: Ed25519KeyPair::read_public +}; + +struct Ed25519KeyPair { + private: Option<[u8;64]>, + public: [u8;32], +} + +impl Ed25519KeyPair { + fn generate(_: Option) -> Box { + let mut seed = [0u8;32]; + let mut rng = rand::thread_rng(); + rng.fill_bytes(&mut seed); + + let (private, public) = ed25519::keypair(&seed); + Box::new(Ed25519KeyPair { + private: Some(private), + public: public + }) + } + + fn import(mut r: &mut Read) -> io::Result> { + use packet::ReadPacketExt; + let id = r.read_utf8()?; + assert_eq!(id, "ssh-ed25519"); + + let pub_len = r.read_uint32()?; + assert_eq!(pub_len, 32); + + let mut public = [0u8;32]; + r.read_exact(&mut public)?; + + let priv_len = r.read_uint32()?; + assert_eq!(priv_len, 64); + + let mut private = [0u8;64]; + r.read_exact(&mut private)?; + + Ok(Box::new(Ed25519KeyPair { + public: public, + private: Some(private) + })) + } + + fn read_public(mut r: &mut Read) -> io::Result> { + use packet::ReadPacketExt; + + let len = r.read_uint32()?; + assert_eq!(len, 32); + + let mut public = [0u8;32]; + r.read_exact(&mut public)?; + + Ok(Box::new(Ed25519KeyPair { + private: None, + public: public + })) + } +} + +impl KeyPair for Ed25519KeyPair { + fn system(&self) -> &'static CryptoSystem { &ED25519 } + + fn has_private(&self) -> bool { + self.private.is_some() + } + + fn verify(&self, data: &[u8], signature: &[u8]) -> Result { + Ok(ed25519::verify(data, &self.public, signature)) + } + + fn sign(&self, data: &[u8]) -> Result, ()> { + if let Some(private_key) = self.private { + let signature = ed25519::signature(data, &private_key); + Ok(signature.to_vec()) + } + else { + Err(()) + } + } + + fn write_public(&self, w: &mut Write) -> io::Result<()> { + use packet::WritePacketExt; + w.write_string("ssh-ed25519")?; + w.write_bytes(&self.public) + } + + fn export(&self, w: &mut Write) -> io::Result<()> { + use packet::WritePacketExt; + w.write_string("ssh-ed25519")?; + w.write_bytes(&self.public)?; + if let Some(private_key) = self.private { + w.write_bytes(&private_key)?; + } + Ok(()) + } +} diff --git a/src/key/mod.rs b/src/key/mod.rs index 0c53a79..fd389a3 100644 --- a/src/key/mod.rs +++ b/src/key/mod.rs @@ -1,41 +1,26 @@ -use std::io::{Result, Read, Write}; +use std::io::{self, Read, Write}; -mod rsa; +//mod rsa; +mod ed25519; -pub use self::rsa::RSA; +//pub use self::rsa::RSA; +pub use self::ed25519::ED25519; -pub trait Key { +pub trait KeyPair { fn system(&self) -> &'static CryptoSystem; - fn read(&self, r: &mut Read) -> Result> - where - Self: Sized; + fn has_private(&self) -> bool; - fn import(&self, r: &mut Read) -> Result> - where - Self: Sized, - { - self.read(r) - } + fn verify(&self, data: &[u8], signature: &[u8]) -> Result; + fn sign(&self, data: &[u8]) -> Result, ()>; - fn write(&self, w: &mut Write) -> Result<()>; - - fn export(&self, w: &mut Write) -> Result<()> { - self.write(w) - } + fn write_public(&self, w: &mut Write) -> io::Result<()>; + fn export(&self, w: &mut Write) -> io::Result<()>; } -pub trait PublicKey: Key { - fn encrypt(&self, data: &[u8]) -> Vec; -} - -pub trait PrivateKey: Key { - fn sign(&self, data: &[u8]) -> Vec; -} - -type KeyPair = (Box, Box); - pub struct CryptoSystem { pub id: &'static str, - pub generate_key_pair: fn(bits: u32) -> KeyPair + pub generate_key_pair: fn(bits: Option) -> Box, + pub import: fn(r: &mut Read) -> io::Result>, + pub read_public: fn(r: &mut Read) -> io::Result> } diff --git a/src/key/rsa.rs b/src/key/rsa.rs index 0598f16..4d92d40 100644 --- a/src/key/rsa.rs +++ b/src/key/rsa.rs @@ -2,7 +2,7 @@ use key::{Key, PublicKey, PrivateKey, KeyPair, CryptoSystem}; use std::io::{Read, Write, Result}; pub static RSA: CryptoSystem = CryptoSystem { - id: "ssh-rsa", + id: "rsa", generate_key_pair: generate_key_pair, }; diff --git a/src/lib.rs b/src/lib.rs index 1cec66e..6ccc8ae 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,5 @@ extern crate byteorder; extern crate rand; -extern crate ring; extern crate crypto; extern crate num_bigint; #[macro_use] diff --git a/src/packet.rs b/src/packet.rs index b026711..d33335d 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -126,11 +126,15 @@ pub trait WritePacketExt: WriteBytesExt { fn write_string(&mut self, s: &str) -> Result<()> { let bytes = s.as_bytes(); + self.write_bytes(bytes) + } + + fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> { self.write_u32::(bytes.len() as u32)?; self.write_all(bytes) } - fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> { + fn write_raw_bytes(&mut self, bytes: &[u8]) -> Result<()> { self.write_all(bytes) } @@ -140,7 +144,6 @@ pub trait WritePacketExt: WriteBytesExt { fn write_mpint(&mut self, value: BigInt) -> Result<()> { let bytes = value.to_signed_bytes_be(); - self.write_u32::(bytes.len() as u32)?; self.write_bytes(bytes.as_slice()) } diff --git a/src/session.rs b/src/session.rs index be12b3b..bf9bac8 100644 --- a/src/session.rs +++ b/src/session.rs @@ -91,7 +91,7 @@ impl Session { let mut packet = Packet::new(MessageType::KexInit); packet.with_writer(&|w| { - w.write_bytes(cookie.as_slice()); + w.write_raw_bytes(cookie.as_slice()); w.write_list(KEY_EXCHANGE); w.write_list(HOST_KEY); w.write_list(ENCRYPTION); diff --git a/tests/crypto_systems.rs b/tests/crypto_systems.rs new file mode 100644 index 0000000..8462742 --- /dev/null +++ b/tests/crypto_systems.rs @@ -0,0 +1,29 @@ +extern crate ssh; +extern crate rand; + +use rand::Rng; +use std::io::Cursor; +use ssh::key::{self, CryptoSystem, KeyPair}; + +fn test_export_import(keypair: &Box) -> Box { + let mut buffer = Vec::new(); + keypair.export(&mut buffer).unwrap(); + + (keypair.system().import)(&mut Cursor::new(buffer)).unwrap() +} + +fn test_crypto_system(system: &CryptoSystem, key_size: Option) { + let keypair = (system.generate_key_pair)(key_size); + let keypair2 = test_export_import(&keypair); + + let mut buffer = [0;4096]; + let mut rng = rand::thread_rng(); + rng.fill_bytes(&mut buffer); + + let signature = keypair.sign(&buffer).unwrap(); + let verified = keypair2.verify(&buffer, signature.as_slice()).unwrap(); + assert!(verified) +} + +#[test] +fn test_ed25519() { test_crypto_system(&key::ED25519, None); }