Browse Source

analysis and add typing hint

akkariiin/Experimental
Akkariiin 6 years ago
parent
commit
0b1ddbb432
  1. 164
      shadowsocks/obfsplugin/auth_akarin.py

164
shadowsocks/obfsplugin/auth_akarin.py

@ -297,7 +297,7 @@ class auth_akarin_rand(auth_base):
v = self.trapezoid_random_float(d) v = self.trapezoid_random_float(d)
return int(v * max_val) return int(v * max_val)
def send_rnd_data_len(self, buf_size: int, last_hash, random: xorshift128plus) -> int: def send_rnd_data_len(self, buf_size: int, last_hash: bytes, random: xorshift128plus) -> int:
if buf_size + self.server_info.overhead > self.send_tcp_mss: if buf_size + self.server_info.overhead > self.send_tcp_mss:
random.init_from_bin_len(last_hash, buf_size) random.init_from_bin_len(last_hash, buf_size)
return random.next() % 521 return random.next() % 521
@ -331,7 +331,7 @@ class auth_akarin_rand(auth_base):
random.init_from_bin(last_hash) random.init_from_bin(last_hash)
return random.next() % 127 return random.next() % 127
def rnd_data(self, buf_size: int, buf: bytes, last_hash, random: xorshift128plus) -> bytes: def rnd_data(self, buf_size: int, buf: bytes, last_hash: bytes, random: xorshift128plus) -> bytes:
rand_len = self.send_rnd_data_len(buf_size, last_hash, random) rand_len = self.send_rnd_data_len(buf_size, last_hash, random)
rnd_data_buf = rand_bytes(rand_len) rnd_data_buf = rand_bytes(rand_len)
@ -345,6 +345,31 @@ class auth_akarin_rand(auth_base):
return buf return buf
def pack_client_data(self, buf: bytes) -> bytes: def pack_client_data(self, buf: bytes) -> bytes:
"""
pack_data with_cmd=
|ushort(0xff00^ushort(last_client_hash[14:16]))|
|ushort(len(origin data)^ushort(last_client_hash[12:14]))|encrypt(origin data)|
pack_data without_cmd=
|ushort(len(origin data)^ushort(last_client_hash[14:16]))|encrypt(origin data)|
HMAC_key=
|user_key|uint(last_pack_id)|
next_client_hash=
HMAC_sign(key:(HMAC_key), be sign data:(pack_data))
next_pack_id=
uint((pack_id + 1)&0xFFFFFFFF)
pack_out=
|(pack_data)|next_client_hash|
:param buf: bytes origin data
:return: bytes packed data
"""
buf = self.encryptor.encrypt(buf) buf = self.encryptor.encrypt(buf)
if self.send_back_cmd: if self.send_back_cmd:
cmd_len = 2 cmd_len = 2
@ -359,12 +384,33 @@ class auth_akarin_rand(auth_base):
length = len(buf) ^ struct.unpack('<H', self.last_client_hash[14:])[0] length = len(buf) ^ struct.unpack('<H', self.last_client_hash[14:])[0]
data = struct.pack('<H', length) + data data = struct.pack('<H', length) + data
mac_key = self.user_key + struct.pack('<I', self.pack_id) mac_key = self.user_key + struct.pack('<I', self.pack_id)
self.last_client_hash = hmac.new(mac_key, data, self.hashfunc).digest() self.last_client_hash: bytes = hmac.new(mac_key, data, self.hashfunc).digest()
data += self.last_client_hash[:2] data += self.last_client_hash[:2]
self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF
return data return data
def pack_server_data(self, buf): def pack_server_data(self, buf: bytes) -> bytes:
"""
pack_data=
|ushort(len(origin data)^ushort(last_client_hash[14:16]))|encrypt(origin data)|
HMAC_key=
|user_key|uint(last_pack_id)|
next_client_hash=
HMAC_sign(key:(HMAC_key), be sign data:(pack_data))
next_pack_id=
uint((pack_id + 1)&0xFFFFFFFF)
pack_out=
|(pack_data)|next_client_hash|
:param buf: bytes origin data
:return: bytes packed data
"""
buf = self.encryptor.encrypt(buf) buf = self.encryptor.encrypt(buf)
data = self.rnd_data(len(buf), buf, self.last_server_hash, self.random_server) data = self.rnd_data(len(buf), buf, self.last_server_hash, self.random_server)
mac_key = self.user_key + struct.pack('<I', self.pack_id) mac_key = self.user_key + struct.pack('<I', self.pack_id)
@ -377,8 +423,79 @@ class auth_akarin_rand(auth_base):
self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF self.pack_id = (self.pack_id + 1) & 0xFFFFFFFF
return data return data
def pack_auth_data(self, auth_data, buf): def pack_auth_data(self, auth_data: bytes, buf: bytes) -> bytes:
"""
### construct the client first&auth pack
plain_auth_data=
|auth_data|ushort(server_info.overhead)|ushort(tcp_mss)|
check_head 1st_pair=
|rand_bytes(4)|
mac_key=
|server_info.iv|server_info.key|
client_hash=
|HMAC_sign(mac_key, data:(check_head 1st_pair))|
check_head 2ed_pair=
|client_hash[:8]|
check_head=
|(check_head 1st_pair)|(check_head 2ed_pair)|
user_origin_id=
|(uid in protocol_param)| or |rand_bytes(4)|
user_encoded_id=
|uint_bytes(uint_number(uid)^uint_number(client_hash[8:12]))|
{== equal as ==|uint_bytes(uid)^client_hash[8:12]|}
encryptor_1_key=
|bytes(base64(user_key))|protocol_salt|
encrypted_auth_data=
|user_encoded_id|
|encryptor(
key:(encryptor_1_key),
data:(plain_auth_data),
mode:('aes-128-cbc'),
encipher_vi:(b'\x00' * 16)
)[16:]|
server_hash=
|HMAC_sign(user_key, encrypted_auth_data)|
pack_out=
|check_head|encrypted_auth_data|server_hash[:4]|pack_client_data(buf)|
connect_encryptor_key=
|bytes(base64(user_key))|bytes(base64(client_hash[0:16]))|
connect_encryptor_encipher_vi=
|client_hash[:8]|
connect_encryptor_decipher_vi=
|server_hash[:8]|
connect_encryptor=
|encryptor(
key:(connect_encryptor_key),
data:(plain_auth_data),
mode:('chacha20'),
encipher_vi:(connect_encryptor_encipher_vi),
decipher_vi:(connect_encryptor_decipher_vi)
)|
:param auth_data:
:param buf:
:return:
"""
data = auth_data data = auth_data
# TODO FIX THIS this need be a little ending encode ushort ,
# TODO it need same as decoder on `server_post_decrypt()` for recv_tcp_mss
# a random size tcp max size for mss exchange when c->s
self.send_tcp_mss = struct.unpack('>H', rand_bytes(2))[0] % 1024 + 400 self.send_tcp_mss = struct.unpack('>H', rand_bytes(2))[0] % 1024 + 400
data = data + (struct.pack('<H', self.server_info.overhead) + struct.pack('<H', self.send_tcp_mss)) data = data + (struct.pack('<H', self.server_info.overhead) + struct.pack('<H', self.send_tcp_mss))
mac_key = self.server_info.iv + self.server_info.key mac_key = self.server_info.iv + self.server_info.key
@ -387,35 +504,62 @@ class auth_akarin_rand(auth_base):
self.last_client_hash = hmac.new(mac_key, check_head, self.hashfunc).digest() self.last_client_hash = hmac.new(mac_key, check_head, self.hashfunc).digest()
check_head += self.last_client_hash[:8] check_head += self.last_client_hash[:8]
# TODO FIX THIS generate default user id before `if`
# if exist, get the user origin_id/key from protocol_param
# otherwise, use `rand_bytes(4)` as default user origin_id, the global protocol key(password) as user key
if b':' in to_bytes(self.server_info.protocol_param): if b':' in to_bytes(self.server_info.protocol_param):
try: try:
items = to_bytes(self.server_info.protocol_param).split(b':') items = to_bytes(self.server_info.protocol_param).split(b':')
self.user_key = items[1] self.user_key = items[1]
uid = struct.pack('<I', int(items[0])) uid: bytes = struct.pack('<I', int(items[0]))
except: except:
uid = rand_bytes(4) uid: bytes = rand_bytes(4)
else: else:
uid = rand_bytes(4) uid: bytes = rand_bytes(4)
if self.user_key is None: if self.user_key is None:
self.user_key = self.server_info.key self.user_key = self.server_info.key
# use user key to initial a aes-128-cbc encryptor, vi is 16-bytes 0x00
# and use it to encrypt plain auth data
encryptor = encrypt.Encryptor( encryptor = encrypt.Encryptor(
to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16) to_bytes(base64.b64encode(self.user_key)) + self.salt, 'aes-128-cbc', b'\x00' * 16)
uid = struct.unpack('<I', uid)[0] ^ struct.unpack('<I', self.last_client_hash[8:12])[0] uid = struct.unpack('<I', uid)[0] ^ struct.unpack('<I', self.last_client_hash[8:12])[0]
uid = struct.pack('<I', uid) uid = struct.pack('<I', uid)
data = uid + encryptor.encrypt(data)[16:] data = uid + encryptor.encrypt(data)[16:]
self.last_server_hash = hmac.new(self.user_key, data, self.hashfunc).digest() self.last_server_hash = hmac.new(self.user_key, data, self.hashfunc).digest()
data = check_head + data + self.last_server_hash[:4] data = check_head + data + self.last_server_hash[:4]
# use user_key and client_hash to initial a chacha20 encryptor as connect encryptor, vi is client_hash[:8]
self.encryptor = encrypt.Encryptor( self.encryptor = encrypt.Encryptor(
to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'chacha20', to_bytes(base64.b64encode(self.user_key)) + to_bytes(base64.b64encode(self.last_client_hash)), 'chacha20',
self.last_client_hash[:8]) self.last_client_hash[:8])
# set encryptor's iv_sent state to True, to make it not place vi at front of first encrypted pack
# *** because the receive point can re-construct encipher.vi from client_hash[:8]
# client_hash[:8] are the check_head[4:8]
# client_hash[0:16] are calc from `HMAC(mac_key, check_head[0:4])`
# mac_key=server.vi+server.key
# so , we don't need send encryptor's encipher's vi
self.encryptor.encrypt(b'') self.encryptor.encrypt(b'')
# use server_hash[:8] as encryptor's decipher's iv to init encryptor's decipher
# *** because the receive point can re-construct decipher.vi from server_hash[:8]
# server_hash[0:16] can calc from `HMAC(user_key, encrypted_auth_data)`
# server_hash[:4] are the verifier for the `user_key` and `encrypted_auth_data`,
# it place back at encrypted_auth_data
# same as above , we dont need send decipher's vi
self.encryptor.decrypt(self.last_server_hash[:8]) self.encryptor.decrypt(self.last_server_hash[:8])
return data + self.pack_client_data(buf) return data + self.pack_client_data(buf)
def auth_data(self): def auth_data(self) -> bytes:
utc_time = int(time.time()) & 0xFFFFFFFF """
auth_data=
|uint(utc_time)|connection_id:(rand_bytes(4))|uint(connection_id)|
:return: bytes auth data
"""
utc_time: int = int(time.time()) & 0xFFFFFFFF
if self.server_info.data.connection_id > 0xFF000000: if self.server_info.data.connection_id > 0xFF000000:
self.server_info.data.local_client_id = b'' self.server_info.data.local_client_id = b''
if not self.server_info.data.local_client_id: if not self.server_info.data.local_client_id:

Loading…
Cancel
Save