150 lines
4.1 KiB
Python
150 lines
4.1 KiB
Python
|
try:
|
||
|
import ujson as json
|
||
|
except:
|
||
|
import json
|
||
|
from appPublic.rsa import RSA
|
||
|
from appPublic.rc4 import RC4
|
||
|
from appPublic.uniqueID import getID
|
||
|
|
||
|
import brotli
|
||
|
import zlib
|
||
|
import struct
|
||
|
|
||
|
DATA_TYPE_BYTES = 1
|
||
|
DATA_TYPE_STR = 2
|
||
|
DATA_TYPE_JSON = 3
|
||
|
|
||
|
class DataEncoder:
|
||
|
"""
|
||
|
security data packing - unpacking object
|
||
|
packs data:
|
||
|
encode data with random key's rc4 crypt algorithm,
|
||
|
encode rc4's key with receiver's public key
|
||
|
sign data with sender's private key
|
||
|
packs data using struct in follows order
|
||
|
0: data format(18 bytes)
|
||
|
1. datatype(c)
|
||
|
2. encoded data(length=len(d))
|
||
|
3. encoded_rc4key(length=len(k))
|
||
|
4. sign(signs from (0+1+2+3) data) (length=len(s))
|
||
|
5. compress data and return compressed dta
|
||
|
return packed data
|
||
|
unpacks data:
|
||
|
0. decompress data
|
||
|
1. get 18 bytes fmt data, erase tails b'0x00'
|
||
|
2. using fmt to unpack data[18:]
|
||
|
3. verify sign
|
||
|
4. decode k
|
||
|
5. decode data usig decoded k with rc4 algorithm
|
||
|
6. convert data type to origin data type
|
||
|
7. return converted data
|
||
|
"""
|
||
|
def __init__(self, myid, func_get_peer_pubkey, private_file=None):
|
||
|
self.myid = myid
|
||
|
self.func_get_peer_pubkey = func_get_peer_pubkey
|
||
|
self.public_keys = {}
|
||
|
self.private_file = private_file
|
||
|
self.rsa = RSA()
|
||
|
self.rc4 = RC4()
|
||
|
if self.private_file:
|
||
|
self.private_key = self.rsa.read_privatekey(self.private_file)
|
||
|
else:
|
||
|
self.private_key = rsa.create_privaatekey()
|
||
|
self.public_key = self.rsa.create_publickey(self.private_key)
|
||
|
|
||
|
def identify_datatype(self, data):
|
||
|
if isinstance(data, bytes):
|
||
|
return DATA_TYPE_BYTES, data
|
||
|
if isinstance(data, str):
|
||
|
return DATA_TYPE_STR, data.encode('utf-8')
|
||
|
data = json.dumps(data).encode('utf-8')
|
||
|
return DATA_TYPE_JSON, data
|
||
|
|
||
|
def my_text_publickey(self):
|
||
|
pk = self.public_key()
|
||
|
return self.rsa.publickeyText(pk)
|
||
|
|
||
|
def exist_peer_publickeys(self, peer_id):
|
||
|
return True if self.public_keys.get(peer_id, False) else False
|
||
|
|
||
|
def set_peer_pubkey(self, peer_id, pubkey):
|
||
|
self.public_keys[peer_id] = pubkey
|
||
|
|
||
|
def get_peer_text_pubkey(self, peer_id):
|
||
|
pk = self.get_peer_pubkey()
|
||
|
txtpk = self.rsa. publickeyText(pk)
|
||
|
return txtpk
|
||
|
|
||
|
def set_peer_text_pubkey(self, peer_id, text_pubkey):
|
||
|
pk = self.rsa.publickeyFromText(text_pubkey)
|
||
|
self.set_peer_pubkey(peer_id, pk)
|
||
|
|
||
|
def get_peer_pubkey(self, peer_id):
|
||
|
pubkey = self.public_keys.get(peer_id)
|
||
|
if not pubkey:
|
||
|
try:
|
||
|
self.func_get_peer_pubkey(peer_id)
|
||
|
except:
|
||
|
raise Exception('Can not get peer public key(%s)')
|
||
|
pubkey = self.public_keys.get(peer_id)
|
||
|
return pubkey
|
||
|
|
||
|
def pack(self, peer_id, data):
|
||
|
pk = self.get_peer_pubkey(peer_id)
|
||
|
t, d = self.identify_datatype(data)
|
||
|
d, k = self.encode_data(pk, d)
|
||
|
f = 'b%05ds%03ds' % (len(d), len(k))
|
||
|
f1 = f + '256s'
|
||
|
pd1 = struct.pack('18s', f1.encode('utf-8'))
|
||
|
pd2 = struct.pack(f, t, d, k)
|
||
|
pd = pd1 + pd2
|
||
|
s = self.sign_data(pd)
|
||
|
pd += s
|
||
|
self.pack_d = [t,d,k,s]
|
||
|
origin_len = len(pd)
|
||
|
pd = brotli.compress(pd)
|
||
|
pdz = zlib.compress(pd)
|
||
|
print('brotli lenght=%d, zlib length=%d, origin length=%d' % \
|
||
|
(len(pd), len(pdz), origin_len)
|
||
|
)
|
||
|
return pd
|
||
|
|
||
|
def unpack(self, peer_id, data):
|
||
|
data = brotli.decompress(data)
|
||
|
org_data = data
|
||
|
pk = self.get_peer_pubkey(peer_id)
|
||
|
f = data[:18]
|
||
|
f.strip(b'0x00')
|
||
|
f = f.decode('utf-8')
|
||
|
data = data[18:]
|
||
|
t, d, k, s = struct.unpack(f, data)
|
||
|
self.unpack_d = [t,d,k,s]
|
||
|
data1 = org_data[:org_data.index(s)]
|
||
|
if not self.verify_sign(data1, s, pk):
|
||
|
raise Exception('data sign verify failed')
|
||
|
data = self.decode_data(d, k)
|
||
|
if t == DATA_TYPE_BYTES:
|
||
|
return data
|
||
|
if t == DATA_TYPE_STR:
|
||
|
return data.decode('utf-8')
|
||
|
return json.loads(data)
|
||
|
|
||
|
def encode_data(self, peer_pubkey, data):
|
||
|
key = getID()
|
||
|
if isinstance(key, str):
|
||
|
key = key.encode('utf-8')
|
||
|
ctext = self.rc4.encode_bytes(data, key)
|
||
|
encoded_key = self.rsa.encode_bytes(peer_pubkey, key)
|
||
|
print('key length compare,', len(key), len(encoded_key))
|
||
|
return ctext, encoded_key
|
||
|
|
||
|
def sign_data(self, data):
|
||
|
return self.rsa.sign_bdata(self.private_key, data)
|
||
|
|
||
|
def decode_data(self, data, encoded_key):
|
||
|
key = self.rsa.decode_bytes(self.private_key, encoded_key)
|
||
|
return self.rc4.decode_bytes(data, key)
|
||
|
|
||
|
def verify_sign(self, data, sign, peer_pubkey):
|
||
|
return self.rsa.check_sign_bdata(peer_pubkey, data, sign)
|