#coding:utf-8
import functools
import base64
import binascii
class Des:
    """
    DES加密解密类
    加密方法： Des.Encrypt(unencryptedStr,key)
    解密方法： Des.Decrypt(encryptedStr,key)
    """
    PC1_Table=[
               57, 49, 41, 33, 25, 17,  9,  1, 58, 50, 42, 34, 26, 18,
               10,  2, 59, 51, 43, 35, 27, 19, 11,  3, 60, 52, 44, 36,
               63, 55, 47, 39, 31, 23, 15,  7, 62, 54, 46, 38, 30, 22,
               14,  6, 61, 53, 45, 37, 29, 21, 13,  5, 28, 20, 12,  4]
    # __k1=PC1_Table
    PC2_Table=[
               14, 17, 11, 24,  1,  5,  3, 28, 15,  6, 21, 10,
               23, 19, 12,  4, 26,  8, 16,  7, 27, 20, 13,  2,
               41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48,
               44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32]
    # __k2=PC2_Table
    Shift_Table=[1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1]
    # __k0=Shift_Table
    IP_Table=[
              58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4,
              62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8,
              57, 49, 41, 33, 25, 17,  9, 1, 59, 51, 43, 35, 27, 19, 11, 3,
              61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7]
    # __ip=IP_Table
    E_Table=[
             32,  1,  2,  3,  4,  5,  4,  5,  6,  7,  8,  9,
             8,  9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17,
             16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25,
             24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32,  1]
    # __e=E_Table
    S_Box=[
           [
           14,  4,  13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,
           0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,
           4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,
           15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13,
           ],
           [
           15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,
           3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,
           0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,
           13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9,
           ],
           [
           10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,
           13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,
           13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,
           1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12,
           ],
           [
           7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,
           13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,
           10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,
           3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14,
           ],
           [
           2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,
           14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,
           4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,
           11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3,
           ],
           [
           12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,
           10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,
           9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,
           4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13,
           ],
           [
           4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,
           13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,
           1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,
           6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12,
           ],
           [
           13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,
           1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,
           7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,
           2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11]]
    # __s=S_Box
    P_Table=[
             16, 7, 20, 21, 29, 12, 28, 17, 1,  15, 23, 26, 5,  18, 31, 10,
             2,  8, 24, 14, 32, 27, 3,  9,  19, 13, 30, 6,  22, 11, 4,  25]
    # __p=P_Table
    IPR_Table=[
               40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31,
               38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29,
               36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27,
               34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41,  9, 49, 17, 57, 25]
    # __ip1=IPR_Table
    
    szSubKeys=[0 for i in range(48*32)]
    szCiphertextRaw=''
    szPlaintextRaw=[]
    szCiphertextInBytes=[]
    szPlaintextInBytes=[]
    szCiphertextInBinary=[]
    szCiphertextInHex=[]
    szPlaintext=[]
    szFCiphertextAnyLength=''
    szFPlaintextAnyLength=''
    
    # __hex_bin = {
    #     '0':'0000','1':'0001','2':'0010','3':'0011',
    #     '4':'0100','5':'0101','6':'0110','7':'0111',
    #     '8':'1000','9':'1001','a':'1010','b':'1011',
    #     'c':'1100','d':'1101','e':'1110','f':'1111',
    #     ' ':'0000'
    # }
    # __re = lambda t, s: ''.join(s[i-1] for i in t)
    #
    # __IP = functools.partial(__re, __ip)
    # __IP1 = functools.partial(__re, __ip1)
    # __E = functools.partial(__re, __e)
    # __P = functools.partial(__re, __p)
    # __K1 = functools.partial(__re, __k1)
    # __K2 = functools.partial(__re, __k2)
    # __B = functools.partial( lambda hex_bin, s: ''.join(hex_bin[w]
    #     for w in ''.join('%2x' % ord(w) for w in s))
    #     , __hex_bin)
    # __DB = functools.partial( lambda s: ''.join(chr(int(s[i:i+8], 2)) for i in range(0, len(s), 8)))
    # __S = functools.partial( lambda hex_bin, __s, s: ''.join(hex_bin['%x' % __s[i][
    #     int(s[i*6]+s[i*6+5], 2)*16 + int(s[i*6+1:i*6+5], 2)]] for i in range(8))
    #     , __hex_bin, __s)
    # __F = functools.partial( lambda s, k: ''.join('0' if s[i]==k[i] else '1' for i in range(len(s))))
    # __K0 = functools.partial( lambda k0, K2, k: map(K2,
    #     (k[k0[i]:28]+k[0:k0[i]] + k[k0[i]+28:56]+k[28:k0[i]+28] for i in range(16)))
    #     , __k0, __K2)
    # __K = functools.partial( lambda K1, K0, k: K0(K1(k))
    #     , __K1, __K0)
    
    # def __init__(self):
    #     #self.key='cloud'
    #     pass
    # def input_key(self, key, base=10):
    #     if base == 2:
    #         pass
    #     elif base == 16:
    #         key = ''.join(self.__class__.__hex_bin[w] for w in key)
    #     else:
    #         key = self.__class__.__B(key)
    #     self.__k = self.__class__.__K(key)
    #
    # # def __code(self, s, k):
    #     s = self.__IP(s)
    #     l, r = s[0:32], s[32:64]
    #     for i in range(16):
    #         r_t = r
    #         r = self.__E(r)
    #         r = self.__F(r, k[i])
    #         r = self.__S(r)
    #         r = self.__P(r)
    #         r = self.__F(r, l)
    #         l = r_t
    #     return self.__class__.__IP1(r+l)
    #
    # def encode(self, s):
    #     a = ''
    #     s += ' ' * ((8-len(s)%8)%8)
    #     for i in range(0, len(s), 8):
    #         before = self.__class__.__B(s[i:i+8])
    #         after = self.__code(before, self.__k)
    #         a += '%16x' % int(after, 2)
    #     return ''.join(w if w!=' ' else '0' for w in a)
    #
    # def decode(self, s):
    #     a = ''
    #     s.lower()
    #     for i in range(0, len(s), 16):
    #         before = ''.join(self.__class__.__hex_bin[s[j]] for j in range(i, i+16))
    #         after = self.__code(before, self.__k[::-1])
    #         a += self.__class__.__DB(after)
    #     return a.rstrip().decode('utf-8')
    #
    # # @staticmethod
    # def binbase64(data):
    #     _CODE_CHAR = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
    #     """
    #     Convert binary data to Base64 format string.
    #     """
    #     base64str = ""
    #
    #     for i in range(len(data)/3):
    #         datavalue = ((data[3*i] << 16) | (data[3*i+1] << 8) | data[3*i+2])
    #         for j in range(4):
    #             base64str += _CODE_CHAR[(datavalue >> 6*(3-j)) & 0x3F]
    #
    #     dataremain = len(data) % 3
    #     if dataremain == 1:
    #         datavalue = data[-1] << 16;
    #         base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F]
    #         base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F]
    #         base64str += "=="
    #     elif dataremain == 2:
    #         datavalue = (data[-2] << 16) | (data[-1] << 8);
    #         base64str += _CODE_CHAR[(datavalue >> 18) & 0x3F]
    #         base64str += _CODE_CHAR[(datavalue >> 12) & 0x3F]
    #         base64str += _CODE_CHAR[(datavalue >> 6) & 0x3F]
    #         base64str += "="
    #     return base64str
    
    @staticmethod
    def CharToBitString(character):
        charInt=ord(character)
        bitString=''
        bitString=str(charInt//128)+bitString
        charInt%=128
        bitString=str(charInt//64)+bitString
        charInt%=64
        bitString=str(charInt//32)+bitString
        charInt%=32
        bitString=str(charInt//16)+bitString
        charInt%=16
        bitString=str(charInt//8)+bitString
        charInt%=8
        bitString=str(charInt//4)+bitString
        charInt%=4
        bitString=str(charInt//2)+bitString
        charInt%=2
        bitString=str(charInt)+bitString
        return bitString[::-1]
    @staticmethod
    def StringToBitString(characterString):
        while len(characterString)<8:
            characterString+='\0'
        bitString=''
        for index in range(len(characterString)):
            bitString+=Des.CharToBitString(characterString[index])
        return bitString
    @staticmethod
    def Bits2Byte(bits):
        byteInt=int(bits[0])*128+int(bits[1])*64+int(bits[2])*32+int(bits[3])*16+int(bits[4])*8+int(bits[5])*4+int(bits[6])*2+int(bits[7])
        return chr(byteInt)
    @staticmethod
    def Bits2Bytes(srcBits,sizeBits=64):
        dstBytes=''
        for i in range(sizeBits//8):
            dstBytes+=Des.Bits2Byte(srcBits[8*i:8*i+8])
        return dstBytes
    @staticmethod
    def Int2Bits(_src):
        bits=''
        srcInt=_src%16
        bits+=str(srcInt//8)
        srcInt%=8
        bits+=str(srcInt//4)
        srcInt%=4
        bits+=str(srcInt//2)
        srcInt%=2
        bits+=str(srcInt)
        return bits
    @staticmethod
    def EncryptData(_srcBytes,keyN):
        szSrcBits=Des.StringToBitString(_srcBytes)
        sz_IP=Des.InitialPermuteData(szSrcBits)
        sz_Li=sz_IP[0:32]
        sz_Ri=sz_IP[32:]
        for i in range(16):
            sz_Li,sz_Ri=Des.FunctionF(sz_Li, sz_Ri, i)
        sz_Final64=sz_Ri[0:32]
        sz_Final64+=sz_Li[0:32]
        for j in range(64):
            Des.szCiphertextRaw+=sz_Final64[Des.IPR_Table[j]-1]
        Des.szCiphertextInBytes=Des.Bits2Bytes(Des.szCiphertextRaw)
        Des.szCiphertextRaw=''
    @staticmethod
    def FunctionF(sz_Li,sz_Ri,iKey,keyN=0):
        sz_Key=Des.szSubKeys[keyN*768+iKey*48:keyN*768+iKey*48+48]
        sz_48R=Des.ExpansionR(sz_Ri)
        sz_xor48=Des.XOR(sz_48R, sz_Key)
        s_Compress32=Des.CompressFuncS(sz_xor48)
        sz_P32=Des.PermutationP(s_Compress32)
        sz_Rii=Des.XOR(sz_P32,sz_Li,32)
        sz_Li=sz_Ri[0:32]
        sz_Ri=sz_Rii[0:32]
        return (sz_Li,sz_Ri)
    @staticmethod
    def ExpansionR(_src):
        _dst=''
        for i in range(48):
            _dst+=str(_src[Des.E_Table[i]-1])
        return _dst
    @staticmethod
    def XOR(szParam1,szParam2,uiParamLength=48):
        xorResult=''
        for i in range(uiParamLength):
            if(szParam1[i]==szParam2[i]):
                xorResult+='0'
            else:
                xorResult+='1'
        return xorResult
    @staticmethod
    def CompressFuncS(_src48):
        _dst32=''
        bTemp=[0 for i in range(48)]
        for i in range(8):
            bTemp[i*6:i*6+6]=_src48[i*6:i*6+6]
            iX=2*int(bTemp[i*6])+int(bTemp[i*6+5])
            iY=int(bTemp[i*6+1])*8+int(bTemp[i*6+2])*4+int(bTemp[i*6+3])*2+int(bTemp[i*6+4])
            dstBits=Des.Int2Bits(Des.S_Box[i][iX*16+iY])
            _dst32+=dstBits
        return _dst32
    @staticmethod
    def PermutationP(_src):
        _dst=''
        for i in range(32):
            _dst+=_src[Des.P_Table[i]-1]
        return _dst
    @staticmethod
    def InitializeKey(srcBytes,keyN):
#        sz_64key=[]
        sz_64key=Des.StringToBitString(srcBytes)
        sz_56key=''
        for i in range(56):
            sz_56key+=sz_64key[Des.PC1_Table[i]-1]
        Des.CreateSubKey(sz_56key)
        #Des.CreateSubKey(sz_56key,keyN)
    @staticmethod
    def CreateSubKey(sz_56key,keyN=0):
        szCi=[]
        szDi=[]
        szTmpL=sz_56key[0:28]
        szTmpR=sz_56key[28:56]
        for i in range(16):
            szCi=szTmpL[Des.Shift_Table[i]:]
            szCi+=szTmpL[0:Des.Shift_Table[i]]
            szDi=szTmpR[Des.Shift_Table[i]:]
            szDi+=szTmpR[0:Des.Shift_Table[i]]
            szTmp56=szCi
            szTmp56+=szDi
            for j in range(48):
                Des.szSubKeys[keyN*768+i*48+j]=szTmp56[Des.PC2_Table[j]-1]
            szTmpL=szCi[0:28]
            szTmpR=szDi[0:28]
       
    @staticmethod
    def InitialPermuteData(_src):
        _dest=''
        for i in range(64):
            _dest+=_src[Des.IP_Table[i]-1]
        return _dest
    @staticmethod
    def Encrypt(unencryptedStr,key='massclou'):
        """
        加密方法
        :param unencryptedStr:待加密字符串
        :param key:密钥字符串
        :return:加密后的字符串
        """
        Des.szFCiphertextAnyLength=''
        Des.InitializeKey(key, len(key))
        Des.EncryptAnyLength(unencryptedStr,len(unencryptedStr),len(key))
        #print Des.szFCiphertextAnyLength
        return Des.GetCiphertextAnyLength()

    @staticmethod
    def EncryptAnyLength(_srcBytes,_bytesLength,keyN):
        if _bytesLength == 8:
            Des.EncryptData(_srcBytes,keyN)
            Des.szFCiphertextAnyLength=Des.szCiphertextInBytes[0:8]
        elif _bytesLength < 8:
            _temp8bytes=_srcBytes[0:_bytesLength]
            while len(_temp8bytes)<8:
                _temp8bytes+='\0'
            Des.EncryptData(_temp8bytes,keyN)
            # print Des.szCiphertextInBytes[0:8]
            Des.szFCiphertextAnyLength=Des.szCiphertextInBytes[0:8]
        elif _bytesLength > 8:
            iParts=_bytesLength//8
            iResidue=_bytesLength%8
            for i in range(iParts):
                szLast8Bits=_srcBytes[8*i:8*i+8]
                Des.EncryptData(szLast8Bits,keyN)
                Des.szFCiphertextAnyLength+=Des.szCiphertextInBytes[0:8]
            if iResidue != 0:
                szLast8Bits=_srcBytes[8*iParts:]
                while len(szLast8Bits)<8:
                    szLast8Bits+='\0'
                Des.EncryptData(szLast8Bits,keyN)
                # print Des.szCiphertextInBytes
                Des.szFCiphertextAnyLength+=Des.szCiphertextInBytes[0:8]
            # print Des.szFCiphertextAnyLength
    @staticmethod
    def GetCiphertextAnyLength():
        return base64.encodestring(Des.szFCiphertextAnyLength)[0:-1]
    @staticmethod
    def Decrypt(encryptedStr,key='massclou'):
        """
        解密方法
        :param encryptedStr:待解密字符串
        :param key:解密密钥
        :return:解密后的字符串
        """
        Des.szFPlaintextAnyLength=''
        Des.InitializeKey(key, len(key))
        dd=base64.decodestring(encryptedStr)
        Des.DecryptAnyLength(dd,len(key))
        return Des.GetPlaintextAnyLength()
        pass
    @staticmethod
    def DecryptAnyLength(_srcBytes,keyN):
        _bytesLength=len(_srcBytes)
        if _bytesLength == 8:
            Des.DecryptData(_srcBytes,keyN)
            Des.szFPlaintextAnyLength=Des.szPlaintextInBytes[0:8]
            pass
        elif _bytesLength < 8:
            _temp8bytes=_srcBytes
            while len(_temp8bytes) < 8:
                _temp8bytes+='\0'
            Des.DecryptData(_temp8bytes,keyN)
            Des.szFPlaintextAnyLength=Des.szPlaintextInBytes[0:_bytesLength]
            pass
        elif _bytesLength > 8:
            iParts=_bytesLength//8
            iResidue=_bytesLength%8
            for i in range(iParts):
                szLast8Bits=_srcBytes[8*i:8*i+8]
                Des.DecryptData(szLast8Bits,keyN)
                Des.szFPlaintextAnyLength+=Des.szPlaintextInBytes[0:8]
                pass
            if iResidue != 0:
                szLast8Bits=_srcBytes[8*iParts:]
                while len(szLast8Bits) < 8:
                    szLast8Bits+='\0'
                Des.DecryptData(szLast8Bits,keyN)
                Des.szFPlaintextAnyLength+=Des.szPlaintextInBytes[0:iResidue]
                pass
            pass
        pass
    @staticmethod
    def GetPlaintextAnyLength():
        while Des.szFPlaintextAnyLength[-1] == '\0':
            Des.szFPlaintextAnyLength=Des.szFPlaintextAnyLength[0:-1]
        return str(Des.szFPlaintextAnyLength)
        pass
    @staticmethod
    def DecryptData(_srcBytes,keyN):
        szSrcBits=Des.Bytes2Bits(_srcBytes)
        # print szSrcBits
        sz_IP=Des.InitialPermuteData(szSrcBits)
        # print sz_IP
        sz_Ri=sz_IP[0:32]
        sz_Li=sz_IP[32:64]
        for i in range(16):
            sz_Ri,sz_Li=Des.FunctionF(sz_Ri, sz_Li, 15-i)
            pass
        sz_Final64=''+sz_Li[0:32]
        sz_Final64+=sz_Ri[0:32]
        for i in range(64):
            Des.szPlaintextRaw+=sz_Final64[Des.IPR_Table[i]-1]
            pass
        Des.szPlaintextInBytes=Des.Bits2Bytes(Des.szPlaintextRaw,64)
        Des.szPlaintextRaw=''
        # print Des.szPlaintextInBytes
        pass
    @staticmethod
    def InitialPermuteData(_src):
        _dst=''
        for i in range(64):
            _dst+=_src[Des.IP_Table[i]-1]
            pass
        return _dst
        pass
    @staticmethod
    def Byte2Bits(srcByte):
        dstBits=''
        if srcByte//128 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=128
        if srcByte//64 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=64
        if srcByte//32 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=32
        if srcByte//16 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=16
        if srcByte//8 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=8
        if srcByte//4 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=4
        if srcByte//2 == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        srcByte%=2
        if srcByte == 1:
            dstBits+='1'
        else:
            dstBits+='0'
        return dstBits
        pass
    @staticmethod
    def Bytes2Bits(srcBytes):
        dstBits=''
        for i in range(len(srcBytes)):
            dstBits+=Des.Byte2Bits(ord(srcBytes[i]))
        return dstBits
        pass
if __name__ == '__main__':
    des=Des()


    en_str = Des.Encrypt("Pass1234")  #加密
    print Des.Decrypt(en_str)
    en_str = Des.Encrypt("Pass1234")  #加密
    print en_str
    dec_str = Des.Decrypt(en_str) #解密
    print "[%s]" %dec_str
    # des.input_key(Des.StringToBitString('massclou'),2)
    # encryptedStr=des.encode('123456')
    # print encryptedStr
    # print base64.encodestring(encryptedStr.decode('hex'))
    # print 'right: '+'lTXhvSj4mzk='
    #print encryptedStr.decode('hex')
    # unencryptedStr=des.decode(encryptedStr)
    # print unencryptedStr
    # cryptedStr = des.Encrypt('Pass', 'massclou')

    # cryptedStr=Des.Encrypt('Pass95949592@#', 'massclou')
    #
    # print cryptedStr
    # print len(cryptedStr)
    # print des.Decrypt(cryptedStr)
    # pass
    #