follows libev branch modification, add hkdf key hash and salt logic

This commit is contained in:
Y. T. Chung
2017-02-17 23:26:51 +08:00
parent 0cb7bd428b
commit 77368c4aed
8 changed files with 145 additions and 29 deletions

View File

@@ -8,6 +8,9 @@ documentation = "https://docs.rs/shadowsocks-rust"
keywords = ["shadowsocks", "proxy", "socks", "socks5", "firewall"]
license = "MIT"
[features]
key-derive-argon2 = ["argon2rs"]
[lib]
name = "shadowsocks"
@@ -45,4 +48,7 @@ num_cpus = "1.1"
lazy_static = "0.2"
serde_json = "0.9"
base64 = "0.3"
argon2rs = "0.2"
[dependencies.argon2rs]
version = "0.2"
optional = true

View File

@@ -179,6 +179,7 @@ impl ServerConfig {
/// Creates a new ServerConfig
pub fn new(addr: ServerAddr, pwd: String, method: CipherType, timeout: Option<Duration>) -> ServerConfig {
let enc_key = method.bytes_to_key(pwd.as_bytes());
trace!("Initialize config with pwd: {:?}, key: {:?}", pwd, enc_key);
ServerConfig {
addr: addr,
password: pwd,

View File

@@ -25,6 +25,10 @@ use crypto::cipher::{CipherType, CipherCategory, CipherResult};
use crypto::crypto::CryptoAeadCrypto;
use rust_crypto::hkdf::{hkdf_expand, hkdf_extract};
use rust_crypto::sha1::Sha1;
use rust_crypto::digest::Digest;
pub trait AeadEncryptor {
fn encrypt(&mut self, input: &[u8], output: &mut [u8], tag: &mut [u8]);
}
@@ -57,4 +61,21 @@ pub fn new_aead_decryptor(t: CipherType, key: &[u8], nounce: &[u8]) -> Box<AeadD
_ => unreachable!(),
}
}
const SUBKEY_INFO: &'static [u8] = b"ss-subkey";
pub fn make_skey(t: CipherType, key: &[u8], salt: &[u8]) -> Vec<u8> {
assert!(t.category() == CipherCategory::Aead);
let sha1 = Sha1::new();
let output_bytes = sha1.output_bytes();
let mut prk = vec![0u8; output_bytes];
hkdf_extract(sha1, salt, key, &mut prk);
let mut skey = vec![0u8; key.len()];
hkdf_expand(Sha1::new(), &prk, SUBKEY_INFO, &mut skey);
skey
}

View File

@@ -31,6 +31,7 @@ use crypto::digest::{self, DigestType, Digest};
use openssl::symm;
#[cfg(feature = "key-derive-argon2")]
use argon2rs::{Argon2, Variant};
/// Cipher result
@@ -167,6 +168,37 @@ impl CipherType {
}
}
/// Extends key to match the required key length
#[cfg(not(feature = "key-derive-argon2"))]
pub fn bytes_to_key(&self, key: &[u8]) -> Vec<u8> {
let iv_len = self.iv_size();
let key_len = self.key_size();
let mut digest = digest::with_type(DigestType::Md5);
let mut result = Vec::new();
let mut m = Vec::new();
let mut loop_count = 0;
while loop_count * digest.digest_len() < (key_len + iv_len) {
let mut vkey = m.clone();
vkey.extend_from_slice(key);
digest.update(&vkey);
m.clear();
digest.digest(&mut m);
loop_count += 1;
digest.reset();
result.extend_from_slice(&m[..]);
}
result.resize(key_len, 0);
result
}
#[cfg(feature = "key-derive-argon2")]
fn aead_key_derive(&self, key: &[u8]) -> Vec<u8> {
// We should use crypto_pwhash in libsodium
// Salt is b"shadowsocks hash"
@@ -185,6 +217,7 @@ impl CipherType {
}
/// Extends key to match the required key length
#[cfg(feature = "key-derive-argon2")]
pub fn bytes_to_key(&self, key: &[u8]) -> Vec<u8> {
match self.category() {
CipherCategory::Aead => self.aead_key_derive(key),
@@ -244,18 +277,22 @@ impl CipherType {
}
}
/// Generate a random initialize vector for this cipher
pub fn gen_init_vec(&self) -> Vec<u8> {
let iv_len = self.iv_size();
let mut iv = Vec::with_capacity(iv_len);
fn gen_random_bytes(len: usize) -> Vec<u8> {
let mut iv = Vec::with_capacity(len);
unsafe {
iv.set_len(iv_len);
iv.set_len(len);
}
rand::thread_rng().fill_bytes(iv.as_mut_slice());
iv
}
/// Generate a random initialize vector for this cipher
pub fn gen_init_vec(&self) -> Vec<u8> {
let iv_len = self.iv_size();
CipherType::gen_random_bytes(iv_len)
}
/// Get category of cipher
pub fn category(&self) -> CipherCategory {
match *self {
@@ -278,6 +315,17 @@ impl CipherType {
_ => panic!("Only support AEAD ciphers, found {:?}", self),
}
}
/// Get nonce size for AEAD ciphers
pub fn salt_size(&self) -> usize {
assert!(self.category() == CipherCategory::Aead);
self.key_size()
}
/// Get salt for AEAD ciphers
pub fn gen_salt(&self) -> Vec<u8> {
CipherType::gen_random_bytes(self.salt_size())
}
}
impl FromStr for CipherType {

View File

@@ -32,6 +32,7 @@ use rust_crypto::aes::KeySize;
use crypto::{StreamCipher, CipherType, CipherResult};
use crypto::{AeadDecryptor, AeadEncryptor};
use crypto::cipher::Error;
use crypto::aead::make_skey;
/// Cipher provided by Rust-Crypto
pub enum CryptoCipher {
@@ -80,37 +81,51 @@ pub struct CryptoAeadCrypto {
cipher: CryptoAeadCryptoVariant,
cipher_type: CipherType,
key: Vec<u8>,
nounce: Vec<u8>,
nonce: Vec<u8>,
}
impl CryptoAeadCrypto {
pub fn new(t: CipherType, key: &[u8], nounce: &[u8]) -> CryptoAeadCrypto {
pub fn new(t: CipherType, key: &[u8], salt: &[u8]) -> CryptoAeadCrypto {
// TODO: Check if salt is duplicated
let nonce_size = t.iv_size();
let nonce = vec![0u8; nonce_size];
let skey = make_skey(t, key, salt);
let cipher = CryptoAeadCrypto::new_variant(t, &skey, &nonce);
CryptoAeadCrypto {
cipher: CryptoAeadCrypto::new_variant(t, key, nounce),
cipher: cipher,
cipher_type: t,
key: key.to_owned(),
nounce: nounce.to_owned(),
key: skey,
nonce: nonce,
}
}
fn new_variant(t: CipherType, key: &[u8], nounce: &[u8]) -> CryptoAeadCryptoVariant {
fn new_variant(t: CipherType, key: &[u8], nonce: &[u8]) -> CryptoAeadCryptoVariant {
match t {
CipherType::Aes128Gcm => {
CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize128, key, nounce, &[]))
}
CipherType::Aes192Gcm => {
CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize192, key, nounce, &[]))
}
CipherType::Aes256Gcm => {
CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize256, key, nounce, &[]))
}
CipherType::Aes128Gcm => CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize128, key, nonce, &[])),
CipherType::Aes192Gcm => CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize192, key, nonce, &[])),
CipherType::Aes256Gcm => CryptoAeadCryptoVariant::AesGcm(AesGcm::new(KeySize::KeySize256, key, nonce, &[])),
_ => panic!("Unsupported {:?}", t),
}
}
fn increase_nonce(&mut self) {
let mut adding = 1;
for v in self.nonce.iter_mut() {
if adding == 0 {
break;
}
let (r, overflow) = v.overflowing_add(adding);
*v = r;
adding = if overflow { 1 } else { 0 };
}
}
fn reset(&mut self) {
let var = CryptoAeadCrypto::new_variant(self.cipher_type, &self.key, &self.nounce);
self.increase_nonce();
let var = CryptoAeadCrypto::new_variant(self.cipher_type, &self.key, &self.nonce);
mem::replace(&mut self.cipher, var);
}
}

View File

@@ -102,6 +102,7 @@ extern crate rand;
extern crate crypto as rust_crypto;
extern crate ip;
extern crate openssl;
#[cfg(feature = "key-derive-argon2")]
extern crate argon2rs;
extern crate futures;

View File

@@ -273,6 +273,8 @@ impl<W> EncryptedWrite for EncryptedWriter<W>
let mut encrypted_data_len = [0u8; 2];
self.cipher.encrypt(&data_len_buf[..], &mut encrypted_data_len, &mut tag_buf[..]);
trace!("LENGTH TAG: {:?}", tag_buf);
buf.extend_from_slice(&encrypted_data_len);
buf.extend_from_slice(&tag_buf);
@@ -281,6 +283,8 @@ impl<W> EncryptedWrite for EncryptedWriter<W>
buf.resize(orig_buf_len + data.len(), 0);
self.cipher.encrypt(data, &mut buf[orig_buf_len..], &mut tag_buf);
trace!("DATA TAG: {:?}", tag_buf);
buf.append(&mut tag_buf);

View File

@@ -230,12 +230,24 @@ pub fn proxy_handshake(remote_stream: TcpStream,
// Send initialize vector to remote and create encryptor
let local_iv = svr_cfg.method().gen_init_vec();
trace!("Going to send initialize vector: {:?}", local_iv);
let method = svr_cfg.method();
let prev_buf = match method.category() {
CipherCategory::Stream => {
let local_iv = method.gen_init_vec();
trace!("Going to send initialize vector: {:?}", local_iv);
local_iv
}
CipherCategory::Aead => {
let local_salt = method.gen_salt();
trace!("Going to send salt: {:?}", local_salt);
local_salt
}
};
try_timeout(write_all(w, local_iv), timeout, &handle).and_then(move |(w, local_iv)| {
try_timeout(write_all(w, prev_buf), timeout, &handle).and_then(move |(w, prev_buf)| {
match svr_cfg.method().category() {
CipherCategory::Stream => {
let local_iv = prev_buf;
let encryptor = crypto::new_stream(svr_cfg.method(),
svr_cfg.key(),
&local_iv[..],
@@ -244,7 +256,8 @@ pub fn proxy_handshake(remote_stream: TcpStream,
Ok(From::from(StreamEncryptedWriter::new(w, encryptor)))
}
CipherCategory::Aead => {
let wtr = AeadEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_iv[..]);
let local_salt = prev_buf;
let wtr = AeadEncryptedWriter::new(w, svr_cfg.method(), svr_cfg.key(), &local_salt[..]);
Ok(From::from(wtr))
}
}
@@ -255,14 +268,21 @@ pub fn proxy_handshake(remote_stream: TcpStream,
let svr_cfg = svr_cfg_cloned;
// Decrypt data from remote server
let iv_len = svr_cfg.method().iv_size();
try_timeout(read_exact(r, vec![0u8; iv_len]), timeout, &handle).and_then(move |(r, remote_iv)| {
let method = svr_cfg.method();
let prev_len = match method.category() {
CipherCategory::Stream => method.iv_size(),
CipherCategory::Aead => method.salt_size(),
};
try_timeout(read_exact(r, vec![0u8; prev_len]), timeout, &handle).and_then(move |(r, remote_iv)| {
// TODO: If crypto type is Aead, returns `aead::DecryptedReader` instead
trace!("Got initialize vector {:?}", remote_iv);
match svr_cfg.method().category() {
CipherCategory::Stream => {
trace!("Got initialize vector {:?}", remote_iv);
let decryptor = crypto::new_stream(svr_cfg.method(),
svr_cfg.key(),
&remote_iv[..],