11 Commits
Author SHA1 Message Date
clowwindy af5455c820 fix test 2014-12-31 13:41:51 +08:00
clowwindy ccd1c0b45c implement auth in encrypt module 2014-12-31 09:21:10 +08:00
clowwindy 1423fe1921 support color 2014-12-28 15:24:38 +08:00
clowwindy 5a5c8b9c7e fix coverage server 2014-12-28 15:24:38 +08:00
clowwindy 70559a1030 fix pep8 2014-12-28 15:24:38 +08:00
clowwindy 45ee594d58 coverage server now supports more projects 2014-12-28 15:24:38 +08:00
clowwindy 707b5c1def Merge pull request #237 from v3aqb/patch-1
Update ctypes_libsodium.py
2014-12-28 14:02:59 +08:00
v3aqb d91f7d85d4 Update ctypes_libsodium.py 2014-12-28 08:30:29 +08:00
clowwindy 5a5158b33f update url 2014-12-27 23:36:09 +08:00
clowwindy 7f19c31640 update project url 2014-12-27 23:29:48 +08:00
clowwindy 937693519a update url in readme 2014-12-27 23:29:07 +08:00
10 changed files with 241 additions and 78 deletions
+11 -11
View File
@@ -104,7 +104,7 @@ Documentation
-------------
You can find all the documentation in the wiki:
https://github.com/clowwindy/shadowsocks/wiki
https://github.com/shadowsocks/shadowsocks/wiki
License
-------
@@ -118,23 +118,23 @@ Bugs and Issues
* [Mailing list]
[Android]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android
[Build Status]: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat
[Chinese Readme]: https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
[Android]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android
[Build Status]: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat
[Chinese Readme]: https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E
[Coverage Status]: http://192.81.132.184/result/shadowsocks
[Coverage]: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html
[Debian sid]: https://packages.debian.org/unstable/python/shadowsocks
[the package]: https://pypi.python.org/pypi/shadowsocks
[Encryption]: https://github.com/clowwindy/shadowsocks/wiki/Encryption
[Encryption]: https://github.com/shadowsocks/shadowsocks/wiki/Encryption
[iOS]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help
[Issue Tracker]: https://github.com/clowwindy/shadowsocks/issues?state=open
[Issue Tracker]: https://github.com/shadowsocks/shadowsocks/issues?state=open
[Mailing list]: http://groups.google.com/group/shadowsocks
[OpenSSL for Windows]: http://slproweb.com/products/Win32OpenSSL.html
[OpenWRT]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#openwrt
[OpenWRT]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt
[OS X]: https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help
[PyPI]: https://pypi.python.org/pypi/shadowsocks
[PyPI version]: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat
[TCP_FASTOPEN]: https://github.com/clowwindy/shadowsocks/wiki/TCP-Fast-Open
[Travis CI]: https://travis-ci.org/clowwindy/shadowsocks
[Troubleshooting]: https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting
[Windows]: https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#windows
[TCP_FASTOPEN]: https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open
[Travis CI]: https://travis-ci.org/shadowsocks/shadowsocks
[Troubleshooting]: https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting
[Windows]: https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows
+33 -31
View File
@@ -1,11 +1,11 @@
shadowsocks
===========
|PyPI version| |Build Status|
|PyPI version| |Build Status| |Coverage Status|
A fast tunnel proxy that helps you bypass firewalls.
`中文说明 <https://github.com/clowwindy/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E>`__
`中文说明 <https://github.com/shadowsocks/shadowsocks/wiki/Shadowsocks-%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E>`__
Install
-------
@@ -16,12 +16,12 @@ server.
Client
~~~~~~
- `Windows <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#windows>`__
- `Windows <https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#windows>`__
/ `OS
X <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Shadowsocks-for-OSX-Help>`__
- `Android <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#android>`__
- `Android <https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#android>`__
/ `iOS <https://github.com/shadowsocks/shadowsocks-iOS/wiki/Help>`__
- `OpenWRT <https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients#openwrt>`__
- `OpenWRT <https://github.com/shadowsocks/shadowsocks/wiki/Ports-and-Clients#openwrt>`__
Server
~~~~~~
@@ -77,27 +77,27 @@ On your server create a config file ``/etc/shadowsocks.json``. Example:
Explanation of the fields:
+------------------+---------------------------------------------------------------------------------------------------------+
| Name | Explanation |
+==================+=========================================================================================================+
| server | the address your server listens |
+------------------+---------------------------------------------------------------------------------------------------------+
| server\_port | server port |
+------------------+---------------------------------------------------------------------------------------------------------+
| local\_address | the address your local listens |
+------------------+---------------------------------------------------------------------------------------------------------+
| local\_port | local port |
+------------------+---------------------------------------------------------------------------------------------------------+
| password | password used for encryption |
+------------------+---------------------------------------------------------------------------------------------------------+
| timeout | in seconds |
+------------------+---------------------------------------------------------------------------------------------------------+
| method | default: "aes-256-cfb", see `Encryption <https://github.com/clowwindy/shadowsocks/wiki/Encryption>`__ |
+------------------+---------------------------------------------------------------------------------------------------------+
| fast\_open | use `TCP\_FASTOPEN <https://github.com/clowwindy/shadowsocks/wiki/TCP-Fast-Open>`__, true / false |
+------------------+---------------------------------------------------------------------------------------------------------+
| workers | number of workers, available on Unix/Linux |
+------------------+---------------------------------------------------------------------------------------------------------+
+------------------+-----------------------------------------------------------------------------------------------------------+
| Name | Explanation |
+==================+===========================================================================================================+
| server | the address your server listens |
+------------------+-----------------------------------------------------------------------------------------------------------+
| server\_port | server port |
+------------------+-----------------------------------------------------------------------------------------------------------+
| local\_address | the address your local listens |
+------------------+-----------------------------------------------------------------------------------------------------------+
| local\_port | local port |
+------------------+-----------------------------------------------------------------------------------------------------------+
| password | password used for encryption |
+------------------+-----------------------------------------------------------------------------------------------------------+
| timeout | in seconds |
+------------------+-----------------------------------------------------------------------------------------------------------+
| method | default: "aes-256-cfb", see `Encryption <https://github.com/shadowsocks/shadowsocks/wiki/Encryption>`__ |
+------------------+-----------------------------------------------------------------------------------------------------------+
| fast\_open | use `TCP\_FASTOPEN <https://github.com/shadowsocks/shadowsocks/wiki/TCP-Fast-Open>`__, true / false |
+------------------+-----------------------------------------------------------------------------------------------------------+
| workers | number of workers, available on Unix/Linux |
+------------------+-----------------------------------------------------------------------------------------------------------+
On your server:
@@ -134,7 +134,7 @@ Documentation
-------------
You can find all the documentation in the wiki:
https://github.com/clowwindy/shadowsocks/wiki
https://github.com/shadowsocks/shadowsocks/wiki
License
-------
@@ -144,12 +144,14 @@ MIT
Bugs and Issues
---------------
- `Troubleshooting <https://github.com/clowwindy/shadowsocks/wiki/Troubleshooting>`__
- `Troubleshooting <https://github.com/shadowsocks/shadowsocks/wiki/Troubleshooting>`__
- `Issue
Tracker <https://github.com/clowwindy/shadowsocks/issues?state=open>`__
Tracker <https://github.com/shadowsocks/shadowsocks/issues?state=open>`__
- `Mailing list <http://groups.google.com/group/shadowsocks>`__
.. |PyPI version| image:: https://img.shields.io/pypi/v/shadowsocks.svg?style=flat
:target: https://pypi.python.org/pypi/shadowsocks
.. |Build Status| image:: https://img.shields.io/travis/clowwindy/shadowsocks/master.svg?style=flat
:target: https://travis-ci.org/clowwindy/shadowsocks
.. |Build Status| image:: https://img.shields.io/travis/shadowsocks/shadowsocks/master.svg?style=flat
:target: https://travis-ci.org/shadowsocks/shadowsocks
.. |Coverage Status| image:: http://192.81.132.184/result/shadowsocks
:target: http://192.81.132.184/job/Shadowsocks/ws/htmlcov/index.html
+1 -1
View File
@@ -12,7 +12,7 @@ setup(
description="A fast tunnel proxy that help you get through firewalls",
author='clowwindy',
author_email='clowwindy42@gmail.com',
url='https://github.com/clowwindy/shadowsocks',
url='https://github.com/shadowsocks/shadowsocks',
packages=['shadowsocks', 'shadowsocks.crypto'],
package_data={
'shadowsocks': ['README.rst', 'LICENSE']
+31 -2
View File
@@ -27,7 +27,7 @@ import logging
from ctypes import CDLL, c_char_p, c_int, c_ulonglong, byref, \
create_string_buffer, c_void_p
__all__ = ['ciphers']
__all__ = ['ciphers', 'auths']
libsodium = None
loaded = False
@@ -42,7 +42,7 @@ def load_libsodium():
global loaded, libsodium, buf
from ctypes.util import find_library
for p in ('sodium',):
for p in ('sodium', 'libsodium'):
libsodium_path = find_library(p)
if libsodium_path:
break
@@ -62,6 +62,14 @@ def load_libsodium():
c_char_p, c_ulonglong,
c_char_p)
libsodium.crypto_onetimeauth.restype = c_int
libsodium.crypto_onetimeauth.argtypes = (c_void_p, c_char_p,
c_ulonglong, c_char_p)
libsodium.crypto_onetimeauth_verify.restype = c_int
libsodium.crypto_onetimeauth_verify.argtypes = (c_char_p, c_char_p,
c_ulonglong, c_char_p)
libsodium.sodium_init()
buf = create_string_buffer(buf_size)
@@ -106,11 +114,32 @@ class Salsa20Crypto(object):
return buf.raw[padding:padding + l]
class Poly1305(object):
@staticmethod
def auth(method, key, data):
if not loaded:
load_libsodium()
tag_buf = create_string_buffer(16)
libsodium.crypto_onetimeauth(byref(tag_buf), data, len(data), key)
return tag_buf.raw
@staticmethod
def verify(method, key, data, tag):
if not loaded:
load_libsodium()
r = libsodium.crypto_onetimeauth_verify(tag, data, len(data), key)
return r == 0
ciphers = {
b'salsa20': (32, 8, Salsa20Crypto),
b'chacha20': (32, 8, Salsa20Crypto),
}
auths = {
b'poly1305': (32, 16, Poly1305)
}
def test_salsa20():
from shadowsocks.crypto import util
+67
View File
@@ -0,0 +1,67 @@
#!/usr/bin/env python
# Copyright (c) 2014 clowwindy
#
# 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.
from __future__ import absolute_import, division, print_function, \
with_statement
import hmac
import hashlib
from shadowsocks import common
__all__ = ['auths']
class HMAC(object):
@staticmethod
def auth(method, key, data):
digest = common.to_str(method.replace(b'hmac-', b''))
return hmac.new(key, data, getattr(hashlib, digest)).digest()
@staticmethod
def verify(method, key, data, tag):
digest = common.to_str(method.replace(b'hmac-', b''))
t = hmac.new(key, data, getattr(hashlib, digest)).digest()
if hasattr(hmac, 'compare_digest'):
return hmac.compare_digest(t, tag)
else:
return _time_independent_equals(t, tag)
# from tornado
def _time_independent_equals(a, b):
if len(a) != len(b):
return False
result = 0
if type(a[0]) is int: # python3 byte strings
for x, y in zip(a, b):
result |= x ^ y
else: # python2
for x, y in zip(a, b):
result |= ord(x) ^ ord(y)
return result == 0
auths = {
b'hmac-md5': (32, 16, HMAC),
b'hmac-sha256': (32, 32, HMAC),
}
+79 -23
View File
@@ -29,17 +29,23 @@ import hashlib
import logging
from shadowsocks.crypto import m2, rc4_md5, salsa20_ctr,\
ctypes_openssl, ctypes_libsodium, table
ctypes_openssl, ctypes_libsodium, table, hmac
from shadowsocks import common
method_supported = {}
method_supported.update(rc4_md5.ciphers)
method_supported.update(salsa20_ctr.ciphers)
method_supported.update(ctypes_openssl.ciphers)
method_supported.update(ctypes_libsodium.ciphers)
ciphers_supported = {}
ciphers_supported.update(rc4_md5.ciphers)
ciphers_supported.update(salsa20_ctr.ciphers)
ciphers_supported.update(ctypes_openssl.ciphers)
ciphers_supported.update(ctypes_libsodium.ciphers)
# let M2Crypto override ctypes_openssl
method_supported.update(m2.ciphers)
method_supported.update(table.ciphers)
ciphers_supported.update(m2.ciphers)
ciphers_supported.update(table.ciphers)
auths_supported = {}
auths_supported.update(hmac.auths)
auths_supported.update(ctypes_libsodium.auths)
def random_string(length):
@@ -50,22 +56,14 @@ def random_string(length):
return os.urandom(length)
cached_keys = {}
def try_cipher(key, method=None):
def try_cipher(key, method=None, auth=None):
Encryptor(key, method)
auth_create(b'test', key, b'test', auth)
def EVP_BytesToKey(password, key_len, iv_len):
# equivalent to OpenSSL's EVP_BytesToKey() with count 1
# so that we make the same key and iv as nodejs version
if hasattr(password, 'encode'):
password = password.encode('utf-8')
cached_key = '%s-%d-%d' % (password, key_len, iv_len)
r = cached_keys.get(cached_key, None)
if r:
return r
m = []
i = 0
while len(b''.join(m)) < (key_len + iv_len):
@@ -79,7 +77,6 @@ def EVP_BytesToKey(password, key_len, iv_len):
ms = b''.join(m)
key = ms[:key_len]
iv = ms[key_len:key_len + iv_len]
cached_keys[cached_key] = (key, iv)
return key, iv
@@ -102,15 +99,14 @@ class Encryptor(object):
def get_method_info(self, method):
method = method.lower()
m = method_supported.get(method)
m = ciphers_supported.get(method)
return m
def iv_len(self):
return len(self.cipher_iv)
def get_cipher(self, password, method, op, iv):
if hasattr(password, 'encode'):
password = password.encode('utf-8')
password = common.to_bytes(password)
m = self._method_info
if m[0] > 0:
key, iv_ = EVP_BytesToKey(password, m[0], m[1])
@@ -150,7 +146,8 @@ class Encryptor(object):
def encrypt_all(password, method, op, data):
result = []
method = method.lower()
(key_len, iv_len, m) = method_supported[method]
password = common.to_bytes(password)
(key_len, iv_len, m) = ciphers_supported[method]
if key_len > 0:
key, _ = EVP_BytesToKey(password, key_len, iv_len)
else:
@@ -166,6 +163,42 @@ def encrypt_all(password, method, op, data):
return b''.join(result)
def auth_create(data, password, iv, method):
if method is None:
return data
# prepend hmac to data
password = common.to_bytes(password)
method = method.lower()
method_info = auths_supported.get(method)
if not method_info:
logging.error('method %s not supported' % method)
sys.exit(1)
key_len, tag_len, m = method_info
key, _ = EVP_BytesToKey(password + iv, key_len, 0)
tag = m.auth(method, key, data)
return tag + data
def auth_open(data, password, iv, method):
if not method:
return data
# verify hmac and remove the hmac or return None
password = common.to_bytes(password)
method = method.lower()
method_info = auths_supported.get(method)
if not method_info:
logging.error('method %s not supported' % method)
sys.exit(1)
key_len, tag_len, m = method_info
key, _ = EVP_BytesToKey(password + iv, key_len, 0)
if len(data) <= tag_len:
return None
result = data[tag_len:]
if not m.verify(method, key, result, data[:tag_len]):
return None
return result
CIPHERS_TO_TEST = [
b'aes-128-cfb',
b'aes-256-cfb',
@@ -175,6 +208,13 @@ CIPHERS_TO_TEST = [
b'table',
]
AUTHS_TO_TEST = [
None,
b'hmac-md5',
b'hmac-sha256',
b'poly1305',
]
def test_encryptor():
from os import urandom
@@ -198,6 +238,22 @@ def test_encrypt_all():
assert plain == plain2
def test_auth():
from os import urandom
plain = urandom(10240)
for method in AUTHS_TO_TEST:
logging.warn(method)
boxed = auth_create(plain, b'key', b'iv', method)
unboxed = auth_open(boxed, b'key', b'iv', method)
assert plain == unboxed
if method is not None:
b = common.ord(boxed[0])
b ^= 1
attack = common.chr(b) + boxed[1:]
assert auth_open(attack, b'key', b'iv', method) is None
if __name__ == '__main__':
test_encrypt_all()
test_encryptor()
test_auth()
+1 -1
View File
@@ -49,7 +49,7 @@ def main():
utils.print_shadowsocks()
encrypt.try_cipher(config['password'], config['method'])
encrypt.try_cipher(config['password'], config['method'], config['auth'])
try:
logging.info("starting local at %s:%d" %
+1 -1
View File
@@ -57,7 +57,7 @@ def main():
else:
config['port_password'][str(server_port)] = config['password']
encrypt.try_cipher(config['password'], config['method'])
encrypt.try_cipher(config['password'], config['method'], config['auth'])
tcp_servers = []
udp_servers = []
dns_resolver = asyncdns.DNSResolver()
+1
View File
@@ -173,6 +173,7 @@ def get_config(is_local):
config['password'] = config.get('password', '')
config['method'] = config.get('method', 'aes-256-cfb')
config['auth'] = config.get('auth', None)
config['port_password'] = config.get('port_password', None)
config['timeout'] = int(config.get('timeout', 300))
config['fast_open'] = config.get('fast_open', False)
+16 -8
View File
@@ -6,16 +6,24 @@ if __name__ == '__main__':
import urllib
class MainHandler(tornado.web.RequestHandler):
def get(self):
with open('/tmp/shadowsocks-coverage', 'rb') as f:
coverage = f.read().strip()
self.redirect(('https://img.shields.io/badge/'
'coverage-%s-brightgreen.svg'
'?style=flat') %
urllib.quote(coverage))
def get(self, project):
try:
with open('/tmp/%s-coverage' % project, 'rb') as f:
coverage = f.read().strip()
n = int(coverage.strip('%'))
if n > 80:
color = 'brightgreen'
else:
color = 'yellow'
self.redirect(('https://img.shields.io/badge/'
'coverage-%s-%s.svg'
'?style=flat') %
(urllib.quote(coverage), color))
except IOError:
raise tornado.web.HTTPError(404)
application = tornado.web.Application([
(r"/shadowsocks", MainHandler),
(r"/([a-zA-Z0-9\-_]+)", MainHandler),
])
if __name__ == "__main__":