From abb40764ab5ada354d8183b98bcf06004491b633 Mon Sep 17 00:00:00 2001 From: Thomas Gatzweiler Date: Sat, 15 Jul 2017 09:28:03 +0200 Subject: [PATCH] Add public/private key traits --- Cargo.lock | 87 ++++++++++++++++++++++++++++++ Cargo.toml | 12 +++++ README.md | 21 +++++--- src/bin/ssh-keygen.rs | 9 ++++ src/bin/ssh.rs | 2 + src/key/mod.rs | 41 ++++++++++++++ src/key/rsa.rs | 70 ++++++++++++++++++++++++ src/key_exchange/curve25519.rs | 2 +- src/key_exchange/dh_group_sha1.rs | 89 +++++++++++++++++++++++++++++++ src/key_exchange/mod.rs | 2 +- src/lib.rs | 15 +++--- src/packet.rs | 6 ++- src/session.rs | 2 +- 13 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 src/bin/ssh-keygen.rs create mode 100644 src/key/mod.rs create mode 100644 src/key/rsa.rs create mode 100644 src/key_exchange/dh_group_sha1.rs diff --git a/Cargo.lock b/Cargo.lock index 4ed90da..e97c217 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,8 +5,10 @@ dependencies = [ "byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", "num-bigint 0.1.39 (git+https://github.com/rust-num/num)", + "num-traits 0.1.39 (git+https://github.com/rust-num/num)", "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)", + "ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -15,6 +17,25 @@ name = "byteorder" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "coco" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "either" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "futures" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "gcc" version = "0.3.51" @@ -29,6 +50,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lazy_static" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.26" @@ -63,6 +89,14 @@ name = "num-traits" version = "0.1.39" source = "git+https://github.com/rust-num/num#d159ed63be98c8ff01b62cbbf912d721e2b0eb41" +[[package]] +name = "num_cpus" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.3.15" @@ -71,11 +105,44 @@ dependencies = [ "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "rayon" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rayon-core" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "redox_syscall" version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ring" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)", + "rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", + "untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rust-crypto" version = "0.2.36" @@ -93,6 +160,11 @@ name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "scopeguard" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "time" version = "0.1.38" @@ -104,6 +176,11 @@ dependencies = [ "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "untrusted" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.8" @@ -116,17 +193,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum byteorder 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff81738b726f5d099632ceaffe7fb65b90212e8dce59d518729e7e8634032d3d" +"checksum coco 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c06169f5beb7e31c7c67ebf5540b8b472d23e3eade3b2ec7d1f5b504a85f91bd" +"checksum either 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18785c1ba806c258137c937e44ada9ee7e69a37e3c72077542cd2f069d78562a" +"checksum futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4b63a4792d4f8f686defe3b39b92127fea6344de5d38202b2ee5a11bbbf29d6a" "checksum gcc 0.3.51 (registry+https://github.com/rust-lang/crates.io-index)" = "120d07f202dcc3f72859422563522b66fe6463a4c513df062874daad05f85f0a" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3b37545ab726dd833ec6420aaba8231c5b320814b9029ad585555d2a03e94fbf" "checksum libc 0.2.26 (registry+https://github.com/rust-lang/crates.io-index)" = "30885bcb161cf67054244d10d4a7f4835ffd58773bc72e07d35fecf472295503" "checksum log 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "880f77541efa6e5cc74e76910c9884d9859683118839d6a1dc3b11e63512565b" "checksum num-bigint 0.1.39 (git+https://github.com/rust-num/num)" = "" "checksum num-integer 0.1.34 (git+https://github.com/rust-num/num)" = "" "checksum num-traits 0.1.39 (git+https://github.com/rust-num/num)" = "" +"checksum num_cpus 1.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "aec53c34f2d0247c5ca5d32cca1478762f301740468ee9ee6dcb7a0dd7a0c584" "checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d" +"checksum rayon 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a77c51c07654ddd93f6cb543c7a849863b03abc7e82591afda6dc8ad4ac3ac4a" +"checksum rayon-core 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7febc28567082c345f10cddc3612c6ea020fc3297a1977d472cf9fdb73e6e493" "checksum redox_syscall 0.1.26 (registry+https://github.com/rust-lang/crates.io-index)" = "9df6a71a1e67be2104410736b2389fb8e383c1d7e9e792d629ff13c02867147a" +"checksum ring 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2a6dc7fc06a05e6de183c5b97058582e9da2de0c136eafe49609769c507724" "checksum rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +"checksum scopeguard 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c79eb2c3ac4bc2507cda80e7f3ac5b88bd8eae4c0914d5663e6a8933994be918" "checksum time 0.1.38 (registry+https://github.com/rust-lang/crates.io-index)" = "d5d788d3aa77bc0ef3e9621256885555368b47bd495c13dd2e7413c89f845520" +"checksum untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b65243989ef6aacd9c0d6bd2b822765c3361d8ed352185a6f3a41f3a718c673" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/Cargo.toml b/Cargo.toml index 250be36..a0085de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,10 +10,17 @@ path = "src/lib.rs" [[bin]] name = "ssh" path = "src/bin/ssh.rs" +doc = false [[bin]] name = "sshd" path = "src/bin/sshd.rs" +doc = false + +[[bin]] +name = "ssh-keygen" +path = "src/bin/ssh-keygen.rs" +doc = false [dependencies] byteorder = "^1.0" @@ -21,6 +28,11 @@ log = "^0.3" rust-crypto = "^0.2" rand = "^0.3" num-bigint = { git = "https://github.com/rust-num/num" } +num-traits = { git = "https://github.com/rust-num/num" } + +[dependencies.ring] +version = "^0.11.0" +features = ["rsa_signing"] [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.1" diff --git a/README.md b/README.md index eb4078f..0a515c2 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ # redox-ssh -A ssh client and server written entirely on rust, primarily targeted at (http://redox-os.org). +A ssh client and server written entirely on rust, primarily targeted at [Redox OS](http://redox-os.org). ## Features Currently implemented features, ordered by priority: - [ ] SSH Server - [ ] SSH Client - [ ] Encryption - [ ] Public key authentication - [ ] Port forwarding - [ ] SCP File Transfers + - [ ] SSH Server + - [ ] SSH Client + - [ ] Key Exchange algorithms + - [ ] `diffie-hellman-group-exchange-sha1` + - [ ] `curve25519-sha256` + - [ ] Public Key algorithms + - [ ] `ssh-rsa` + - [ ] `ssh-ed25519` + - [ ] Encryption algorithms + - [ ] `aes256-ctr` + - [ ] `aes256-gcm` + - [ ] Port forwarding + - [ ] SCP File Transfers ## License diff --git a/src/bin/ssh-keygen.rs b/src/bin/ssh-keygen.rs new file mode 100644 index 0000000..714fee2 --- /dev/null +++ b/src/bin/ssh-keygen.rs @@ -0,0 +1,9 @@ +extern crate ssh; +use std::io::prelude::*; +use std::fs::File; + +pub fn main() { + let keys = (ssh::key::RSA.generate_key_pair)(1024); + let mut buffer = File::create("key.pub").unwrap(); + keys.0.write(&mut buffer); +} diff --git a/src/bin/ssh.rs b/src/bin/ssh.rs index d018d0c..eee6549 100644 --- a/src/bin/ssh.rs +++ b/src/bin/ssh.rs @@ -1,3 +1,5 @@ +extern crate ssh; + pub fn main() { println!("Hello from ssh!"); } diff --git a/src/key/mod.rs b/src/key/mod.rs new file mode 100644 index 0000000..0c53a79 --- /dev/null +++ b/src/key/mod.rs @@ -0,0 +1,41 @@ +use std::io::{Result, Read, Write}; + +mod rsa; + +pub use self::rsa::RSA; + +pub trait Key { + fn system(&self) -> &'static CryptoSystem; + + fn read(&self, r: &mut Read) -> Result> + where + Self: Sized; + + fn import(&self, r: &mut Read) -> Result> + where + Self: Sized, + { + self.read(r) + } + + fn write(&self, w: &mut Write) -> Result<()>; + + fn export(&self, w: &mut Write) -> Result<()> { + self.write(w) + } +} + +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 +} diff --git a/src/key/rsa.rs b/src/key/rsa.rs new file mode 100644 index 0000000..0598f16 --- /dev/null +++ b/src/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: "ssh-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/src/key_exchange/curve25519.rs b/src/key_exchange/curve25519.rs index 1af72e0..14c30fa 100644 --- a/src/key_exchange/curve25519.rs +++ b/src/key_exchange/curve25519.rs @@ -12,7 +12,7 @@ impl Curve25519 { } impl KeyExchange for Curve25519 { - fn process(&self, packet: &Packet) -> KeyExchangeResult { + fn process(&mut self, packet: &Packet) -> KeyExchangeResult { KeyExchangeResult::Ok(None) } } diff --git a/src/key_exchange/dh_group_sha1.rs b/src/key_exchange/dh_group_sha1.rs new file mode 100644 index 0000000..4d198ba --- /dev/null +++ b/src/key_exchange/dh_group_sha1.rs @@ -0,0 +1,89 @@ +use key_exchange::{KeyExchange, KeyExchangeResult}; +use message::MessageType; +use num_bigint::{BigInt, RandBigInt, ToBigInt}; +use packet::{Packet, ReadPacketExt, WritePacketExt}; +use rand; + +const DH_GEX_GROUP: u8 = 31; +const DH_GEX_INIT: u8 = 32; +const DH_GEX_REPLY: u8 = 33; +const DH_GEX_REQUEST: u8 = 34; + +/// Second Oakley Group +/// Source: https://tools.ietf.org/html/rfc2409#section-6.2 +#[cfg_attr(rustfmt, rustfmt_skip)] +static OAKLEY_GROUP_2: &[u32] = &[ + 0xFFFFFFFF, 0xFFFFFFFF, 0xC90FDAA2, 0x2168C234, 0xC4C6628B, 0x80DC1CD1, + 0x29024E08, 0x8A67CC74, 0x020BBEA6, 0x3B139B22, 0x514A0879, 0x8E3404DD, + 0xEF9519B3, 0xCD3A431B, 0x302B0A6D, 0xF25F1437, 0x4FE1356D, 0x6D51C245, + 0xE485B576, 0x625E7EC6, 0xF44C42E9, 0xA637ED6B, 0x0BFF5CB6, 0xF406B7ED, + 0xEE386BFB, 0x5A899FA5, 0xAE9F2411, 0x7C4B1FE6, 0x49286651, 0xECE65381, + 0xFFFFFFFF, 0xFFFFFFFF +]; + +pub struct DhGroupSha1 { + g: Option, + p: Option, + e: Option, +} + +impl DhGroupSha1 { + pub fn new() -> DhGroupSha1 { + DhGroupSha1 { + g: None, + p: None, + e: None + } + } +} + +impl KeyExchange for DhGroupSha1 { + fn process(&mut self, packet: &Packet) -> KeyExchangeResult { + match packet.msg_type() { + MessageType::KeyExchange(DH_GEX_REQUEST) => { + let mut reader = packet.reader(); + let min = reader.read_uint32().unwrap(); + let opt = reader.read_uint32().unwrap(); + let max = reader.read_uint32().unwrap(); + + println!("Key Sizes: Min {}, Opt {}, Max {}", min, opt, max); + + let mut rng = rand::thread_rng(); + let g = rng.gen_biguint(opt as usize).to_bigint().unwrap(); + let p = rng.gen_biguint(opt as usize).to_bigint().unwrap(); + + let mut packet = Packet::new(MessageType::KeyExchange(DH_GEX_GROUP)); + packet.with_writer(&|w| { + w.write_mpint(g.clone()); + w.write_mpint(p.clone()); + }); + + self.g = Some(g); + self.p = Some(p); + + KeyExchangeResult::Ok(Some(packet)) + }, + MessageType::KeyExchange(DH_GEX_INIT) => { + let mut reader = packet.reader(); + let e = reader.read_mpint().unwrap(); + + println!("Received e: {:?}", e); + + let mut packet = Packet::new(MessageType::KeyExchange(DH_GEX_REPLY)); + packet.with_writer(&|w| { + w.write_string("HELLO WORLD"); + w.write_mpint(e.clone()); + w.write_string("HELLO WORLD"); + }); + + self.e = Some(e); + + KeyExchangeResult::Ok(Some(packet)) + }, + _ => { + debug!("Unhandled key exchange packet: {:?}", packet); + KeyExchangeResult::Error(None) + } + } + } +} diff --git a/src/key_exchange/mod.rs b/src/key_exchange/mod.rs index e9ab740..c49f62c 100644 --- a/src/key_exchange/mod.rs +++ b/src/key_exchange/mod.rs @@ -14,5 +14,5 @@ pub enum KeyExchangeResult { } pub trait KeyExchange { - fn process(&self, packet: &Packet) -> KeyExchangeResult; + fn process(&mut self, packet: &Packet) -> KeyExchangeResult; } diff --git a/src/lib.rs b/src/lib.rs index e884ec9..1cec66e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,16 +1,19 @@ extern crate byteorder; extern crate rand; +extern crate ring; extern crate crypto; extern crate num_bigint; #[macro_use] extern crate log; -pub mod algorithm; -pub mod protocol; +mod algorithm; +mod protocol; +mod packet; +mod message; +mod session; +mod key_exchange; + +pub mod key; pub mod server; -pub mod packet; -pub mod message; -pub mod session; -pub mod key_exchange; pub use self::server::{Server, ServerConfig}; diff --git a/src/packet.rs b/src/packet.rs index cccfa0c..b026711 100644 --- a/src/packet.rs +++ b/src/packet.rs @@ -88,6 +88,10 @@ pub trait ReadPacketExt: ReadBytesExt { Ok(BigInt::from_signed_bytes_be(bytes.as_slice())) } + fn read_uint32(&mut self) -> Result { + Ok(self.read_u32::()?) + } + fn read_bytes(&mut self, len: usize) -> Result> { let mut buffer = Vec::with_capacity(len); self.take(len as u64).read_to_end(&mut buffer)?; @@ -136,7 +140,7 @@ 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 + 1)?; + self.write_u32::(bytes.len() as u32)?; self.write_bytes(bytes.as_slice()) } diff --git a/src/session.rs b/src/session.rs index 8c95033..be12b3b 100644 --- a/src/session.rs +++ b/src/session.rs @@ -40,7 +40,7 @@ impl Session { self.kex_init(packet); } MessageType::KeyExchange(_) => { - if let Some(ref kex) = self.key_exchange { + if let Some(ref mut kex) = self.key_exchange { match kex.process(packet) { KeyExchangeResult::Ok(Some(packet)) => { packet.write_to(&mut self.stream); }, KeyExchangeResult::Error(Some(packet)) => { packet.write_to(&mut self.stream); },