Ejemplo n.º 1
0
    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
            >>> from arxpy.differential.derivative import XDA
            >>> alpha = XorDiff(Constant(0, 4)), XorDiff(Constant(0, 4))
            >>> f = XDA(alpha)
            >>> f.has_probability_one(XorDiff(Constant(0, 4)))
            0b1

        """
        is_possible = self.is_possible(output_diff)

        a, b = [d.val for d in self.input_diff]
        c = output_diff.val
        n = a.width

        def eq(x, y, z):
            if not isinstance(x, core.Constant):
                if isinstance(y, core.Constant):
                    return eq(y, x, z)
                elif isinstance(z, core.Constant):
                    return eq(z, x, y)

            return (~x ^ y) & (~x ^ z)

        # not optimized

        return is_possible & operation.BvComp(
            (~eq(a, b, c))[n-2:],
            core.Constant(0, n - 1)
        )
Ejemplo n.º 2
0
    def is_valid(self):
        """Return the bv expression for non-zero propagation probability.

            >>> from arxpy.bitvector.core import Constant
            >>> from arxpy.diffcrypt.difference import DiffVar
            >>> from arxpy.diffcrypt.differential import RXDBvAdd
            >>> a, b, c = DiffVar("a", 8), DiffVar("b", 8), DiffVar("c", 8)
            >>> rxda = RXDBvAdd([a, b], c)
            >>> rxda.is_valid()  # doctest: +ELLIPSIS
            0b111111 == (~(((((c >> 0x01) ^ ((a >> 0x01) ^ (b >> 0x01))) ...
            >>> zero = Constant(0, 8)
            >>> rxda.is_valid().xreplace({a: zero, b: zero, c: zero})
            0b1

        """
        # (I ^ SHL)(da ^ db ^ dc)[1:] <= SHL((da ^ dc) OR (db ^ dc))[1:]

        alpha, beta = self.input_diff
        gamma = self.output_diff
        # da, db, dc = alpha[:1], beta[:1], gamma[:1]  # boolector error
        da, db, dc = alpha >> 1, beta >> 1, gamma >> 1  # ignore LSB

        lhs = (da ^ db ^ dc) ^ ((da ^ db ^ dc) << 1)
        rhs = ((da ^ dc) | (db ^ dc)) << 1

        def bitwise_implication(x, y):
            return (~x) | y

        n = lhs.width
        return operation.BvComp(
            bitwise_implication(lhs[n - 2:1], rhs[n - 2:1]),  # ignore MSB, LSB
            ~core.Constant(0, n - 2))
Ejemplo n.º 3
0
 def lz(x):
     if are_cte_differences:
         return extraop.LeadingZeros(x)
     else:
         aux = core.Variable("{}_{}lz".format(prefix, self._i_auxvar), x.width)
         self._i_auxvar += 1
         assertions.append(operation.BvComp(aux, extraop.LeadingZeros(x)))
         return aux
Ejemplo n.º 4
0
 def rev(x):
     if are_cte_differences:
         return extraop.Reverse(x)
     else:
         aux = core.Variable("{}_{}rev".format(prefix, self._i_auxvar), x.width)
         self._i_auxvar += 1
         assertions.append(operation.BvComp(aux, extraop.Reverse(x)))
         return aux
Ejemplo n.º 5
0
    def is_possible(self, output_diff):
        """Return whether the given output `RXDiff` is possible.

            >>> from arxpy.bitvector.core import Constant, Variable
            >>> from arxpy.differential.difference import RXDiff
            >>> from arxpy.differential.derivative import RXDA
            >>> alpha = RXDiff(Constant(0, 4)), RXDiff(Constant(0, 4))
            >>> f = RXDA(alpha)
            >>> beta = RXDiff(Constant(0, 4))
            >>> f.is_possible(beta)
            0b1
            >>> a0, a1, b = Variable("a0", 4), Variable("a1", 4), Variable("b", 4)
            >>> alpha = RXDiff(a0), RXDiff(a1)
            >>> f = RXDA(alpha)
            >>> beta = RXDiff(b)
            >>> result = f.is_possible(beta)
            >>> result  # doctest:+NORMALIZE_WHITESPACE
            0b11 == (~(((((a0[:1]) ^ (a1[:1]) ^ (b[:1])) << 0b001) ^ (a0[:1]) ^ (a1[:1]) ^ (b[:1]))[:1]) |
            ((((a0[:1]) ^ (b[:1])) | ((a1[:1]) ^ (b[:1])))[1:]))
            >>> result.xreplace({a0: Constant(0, 4), a1: Constant(0, 4), b: Constant(0, 4)})
            0b1
            >>> a1 = Constant(0, 4)
            >>> alpha = RXDiff(a0), RXDiff(a1)
            >>> f = RXDA(alpha)
            >>> beta = RXDiff(b)
            >>> result = f.is_possible(beta)
            >>> result  # doctest:+NORMALIZE_WHITESPACE
            0b11 == (~(((((a0[:1]) ^ (b[:1])) << 0b001) ^ (a0[:1]) ^ (b[:1]))[:1])
            | ((((a0[:1]) ^ (b[:1])) | (b[:1]))[1:]))

        See `Derivative.is_possible` for more information.
        """
        # (I ^ SHL)(da ^ db ^ dc)[1:] <= SHL((da ^ dc) OR (db ^ dc))[1:]

        # one = core.Constant(1, self.input_diff[0].val.width)  # alt v1

        alpha, beta = [d.val for d in self.input_diff]
        gamma = output_diff.val
        # da, db, dc = alpha >> one, beta >> one, gamma >> one  # alt v1
        da, db, dc = alpha[:1], beta[:1], gamma[:1]  # ignore LSB

        one = core.Constant(1, da.width)

        # lhs = (da ^ db ^ dc) ^ ((da ^ db ^ dc) << one)  # alt v1
        # rhs = ((da ^ dc) | (db ^ dc)) << one
        lhs = ((da ^ db ^ dc) ^ ((da ^ db ^ dc) << one))[:1]
        rhs = (((da ^ dc) | (db ^ dc)) << one)[:1]

        def bitwise_implication(x, y):
            return (~x) | y

        # alt v1
        # n = lhs.width
        # return operation.BvComp(
        #     bitwise_implication(lhs[n - 2:1], rhs[n - 2:1]),  # ignore MSB, LSB
        #     ~ core.Constant(0, n - 2))
        return operation.BvComp(bitwise_implication(lhs, rhs), ~core.Constant(0, lhs.width))  # alt v1
Ejemplo n.º 6
0
    def is_possible(self, output_diff):
        """Return whether the given output `XorDiff` is possible.

            >>> from arxpy.bitvector.core import Constant, Variable
            >>> from arxpy.differential.difference import XorDiff
            >>> from arxpy.differential.derivative import XDA
            >>> alpha = XorDiff(Constant(0, 4)), XorDiff(Constant(0, 4))
            >>> f = XDA(alpha)
            >>> beta = XorDiff(Constant(0, 4))
            >>> f.is_possible(beta)
            0b1
            >>> a0, a1, b = Variable("a0", 4), Variable("a1", 4), Variable("b", 4)
            >>> alpha = XorDiff(a0), XorDiff(a1)
            >>> f = XDA(alpha)
            >>> beta = XorDiff(b)
            >>> result = f.is_possible(beta)
            >>> result
            0x0 == ((~(a0 << 0x1) ^ (a1 << 0x1)) & (~(a0 << 0x1) ^ (b << 0x1)) & ((a0 << 0x1) ^ b ^ a0 ^ a1))
            >>> result.xreplace({a0: Constant(0, 4), a1: Constant(0, 4), b: Constant(0, 4)})
            0b1
            >>> a1 = Constant(0, 4)
            >>> alpha = XorDiff(a0), XorDiff(a1)
            >>> f = XDA(alpha)
            >>> beta = XorDiff(b)
            >>> result = f.is_possible(beta)
            >>> result
            0x0 == (~(a0 << 0x1) & ~(b << 0x1) & (a0 ^ b))

        See `Derivative.is_possible` for more information.
        """
        a, b = [d.val for d in self.input_diff]
        c = output_diff.val

        one = core.Constant(1, a.width)

        def eq(x, y, z):
            if not isinstance(x, core.Constant):
                if isinstance(y, core.Constant):
                    return eq(y, x, z)
                elif isinstance(z, core.Constant):
                    return eq(z, x, y)

            return (~x ^ y) & (~x ^ z)

        def xor_shift(x, y, z):
            if not isinstance(x, core.Constant):
                if isinstance(y, core.Constant):
                    return xor_shift(y, x, z)
                elif isinstance(z, core.Constant):
                    return xor_shift(z, x, y)

            return (x ^ y ^ z ^ (x << one))

        return operation.BvComp(
            eq(a << one, b << one, c << one) & xor_shift(a, b, c),
            core.Constant(0, a.width))
Ejemplo n.º 7
0
    def is_possible(self, output_diff):
        """Return whether the given output `XorDiff` is possible.

            >>> from arxpy.bitvector.core import Constant, Variable
            >>> from arxpy.differential.difference import XorDiff
            >>> from arxpy.differential.derivative import XDCA
            >>> alpha, cte = XorDiff(Constant(0, 4)), Constant(1, 4)
            >>> f = XDCA(alpha, cte)
            >>> f.is_possible(XorDiff(Constant(0, 4)))
            0b1
            >>> u, v = Variable("u", 4), Variable("v", 4)
            >>> f = XDCA(XorDiff(u), cte)
            >>> result = f.is_possible(XorDiff(v))
            >>> result  # doctest: +NORMALIZE_WHITESPACE
            0x0 == (((u << 0x1) & (v << 0x1) & ~(0x9 ^ u ^ v) & (~((~(u << 0x1) & ~(v << 0x1)) << 0x1) |
                   (~((~(u << 0x1) & ~(v << 0x1)) << 0x1) ^ ((0x9 & ~(u << 0x1) & ~(v << 0x1)) + ~((~(u << 0x1) &
                   ~(v << 0x1)) << 0x1)) ^ (0x9 & ~(u << 0x1) & ~(v << 0x1))))) | (~(u << 0x1) & ~(v << 0x1) & (u ^ v)))
            >>> result.xreplace({u: Constant(0, 4), v: Constant(0, 4)})
            0b1

        See `Derivative.is_possible` for more information.
        """
        u = self.input_diff[0].val
        v = output_diff.val
        a = self.op.constant

        effective_width = self._effective_width
        index_one = self._index_first_one

        if effective_width == 0:
            return operation.BvComp(u, v)
        elif effective_width == 1:
            return operation.BvComp(u[index_one:], v[index_one:]) & \
                   operation.BvComp(~u[index_one] ^ (u[index_one+1] ^ v[index_one+1]), core.Constant(1, 1))
        else:
            if index_one == 0:
                c = core.Constant(1, 1)
            else:
                c = operation.BvComp(u[index_one-1:], v[index_one-1:])
            u = difference.XorDiff(u[:index_one])
            v = difference.XorDiff(v[:index_one])
            der = type(self)([u], a[:index_one])
            return c & der._is_possible(v)
Ejemplo n.º 8
0
    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
            >>> from arxpy.differential.derivative import XDCA
            >>> alpha, cte = XorDiff(Constant(0, 4)), Constant(1, 4)
            >>> f = XDCA(alpha, cte)
            >>> f.has_probability_one(XorDiff(Constant(0, 4)))
            0b1
            >>> u, v = Variable("u", 4), Variable("v", 4)
            >>> f = XDCA(XorDiff(u), cte)
            >>> result = f.has_probability_one(XorDiff(v))
            >>> result
            0xf == (~(u << 0x1) & ~(v << 0x1) & ~(u ^ v))
            >>> result.xreplace({u: Constant(0, 4), v: Constant(0, 4)})
            0b1

        """
        u = self.input_diff[0].val
        v = output_diff.val
        a = self.op.constant

        effective_width = self._effective_width
        index_one = self._index_first_one

        if effective_width == 0:
            return operation.BvComp(u, v)
        elif effective_width == 1:
            return operation.BvComp(u[index_one:], v[index_one:]) & \
                   operation.BvComp(~u[index_one] ^ (u[index_one+1] ^ v[index_one+1]), core.Constant(1, 1))
        else:
            if index_one == 0:
                c = core.Constant(1, 1)
            else:
                c = operation.BvComp(u[index_one-1:], v[index_one-1:])
            u = difference.XorDiff(u[:index_one])
            v = difference.XorDiff(v[:index_one])
            der = type(self)([u], a[:index_one])
            return c & der._has_probability_one(v)
Ejemplo n.º 9
0
    def _is_possible(self, output_diff, debug=False):
        u = self.input_diff[0].val
        v = output_diff.val
        a = self.op.constant
        n = a.width

        one = core.Constant(1, n)

        assert self._effective_width == n - 1

        def carry(x, y):
            # carry[i] = Maj(x[i-1], y[i-1], carry[i-1])
            return (x + y) ^ x ^ y

        # # S_0 well defined
        case11_ = (u << one) & (v << one)  # i-bit is True if S_i = 11*
        case00_ = (~(u << one)) & (~(v << one))  # i-bit is True if S_i = 00*
        case__1 = u ^ v
        a = a << one  # i-bit holds a[i-1]

        local_eq_a = ~(a ^ (a << one))  # i-bit is True if a[i-1] == a[i-2]
        case00_prev = case00_ << one  # i-bit is True if S_{i-1} = 00*

        c = carry(local_eq_a & case00_, (~case00_prev))

        case001 = case00_ & case__1
        bad_case11_ = case11_ & ~(case__1 ^ local_eq_a) & (c | ~case00_prev)

        bad_events = (case001 | bad_case11_)
        is_valid = operation.BvComp(bad_events, core.Constant(0, bad_events.width))

        if debug:
            print("\n\n ~~ ")
            print("u:           ", u.bin())
            print("v:           ", v.bin())
            print("a:           ", a.bin())
            print("case11_:     ", case11_.bin())
            print("case00_:     ", case00_.bin())
            print("case__1:     ", case__1.bin())
            print("case001:     ", case001.bin())
            print("case00_prev: ", case00_prev.bin())
            print("local_eq_a:  ", local_eq_a.bin())
            print("c:           ", c.bin())
            print("bad_case11_: ", bad_case11_.bin())
            print("bad_events:  ", bad_events.bin())
            print("is_valid    :", is_valid.bin())
            print("\n\n")

        return is_valid
Ejemplo n.º 10
0
    def _has_probability_one(self, output_diff):
        u = self.input_diff[0].val
        v = output_diff.val
        a = self.op.constant
        n = a.width

        assert self._effective_width == n - 1

        one = core.Constant(1, n)

        def all_ones(width):
            return ~ core.Constant(0, width)

        case00_ = (~(u << one)) & (~(v << one))  # i-bit is True if S_i = 00*
        case__1 = u ^ v
        case000 = case00_ & (~case__1)

        return operation.BvComp(case000, all_ones(n))
Ejemplo n.º 11
0
    def is_valid(self):
        """Return the bv expression for non-zero propagation probability.

            >>> from arxpy.bitvector.core import Constant
            >>> from arxpy.diffcrypt.difference import DiffVar
            >>> from arxpy.diffcrypt.differential import XDBvAdd
            >>> a, b, c = DiffVar("a", 8), DiffVar("b", 8), DiffVar("c", 8)
            >>> xda = XDBvAdd([a, b], c)
            >>> xda.is_valid()  # doctest: +ELLIPSIS
            0x00 == (((~(a << 0x01) ^ (b << 0x01)) & (~(a << 0x01) ...
            >>> zero = Constant(0, 8)
            >>> xda.is_valid().xreplace({a: zero, b: zero, c: zero})
            0b1

        """
        a, b = self.input_diff
        c = self.output_diff

        def eq(x, y, z):
            return (~x ^ y) & (~x ^ z)

        return operation.BvComp(
            eq(a << 1, b << 1, c << 1) & (a ^ b ^ c ^ (b << 1)),
            core.Constant(0, a.width))
Ejemplo n.º 12
0
    def _generate(self):
        """Generate the SMT problem."""
        self.assertions = []

        # Forbid zero input difference with XOR difference

        if self.ch.diff_type == difference.XorDiff:
            if self.parent_ch is not None and self.parent_ch.outer_ch == self.ch:
                inner_noutputs = len(self.parent_ch.inner_ch.output_diff)
                non_zero_input_diff = self.ch.input_diff[:-inner_noutputs]
            else:
                non_zero_input_diff = self.ch.input_diff
            non_zero_input_diff = functools.reduce(operation.Concat,
                                                   non_zero_input_diff)
            zero = core.Constant(0, non_zero_input_diff.width)
            self.assertions.append(
                operation.BvNot(operation.BvComp(non_zero_input_diff, zero)))

        # Assertions of the weights of the non-deterministic steps

        self.op_weights = []
        for var, propagation in self.ch.items():
            if isinstance(propagation, differential.Differential):
                self.assertions.append(propagation.is_valid())
                weight_value = propagation.weight()
                weight_var = core.Variable(propagation._weight_var_name(),
                                           weight_value.width)
                self.assertions.append(
                    operation.BvComp(weight_var, weight_value))
                self.op_weights.append(weight_var)
            else:
                self.assertions.append(operation.BvComp(var, propagation))

        # Characteristic weight assignment

        max_value = 0
        for ow in self.op_weights:
            max_value += (2**ow.width) - 1
        width = max(max_value.bit_length(), 1)  # for trivial characteristic
        ext_op_weights = []
        for ow in self.op_weights:
            ext_op_weights.append(operation.ZeroExtend(ow, width - ow.width))

        name_ch_weight = "w_{}_{}".format(
            ''.join([str(i) for i in self.ch.input_diff]),
            ''.join([str(i) for i in self.ch.output_diff]))
        ch_weight = core.Variable(name_ch_weight, width)

        self.assertions.append(operation.BvComp(ch_weight,
                                                sum(ext_op_weights)))

        # Condition between the weight and the target weight

        weight_function = self.ch.get_weight_function()
        target_weight = int(weight_function(self.target_weight))

        width = max(ch_weight.width, target_weight.bit_length())
        self.ch_weight = operation.ZeroExtend(ch_weight,
                                              width - ch_weight.width)

        if self.equality:
            self.assertions.append(
                operation.BvComp(self.ch_weight, target_weight))
        else:
            self.assertions.append(
                operation.BvUlt(self.ch_weight, target_weight))

        self.assertions = tuple(self.assertions)