def are_CCZ_equivalent(s1, s2): s1, s2 = convert_sboxes(s1, s2) assert_equal_sizes(s1, s2) lin1 = s1.is_linear() lin2 = s2.is_linear() if lin1 ^ lin2: return False if lin1 and lin2: return True inp, out = s1.n, s1.m M1 = matrix(GF(2), 1 + inp + out, 2**inp) M2 = matrix(GF(2), 1 + inp + out, 2**inp) for x in range(2**inp): M1.set_column( x, Bin.concat(Bin(1, 1) + Bin(x, inp) + Bin(s1[x], out)).tuple) M2.set_column( x, Bin.concat(Bin(1, 1) + Bin(x, inp) + Bin(s2[x], out)).tuple) L1 = LinearCode(M1) L2 = LinearCode(M2) # Annoying: this is not checked in "is_permutation_equivalent" code! # raises a TypeError instead if L1.generator_matrix().nrows() != L2.generator_matrix().nrows(): return False return L1.is_permutation_equivalent(L2)
def mat_field_mul_const(field, c): assert field.base_ring() == GF(2) d = field.degree() m = matrix(GF(2), d, d) for i, e in enumerate(reversed(range(d))): x = 1 << e res = field.fetch_int(x) * field.fetch_int(c) res = res.integer_representation() m.set_column(i, Bin(res, d).tuple) return m
def matrix_mult_int_rev(mat, x): """ LSB to MSB vector >>> matrix_mult_int_rev( \ matrix(GF(2), [[1, 0, 1], [1, 0, 0]]), \ 0b110) # read as 6 -> 0,1,1 -> 1,0 -> 1 1 """ assert mat.base_ring() == GF(2) n = mat.ncols() x = vector(GF(2), Bin(x, n).tuple[::-1]) y = mat * x return Bin(y[::-1]).int
def matrix_mult_int(mat, x): """ MSB to LSB vector >>> matrix_mult_int( \ matrix(GF(2), [[1, 0, 1], [1, 0, 0]]), \ 0b110) # read as 6 -> 1,1,0 -> 1,1 -> 3 3 """ assert mat.base_ring() == GF(2) n = mat.ncols() x = vector(GF(2), Bin(x, n).tuple) y = mat * x return Bin(y).int
def as_matrix(self): assert self.is_linear() m = matrix(GF(2), self.output_size(), self.input_size()) for e in range(self.input_size()): vec = Bin(self[2**e], self.output_size()).tuple m.set_column(self.input_size() - 1 - e, vec) return m
def xor_cmul_ddt(self, F=None): if F is None: F = GF(self.insize, name='a') xcddt = matrix(ZZ, self.insize, self.outsize) for x in range(self.insize): for dx in range(1, self.insize): x2 = x ^ dx y = self[x] y2 = self[x2] dy = (F.fetch_int(y2) * F.fetch_int(y) **(self.outsize - 2)).integer_representation() xcddt[dx, dy] += 1 return xcddt
def cmul_xor_ddt(self, F=None): if F is None: F = GF(self.insize, name='a') cxddt = matrix(ZZ, self.insize, self.outsize) for x in range(1, self.insize): for dx in range(2, self.insize): x2 = (F.fetch_int(x) * F.fetch_int(dx)).integer_representation() y = self[x] y2 = self[x2] dy = y2 ^ y cxddt[dx, dy] += 1 return cxddt
def hdim(self, right_to_left=False): """ hdim[i,j] = i-th output bit contains monomial x1...xn/xj """ res = matrix(GF(2), self.in_bits, self.out_bits) anf = mobius(tuple(self)) for j in range(self.in_bits): mask = (1 << self.in_bits) - 1 mask ^= 1 << (self.in_bits - 1 - j) res.set_column(j, Bin(anf[mask], self.out_bits).tuple) if right_to_left: res = res[::-1, ::-1] return res
def test(): from cry.sagestuff import randint, random_matrix, choice, primes ps = list(primes(20)) for itr in range(100): fld = GF(choice(ps)) m = random_matrix(fld, randint(2, 50), randint(2, 50)) print(f"#{itr}", ":", m.nrows(), "x", m.ncols(), "rank", m.rank()) def oracle(x): nonlocal m return m * vector(fld, x) A = AffineSystem(oracle, m.ncols(), field=fld) x = random_vector(fld, m.ncols()) y = A.matrix * x num = 0 for x in A.solve(y, all=True): assert tuple(oracle(x)) == tuple(y) num += 1 if num > 10: break
def random_bit_permutation(n): return from_matrix(random_permutation_matrix(GF(2), n))
def random_linear(n, m=None): if m is None: m = n return from_matrix(random_matrix(GF(2), m, n))
def random_linear_permutation(n): return from_matrix(random_invertible_matrix(GF(2), n))
def all_irreducible_polynomials(N, p=2): FR = PolynomialRing(GF(p), names=("X", )) for poly in FR.polynomials(of_degree=N): if poly.is_irreducible(): yield poly
def mat_from_linear_func(m, n, func): mat = matrix(GF(2), n, m) for i, e in enumerate(reversed(range(m))): x = 1 << e mat.set_column(i, Bin(func(x), n).tuple) return mat
def get_x(cls, n=None, field=None): assert (n is not None) ^ (field is not None) field = field or GF(2**n, name='a') return PolynomialRing(field, names='x').gen()
def power(e, n=None, field=None): assert (n is not None) ^ (field is not None) field = field or GF(2**n, name='a') x = PolynomialRing(field, names='x').gen() return SBox2(x**e)
from cry.sagestuff import matrix, GF, Permutation F2 = GF(2) class Perm: def __init__(self, p, n=None): self.p = tuple(p) self.n = n if n is not None else (max(self.p) + 1) self.m = len(self.p) assert all(0 <= i < self.n for i in self.p) @classmethod def from_matrix(cls, m): res = [None] * m.nrows() for y, x in m.support(): res[y] = x return cls(res, n=m.ncols()) def to_matrix(self, field=F2): m = matrix(Permutation([v + 1 for v in self.p])) m = m.transpose().change_ring(field) return m def complete(self, n): assert n >= self.n s = set(self.p) return self.Perm(self.p + tuple(v for v in range(n) if v not in s)) def __invert__(self): res = [None] * len(self)
def minilat_binary(self, mod=4): """minilat % mod and then mod/2 -> 1, 0 -> 0""" mat = self.minilat() % 4 for x in mat.list(): assert x in (0, mod / 2), "invalid mod for given function" return (mat.lift() / (mod / 2)).change_ring(GF(2))
def all_fields_of_dimension(N, p=2, name='a'): for poly in all_irreducible_polynomials(N, p): yield GF(p**N, name=name, modulus=poly)