Skip to content

Commit

Permalink
Finish 3.2.1
Browse files Browse the repository at this point in the history
重新发布 3.2.1
修复 auth_chain_f
-------------------
添加 auth_chain_e/f
添加 xchacha20 xsalsa20
修复内存泄漏

Note: xchacha20 xsalsa20 需要 libsodium 1.0.12 及以上版本支持
  • Loading branch information
Akkariiin committed Oct 15, 2017
2 parents 6ebd86f + f0ec72c commit a3cf025
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 10 deletions.
27 changes: 24 additions & 3 deletions shadowsocks/crypto/openssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from __future__ import absolute_import, division, print_function, \
with_statement

from ctypes import c_char_p, c_int, c_long, byref,\
from ctypes import c_char_p, c_int, c_long, byref, \
create_string_buffer, c_void_p

from shadowsocks import common
Expand Down Expand Up @@ -77,6 +77,7 @@ def load_cipher(cipher_name):
return cipher()
return None


def rand_bytes(length):
if not loaded:
load_openssl()
Expand All @@ -86,6 +87,7 @@ def rand_bytes(length):
raise Exception('RAND_bytes return error')
return buf.raw


class OpenSSLCrypto(object):
def __init__(self, cipher_name, key, iv, op):
self._ctx = None
Expand Down Expand Up @@ -130,9 +132,14 @@ def clean(self):


ciphers = {
# CBC mode need a special use way that different from other.
# CBC mode encrypt message with 16n length, and need 16n+1 length space to decrypt it , otherwise don't decrypt it
'aes-128-cbc': (16, 16, OpenSSLCrypto),
'aes-192-cbc': (24, 16, OpenSSLCrypto),
'aes-256-cbc': (32, 16, OpenSSLCrypto),
'aes-128-gcm': (16, 16, OpenSSLCrypto),
'aes-192-gcm': (24, 16, OpenSSLCrypto),
'aes-256-gcm': (32, 16, OpenSSLCrypto),
'aes-128-cfb': (16, 16, OpenSSLCrypto),
'aes-192-cfb': (24, 16, OpenSSLCrypto),
'aes-256-cfb': (32, 16, OpenSSLCrypto),
Expand Down Expand Up @@ -162,7 +169,6 @@ def clean(self):


def run_method(method):

cipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 1)
decipher = OpenSSLCrypto(method, b'k' * 32, b'i' * 16, 0)

Expand Down Expand Up @@ -197,5 +203,20 @@ def test_rc4():
run_method('rc4')


def test_all():
for k, v in ciphers.items():
print(k)
try:
run_method(k)
except AssertionError as e:
eprint("AssertionError===========" + k)
eprint(e)


def eprint(*args, **kwargs):
import sys
print(*args, file=sys.stderr, **kwargs)


if __name__ == '__main__':
test_aes_128_cfb()
test_all()
55 changes: 50 additions & 5 deletions shadowsocks/crypto/sodium.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
from ctypes import c_char_p, c_int, c_ulong, c_ulonglong, byref, \
create_string_buffer, c_void_p

import logging

from shadowsocks.crypto import util

__all__ = ['ciphers']
Expand Down Expand Up @@ -55,10 +57,31 @@ def load_libsodium():
try:
libsodium.crypto_stream_chacha20_ietf_xor_ic.restype = c_int
libsodium.crypto_stream_chacha20_ietf_xor_ic.argtypes = (c_void_p, c_char_p,
c_ulonglong,
c_char_p, c_ulong,
c_char_p)
c_ulonglong,
c_char_p, c_ulong,
c_char_p)
except:
logging.info("ChaCha20 IETF not support.")
pass

try:
libsodium.crypto_stream_xsalsa20_xor_ic.restype = c_int
libsodium.crypto_stream_xsalsa20_xor_ic.argtypes = (c_void_p, c_char_p,
c_ulonglong,
c_char_p, c_ulonglong,
c_char_p)
except:
logging.info("XSalsa20 not support.")
pass

try:
libsodium.crypto_stream_xchacha20_xor_ic.restype = c_int
libsodium.crypto_stream_xchacha20_xor_ic.argtypes = (c_void_p, c_char_p,
c_ulonglong,
c_char_p, c_ulonglong,
c_char_p)
except:
logging.info("XChaCha20 not support. XChaCha20 only support since libsodium v1.0.12")
pass

buf = create_string_buffer(buf_size)
Expand All @@ -79,6 +102,10 @@ def __init__(self, cipher_name, key, iv, op):
self.cipher = libsodium.crypto_stream_chacha20_xor_ic
elif cipher_name == 'chacha20-ietf':
self.cipher = libsodium.crypto_stream_chacha20_ietf_xor_ic
elif cipher_name == 'xchacha20':
self.cipher = libsodium.crypto_stream_xchacha20_xor_ic
elif cipher_name == 'xsalsa20':
self.cipher = libsodium.crypto_stream_xsalsa20_xor_ic
else:
raise Exception('Unknown cipher')
# byte counter, not block counter
Expand Down Expand Up @@ -107,10 +134,13 @@ def update(self, data):
def clean(self):
pass


ciphers = {
'salsa20': (32, 8, SodiumCrypto),
'chacha20': (32, 8, SodiumCrypto),
'chacha20-ietf': (32, 12, SodiumCrypto),
'xchacha20': (32, 24, SodiumCrypto),
'xsalsa20': (32, 24, SodiumCrypto),
}


Expand All @@ -122,21 +152,36 @@ def test_salsa20():


def test_chacha20():

cipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 1)
decipher = SodiumCrypto('chacha20', b'k' * 32, b'i' * 16, 0)

util.run_cipher(cipher, decipher)


def test_chacha20_ietf():

cipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 1)
decipher = SodiumCrypto('chacha20-ietf', b'k' * 32, b'i' * 16, 0)

util.run_cipher(cipher, decipher)


def test_xchacha20():
cipher = SodiumCrypto('xchacha20', b'k' * 32, b'i' * 24, 1)
decipher = SodiumCrypto('xchacha20', b'k' * 32, b'i' * 24, 0)

util.run_cipher(cipher, decipher)


def test_xsalsa20():
cipher = SodiumCrypto('xsalsa20', b'k' * 32, b'i' * 24, 1)
decipher = SodiumCrypto('xsalsa20', b'k' * 32, b'i' * 24, 0)

util.run_cipher(cipher, decipher)


if __name__ == '__main__':
test_chacha20_ietf()
test_chacha20()
test_salsa20()
test_xchacha20()
test_xsalsa20()
7 changes: 7 additions & 0 deletions shadowsocks/crypto/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@


def find_library_nt(name):
# type: (str) -> list
"""
find lib in windows in all the directory in path env
:param name: can end with `.dll` or not
:return: lib results list
"""
# modified from ctypes.util
# ctypes.util.find_library just returns first result he found
# but we want to try them all
Expand Down
2 changes: 1 addition & 1 deletion shadowsocks/obfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
method_supported.update(auth_chain.obfs_map)

def mu_protocol():
return ["auth_aes128_md5", "auth_aes128_sha1", "auth_chain_a"]
return ["auth_aes128_md5", "auth_aes128_sha1", "auth_chain_a", "auth_chain_b", "auth_chain_c", "auth_chain_d", "auth_chain_e"]

class server_info(object):
def __init__(self, data):
Expand Down
53 changes: 52 additions & 1 deletion shadowsocks/obfsplugin/auth_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,17 @@ def create_auth_chain_e(method):
return auth_chain_e(method)


def create_auth_chain_f(method):
return auth_chain_f(method)


obfs_map = {
'auth_chain_a': (create_auth_chain_a,),
'auth_chain_b': (create_auth_chain_b,),
'auth_chain_c': (create_auth_chain_c,),
'auth_chain_d': (create_auth_chain_d,),
'auth_chain_e': (create_auth_chain_e,),
'auth_chain_f': (create_auth_chain_f,),
}


Expand Down Expand Up @@ -845,7 +850,7 @@ def rnd_data_len(self, buf_size, last_hash, random):

class auth_chain_e(auth_chain_d):
def __init__(self, method):
super(auth_chain_d, self).__init__(method)
super(auth_chain_e, self).__init__(method)
self.salt = b"auth_chain_e"
self.no_compatible_method = 'auth_chain_e'

Expand All @@ -858,3 +863,49 @@ def rnd_data_len(self, buf_size, last_hash, random):
# use the mini size in the data_size_list0
pos = bisect.bisect_left(self.data_size_list0, other_data_size)
return self.data_size_list0[pos] - other_data_size


# auth_chain_f
# when every connect create, generate size_list will different when every day or every custom time interval which set in the config
class auth_chain_f(auth_chain_e):
def __init__(self, method):
super(auth_chain_f, self).__init__(method)
self.salt = b"auth_chain_f"
self.no_compatible_method = 'auth_chain_f'

def set_server_info(self, server_info):
self.server_info = server_info
try:
max_client = int(server_info.protocol_param.split('#')[0])
except:
max_client = 64
try:
self.key_change_interval = int(server_info.protocol_param.split('#')[1]) # config are in second
except:
self.key_change_interval = 60 * 60 * 24 # a day by second
self.key_change_datetime_key = int(int(time.time()) / self.key_change_interval)
self.key_change_datetime_key_bytes = [] # big bit first list
for i in range(7, -1, -1): # big-ending compare to c
self.key_change_datetime_key_bytes.append((self.key_change_datetime_key >> (8 * i)) & 0xFF)
self.server_info.data.set_max_client(max_client)
self.init_data_size(self.server_info.key)

def init_data_size(self, key):
if self.data_size_list0:
self.data_size_list0 = []
random = xorshift128plus()
# key xor with key_change_datetime_key
new_key = bytearray(key)
for i in range(0, 8):
new_key[i] ^= self.key_change_datetime_key_bytes[i]
random.init_from_bin(new_key)
# 补全数组长为12~24-1
list_len = random.next() % (8 + 16) + (4 + 8)
for i in range(0, list_len):
self.data_size_list0.append(int(random.next() % 2340 % 2040 % 1440))
self.data_size_list0.sort()
old_len = len(self.data_size_list0)
self.check_and_patch_data_size(random)
# if check_and_patch_data_size are work, re-sort again.
if old_len != len(self.data_size_list0):
self.data_size_list0.sort()

0 comments on commit a3cf025

Please sign in to comment.