def mul_Y(self, X, Y): ret = Vector(self.nbits) i = 0 for i in range(0, self.nbits): Yi_vec = Vector(self.nbits, Y[i]) self.iadd_Y(ret, self.lshift_n(X, i) * Yi_vec) return ret
def __init__(self, nbits): self.__set_nbits(nbits) self.gen_x = Vector(nbits) self.use_esf = False self.use_opt_mba = True for i in range(0, nbits): self.gen_x[i] = symbol("__gen_X_%d" % i)
def rol_n(self, X, n): # rol(0b(d b c a), 1) = 0b(b c a d) # rol(vec(a,b,c,d), 1) = vec(d,a,c,b)) ret = Vector(self.nbits) for i in range(self.nbits): ret[i] = X[(i - n) % self.nbits] return ret
def flatten_vec(V): n = 8 * len(V) ret = Vector(n) for i in range(len(V)): for j in range(8): ret[i * 8 + j] = V[i][j].copy() return ret
def flat_vec(L): lv = len(L[0].vec) s = lv * len(L) ret = Vector(s) for i in range(s): ret[i] = L[i // lv][i % lv] return ret
def __identify(app, in_name): # TODO: identify number of independant inputs NL = app.nl().vector() M = app.matrix() nbits_in = M.ncols() nbits_out = M.nlines() if nbits_in != nbits_out: raise ValueError("do not yet support application with a different\ number of input and output bits!") mba = MBA(nbits_in) var_in = mba.var(in_name) if NL != Vector(len(NL)): return _identify_nonaffine(app, var_in) C = EX.ExprCst(mba.from_vec(app.cst()).to_cst(), nbits_out) if M == Matrix(nbits_out, nbits_in): # This is just a constant return C ret = EX.ExprBV(var_in) matrix_empty = 0 # Find empty columns in the matrix. for j in range(nbits_in): is_zero = reduce(operator.and_, (M.at(i, j) == imm(0) for i in range(nbits_out)), True) if is_zero: matrix_empty |= 1 << j matrix_and_mask = (~matrix_empty) % (2**nbits_out) if matrix_empty != 0: ret = EX.ExprAnd(ret, EX.ExprCst(matrix_and_mask, nbits_out)) if mba.from_vec(M * var_in.vec) ^ (var_in & matrix_empty) == var_in: # This is a XOR return EX.ExprXor(ret, C) raise ValueError("unable to identify an expression")
def sub_n_mba(self, X, n): null = Vector(self.nbits) n = self.get_vector_from_cst(n) while (n != null): X = simplify_inplace(self.xor_Y(X, n)) n = simplify_inplace(self.and_Y(self.lshift_n(X, 1), self.lshift_n(n, 1))) return (X)
def and_n(self, X, n): if n < 0: n = n & self.max_uint ret = Vector(self.nbits) for i in range(self.nbits): if n & (1 << i): ret[i] = X[i] return ret
def or_n(self, X, n): ret = Vector(self.nbits) for i in range(self.nbits): if n & (1 << i): ret[i] = imm(1) else: ret[i] = X[i] return ret
def eval_expr(e, use_esf=False): ectx = init_ctxes(e) ret = Vector(e.nbits) for i in range(e.nbits): ret[i] = ectx.eval(ret, i, use_esf) mba = MBA(len(ret)) return mba.from_vec(ret)
def iadd_n_mba(self, X, n): null = Vector(self.nbits) n = self.get_vector_from_cst(n) while (n != null): carry = simplify_inplace(self.and_Y(X, n)) self.ixor_Y(X, n) simplify_inplace(X) n = self.lshift_n(carry, 1) return X
def symbpermut2expr(self, P, X): ret = Vector(self.nbits) nbits_in = (len(P) - 1).bit_length() for k, v in enumerate(P): test = test_N(nbits_in, X, k) for i in range(self.nbits): ret[i] += v[i] * test simplify_inplace(ret) return ret
def mul_n_org(self, X, n): n = n & self.max_uint ret = Vector(self.nbits) i = 0 while n > 0: if (n & 1) == 1: self.iadd_lshifted_Y(ret, X, i) n >>= 1 i += 1 return ret
def div_n(self, X, n): ret = Vector(self.nbits * 2 + 1) for i in range(self.nbits): ret[i] = X[i] nc = (2**self.nbits / n) * n - 1 for p in range(self.nbits, 2 * self.nbits + 1): if (2**p > nc * (n - 1 - ((2**p - 1) % n))): break else: raise RuntimeError("division: unable to find the shifting count") m = (2**p + n - 1 - ((2**p - 1) % n)) // n self.__set_nbits(2 * self.nbits + 1) ret = self.mul_n(ret, m) ret = self.rshift_n(ret, p) self.__set_nbits((self.nbits - 1) // 2) final_ret = Vector(self.nbits) for i in range(self.nbits): final_ret[i] = ret[i] return final_ret
def solve(self, X): nbits = len(X.vec) idx_base = X[0].sym_idx() for I0 in self.I0s: I0.difference_update(self.I1) if (len(I0) == 0): return [] self.I0s = [I0 for I0 in self.I0s if len(I0) > 0] fix1 = Vector(X.vec) for idx in self.I1: idx = self.__idx(idx, idx_base, nbits) fix1[idx] = imm(1) if (len(self.I0s) == 0): return [fix1] ret = [] def iter_zeros(idxes, I0s): if len(I0s) == 0: yield idxes return for i,I0 in enumerate(I0s): if I0.isdisjoint(idxes): next_I0 = I0 next_idx = i break else: yield idxes return for idx in next_I0: new_idxes = idxes + [idx] for z in iter_zeros(new_idxes, I0s[next_idx+1:]): yield z for idxes in iter_zeros([], self.I0s): new = Vector(fix1) for idx in idxes: new[self.__idx(idx, idx_base, nbits)] = imm(0) ret.append(new) return ret
def flatten(vars_): vecs = [v.vec if isinstance(v, MBAVariable) else v for v in vars_] total_len = sum((len(v) for v in vecs)) ret = Vector(total_len) i = 0 for v in vecs: for j in range(len(v)): ret[i] = v[j] i += 1 mba = MBA(total_len) return mba.from_vec(ret)
def mul_n(self, X, n): if (n == 1): return X ret = Vector(self.nbits) if (n == 0): return ret n = n & self.max_uint i = 0 final_sum = 0 not_x = None def compute_not_x(not_x): if not_x is None: not_x = self.not_X(X) return not_x while n > 0: # Optimisations from the Hacker's delight nz = next_zero_bit(n) if (nz >= 3): not_x = compute_not_x(not_x) self.iadd_lshifted_Y(ret, X, nz + i) self.iadd_lshifted_Y(ret, not_x, i) final_sum += 1 << i n >>= nz i += nz else: bits4 = n & 0b1111 if bits4 == 0b1011: not_x = compute_not_x(not_x) self.iadd_lshifted_Y(ret, X, 4 + i) self.iadd_lshifted_Y(ret, not_x, 2 + i) self.iadd_lshifted_Y(ret, not_x, i) final_sum += 1 << (i + 2) final_sum += 1 << i n >>= 4 i += 4 elif bits4 == 0b1101: not_x = compute_not_x(not_x) self.iadd_lshifted_Y(ret, X, 4 + i) self.iadd_lshifted_Y(ret, not_x, 1 + i) self.iadd_lshifted_Y(ret, not_x, i) final_sum += 1 << (i + 1) final_sum += 1 << i n >>= 4 i += 4 else: if (n & 1) == 1: self.iadd_lshifted_Y(ret, X, i) n >>= 1 i += 1 if final_sum > 0: self.iadd_n(ret, final_sum & self.max_uint) return ret
def add_Y(self, X, Y): carry = imm(0) ret = Vector(self.nbits) if self.use_esf: for i in range(0, self.nbits): ret[i] = simplify_inplace(X[i] + Y[i] + carry) carry = esf(2, [X[i], Y[i], carry]) else: for i in range(0, self.nbits): sum_XY = simplify_inplace(X[i] + Y[i]) ret[i] = simplify_inplace(sum_XY + carry) carry = simplify_inplace(X[i] * Y[i] + (carry * sum_XY)) return ret
def vectorial_decomp(self, symbols): ''' Compute the vectorial decomposition of the expression according to the given symbols. symbols is a list that represents the input of the resulting application. They are considerated as a flatten vector of bits. Args: symbols: TODO Returns: An :class:`pytanque.App` object Example: >>> mba = MBA(4) >>> x = mba.var('x') >>> y = mba.var('y') >>> e = x^y^6 >>> e.vectorial_decomp([x,y]) App NL = Vec([ 0, 0, 0, 0 ]) AffApp matrix = Mat([ [1, 0, 0, 0, 1, 0, 0, 0] [0, 1, 0, 0, 0, 1, 0, 0] [0, 0, 1, 0, 0, 0, 1, 0] [0, 0, 0, 1, 0, 0, 0, 1] ]) AffApp cst = Vec([ 0, 1, 1, 0 ]) ''' try: symbols = [s.vec for s in symbols] N = sum(map(lambda s: len(s), symbols)) symbols_ = Vector(N) i = 0 for v in symbols: for s in v: symbols_[i] = s i += 1 symbols = symbols_ except TypeError: pass return self.mba.vectorial_decomp(symbols, self.vec)
def iadd_Y(self, X, Y): carry = imm(0) ret = Vector(self.nbits) if self.use_esf: for i in range(0, self.nbits): new_carry = esf(2, [X[i], Y[i], carry]) X[i] += simplify_inplace(Y[i] + carry) carry = new_carry else: for i in range(0, self.nbits): sum_XY = simplify_inplace(X[i] + Y[i]) new_carry = simplify_inplace(X[i] * Y[i] + (carry * sum_XY)) X[i] = sum_XY + carry carry = new_carry return ret
def permut2expr(self, P, X): ret = Vector(self.nbits) v0 = P[0] nbits_in = (len(P) - 1).bit_length() for k, v in enumerate(P[1:]): v ^= v0 if v == 0: continue k += 1 test = test_N(nbits_in, X, k) for i in range(self.nbits): if ((v >> i) & 1) == 1: ret[i] += test for i in range(self.nbits): ret[i] += imm((v0 >> i) & 1) simplify_inplace(ret) return ret
def app_inverse(A): M = A.matrix() NL = A.nl() V = A.cst() Minv = M.inverse() if Minv.ncols() == 0: return None N = V.size() mba = MBA(N) Y = mba.var('Y') G0 = Minv * Y.vec + Minv * V G1nl = simplify_inplace(Minv * NL(Y.vec)) # Check if G1 is inversible through a dependency graph DG = nx.DiGraph() idx_base = Y[0].sym_idx() for i, e in enumerate(G1nl): for d in get_depends_as_set(e): DG.add_edge(Y[i].sym_idx() - idx_base, d.sym_idx() - idx_base) if not nx.is_directed_acyclic_graph(DG): return None # Use the graph to get the inverse of G1 X = mba.var('X') resolve_order = nx.topological_sort(DG, reverse=True) solved = dict() for i in resolve_order: e = G1nl[i] if e.is_imm() and e.imm_value() == 0: solved[i] = X[i] else: # Doing this in the reversed topological order should make this always work! solved[i] = simplify_inplace(X[i] + simplify_inplace( subs_exprs(G1nl[i], [Y[j] for j in six.iterkeys(solved)], list(six.itervalues(solved))))) G1inv = Vector(N) for i in range(N): G1inv[i] = solved.get(i, X[i]) G1inv = MBAVariable(mba, G1inv) Finv = G1inv.eval({X: G0}) Finv = MBAVariable(mba, Finv) return Finv.vectorial_decomp([Y])
def get_vector_from_cst(nbits, n): vec = Vector(nbits) vec.set_int_be(n, nbits) return vec
def var_symbols(self, name): symbols = [symbol("%s%d" % (name, i)) for i in range(0, self.nbits)] M = Vector(self.nbits) for i in range(0, self.nbits): M[i] = symbols[i] return M
def from_bytes(self, s): ret = Vector(self.nbits) for i, c in enumerate(six.iterbytes(s)): for j in range(8): ret[i * 8 + j] = imm((c >> j) & 1) return ret
def ror_n(self, X, n): ret = Vector(self.nbits) for i in range(self.nbits): ret[i] = X[(i + n) % self.nbits] return ret
def or_exp(self, X, e): if self.use_esf: E = Vector(self.nbits, e) return self.or_Y(X, E) else: return self.xor_exp(self.and_exp(X, e), self.xor_exp(X, e))