#!/usr/bin/env python3 from Cryptotools.Groups.group import Group class Galois: """ This class contain the Galois Field (Finite Field) Attributes: q (Integer): it's the number of the GF operation (Function): Function for generating the group identityAdd (Integer): it's the identity element in the GF(n) for the addition operation identityMul (Integer): it's the identity element in the GF(n) for the multiplicative operation """ def __init__(self, q, operation): self._q = q self._operation = operation self._identityAdd = 0 self._identityMul = 1 self._F = [x for x in range(q)] self._add = [[0 for x in range(q)] for y in range(q)] # TODO: May do we do a deep copy between all groups ? self._div = [[0 for x in range(q)] for y in range(q)] self._mul = [[0 for x in range(q)] for y in range(q)] self._sub = [[0 for x in range(q)] for y in range(q)] self._primitiveRoot = list() def primitiveRoot(self): """ In this function, we going to find the primitive root modulo n of the galois field Returns: Return the list of primitive root of the GF(q) """ for x in range(2, self._q): z = list() for entry in range(1, self._q): res = self._operation(x, entry, self._q) if res not in z: z.append(res) if self.isPrimitiveRoot(z, self._q - 1): if x not in self._primitiveRoot: # It's dirty, need to find why we have duplicate entry self._primitiveRoot.append(x) return z def getPrimitiveRoot(self): """ Return the list of primitives root Returns: Return the primitive root """ return self._primitiveRoot def isPrimitiveRoot(self, z, length): """ Check if z is a primitive root Args: z (list): check if z is a primitive root length (Integer): Length of the GF(q) """ if len(z) == length: return True return False def add(self): """ This function do the operation + on the Galois Field Returns: Return a list of the group with the addition operation """ for x in range(0, self._q): for y in range(0, self._q): self._add[x][y] = (x + y) % self._q return self._add def _inverseModular(self, a, n): """ This function find the reverse modular of a by n Returns: Return the reverse modular """ for b in range(1, n): if (a * b) % n == 1: inv = b break return inv def div(self): """ This function do the operation / on the Galois Field Returns: Return a list of the group with the division operation """ for x in range(1, self._q): for y in range(1, self._q): inv = self._inverseModular(y, self._q) self._div[x][y] = (x * inv) % self._q return self._div def mul(self): """ This function do the operation * on the Galois Field Returns: Return a list of the group with the multiplication operation """ for x in range(0, self._q): for y in range(0, self._q): self._mul[x][y] = (x * y) % self._q return self._mul def sub(self): """ This function do the operation - on the Galois Field Returns: Return a list of the group with the subtraction operation """ for x in range(0, self._q): for y in range(0, self._q): self._sub[x][y] = (x - y) % self._q return self._sub def check_closure_law(self, arithmetic): """ This function check the closure law. By definition, every element in the GF is an abelian group, which respect the closure law: for a and b belongs to G, a + b belongs to G, the operation is a binary operation Args: Arithmetics (str): contain the operation to be made, must be '+', '*', '/' or /-' """ if arithmetic not in ['+', '*', '/', '-']: raise Exception("The arithmetic need to be '+', '*', '/' or '-'") if arithmetic == '+': G = self._add elif arithmetic == '*': G = self._mul elif arithmetic == '/': G = self._div else: G = self._sub start = 0 """ In case of multiplicative, we bypass the first line, because all elements are zero, otherwise the test fail """ if arithmetic == '*' or arithmetic == '/': start = 1 isClosure = True for x in range(start, self._q): gr = Group(self._q, G[x], self._operation) if not gr.closure(): isClosure = False del gr if isClosure: print(f"The group {arithmetic} respect closure law") else: print(f"The group {arithmetic} does not respect closure law") def check_identity_add(self): """ This function check the identity element and must satisfy this condition: $a + 0 = a$ for each element in the GF(n) In Group Theory, an identity element is an element in the group which do not change the value every element in the group Returns: """ for x in self._F: if not self._identityAdd + x == x: raise Exception( f"The identity element {self._identityAdd} "\ "do not satisfy $a + element = a$" ) def check_identity_mul(self): """ This function check the identity element and must satisfy this condition: $a * 1 = a$ for each element in the GF(n) In Group Theory, an identity element is an element in the group which do not change the value every element in the group Returns: """ for x in self._F: if not self._identityMul * x == x: raise Exception( f"The identity element {self._identityAdd} "\ "do not satisfy $a * element = a$" ) def printMatrice(self, m): """ This function print the GF(m) Args: m (list): Matrix of the GF """ header = str() header = " " for x in range(0, self._q): header += f"{x} " header += "\n--|" + "-" * (len(header)- 3) +"\n" s = str() for x in range(0, self._q): s += f"{x} | " for y in range(0, self._q): s += f"{m[x][y]} " s += "\n" s = header + s print(s)