mirror of
https://github.com/shadowsocks/shadowsocks-rust.git
synced 2026-02-09 01:59:16 +08:00
follows libev branch modification, add hkdf key hash and salt logic
This commit is contained in:
@@ -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
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
|
||||
@@ -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[..],
|
||||
|
||||
Reference in New Issue
Block a user