Browse Source

use random iv so we finally have strong encryption now

1.4
clowwindy 12 years ago
parent
commit
9a6a934261
  1. 5
      README.md
  2. 3
      config.json
  3. 68
      encrypt.py
  4. 8
      local.py
  5. 11
      server.py

5
README.md

@ -1,13 +1,16 @@
shadowsocks shadowsocks
=========== ===========
[![Build Status](https://travis-ci.org/clowwindy/shadowsocks.png)](https://travis-ci.org/clowwindy/shadowsocks) [![Build Status](https://travis-ci.org/clowwindy/shadowsocks.png)](https://travis-ci.org/clowwindy/shadowsocks)
Current version: 1.1.1 Current version: 1.1.1
shadowsocks is a lightweight tunnel proxy which can help you get through firewalls shadowsocks is a lightweight tunnel proxy which can help you get through firewalls
Other ports and clients can be found [here](https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients). Other ports and clients can be found [here](https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients).
Warning: AES, DES, blowfish encryption methods are not implemented the same as other versions(Nodejs, etc) yet, please don't use those methods
if you want to use Python version together with other versions!
usage usage
----------- -----------

3
config.json

@ -3,5 +3,6 @@
"server_port":8388, "server_port":8388,
"local_port":1080, "local_port":1080,
"password":"barfoo!", "password":"barfoo!",
"timeout":600 "timeout":600,
"method":null
} }

68
encrypt.py

@ -27,6 +27,11 @@ import struct
import logging import logging
def random_string(length):
import M2Crypto.Rand
return M2Crypto.Rand.rand_bytes(length)
def get_table(key): def get_table(key):
m = hashlib.md5() m = hashlib.md5()
m.update(key) m.update(key)
@ -55,7 +60,7 @@ def init_table(key, method=None):
encrypt_table = ''.join(get_table(key)) encrypt_table = ''.join(get_table(key))
decrypt_table = string.maketrans(encrypt_table, string.maketrans('', '')) decrypt_table = string.maketrans(encrypt_table, string.maketrans('', ''))
else: else:
get_cipher(key, method, 1) Encryptor(key, method) # make an Encryptor to test if the settings if OK
def EVP_BytesToKey(password, key_len, iv_len): def EVP_BytesToKey(password, key_len, iv_len):
@ -89,39 +94,62 @@ method_supported = {
} }
def get_cipher(password, method, op):
import M2Crypto.EVP
password = password.encode('utf-8')
method = method.lower()
m = method_supported.get(method, None)
if m:
key, iv = EVP_BytesToKey(password, m[0], m[1])
return M2Crypto.EVP.Cipher(method.replace('-', '_'), key, iv, op, key_as_bytes=0, d='md5', salt=None, i=1, padding=1)
logging.error('method %s not supported' % method)
sys.exit(1)
class Encryptor(object): class Encryptor(object):
def __init__(self, key, method=None): def __init__(self, key, method=None):
if method == 'table': if method == 'table':
method = None method = None
self.key = key
self.method = method self.method = method
self.iv = None
self.iv_sent = False
self.cipher_iv = ''
self.decipher = None
if method is not None: if method is not None:
self.cipher = get_cipher(key, method, 1) self.cipher = self.get_cipher(key, method, 1, iv=random_string(32))
self.decipher = get_cipher(key, method, 0)
else: else:
self.cipher = None self.cipher = None
self.decipher = None
def get_cipher_len(self, method):
method = method.lower()
m = method_supported.get(method, None)
return m
def iv_len(self):
return len(self.cipher_iv)
def get_cipher(self, password, method, op, iv=None):
import M2Crypto.EVP
password = password.encode('utf-8')
method = method.lower()
m = self.get_cipher_len(method)
if m:
key, iv_ = EVP_BytesToKey(password, m[0], m[1])
if iv is None:
iv = iv_[:m[1]]
if op == 1:
self.cipher_iv = iv[:m[1]] # this iv is for cipher, not decipher
return M2Crypto.EVP.Cipher(method.replace('-', '_'), key, iv, op, key_as_bytes=0, d='md5', salt=None, i=1, padding=1)
logging.error('method %s not supported' % method)
sys.exit(1)
def encrypt(self, buf): def encrypt(self, buf):
if self.cipher is None: if self.method is None:
return string.translate(buf, encrypt_table) return string.translate(buf, encrypt_table)
else: else:
return self.cipher.update(buf) if self.iv_sent:
return self.cipher.update(buf)
else:
self.iv_sent = True
return self.cipher_iv + self.cipher.update(buf)
def decrypt(self, buf): def decrypt(self, buf):
if self.cipher is None: if self.method is None:
return string.translate(buf, decrypt_table) return string.translate(buf, decrypt_table)
else: else:
if self.decipher is None:
decipher_iv_len = self.get_cipher_len(self.method)[1]
decipher_iv = buf[:decipher_iv_len]
self.decipher = self.get_cipher(self.key, self.method, 0, iv=decipher_iv)
buf = buf[decipher_iv_len:]
return self.decipher.update(buf) return self.decipher.update(buf)

8
local.py

@ -67,18 +67,18 @@ class Socks5Server(SocketServer.StreamRequestHandler):
while True: while True:
r, w, e = select.select(fdset, [], []) r, w, e = select.select(fdset, [], [])
if sock in r: if sock in r:
data = sock.recv(4096) data = self.encrypt(sock.recv(4096))
if len(data) <= 0: if len(data) <= 0:
break break
result = send_all(remote, self.encrypt(data)) result = send_all(remote, data)
if result < len(data): if result < len(data):
raise Exception('failed to send all data') raise Exception('failed to send all data')
if remote in r: if remote in r:
data = remote.recv(4096) data = self.decrypt(remote.recv(4096))
if len(data) <= 0: if len(data) <= 0:
break break
result = send_all(sock, self.decrypt(data)) result = send_all(sock, data)
if result < len(data): if result < len(data):
raise Exception('failed to send all data') raise Exception('failed to send all data')
finally: finally:

11
server.py

@ -67,17 +67,17 @@ class Socks5Server(SocketServer.StreamRequestHandler):
while True: while True:
r, w, e = select.select(fdset, [], []) r, w, e = select.select(fdset, [], [])
if sock in r: if sock in r:
data = sock.recv(4096) data = self.decrypt(sock.recv(4096))
if len(data) <= 0: if len(data) <= 0:
break break
result = send_all(remote, self.decrypt(data)) result = send_all(remote, data)
if result < len(data): if result < len(data):
raise Exception('failed to send all data') raise Exception('failed to send all data')
if remote in r: if remote in r:
data = remote.recv(4096) data = self.encrypt(remote.recv(4096))
if len(data) <= 0: if len(data) <= 0:
break break
result = send_all(sock, self.encrypt(data)) result = send_all(sock, data)
if result < len(data): if result < len(data):
raise Exception('failed to send all data') raise Exception('failed to send all data')
@ -95,6 +95,9 @@ class Socks5Server(SocketServer.StreamRequestHandler):
try: try:
self.encryptor = encrypt.Encryptor(KEY, METHOD) self.encryptor = encrypt.Encryptor(KEY, METHOD)
sock = self.connection sock = self.connection
iv_len = self.encryptor.iv_len()
if iv_len:
self.decrypt(sock.recv(iv_len))
addrtype = ord(self.decrypt(sock.recv(1))) addrtype = ord(self.decrypt(sock.recv(1)))
if addrtype == 1: if addrtype == 1:
addr = socket.inet_ntoa(self.decrypt(self.rfile.read(4))) addr = socket.inet_ntoa(self.decrypt(self.rfile.read(4)))

Loading…
Cancel
Save