def weight(self): """Return the weight of the differential. >>> from arxpy.bitvector.core import Constant >>> from arxpy.diffcrypt.difference import DiffVar >>> from arxpy.ciphers.simon32_64 import XDF >>> x, y = DiffVar("x", 16), DiffVar("y", 16) >>> d = XDF(x, y) >>> d.weight() # doctest: +ELLIPSIS Ite(0xffff == x, 0b01111, ((0x00ff & ((0x0f0f & ((0x3333 ... >>> zero = Constant(0, 16) >>> d.weight().xreplace({x: zero, y: zero}) 0b00000 """ a, b = self.op.a, self.op.b n = self.input_diff[0].width alpha = self.input_diff[0] BvRol = RotateLeft varibits = BvRol(alpha, a) | BvRol(alpha, b) doublebits = BvRol(alpha, b) & (~BvRol(alpha, a)) & BvRol( alpha, 2 * a - b) hw = _HammingWeight(varibits ^ doublebits) width = max((n - 1).bit_length(), hw.width) value = Ite(BvComp(alpha, ~Constant(0, n)), Constant(n - 1, width), ZeroExtend(hw, width - hw.width)) return value
def test(cls): old_rounds = cls.rounds cls.set_rounds(1) pt = [Constant(0x00000000, 16)] * (N * 4) ct = (0xe9f5, 0xcfab, 0x5198, 0x9eec, 0xcb81, 0x7fb0, 0xd47a, 0x45b7, 0xa5b5, 0xd8da, 0x2cc5, 0x0aa1, 0x7bc9, 0x97f0, 0xc515, 0x7224) assert cls(*pt) == tuple(ct), "{}".format(cls(*pt)) cls.set_rounds(2) pt = [Constant(0x00000000, 16)] * (N * 4) ct = (0x4d2f, 0x383a, 0xa84d, 0xe12c, 0xee36, 0x2b82, 0xb624, 0x1e15, 0x39f9, 0x2ded, 0x54c3, 0x15c9, 0x58fe, 0xbff0, 0x44ad, 0x2b57) assert cls(*pt) == tuple(ct), "{}".format(cls(*pt)) cls.set_rounds(6) pt = [Constant(0x00000000, 16)] * (N * 4) ct = (0x0274, 0x2a3d, 0x9d5e, 0x0319, 0x32b4, 0x2751, 0x745b, 0xa328, 0xd2d4, 0x1ae9, 0x8e70, 0x0fe6, 0x9506, 0xe58d, 0x996b, 0x6075) assert cls(*pt) == tuple(ct), "{}".format(cls(*pt)) cls.set_rounds(old_rounds)
def eval(cls, *W): # w0, w1, ... rk = list(W) for i in range(N, 16): rk.append(Constant(0, 32)) sigma0 = lambda x: ROR(x, 7) ^ ROR(x, 18) ^ (x >> Constant(3, 32)) sigma1 = lambda x: ROR(x, 17) ^ ROR(x, 19) ^ (x >> Constant(10, 32)) for i in range(16, cls.rounds): rk.append(sigma1(rk[i-2]) + rk[i-7] + sigma0(rk[i-15]) + rk[i-16]) if REFERENCE_VERSION: pass else: for i in range(cls.rounds): rk[i] += k_ctes[i] for i in range(cls.rounds): if hasattr(cls, "skip_rounds") and i in cls.skip_rounds: if i < N: rk[i] = W[0] elif N <= i < 16: pass # rk[i] = 0 else: rk[i] = W[0] return rk[:N] + rk[16:cls.rounds]
def test_bv_ch(self, x, y, x_, y_): input_diff_names = ["dx", "dy"] prefix = "d" x = Constant(x, 8) x_ = Constant(x_, 8) y = Constant(y, 8) y_ = Constant(y_, 8) for diff_type in [XorDiff, RXDiff]: ch = BvCharacteristic(MyFunction, diff_type, input_diff_names, prefix) input_diff = [ diff_type.from_pair(x, x_), diff_type.from_pair(y, y_) ] # noinspection PyUnresolvedReferences output_diff = diff_type.from_pair( MyFunction(x, y)[0], MyFunction(x_, y_)[0]) der = first_value(ch.nonlinear_diffs) der.input_diff[0].val = der.input_diff[0].val.xreplace( {diff_type(Variable("dx", 8)): input_diff[0]}) der.input_diff[1].val = der.input_diff[1].val.xreplace( {diff_type(Variable("dx", 8)): input_diff[1]}) self.assertTrue(der.is_possible(output_diff))
def _count_preimages(cls, f, beta): width = f.input_diff[0].val.width if width == 8 and f.diff_type == XorDiff: foo = cls.libXDA.count_XOR_preimage_8bit elif width == 8 and f.diff_type == RXDiff: foo = cls.libRXDA.count_RX_preimage_8bit elif width == 16 and f.diff_type == RXDiff: foo = cls.libRXDA.count_RX_preimage_16bit else: foo = None if foo is not None: result = foo(f.input_diff[0].val, f.input_diff[1].val, beta.val) return int(result) else: num_preimages = 0 # total_preimages = 0 for i, j in itertools.product(range(2**width), range(2**width)): # total_preimages += 1 output_diff = f.eval(Constant(i, width), Constant(j, width)) assert isinstance(output_diff, Difference) and isinstance( beta, Difference) if output_diff == beta: num_preimages += 1 return num_preimages
def test_find_preimage(self, d1, d2, d3): assert DP_WIDTH == 8 d1 = Constant(d1, DP_WIDTH) d2 = Constant(d2, DP_WIDTH) d3 = Constant(d3, DP_WIDTH) for diff_type, der_type in zip([XorDiff, RXDiff], [XDA, RXDA]): alpha = diff_type(d1), diff_type(d2) beta = diff_type(d3) f = der_type(alpha) msg = "{}({} -> {})\n".format(der_type.__name__, alpha, beta) if diff_type == XorDiff: foo = self.__class__.libXDA.find_XOR_preimage_8bit elif diff_type == RXDiff: foo = self.__class__.libRXDA.find_RX_preimage_8bit result_lib = foo(f.input_diff[0].val, f.input_diff[1].val, beta.val) result_lib = result_lib.found result_loop = 0 for i, j in itertools.product(range(2**DP_WIDTH), range(2**DP_WIDTH)): output_diff = f.eval(Constant(i, DP_WIDTH), Constant(j, DP_WIDTH)) assert isinstance(output_diff, Difference) and isinstance( beta, Difference) if output_diff == beta: result_loop = 1 break self.assertEqual(result_lib, result_loop, msg=msg)
def test_rx_linear_op(self, width, x1, y1, x2, y2): x1 = Constant(x1 % (2**width), width) y1 = Constant(y1 % (2**width), width) d1 = RXDiff.from_pair(x1, y1) x2 = Constant(x2 % (2**width), width) y2 = Constant(y2 % (2**width), width) d2 = RXDiff.from_pair(x2, y2) self.assertEqual(RXDiff.from_pair(~x1, ~y1), RXDiff.derivative(BvNot, d1)) self.assertEqual(RXDiff.from_pair(x1 ^ x2, y1 ^ y2), RXDiff.derivative(BvXor, [d1, d2])) cte = x2 BvXor_fix = make_partial_operation(BvXor, tuple([None, cte])) self.assertEqual(RXDiff.from_pair(x1 ^ cte, y1 ^ cte), RXDiff.derivative(BvXor_fix, [d1])) cte = Variable("c", width) BvXor_fix = make_partial_operation(BvShl, tuple([None, cte])) with self.assertRaises(ValueError): XorDiff.derivative(BvXor_fix, d1) r = int(x2) % x1.width RotateLeft_fix = make_partial_operation(RotateLeft, tuple([None, r])) RotateRight_fix = make_partial_operation(RotateRight, tuple([None, r])) self.assertEqual( RXDiff.from_pair(RotateLeft(x1, r), RotateLeft(y1, r)), RXDiff.derivative(RotateLeft_fix, d1)) self.assertEqual( RXDiff.from_pair(RotateRight(x1, r), RotateRight(y1, r)), RXDiff.derivative(RotateRight_fix, d1))
def test_basic_simplify(self): x, y, z = Variable("x", 8), Variable("y", 8), Variable("z", 8) allones = Constant((2 ** 8) - 1, 8) notone = ~Constant(1, 8) # constants self.assertEqual(1 + (x + allones), x) self.assertEqual(1 ^ (x ^ 1), x) self.assertEqual(1 & (x & notone), 0) self.assertEqual(1 | (x | notone), allones) # compatible terms self.assertEqual(x + (y - x), y) self.assertEqual(x ^ (y ^ x), y) self.assertEqual(x & (y & x), x & y) self.assertEqual(x | (y | x), x | y) self.assertEqual((x ^ z) + (y - (x ^ z)), y) self.assertEqual((x + z) ^ (y ^ (x + z)), y) self.assertEqual((x + z) + (y - x), z + y) self.assertEqual((x ^ z) ^ (y ^ x), z ^ y) self.assertEqual((x & z) & (y & (~x)), 0) self.assertEqual((x | z) | (y | (~x)), allones)
def _find_preimage(cls, f, beta): width = f.input_diff[0].val.width if width == 8 and f.diff_type == XorDiff: foo = cls.libXDA.find_XOR_preimage_8bit elif width == 8 and f.diff_type == RXDiff: foo = cls.libRXDA.find_RX_preimage_8bit elif width == 16 and f.diff_type == RXDiff: foo = cls.libRXDA.find_RX_preimage_16bit else: foo = None if foo is not None: result = foo(f.input_diff[0].val, f.input_diff[1].val, beta.val) if result.found: return result.i, result.j else: return None else: for i, j in itertools.product(range(2**width), range(2**width)): output_diff = f.eval(Constant(i, width), Constant(j, width)) assert isinstance(output_diff, Difference) and isinstance( beta, Difference) if output_diff == beta: return i, j else: return None
def has_probability_one(self, output_diff): """Return whether the input diff propagates to the output diff with probability one. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.differential.difference import XorDiff >>> alpha = XorDiff(Constant(0, 16)) >>> f = XDSimonRF(alpha) >>> f.has_probability_one(XorDiff(Constant(0, 16))) 0b1 """ a, b, c = self.op.a, self.op.b, self.op.c n = self.input_diff[0].val.width assert math.gcd(n, a - b) == 1 and a > b and n % 2 == 0 alpha = self.input_diff[0].val Rol = RotateLeft varibits = Rol(alpha, a) | Rol(alpha, b) r = (2 * a - b) % n doublebits = (Rol(alpha, b) & (~Rol(alpha, a)) & Rol(alpha, r)) beta = output_diff.val gamma = beta ^ Rol(alpha, c) case2 = BvComp(Constant(0, n), (gamma & (~varibits)) | ((gamma ^ Rol(gamma, a - b)) & doublebits)) hw = varibits ^ doublebits # no need to PopCount condition = ~BvComp(alpha, ~Constant(0, n)) condition &= case2 condition &= BvComp(hw, Constant(0, hw.width)) return condition
def test_rx_deterministic_propagation(self, width, x1, y1, x2, y2): x1 = Constant(x1 % (2**width), width) y1 = Constant(y1 % (2**width), width) d1 = RXDiff.get_difference(x1, y1) x2 = Constant(x2 % (2**width), width) y2 = Constant(y2 % (2**width), width) d2 = RXDiff.get_difference(x2, y2) vd1 = DiffVar("in1", width) vd2 = DiffVar("in2", width) d3 = RXDiff.get_difference(~x1, ~y1) vd3 = RXDiff.propagate(BvNot, vd1) self.assertEqual(d3, vd3.xreplace({vd1: d1})) d3 = RXDiff.get_difference(x1 ^ x2, y1 ^ y2) vd3 = RXDiff.propagate(BvXor, [vd1, vd2]) self.assertEqual(d3, vd3.xreplace({vd1: d1, vd2: d2})) d3 = RXDiff.get_difference(x1 ^ x2, y1 ^ x2) # Xor with a constant vd3 = RXDiff.propagate(BvXor, [vd1, x2]) self.assertEqual(d3, vd3.xreplace({vd1: d1})) r = int(x2) % x1.width d3 = RXDiff.get_difference(RotateLeft(x1, r), RotateLeft(y1, r)) vd3 = RXDiff.propagate(RotateLeft, [vd1, r]) self.assertEqual(d3, vd3.xreplace({vd1: d1})) d3 = RXDiff.get_difference(RotateRight(x1, r), RotateRight(y1, r)) vd3 = RXDiff.propagate(RotateRight, [vd1, r]) self.assertEqual(d3, vd3.xreplace({vd1: d1}))
def test_initialization(self): x = Constant(0, 8) self.assertTrue(x.is_Atom) self.assertEqual(x.atoms(), {x}) with self.assertRaises(AttributeError): x.val = 0
def eval(cls, x, y): v0 = x v1 = y k = cls.round_keys for i in range(cls.rounds): v0, v1 = v1, v0 + ((((v1 << Constant(4, 32)) ^ (v1 >> Constant(5, 32))) + v1) ^ k[i]) return v0, v1
def test_case(self, d1, d2, d3): # d1, d2 input differences; d3 output difference d1 = Constant(d1, DP_WIDTH) d2 = Constant(d2, DP_WIDTH) d3 = Constant(d3, DP_WIDTH) for diff_type, der_type in zip([XorDiff], [XDS]): alpha = diff_type(d1), diff_type(d2) beta = diff_type(d3) f = der_type(alpha) if VERBOSE: der_name = der_type.__name__ print("{}({} -> {})".format(der_name, alpha, beta)) is_valid = f.is_possible(beta) if VERBOSE: print("\tis_valid:", bool(is_valid)) if is_valid: with Simplification(False): num_preimages = self._count_preimages(f, beta) if VERBOSE: print("\tpreimages found:", num_preimages) self.assertNotEqual(num_preimages, 0) total_preimages = 2**(2 * DP_WIDTH) emp_weight = -math.log(num_preimages / total_preimages, 2) theo_weight = f.weight(beta) # .doit() self.assertLessEqual(theo_weight, f.max_weight()) theo_weight = int(theo_weight) if VERBOSE: print("\tempirical weight:\t", emp_weight) print("\ttheoretical weight:\t", theo_weight) if diff_type == XorDiff: error = 0 else: raise ValueError( "invalid difference: {}".format(diff_type)) self.assertGreaterEqual(emp_weight, theo_weight - error) self.assertLessEqual(emp_weight, theo_weight + error) else: with Simplification(False): preimage = self._find_preimage(f, beta) if VERBOSE: print("\tpreimage found:", preimage) self.assertIsNone(preimage)
def test_xor_linear_op(self, width, x1, y1, x2, y2): x1 = Constant(x1 % (2**width), width) y1 = Constant(y1 % (2**width), width) d1 = XorDiff.from_pair(x1, y1) x2 = Constant(x2 % (2**width), width) y2 = Constant(y2 % (2**width), width) d2 = XorDiff.from_pair(x2, y2) self.assertEqual(XorDiff.from_pair(~x1, ~y1), XorDiff.derivative(BvNot, d1)) self.assertEqual(XorDiff.from_pair(x1 ^ x2, y1 ^ y2), XorDiff.derivative(BvXor, [d1, d2])) cte = x2 BvXor_fix = make_partial_operation(BvXor, tuple([None, cte])) self.assertEqual(XorDiff.from_pair(x1 ^ cte, y1 ^ cte), XorDiff.derivative(BvXor_fix, [d1])) cte = Variable("c", width) BvXor_fix = make_partial_operation(BvXor, tuple([None, cte])) self.assertEqual( XorDiff.from_pair(x1 ^ cte, y1 ^ cte), XorDiff.derivative(BvXor, [d1, XorDiff.from_pair(cte, cte)])) self.assertEqual(XorDiff.from_pair(x1 ^ cte, y1 ^ cte), XorDiff.derivative(BvXor_fix, [d1])) cte = x2 BvAnd_fix = make_partial_operation(BvAnd, tuple([None, cte])) self.assertEqual(XorDiff.from_pair(x1 & cte, y1 & cte), XorDiff.derivative(BvAnd_fix, [d1])) r = int(x2) % x1.width RotateLeft_fix = make_partial_operation(RotateLeft, tuple([None, r])) RotateRight_fix = make_partial_operation(RotateRight, tuple([None, r])) self.assertEqual( XorDiff.from_pair(RotateLeft(x1, r), RotateLeft(y1, r)), XorDiff.derivative(RotateLeft_fix, d1)) self.assertEqual( XorDiff.from_pair(RotateRight(x1, r), RotateRight(y1, r)), XorDiff.derivative(RotateRight_fix, d1)) r = Constant(int(x2) % x1.width, width) BvShl_fix = make_partial_operation(BvShl, tuple([None, r])) BvLshr_fix = make_partial_operation(BvLshr, tuple([None, r])) self.assertEqual(XorDiff.from_pair(x1 << r, y1 << r), XorDiff.derivative(BvShl_fix, d1)) self.assertEqual(XorDiff.from_pair(x1 >> r, y1 >> r), XorDiff.derivative(BvLshr_fix, d1)) i = int(x2) % x1.width j = int(y2) % (i + 1) Extract_fix = make_partial_operation(Extract, tuple([None, i, j])) self.assertEqual(XorDiff.from_pair(x1[i:j], y1[i:j]), XorDiff.derivative(Extract_fix, d1)) self.assertEqual(XorDiff.from_pair(Concat(x1, x2), Concat(y1, y2)), XorDiff.derivative(Concat, [d1, d2]))
def get_constant(i): if i < 20: return Constant(0x5A827999, 32) elif i < 40: return Constant(0x6ED9EBA1, 32) elif i < 60: return Constant(0x8F1BBCDC, 32) else: return Constant(0xCA62C1D6, 32)
def test_invalid_args(self): with self.assertRaises(AssertionError): Constant("1", 8) with self.assertRaises(AssertionError): Constant(0.5, 8) with self.assertRaises(AssertionError): Constant(-1, 8) with self.assertRaises(AssertionError): Constant(9, 2)
def test_f(self, din, dout): width = DP_WIDTH din, dout = Constant(din, width), Constant(dout, width) sin, sout = DiffVar("sin", width), DiffVar("sout", width) for differential_type in [ ciphers.simon32_64.F.differential(XorDiff), ciphers.simon32_64.F.differential(RXDiff), ciphers.simeck32_64.F.differential(XorDiff), ciphers.simeck32_64.F.differential(RXDiff) ]: dbvadd = differential_type(sin, sout) is_valid = dbvadd.is_valid().xreplace({sin: din, sout: dout}) print("{}({} -> {})".format(differential_type.__name__, din, dout)) if is_valid: with Validation(False), Simplification(False): correct, total = self._count_correct_pairs( dbvadd, din, dout) msg = ("{}({} -> {}) is VALID but no correct pairs were found" ).format(differential_type.__name__, din, dout) self.assertNotEqual(correct, 0, msg=msg) emp_weight = -math.log(correct / total, 2) emp_weight = dbvadd.weight_function(emp_weight) w = dbvadd.weight() theo_weight = int(w.xreplace({sin: din, sout: dout})) max_weight = 2**(w.width) - 1 error = max_weight * 0.10 msg = ("{}({} -> {}) has theoretical weight {} " "but empirical weight {:.2f}").format( differential_type.__name__, din, dout, theo_weight, emp_weight) self.assertGreaterEqual(emp_weight, theo_weight - error, msg=msg) self.assertLessEqual(emp_weight, theo_weight + error, msg=msg) else: with Validation(False), Simplification(False): pairs = self._find_correct_pair(dbvadd, din, dout) msg = ( "{}({} -> {}) is INVALID but the correct pair {} was found" ).format(differential_type.__name__, din, dout, pairs) self.assertIsNone(pairs, msg=msg)
def eval(cls, *master_key): mk = list(master_key) s = Constant(0, 32) delta = Constant(0x9E3779B9, 32) k = [] for i in range(cls.rounds): if i % 2 == 0: k.append(s + mk[int(s & Constant(3, 32))]) s += delta else: k.append(s + mk[int((s >> Constant(11, 32)) & Constant(3, 32))]) return k
def _test_case(self, d1, d2, a, precision, version): d1 = Constant(d1, DP_WIDTH) d2 = Constant(d2, DP_WIDTH) a = Constant(a, DP_WIDTH) diff_type = XorDiff der_type = derivative.XDCA old_precision = der_type.precision der_type.precision = precision alpha = diff_type(d1) beta = diff_type(d2) f = der_type(alpha, a) der_repr = "{}_cte={}_prec={}_v={}({} -> {})\n".format( der_type.__name__, a, precision, version, alpha, beta) msg = der_repr is_valid = f.is_possible(beta) msg += "\tis_valid: {}\n".format(is_valid) real_weight = f.exact_weight(beta) msg += "\texact weight: {}\n".format(real_weight) self.assertEqual(is_valid, real_weight != math.inf, msg=msg) if is_valid: theo_weight = int(f.weight(beta, version=version)) # .doit()) self.assertLessEqual(theo_weight, f.max_weight(), msg=msg) theo_weight /= 2**(f.num_frac_bits()) msg += "\ttheo weight: {}\n".format(theo_weight) real_error = theo_weight - real_weight theo_error = f.error() if PRINT_MAX_ERROR and real_error > self.__class__.max_error[0]: self.__class__.max_error = [real_error, der_repr] msg += "\treal_error: {}\n".format(round(real_error, ERROR_DIGITS)) msg += "\ttheo_error: {}\n".format(round(theo_error, ERROR_DIGITS)) if PRINT_DISTR_ERROR: self.distr_error[round(real_error, ERROR_DIGITS)] += 1 self.assertGreaterEqual(real_error, 0, msg=msg) self.assertLessEqual(real_error, theo_error, msg=msg) der_type.precision = old_precision
def test_pysmt_operations(self, width, x, y): try: from pysmt import shortcuts as sc except ImportError: return modulus = 2 ** width x = x % modulus y = y % modulus bvx = Constant(x, width) bvy = Constant(y, width) psx = sc.BV(x, width) psy = sc.BV(y, width) def eval_pysmt(pysmt_var): return pysmt_var.simplify().constant_value() self.assertEqual(~bvx, eval_pysmt(sc.BVNot(psx))) self.assertEqual(bvx & bvy, eval_pysmt(sc.BVAnd(psx, psy))) self.assertEqual(bvx | bvy, eval_pysmt(sc.BVOr(psx, psy))) self.assertEqual(bvx ^ bvy, eval_pysmt(sc.BVXor(psx, psy))) self.assertEqual(BvComp(bvx, bvy), eval_pysmt(sc.BVComp(psx, psy))) self.assertEqual((bvx < bvy), eval_pysmt(sc.BVULT(psx, psy))) self.assertEqual((bvx <= bvy), eval_pysmt(sc.BVULE(psx, psy))) self.assertEqual((bvx > bvy), eval_pysmt(sc.BVUGT(psx, psy))) self.assertEqual((bvx >= bvy), eval_pysmt(sc.BVUGE(psx, psy))) r = y % bvx.width self.assertEqual(bvx << bvy, eval_pysmt(sc.BVLShl(psx, psy))) self.assertEqual(bvx >> bvy, eval_pysmt(sc.BVLShr(psx, psy))) self.assertEqual(RotateLeft(bvx, r), eval_pysmt(sc.BVRol(psx, r))) self.assertEqual(RotateRight(bvx, r), eval_pysmt(sc.BVRor(psx, r))) bvb = Constant(y % 2, 1) psb = sc.Bool(bool(bvb)) self.assertEqual(Ite(bvb, bvx, bvy), eval_pysmt(sc.Ite(psb, psx, psy))) j = y % bvx.width self.assertEqual(bvx[:j], eval_pysmt(sc.BVExtract(psx, start=j))) self.assertEqual(bvx[j:], eval_pysmt(sc.BVExtract(psx, end=j))) self.assertEqual(Concat(bvx, bvy), eval_pysmt(sc.BVConcat(psx, psy))) self.assertEqual(ZeroExtend(bvx, j), eval_pysmt(sc.BVZExt(psx, j))) self.assertEqual(Repeat(bvx, 1 + j), eval_pysmt(psx.BVRepeat(1 + j))) self.assertEqual(-bvx, eval_pysmt(sc.BVNeg(psx))) self.assertEqual(bvx + bvy, eval_pysmt(sc.BVAdd(psx, psy))) self.assertEqual(bvx - bvy, eval_pysmt(sc.BVSub(psx, psy))) self.assertEqual(bvx * bvy, eval_pysmt(sc.BVMul(psx, psy))) if bvy > 0: self.assertEqual(bvx / bvy, eval_pysmt(sc.BVUDiv(psx, psy))) self.assertEqual(bvx % bvy, eval_pysmt(sc.BVURem(psx, psy)))
def eval(cls, x, y): v0 = x v1 = y k = cls.round_keys cls.round_inputs = [] for i in range(cls.rounds): cls.round_inputs.append([v0, v1]) if hasattr(cls, "skip_rounds") and i in cls.skip_rounds: continue v0, v1 = v1, v0 + ((((v1 << Constant(4, 32)) ^ (v1 >> Constant(5, 32))) + v1) ^ k[i]) cls.round_inputs.append([v0, v1]) return v0, v1
def is_possible(self, output_diff): """Return whether the given output `XorDiff` is possible. >>> from arxpy.bitvector.core import Constant, Variable >>> from arxpy.bitvector.context import NotEvaluation >>> from arxpy.bitvector.printing import BvWrapPrinter >>> from arxpy.differential.difference import XorDiff >>> alpha = XorDiff(Constant(0, 16)) >>> f = XDSimonRF(alpha) >>> f.is_possible(XorDiff(Constant(0, 16))) 0b1 >>> u, v = Variable("u", 16), Variable("v", 16) >>> f = XDSimonRF(XorDiff(u)) >>> with NotEvaluation([PopCount]): ... result = f.is_possible(XorDiff(v)) >>> print(BvWrapPrinter().doprint(result)) Ite(0xffff == u, 0b0 == (PopCount(v ^ (u <<< 2))[0]), ==(0x0000, |(~(u <<< 8) & (u <<< 1) & (u <<< 15) & (v ^ (u <<< 2) ^ ((v ^ (u <<< 2)) <<< 7)), ~((u <<< 1) | (u <<< 8)) & (v ^ (u <<< 2)) >>> result.xreplace({u: Constant(0, 16), v: Constant(0, 16)}) 0b1 See `Derivative.is_possible` for more information. """ a, b, c = self.op.a, self.op.b, self.op.c n = self.input_diff[0].val.width assert math.gcd(n, a - b) == 1 and a > b and n % 2 == 0 alpha = self.input_diff[0].val Rol = RotateLeft varibits = Rol(alpha, a) | Rol(alpha, b) r = (2 * a - b) % n doublebits = (Rol(alpha, b) & (~Rol(alpha, a)) & Rol(alpha, r)) beta = output_diff.val gamma = beta ^ Rol(alpha, c) def is_even(x): return BvComp(x[0], Constant(0, 1)) case2 = BvComp(Constant(0, n), (gamma & (~varibits)) | ((gamma ^ Rol(gamma, a - b)) & doublebits)) condition = Ite(BvComp(alpha, ~Constant(0, n)), is_even(PopCount(gamma)), case2) return condition
def test_bvadd(self, d1, d2, d3): width = DP_WIDTH d1, d2, d3 = Constant(d1, width), Constant(d2, width), Constant(d3, width) s1, s2, s3 = DiffVar("s1", width), DiffVar("s2", width), DiffVar("s3", width) # TODO: perform exhaustive test for differential_type in [RXDBvAdd]: # XDBvAdd dbvadd = differential_type([s1, s2], s3) is_valid = dbvadd.is_valid().xreplace({s1: d1, s2: d2, s3: d3}) print("{}({},{} -> {})".format(differential_type.__name__, d1, d2, d3)) if is_valid: with Validation(False), Simplification(False): correct, total = self._count_correct_pairs(dbvadd, d1, d2, d3) msg = ( "{}({},{} -> {}) is VALID but no correct pairs were found" ).format(differential_type.__name__, d1, d2, d3) self.assertNotEqual(correct, 0, msg=msg) emp_weight = - math.log(correct / total, 2) emp_weight = dbvadd.weight_function(emp_weight) w = dbvadd.weight() theo_weight = int(w.xreplace({s1: d1, s2: d2, s3: d3})) max_weight = 2 ** (w.width) - 1 error = max_weight * 0.10 msg = ( "{}({},{} -> {}) has theoretical weight {} " "but empirical weight {:.2f}" ).format(differential_type.__name__, d1, d2, d3, theo_weight, emp_weight) self.assertGreaterEqual(emp_weight, theo_weight - error, msg=msg) self.assertLessEqual(emp_weight, theo_weight + error, msg=msg) else: with Validation(False), Simplification(False): pairs = self._find_correct_pair(dbvadd, d1, d2, d3) msg = ( "{}({},{} -> {}) is INVALID but the correct pair {} was found" ).format(differential_type.__name__, d1, d2, d3, pairs) self.assertIsNone(pairs, msg=msg)
def round_function(cls, A, B, C, D, E, F, G, H, i): if i < N: W = cls.round_keys[i] elif N <= i < 16: if REFERENCE_VERSION: W = Constant(0, 32) else: W = k_ctes[i] else: W = cls.round_keys[i - (16 - N)] delta0 = lambda x: ROR(x, 2) ^ ROR(x, 13) ^ ROR(x, 22) delta1 = lambda x: ROR(x, 6) ^ ROR(x, 11) ^ ROR(x, 25) if REFERENCE_VERSION: T1 = H + delta1(E) + BvIf(E, F, G) + W + k_ctes[i] # ref else: T1 = H + delta1(E) + BvIf(E, F, G) + W # optimized T2 = delta0(A) + BvMaj(A, B, C) return [ T1 + T2, A, B, C, D + T1, E, F, G ]
def eval(cls, *W): # w0, w1, ... rk = list(W) for i in range(N, 16): rk.append(Constant(0, 32)) for i in range(16, cls.rounds): rk.append(ROL(rk[i - 3] ^ rk[i - 8] ^ rk[i - 14] ^ rk[i - 16], 1)) if REFERENCE_VERSION: pass else: for i in range(cls.rounds): rk[i] += get_constant(i) for i in range(cls.rounds): if hasattr(cls, "skip_rounds") and i in cls.skip_rounds: if i < N: rk[i] = W[0] elif N <= i < 16: pass # rk[i] = 0 else: rk[i] = W[0] return rk[:N] + rk[16:cls.rounds]
def eval(cls, KL0, KL1, KL2, KL3, KL4, KL5, KL6, KL7, KR0, KR1, KR2, KR3, KR4, KR5, KR6, KR7): KL = [KL0, KL1, KL2, KL3, KL4, KL5, KL6, KL7] KR = [KR0, KR1, KR2, KR3, KR4, KR5, KR6, KR7] KR1, KR2 = KR[:4], KR[4:] Q = [] for r in range(cls.rounds // 2 + 4): if (r + 1) % 3 == 1: Q.append(xor(KR1, KR2)) elif (r + 1) % 3 == 2: Q.append(KR1) elif (r + 1) % 3 == 0: Q.append(KR2) A = KL[:4] B = KL[4:] D = [Constant(0, 8) for _ in range(4)] K = [] for r in range(cls.rounds // 2 + 4): D, A, B = A, B, cls.fk(*A, *xor(B, xor(D, Q[r]))) K.extend(B) if cls.rounds % 2 != 0: if cls.rounds % 3 == 1: Qr = xor(KR1, KR2) elif cls.rounds % 3 == 2: Qr = KR1 else: assert cls.rounds % 3 == 0 Qr = KR2 D, A, B = A, B, cls.fk(*A, *xor(B, xor(D, Qr))) K.extend(B[:2]) return K
def pi2(R, k_i): if REFERENCE_VERSION: x = RotateLeft(R + k_i, 1) + (R + k_i) + (-Constant(1, 32)) else: assert isinstance(k_i, list) x = RotateLeft(R + k_i[0], 1) + (R + k_i[1]) return RotateLeft(x, 4) ^ x
def pi4(R, k_i): if REFERENCE_VERSION: x = RotateLeft(R + k_i, 2) + (R + k_i) + Constant(1, 32) else: assert isinstance(k_i, list) x = RotateLeft(R + k_i[0], 2) + (R + k_i[1]) return x
def round_function(cls, A, B, C, D, E, i): if i < N: W = cls.round_keys[i] elif N <= i < 16: if REFERENCE_VERSION: W = Constant(0, 32) else: W = get_constant(i) else: W = cls.round_keys[i - (16 - N)] if i < 20: F = BvIf elif i < 40: F = lambda x, y, z: x ^ y ^ z elif i < 60: F = BvMaj else: F = lambda x, y, z: x ^ y ^ z return [ W + ROL(A, 5) + F(B, C, D) + E + get_constant(i) if REFERENCE_VERSION else W + ROL(A, 5) + F(B, C, D) + E, A, ROL(B, 30), C, D ]