InCTF 2021 | Tabula Recta

#inctf2021

global allowed , matrix_entries
allowed = "%UQh13S2dbjj,V6K1g$PTdaN4f!T023KUe#hi0PMOQiVN&cOcLR74+MLfeSgbaR*"
matrix_entries = 37*37 


class Cipher :

    def __init__(self, master_pass) :

        self.n = len(master_pass)
        self.key = [ord(i) for i in master_pass]
        self.mod = len(allowed)

    def create_key(self) :
        S= []
        for i in range(self.mod) :
            S.append(i)
        j = 0
        i = 0
        for i in range(self.mod - 1) :
            i = (i + 1) % self.mod 
            j = (j + S[i] + self.key[i%self.n]) %self.mod    
            S[i], S[j] = S[j], S[i]
        return S 
    
    def gen_stream(self , S , lgth = matrix_entries) :
        i = j = 0
        global key_stream
        key_stream = []
  
        for k in range(0, lgth):
            i = (i + 1) % self.mod
            j = (j + S[i]) % self.mod

            t = (S[i]+S[j]) % self.mod
            key_stream.append(S[t])
  

        return key_stream 
def get_entries(master_pass , lgth = matrix_entries) :
    obj = Cipher(master_pass)
    l = list(map(lambda x: allowed[x],obj.gen_stream(obj.create_key(),lgth)))
    return l

if __name__ == "__main__" :
    import random
    master_pass_len = 2**(random.choice(range(1,7)))
    master_pass = ''.join(chr(random.randint(0,64)) for i in range(master_pass_len))
    f = open("master_pass.key","w")
    f.write(master_pass)
    f.close()
from crypto import get_entries
import math, string
from sage.all import EllipticCurve, Integer
from matrix import *



class Tabula:  
    def __init__(self) :  
        self.E = EllipticCurve(GF(37),[1,5])   
        self.G = self.E.gen(0)  
        global d,allowed 
        allowed = "abcdefghijKLMNOPQRSTUV0123467!#$%&*+,"  
        d = {}  
        for i in range(37) :   
            d[allowed[i]] = self.E.points()[i+1]   
        self.A = [[-1,5,-1],[-2,11,7],[1,-5,2]]  
        self.rows = 37
        self.table = [[' ']*self.rows for i in range(self.rows)]
        
    def update_table(self, rows , table_entries) :
        self.rows = rows
        for i in range(rows) :
            for j in range(rows) :
                self.table[i][j] = table_entries[i*rows + j]
            #print(self.table[i])


    
    def map_matrix(self, m) :
        inf = self.E.points()[0]  
        row = 3   
        col = math.ceil(len(m)/3)  
        E_arr = []  
        ind = 0
        for i in range(row) :   
            tmp = []  
            for j in range(math.ceil(len(m)/3)) :     
                if ( ind < len(m)) :   
                    tmp.append( d[m[ind]] )  
                else  :   
                    tmp.append(inf)
                ind += 1
            E_arr.append(tmp)  

        return Matrix((row,col),E_arr)

    def encrypt(self,m) : 
        mat = [[-1,5,-1],[-2,11,7],[1,-5,2]]
        A = Matrix((3,3), mat)
        P = self.map_matrix(m)
        Q = A.__mul__(P)

        (C1,C2) = (P.__mul__(25), Q.__add__(P.__mul__(325))) 
        points = []
        for i in range(P.rows) :
            for j in range(P.cols) :
                c1 = C1.M[i][j]
                c2 = C2.M[i][j]

                points.append(((c1[0],c1[1]),(c2[0],c2[1])))
                if((i*P.rows + j) == len(m)) :
                    break
        return points
    def gen_pass(self, website, master_pass , pass_len) :
        """
            website     : name of the website you want to generate the password for eg: www.amazon.com
            master_pass : master password for your stateless password manager
            pass_len    : length of the password to be generated/retrieved for the website
        """
        site = website.split(".")[1]
        entries = get_entries(master_pass)
        self.update_table( 37, entries) 
        table_row = sum([ord(i) for i in site])%self.rows 
        #print("website :", website)
        pswd = ''.join([self.table[table_row][i] for i in range(pass_len)])
        points = self.encrypt(pswd)
        pswd_set = [(self.table[points[i][0][0]][points[i][1][1]],(points[i][0],points[i][1])) for i in range(pass_len)]
        #print(pswd_set)
        pswd = ''.join(p[0] for p in pswd_set)
        return pswd
if __name__ == "__main__" :
    obj = Tabula()
    #master_pass = open("master_pass.key").read()
    #print(obj.gen_pass("www.gameison.com",master_pass,12))
class Matrix:
    def __init__(self,dims , A=None) :
        self.rows = dims[0]
        self.cols = dims[1]
        if(A == None)  :
            self.M = [[0] * self.cols for i in range(self.rows)]
        else :
            self.M = A
        
    def __str__(self) :
        m = ""
        for i in range(self.rows) :
            m += str(self.M[i])+"\n"
        return m
    def __add__(self,other) :
        C = Matrix(dims = (self.rows,self.cols))
        if isinstance(other,Matrix) :
            for i in range(self.rows) :
                for j in range(self.cols) :
                    C.M[i][j] = self.M[i][j] + other.M[i][j]
        else :
            print("Not matching type")
        return C
    def __radd__(self,other) :
        return self.__add__(other)
    def __mul__(self, other) :

        if isinstance(other,Matrix) :
            C = Matrix(dims = (self.rows,other.cols))
            for i in range(self.rows) :
                for j in range(other.cols) :
                    acc = 0
                    for k in range(other.rows) :
                        acc += self.M[i][k] * other.M[k][j]
                    #print(acc)
                    C.M[i][j] = acc
        else :
            C = Matrix(dims = (self.rows,self.cols))

            for i in range(self.rows) :
                for j in range(self.cols) :
                    C.M[i][j] = self.M[i][j] * other
        return C
    def __rmul__(self,other) :
        return self.__mul__(other)

    def __getitem__(self,key) :
        if isinstance(key, tuple) :
            i = key[0]
            j = key[1]
            return self.M[i][j]
    def __setitem__(self,key,value) :
        if isinstance(key,tuple) :
            i = key[0]
            j = key[1]
            self.M[i][j] = value
    def __sub__(self,other) : 
        C = Matrix(dims = (self.rows,self.cols)) 
        if isinstance(other,Matrix) : 
            for i in range(self.rows) : 
                for j in range(self.cols) : 
                    C.M[i][j] = self.M[i][j] - other.M[i][j] 
        else : 
            print("Not matching type") 
        return C 
    def __rsub__(self,other) : 
        return self.__sub__(other)


if __name__ == "__main__" :
    X = [[1,2,3],[4,5,6],[7,8,9]]                       
    Y = [[10,11,12],[13,14,15],[16,17,18]]              
    R = [[84,90,96],[201,216,231],[318,342,366]]        
    X = Matrix((3,3),X)                                 
    Y = Matrix((3,3),Y)                                 
    Res = X.__mul__(Y).M                                

    assert Res == R
    print("Script successful")