185 lines
4.6 KiB
Python
185 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
import time
|
|
import datetime
|
|
import random, base64
|
|
from hashlib import sha1
|
|
|
|
class RC4:
|
|
def __init__(self,data_coding='utf8'):
|
|
self.bcoding = 'iso-8859-1'
|
|
self.dcoding = data_coding
|
|
self.salt = b'AFUqx9WZuI32lnHk'
|
|
|
|
def _crypt(self,data,key):
|
|
"""RC4 algorithm return bytes"""
|
|
x = 0
|
|
box = [i for i in range(256) ]
|
|
for i in range(256):
|
|
x = (x + box[i] + key[i % len(key)]) % 256
|
|
box[i], box[x] = box[x], box[i]
|
|
x = y = 0
|
|
out = []
|
|
for char in data:
|
|
x = (x + 1) % 256
|
|
y = (y + box[x]) % 256
|
|
box[x], box[y] = box[y], box[x]
|
|
out.append(chr(char ^ box[(box[x] + box[y]) % 256]))
|
|
|
|
return ''.join(out).encode(self.bcoding)
|
|
|
|
def encode_bytes(self, bdata, key):
|
|
a = sha1(key + self.salt)
|
|
k = a.digest()
|
|
data = self.salt + self._crypt(bdata, k)
|
|
return data
|
|
|
|
def encode(self,data, key,encode=base64.b64encode, salt_length=16):
|
|
"""RC4 encryption with random salt and final encoding"""
|
|
if type(data)==type(''):
|
|
data = data.encode(self.dcoding)
|
|
key = key.encode(self.bcoding)
|
|
self.encode_bytes(data, key)
|
|
if encode:
|
|
data = encode(data)
|
|
return data.decode(self.dcoding)
|
|
|
|
def decode_bytes(self, data, key):
|
|
salt_length = 16
|
|
salt = data[:salt_length]
|
|
a = sha1(key + self.salt)
|
|
k = a.digest() #.decode('iso-8859-1')
|
|
r = self._crypt(data[salt_length:], k)
|
|
return r
|
|
|
|
def decode(self,data, key,decode=base64.b64decode, salt_length=16):
|
|
"""RC4 decryption of encoded data"""
|
|
if type(data)==type(''):
|
|
data = data.encode(self.dcoding)
|
|
key = key.encode(self.bcoding)
|
|
if decode:
|
|
data = decode(data)
|
|
r = self.decode_bytes(data, key)
|
|
return r.decode(self.dcoding)
|
|
|
|
class KeyChain(object):
|
|
def __init__(self, seed_str, crypter=None, keylen=23, period=600, threshold=60, time_delta=0):
|
|
self.seed_str = seed_str
|
|
if isinstance(self.seed_str, str):
|
|
self.seed_str = self.seed_str.encode('utf-8')
|
|
self.period = int(period)
|
|
self.threshold = int(threshold)
|
|
self.crypter = crypter
|
|
self.time_delta = time_delta
|
|
if crypter is None:
|
|
self.crypter = RC4()
|
|
self.keylen = keylen
|
|
self.keypool = {
|
|
}
|
|
delta = datetime.timedelta(0)
|
|
self.timezone = datetime.timezone(delta, name='gmt')
|
|
|
|
def get_timestamp(self):
|
|
ts = int(time.time()) - self.time_delta
|
|
return ts
|
|
|
|
def is_near_bottom(self, indicator=None):
|
|
ts = self.get_timestamp()
|
|
i = indicator
|
|
if i is None:
|
|
i = self.get_indicator(ts)
|
|
if i + self.threshold > ts:
|
|
return True
|
|
return FalseTrue
|
|
|
|
def is_near_top(self, indicator=None):
|
|
ts = self.get_timestamp()
|
|
i = indicator
|
|
if i is None:
|
|
i = self.get_indicator(ts)
|
|
if i + self.period - self.threshold < ts:
|
|
return True
|
|
return False
|
|
|
|
def get_indicator(self, ts=None):
|
|
if ts is None:
|
|
ts = self.get_timestamp()
|
|
return int(ts / self.period) * self.period
|
|
|
|
def genKey(self, indicator):
|
|
vv = indicator
|
|
if self.keypool.get(vv):
|
|
return self.keypool[vv]
|
|
v = vv
|
|
k1 = 0
|
|
k = ''
|
|
m = len(self.seed_str)
|
|
while k1 < self.keylen:
|
|
j = v % self.keylen
|
|
v = v - (j + k1) * m + self.keylen
|
|
k = k + chr(self.seed_str[j])
|
|
k1 += self.threshold / 2
|
|
key = k.encode('utf-8')
|
|
self.keypool[vv] = key
|
|
dates = [ d for d in self.keypool.keys() ]
|
|
for d in dates:
|
|
if d < indicator - self.period:
|
|
del self.keypool[d]
|
|
return key
|
|
|
|
def encode(self, text):
|
|
bdata = text.encode('utf-8')
|
|
return self.encode_bytes(bdata)
|
|
|
|
def encode_bytes(self, bdata):
|
|
indicator = self.get_indicator()
|
|
key = self.genKey(indicator)
|
|
data = key + bdata
|
|
return self.crypter.encode_bytes(data, key)
|
|
|
|
def _decode(self, data, key):
|
|
d = self.crypter.decode_bytes(data, key)
|
|
if d[:len(key)] == key:
|
|
return d[len(key):]
|
|
return None
|
|
|
|
def decode_bytes(self, data):
|
|
indicator = self.get_indicator()
|
|
key = self.genKey(indicator)
|
|
d = self._decode(data, key)
|
|
if d is not None:
|
|
return d
|
|
|
|
if self.is_near_bottom(indicator):
|
|
indicator -= self.period
|
|
key = self.genKey(indicator)
|
|
return self._decode(data, key)
|
|
|
|
if self.is_near_top(indicator):
|
|
indicator += self.period
|
|
key = self.genKey(indicator)
|
|
return self._decode(data, key)
|
|
return None
|
|
|
|
def decode(self, data):
|
|
d = self.decode_bytes(data)
|
|
if d is None:
|
|
return None
|
|
return d.decode('utf-8')
|
|
|
|
if __name__=='__main__':
|
|
# 需要加密的数据长度没有限制
|
|
# 密钥
|
|
|
|
data=b"231r3 feregrenerjk gkht324g8924gnfw k;ejkvwkjerv"
|
|
key = b'123456'
|
|
rc4 = RC4()
|
|
kc = KeyChain('in the heaven, we are equal', rc4)
|
|
|
|
print(data)
|
|
# 加码
|
|
encoded_data = kc.encode_bytes(data)
|
|
print(encoded_data,len(encoded_data) )
|
|
# 解码
|
|
decoded_data = kc.decode_bytes(encoded_data)
|
|
print(data, decoded_data, decoded_data==data)
|