From 2bb0df72ffb95a0969f51b3fdf751f5063b02695 Mon Sep 17 00:00:00 2001 From: "Y. T. Chung" Date: Tue, 28 Oct 2014 02:22:51 +0800 Subject: [PATCH] add rc4-md5 method, fixed bugs --- Cargo.toml | 7 +- src/crypto/cipher.rs | 13 +++- src/crypto/digest.rs | 35 ++++++++++ src/crypto/mod.rs | 3 +- src/crypto/openssl.rs | 155 ++++++++++++++++++++++++++++++++++++++++-- src/lib.rs | 2 - 6 files changed, 202 insertions(+), 13 deletions(-) create mode 100644 src/crypto/digest.rs diff --git a/Cargo.toml b/Cargo.toml index 66239de6..5a224f2e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,8 @@ default = [ "cipher-cast5-cfb", "cipher-des-cfb", "cipher-rc2-cfb", + "cipher-rc4", + "cipher-rc4-md5", ] cipher-aes-cfb = [] @@ -27,6 +29,8 @@ cipher-cast5-cfb = [] cipher-des-cfb = [] cipher-idea-cfb = [] cipher-rc2-cfb = [] +cipher-rc4 = [] +cipher-rc4-md5 = [] cipher-seed-cfb = [] [[bin]] @@ -38,6 +42,3 @@ path = "src/bin/local.rs" name = "ssserver" path = "src/bin/server.rs" - -[dependencies.rust-crypto] -git = "https://github.com/DaGenix/rust-crypto.git" diff --git a/src/crypto/cipher.rs b/src/crypto/cipher.rs index 36295d84..fcd9c47d 100644 --- a/src/crypto/cipher.rs +++ b/src/crypto/cipher.rs @@ -88,10 +88,14 @@ pub const CIPHER_DES_CFB: &'static str = "des-cfb"; pub const CIPHER_IDEA_CFB: &'static str = "idea-cfb"; #[cfg(feature="cipher-rc2-cfb")] pub const CIPHER_RC2_CFB: &'static str = "rc2-cfb"; +#[cfg(feature="cipher-rc4")] +pub const CIPHER_RC4: &'static str = "rc4"; +#[cfg(feature="cipher-rc4-md5")] +pub const CIPHER_RC4_MD5: &'static str = "rc4-md5"; #[cfg(feature="cipher-seed-cfb")] pub const CIPHER_SEED_CFB: &'static str = "seed-cfb"; -#[deriving(Clone)] +#[deriving(Clone, Show)] pub enum CipherType { CipherTypeUnknown, @@ -128,7 +132,8 @@ pub enum CipherType { #[cfg(feature="cipher-des-cfb")] CipherTypeDesCfb, #[cfg(feature="cipher-idea-cfb")] CipherTypeIdeaCfb, #[cfg(feature="cipher-rc2-cfb")] CipherTypeRc2Cfb, - #[cfg(feature="cipher-rc4-hmac-md5")] CipherTypeRc4HmacMd5, + #[cfg(feature="cipher-rc4")] CipherTypeRc4, + #[cfg(feature="cipher-rc4-md5")] CipherTypeRc4Md5, #[cfg(feature="cipher-seed-cfb")] CipherTypeSeedCfb, } @@ -234,6 +239,10 @@ pub fn with_name(method: &str, key: &[u8]) -> Option { CIPHER_IDEA_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeIdeaCfb, key))), #[cfg(feature="cipher-rc2-cfb")] CIPHER_RC2_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeRc2Cfb, key))), + #[cfg(feature="cipher-rc4")] + CIPHER_RC4 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeRc4, key))), + #[cfg(feature="cipher-rc4-md5")] + CIPHER_RC4_MD5 => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeRc4Md5, key))), #[cfg(feature="cipher-seed-cfb")] CIPHER_SEED_CFB => Some(OpenSSLCrypto(openssl::OpenSSLCipher::new(CipherTypeSeedCfb, key))), diff --git a/src/crypto/digest.rs b/src/crypto/digest.rs new file mode 100644 index 00000000..3efa75d6 --- /dev/null +++ b/src/crypto/digest.rs @@ -0,0 +1,35 @@ +// The MIT License (MIT) + +// Copyright (c) 2014 Y. T. CHUNG + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +// the Software, and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +pub trait Digest { + fn update(&mut self, data: &[u8]); + fn digest(&mut self) -> Vec; +} + +#[deriving(Clone)] +pub enum DigestType { + Md5, + Sha1, + Sha, + UnknownDigest, +} + + diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index 1d35352b..4912cdb8 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -23,8 +23,7 @@ extern crate libc; extern crate test; -extern crate "rust-crypto" as rust_crypto; pub mod cipher; pub mod openssl; -pub mod rust; +pub mod digest; diff --git a/src/crypto/openssl.rs b/src/crypto/openssl.rs index c4c29243..206b6334 100644 --- a/src/crypto/openssl.rs +++ b/src/crypto/openssl.rs @@ -28,6 +28,9 @@ extern crate test; use crypto::cipher::Cipher; use crypto::cipher; +use crypto::digest::Digest; +use crypto::digest; + use std::ptr; use std::clone::Clone; use std::mem::swap; @@ -37,6 +40,8 @@ type EVP_CIPHER_CTX = *const libc::c_void; #[allow(non_camel_case_types)] type EVP_CIPHER = *const libc::c_void; #[allow(non_camel_case_types)] +type EVP_MD_CTX = *mut libc::c_void; +#[allow(non_camel_case_types)] type EVP_MD = *const libc::c_void; #[allow(non_camel_case_types)] type ENGINE = *const libc::c_void; @@ -118,13 +123,125 @@ extern { fn EVP_rc2_cfb64() -> EVP_CIPHER; #[cfg(feature="cipher-seed-cfb")] fn EVP_seed_cfb128() -> EVP_CIPHER; + #[cfg(any(feature="cipher-rc4", feature="cipher-rc4-md5"))] + fn EVP_rc4() -> EVP_CIPHER; // MD + fn EVP_MD_CTX_create() -> EVP_MD_CTX; + fn EVP_MD_CTX_init(ctx: EVP_MD_CTX); + fn EVP_MD_CTX_cleanup(ctx: EVP_MD_CTX); + fn EVP_MD_CTX_destroy(ctx: EVP_MD_CTX); + fn EVP_MD_CTX_copy_ex(out: EVP_MD_CTX, ctx_in: EVP_MD_CTX) -> libc::c_int; + fn EVP_DigestInit_ex(ctx: EVP_MD_CTX, md_type: EVP_MD, engine: ENGINE) -> libc::c_int; + fn EVP_DigestUpdate(ctx: EVP_MD_CTX, d: *const libc::c_void, cnt: libc::size_t) -> libc::c_int; + fn EVP_DigestFinal_ex(ctx: EVP_MD_CTX, md: *mut libc::c_uchar, s: *mut libc::c_uint); + fn EVP_md5() -> EVP_MD; fn EVP_sha() -> EVP_MD; fn EVP_sha1() -> EVP_MD; } +struct OpenSSLDigest { + md_ctx: EVP_MD_CTX, + digest_len: uint, +} + +impl OpenSSLDigest { + pub fn new(t: digest::DigestType) -> OpenSSLDigest { + let ctx = unsafe { + let md_ctx = EVP_MD_CTX_create(); + assert!(!md_ctx.is_null()); + EVP_MD_CTX_init(md_ctx); + md_ctx + }; + + let dlen = unsafe { + let (md, len) = OpenSSLDigest::get_md(t); + EVP_DigestInit_ex(ctx, md, ptr::null()); + len + }; + + OpenSSLDigest { + md_ctx: ctx, + digest_len: dlen, + } + } + + pub fn get_md(t: digest::DigestType) -> (EVP_MD, uint) { + unsafe { + match t { + digest::Md5 => { (EVP_md5(), 16u) }, + digest::Sha => { (EVP_sha(), 20u) }, + digest::Sha1 => { (EVP_sha1(), 20u) }, + + digest::UnknownDigest => { (ptr::null(), 0u) }, + } + } + } +} + +impl Digest for OpenSSLDigest { + fn update(&mut self, data: &[u8]) { + unsafe { + if EVP_DigestUpdate(self.md_ctx, data.as_ptr() as *const libc::c_void, data.len() as libc::size_t) + != 1 as libc::c_int { + fail!("Failed to call EVP_DigestUpdate"); + } + } + } + + fn digest(&mut self) -> Vec { + let mut dig = Vec::from_elem(self.digest_len, 0u8); + unsafe { + EVP_DigestFinal_ex(self.md_ctx, dig.as_mut_ptr(), ptr::null_mut()); + } + dig + } +} + +impl Clone for OpenSSLDigest { + fn clone(&self) -> OpenSSLDigest { + let ctx = unsafe { + let md_ctx = EVP_MD_CTX_create(); + assert!(!md_ctx.is_null()); + if EVP_MD_CTX_copy_ex(md_ctx, self.md_ctx) != 1 as libc::c_int { + EVP_MD_CTX_destroy(md_ctx); + fail!("Failed to call EVP_MD_CTX_copy_ex"); + } + md_ctx + }; + OpenSSLDigest { + md_ctx: ctx, + digest_len: self.digest_len, + } + } + + fn clone_from(&mut self, source: &OpenSSLDigest) { + self.md_ctx = unsafe { + let md_ctx = EVP_MD_CTX_create(); + assert!(!md_ctx.is_null()); + if EVP_MD_CTX_copy_ex(md_ctx, source.md_ctx) != 1 as libc::c_int { + EVP_MD_CTX_destroy(md_ctx); + fail!("Failed to call EVP_MD_CTX_copy_ex"); + } + EVP_MD_CTX_cleanup(self.md_ctx); + EVP_MD_CTX_destroy(self.md_ctx); + md_ctx + }; + self.digest_len = source.digest_len; + } +} + +#[unsafe_destructor] +impl Drop for OpenSSLDigest { + fn drop(&mut self) { + unsafe { + EVP_MD_CTX_cleanup(self.md_ctx); + EVP_MD_CTX_destroy(self.md_ctx); + } + } +} + /// This two modes will be converted into the last parameter of `EVP_CipherInit_ex`. enum CryptoMode { CryptoModeDecrypt, @@ -159,7 +276,7 @@ impl OpenSSLCrypto { let (ctx, _, block_size) = unsafe { let (cipher, key_size, block_size) = OpenSSLCrypto::get_cipher(cipher_type); - assert!(iv.len() >= block_size); + // assert!(iv.len() >= block_size); let evp_ctx = EVP_CIPHER_CTX_new(); assert!(!evp_ctx.is_null()); @@ -253,6 +370,10 @@ impl OpenSSLCrypto { cipher::CipherTypeRc2Cfb => { (EVP_rc2_cfb64(), 16, 8) }, #[cfg(feature="cipher-seed-cfb")] cipher::CipherTypeSeedCfb => { (EVP_seed_cfb128(), 16, 16) }, + #[cfg(feature="cipher-rc4")] + cipher::CipherTypeRc4 => { (EVP_rc4(), 16, 16) }, + #[cfg(feature="cipher-rc4-md5")] + cipher::CipherTypeRc4Md5 => { (EVP_rc4(), 16, 16) }, cipher::CipherTypeUnknown => { (ptr::null(), 0, 0) }, } @@ -366,11 +487,25 @@ impl Cipher for OpenSSLCipher { match self.encryptor { Some(ref encryptor) => { encryptor.update(data) }, None => { - let (key, mut iv) = OpenSSLCrypto::bytes_to_key( + let (key, mut iv) = { + let (key, iv) = OpenSSLCrypto::bytes_to_key( self.cipher_type, self.key.as_slice() ); + match self.cipher_type { + cipher::CipherTypeRc4Md5 => { + let mut md5_digest = OpenSSLDigest::new(digest::Md5); + md5_digest.update(key.as_slice()); + md5_digest.update(iv.as_slice()); + (md5_digest.digest(), iv) + }, + _ => { + (key, iv) + } + } + }; + let encryptor = OpenSSLCrypto::new(self.cipher_type, key.as_slice(), iv.as_slice(), CryptoModeEncrypt); self.encryptor = Some(encryptor); @@ -387,15 +522,27 @@ impl Cipher for OpenSSLCipher { match self.decryptor { Some(ref decryptor) => { decryptor.update(data) }, None => { - let (key, iv) = OpenSSLCrypto::bytes_to_key( + let (_, _, expected_iv_len) = OpenSSLCrypto::get_cipher(self.cipher_type); + let (pad_key, _) = OpenSSLCrypto::bytes_to_key( self.cipher_type, self.key.as_slice() ); // Get the begining IV from the data - let expected_iv_len = iv.len(); let (real_iv, data) = data.split_at(expected_iv_len); + let key = match self.cipher_type { + cipher::CipherTypeRc4Md5 => { + let mut md5_digest = OpenSSLDigest::new(digest::Md5); + md5_digest.update(pad_key.as_slice()); + md5_digest.update(real_iv.as_slice()); + md5_digest.digest() + }, + _ => { + pad_key + } + }; + let decryptor = OpenSSLCrypto::new(self.cipher_type, key.as_slice(), real_iv, CryptoModeDecrypt); self.decryptor = Some(decryptor); diff --git a/src/lib.rs b/src/lib.rs index 9e7ab06a..16cffcaf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,8 +29,6 @@ extern crate serialize; #[phase(plugin, link)] extern crate log; -extern crate native; - pub const VERSION: &'static str = "0.9.0"; pub mod config;