from struct import pack, unpack
import ucryptolib
MODE_ECB = 1


class AES_CMAC:
    def gen_subkey(self, K):
        AES_128 = ucryptolib.aes(bytearray(K), MODE_ECB)

        L = AES_128.encrypt(('\x00'*16).encode())

        LHigh = unpack('>Q',L[:8])[0]
        LLow  = unpack('>Q',L[8:])[0]

        K1High = ((LHigh << 1) | ( LLow >> 63 )) & 0xFFFFFFFFFFFFFFFF
        K1Low  = (LLow << 1) & 0xFFFFFFFFFFFFFFFF

        if (LHigh >> 63):
            K1Low ^= 0x87

        K2High = ((K1High << 1) | (K1Low >> 63)) & 0xFFFFFFFFFFFFFFFF
        K2Low  = ((K1Low << 1)) & 0xFFFFFFFFFFFFFFFF

        if (K1High >> 63):
            K2Low ^= 0x87

        K1 = pack('>QQ', K1High, K1Low)
        K2 = pack('>QQ', K2High, K2Low)

        return K1, K2

    def xor_128(self, N1, N2):
        J = b''
        for i in range(len(N1)):
            J += bytes([N1[i] ^ N2[i]])
        return J

    def pad(self, N):
        const_Bsize = 16
        padLen = 16-len(N)
        return  N + b'\x80' + b'\x00'*(padLen-1)

    def encode(self, K, M):
        const_Bsize = 16
        const_Zero  = b'\x00'*16

        AES_128 = ucryptolib.aes(bytearray(K), MODE_ECB)

        K1, K2 = self.gen_subkey(K)
        n      = int(len(M)/const_Bsize)

        if n == 0:
            n = 1
            flag = False
        else:
            if (len(M) % const_Bsize) == 0:
                flag = True
            else:
                n += 1
                flag = False

        M_n = M[(n-1)*const_Bsize:]
        if flag is True:
            M_last = self.xor_128(M_n,K1)
        else:
            M_last = self.xor_128(self.pad(M_n),K2)

        X = const_Zero
        for i in range(n-1):
            M_i = M[(i)*const_Bsize:][:16]
            Y   = self.xor_128(X, M_i)
            X   = AES_128.encrypt(Y)
        Y = self.xor_128(M_last, X)
        T = AES_128.encrypt(Y)
        return T
