From 1806e5ac5ec4e509e0f5e5a55fa8a3226a81e784 Mon Sep 17 00:00:00 2001 From: Thomas Gatzweiler Date: Sat, 15 Jul 2017 14:35:41 +0200 Subject: [PATCH] Add comments and better error handling --- src/public_key/ed25519.rs | 113 ++++++++++++++++++++++++++++++++++++++ src/public_key/mod.rs | 26 +++++++++ src/public_key/rsa.rs | 70 +++++++++++++++++++++++ tests/public_key.rs | 39 +++++++++++++ 4 files changed, 248 insertions(+) create mode 100644 src/public_key/ed25519.rs create mode 100644 src/public_key/mod.rs create mode 100644 src/public_key/rsa.rs create mode 100644 tests/public_key.rs diff --git a/src/public_key/ed25519.rs b/src/public_key/ed25519.rs new file mode 100644 index 0000000..2c34a39 --- /dev/null +++ b/src/public_key/ed25519.rs @@ -0,0 +1,113 @@ +use public_key::{KeyPair, CryptoSystem}; +use std::io::{self, Read, Write}; +use std::io::ErrorKind::InvalidData; +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; + + if r.read_utf8()? != "ssh-ed25519" { + return Err(io::Error::new(InvalidData, "not a ED25519 key")); + } + + if r.read_uint32()? != 32 { + return Err(io::Error::new(InvalidData, "invalid ED25519 key")); + } + + let mut public = [0u8; 32]; + r.read_exact(&mut public)?; + + if r.read_uint32()? != 64 { + return Err(io::Error::new(InvalidData, "invalid ED25519 key")); + } + + 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; + + if r.read_uint32()? != 32 { + return Err(io::Error::new(InvalidData, "invalid ED25519 key")); + } + + 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/public_key/mod.rs b/src/public_key/mod.rs new file mode 100644 index 0000000..fd389a3 --- /dev/null +++ b/src/public_key/mod.rs @@ -0,0 +1,26 @@ +use std::io::{self, Read, Write}; + +//mod rsa; +mod ed25519; + +//pub use self::rsa::RSA; +pub use self::ed25519::ED25519; + +pub trait KeyPair { + fn system(&self) -> &'static CryptoSystem; + + fn has_private(&self) -> bool; + + fn verify(&self, data: &[u8], signature: &[u8]) -> Result; + fn sign(&self, data: &[u8]) -> Result, ()>; + + fn write_public(&self, w: &mut Write) -> io::Result<()>; + fn export(&self, w: &mut Write) -> io::Result<()>; +} + +pub struct CryptoSystem { + pub id: &'static str, + 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/public_key/rsa.rs b/src/public_key/rsa.rs new file mode 100644 index 0000000..4d92d40 --- /dev/null +++ b/src/public_key/rsa.rs @@ -0,0 +1,70 @@ +use key::{Key, PublicKey, PrivateKey, KeyPair, CryptoSystem}; +use std::io::{Read, Write, Result}; + +pub static RSA: CryptoSystem = CryptoSystem { + id: "rsa", + generate_key_pair: generate_key_pair, +}; + +pub fn generate_key_pair(size: u32) -> KeyPair { + let public = Box::new(RsaPublicKey::new()); + let private = Box::new(RsaPrivateKey::new()); + (public, private) +} + +pub struct RsaPublicKey {} + +impl RsaPublicKey { + pub fn new() -> RsaPublicKey { + RsaPublicKey {} + } +} + +impl Key for RsaPublicKey { + fn system(&self) -> &'static CryptoSystem { + &RSA + } + + fn read(&self, r: &mut Read) -> Result> { + Err(::std::io::Error::new(::std::io::ErrorKind::Other, "")) + } + + fn write(&self, w: &mut Write) -> Result<()> { + Ok(()) + } +} + +impl PublicKey for RsaPublicKey { + fn encrypt(&self, data: &[u8]) -> Vec { + Vec::new() + } +} + +pub struct RsaPrivateKey { +} + +impl RsaPrivateKey { + pub fn new() -> RsaPrivateKey { + RsaPrivateKey { } + } +} + +impl PrivateKey for RsaPrivateKey { + fn sign(&self, data: &[u8]) -> Vec { + Vec::new() + } +} + +impl Key for RsaPrivateKey { + fn system(&self) -> &'static CryptoSystem { + &RSA + } + + fn read(&self, r: &mut Read) -> Result> { + Err(::std::io::Error::new(::std::io::ErrorKind::Other, "")) + } + + fn write(&self, w: &mut Write) -> Result<()> { + Ok(()) + } +} diff --git a/tests/public_key.rs b/tests/public_key.rs new file mode 100644 index 0000000..4b36edd --- /dev/null +++ b/tests/public_key.rs @@ -0,0 +1,39 @@ +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 { + // Export the keypair to a vector and import it again + 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) { + // Generate a key pair + let keypair = (system.generate_key_pair)(key_size); + + // Export and import that key pair again + let keypair2 = test_export_import(&keypair); + + // Generate a random message + let mut buffer = [0;4096]; + let mut rng = rand::thread_rng(); + rng.fill_bytes(&mut buffer); + + // Sign the message and verify it + let signature = keypair.sign(&buffer).unwrap(); + let verified = keypair2.verify(&buffer, signature.as_slice()).unwrap(); + assert!(verified); + + // Corrupt random message and try again + buffer[2342] = !buffer[2342]; + let verified = keypair2.verify(&buffer, signature.as_slice()).unwrap(); + assert!(!verified); +} + +#[test] +fn test_ed25519() { test_crypto_system(&key::ED25519, None); }