arxpy/primitives/lea.py
class LeaCipher(Cipher):
    key_schedule = LeaKeySchedule
    encryption = LeaEncryption
    rounds = 24

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.key_schedule.set_rounds(new_rounds)
        cls.encryption.set_rounds(new_rounds)

    @classmethod
    def test(cls):
        old_rounds = cls.rounds
        cls.set_rounds(24)
        key = [
            Constant(0x3c2d1e0f, 32),
            Constant(0x78695a4b, 32),
            Constant(0xb4a59687, 32),
            Constant(0xf0e1d2c3, 32)
        ]
        pt = [
            Constant(0x13121110, 32),
            Constant(0x17161514, 32),
            Constant(0x1b1a1918, 32),
            Constant(0x1f1e1d1c, 32)
        ]
        ct = [
            Constant(0x354ec89f, 32),
            Constant(0x18c6c628, 32),
            Constant(0xa7c73255, 32),
            Constant(0xfd8b6404, 32)
        ]
        assert cls(pt, key) == tuple(ct)
        cls.set_rounds(old_rounds)
arxpy/smt/search.py
def _get_smart_print(filename=None):
    def smart_print(*msg, **kwargs):
        if filename is not None:
            with open(filename, "a") as fh:
                print(*msg, file=fh, flush=True, **kwargs)
        else:
            print(*msg, flush=True, **kwargs)
    return smart_print
arxpy/primitives/simeck.py
class SimeckRF(SimonRF):
class XDSimeckRF(XDSimonRF):
class SimeckInstance(enum.Enum):
    class SimeckKeySchedule(KeySchedule):
    class SimeckEncryption(Encryption):
    class SimeckCipher(Cipher):
    def xor_derivative(cls, input_diff):
def get_Simeck_instance(simeck_instance):
    def rf(x, y, k):
        def set_rounds(cls, new_rounds):
        def eval(cls, *master_key):
        def set_rounds(cls, new_rounds):
        def eval(cls, x, y):
        def set_rounds(cls, new_rounds):
        def test(cls):
                C = (2 ** n - 4) ^ int(z[i % len(z)])
arxpy/primitives/picipher.py
class PiPermutation(BvFunction):
    """Encryption function."""

    rounds = 3 * 2
    input_widths = [16 for _ in range(N * 4)]
    output_widths = [16 for _ in range(N * 4)]

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds

    # noinspection PyTypeChecker
    @classmethod
    def mul(cls, x, y):
        assert len(x) == 4
        assert len(y) == 4

        # mu
        T = [None for _ in range(12)]
        # order?
        aux01 = x[0] + x[1]
        aux23 = x[2] + x[3]
        T[0] = ROL(aux01 + (PI_MU_CONST_0 + x[2]), PI_MU_ROT_CONST_0)
        T[1] = ROL(aux01 + (PI_MU_CONST_1 + x[3]), PI_MU_ROT_CONST_1)
        T[2] = ROL((x[0] + PI_MU_CONST_2) + aux23, PI_MU_ROT_CONST_2)
        T[3] = ROL((x[1] + PI_MU_CONST_3) + aux23, PI_MU_ROT_CONST_3)
        T[4] = T[0] ^ T[1] ^ T[3]
        T[5] = T[0] ^ T[1] ^ T[2]
        T[6] = T[1] ^ T[2] ^ T[3]
        T[7] = T[0] ^ T[2] ^ T[3]

        # ny
        aux01 = y[0] + y[1]
        aux23 = y[2] + y[3]
        T[0] = ROL((PI_NY_CONST_0 + y[0]) + aux23, PI_NY_ROT_CONST_0)
        T[1] = ROL((PI_NY_CONST_1 + y[1]) + aux23, PI_NY_ROT_CONST_1)
        T[2] = ROL(aux01 + (PI_NY_CONST_2 + y[2]), PI_NY_ROT_CONST_2)
        T[3] = ROL(aux01 + (PI_NY_CONST_3 + y[3]), PI_NY_ROT_CONST_3)
        T[8] = T[1] ^ T[2] ^ T[3]
        T[9] = T[0] ^ T[2] ^ T[3]
        T[10] = T[0] ^ T[1] ^ T[3]
        T[11] = T[0] ^ T[1] ^ T[2]

        # sigma
        z3 = T[4] + T[8]
        z0 = T[5] + T[9]
        z1 = T[6] + T[10]
        z2 = T[7] + T[11]

        return z0, z1, z2, z3

    @classmethod
    def e1(cls, c, j):
        j[:4] = cls.mul(c, j[:4])
        if N > 1:
            for i in range(1, N):
                j[4*i:4*i+4] = cls.mul(j[4*i-4:4*i], j[4*i:4*i+4])
        return j

    @classmethod
    def e2(cls, c, i):
        i[-4:] = cls.mul(i[-4:], c)
        if N > 1:
            for j in range(N-2, -1, -1):
                i[4*j:4*j+4] = cls.mul(i[4*j:4*j+4], i[4*j+4:4*j+8])
                # i[-4*j-4:-4*j]= cls.mul(i[-4*j-4:-4*j], i[-4*j:-4*j+4])
        return i

    @classmethod
    def eval(cls, *args):  # p0, p1, ...
        x = list(args)
        for i in range(cls.rounds):
            if i % 2 == 0:
                i = i // 2  # due to duplicating the number of rounds
                x = cls.e1(PI_CONST[2*i], x)
            else:
                i = (i - 1) // 2
                x = cls.e2(PI_CONST[2*i + 1], x)
        return x

    @classmethod
    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)
arxpy/primitives/shacal1.py
class Shacal1Cipher(Cipher):
    key_schedule = Shacal1KeySchedule
    encryption = Shacal1Encryption
    rounds = 80

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.key_schedule.set_rounds(new_rounds)
        cls.encryption.set_rounds(new_rounds)

    # noinspection SpellCheckingInspection
    @classmethod
    def test(cls):
        # https://www.cosic.esat.kuleuven.be/nessie/testvectors/
        # key = 80000000000000000000000000000000
        # 00000000000000000000000000000000
        # 00000000000000000000000000000000
        # 00000000000000000000000000000000
        # plain = 0000000000000000000000000000000000000000
        # cipher = 0FFD8D43 B4E33C7C 53461BD1 0F27A546 1050D90D

        old_rounds = cls.rounds
        cls.set_rounds(80)

        global REFERENCE_VERSION

        for ref_v in [True, False]:
            REFERENCE_VERSION = ref_v

            key = [Constant(0x80000000, 32)]
            key.extend([Constant(0, 32) for _ in range(1, N)])
            pt = [Constant(0, 32) for _ in range(5)]
            ct = [
                Constant(0x0FFD8D43, 32),
                Constant(0xB4E33C7C, 32),
                Constant(0x53461BD1, 32),
                Constant(0x0F27A546, 32),
                Constant(0x01050D90D, 32)
            ]
            assert cls(pt, key) == tuple(ct)

            if N == 16:
                # key =    00010203 04050607 08090A0B 0C0D0E0F
                #          10111213 14151617 18191A1B 1C1D1E1F
                #          20212223 24252627 28292A2B 2C2D2E2F
                #          30313233 34353637 38393A3B 3C3D3E3F
                # plain =  00112233 44556677 8899AABB CCDDEEFF 10213243
                # cipher = 213A4657 59CE3572 CA2A86D5 A680E948 45BEAA6B

                key = [
                    Constant(0x00010203, 32), Constant(0x04050607, 32), Constant(0x08090A0B, 32), Constant(0x0C0D0E0F, 32),
                    Constant(0x10111213, 32), Constant(0x14151617, 32), Constant(0x18191A1B, 32), Constant(0x1C1D1E1F, 32),
                    Constant(0x20212223, 32), Constant(0x24252627, 32), Constant(0x28292A2B, 32), Constant(0x2C2D2E2F, 32),
                    Constant(0x30313233, 32), Constant(0x34353637, 32), Constant(0x38393A3B, 32), Constant(0x3C3D3E3F, 32),
                ]
                pt = [
                    Constant(0x00112233, 32),
                    Constant(0x44556677, 32),
                    Constant(0x8899AABB, 32),
                    Constant(0xCCDDEEFF, 32),
                    Constant(0x10213243, 32),
                ]
                ct = [
                    Constant(0x213A4657, 32),
                    Constant(0x59CE3572, 32),
                    Constant(0xCA2A86D5, 32),
                    Constant(0xA680E948, 32),
                    Constant(0x45BEAA6B, 32)
                ]
                assert cls(pt, key) == tuple(ct)

        cls.set_rounds(old_rounds)
arxpy/primitives/cham.py
class ChamInstance(enum.Enum):
    class ChamKeySchedule(KeySchedule):
    class ChamEncryption(Encryption):
    class ChamCipher(Cipher):
def get_Cham_instance(cham_instance):
        def set_rounds(cls, new_rounds):
        def eval(cls, *master_key):
        def set_rounds(cls, new_rounds):
        def eval(cls, p0, p1, p2, p3):
        def set_rounds(cls, new_rounds):
        def test(cls):
arxpy/primitives/chaskey.py
class ChaskeyPi(BvFunction):
    rounds = 8
    input_widths = [32, 32, 32, 32]
    output_widths = [32, 32, 32, 32]

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds

    @classmethod
    def eval(cls, v0, v1, v2, v3):
        for i in range(cls.rounds):
            v0 += v1
            v1 = RotateLeft(v1, 5)
            v1 ^= v0
            v0 = RotateLeft(v0, 16)

            v2 += v3
            v3 = RotateLeft(v3, 8)
            v3 ^= v2

            v0 += v3
            v3 = RotateLeft(v3, 13)
            v3 ^= v0

            v2 += v1
            v1 = RotateLeft(v1, 7)
            v1 ^= v2
            v2 = RotateLeft(v2, 16)

        return v0, v1, v2, v3

    @classmethod
    def test(cls):
        old_rounds = cls.rounds
        cls.set_rounds(8)

        pt = [
            Constant(0x00000000, 32)
        ] * 4
        ct = [
            Constant(0x00000000, 32)
        ] * 4
        assert cls(*pt) == tuple(ct)

        cls.set_rounds(old_rounds)
arxpy/primitives/feal.py
class FealCipher(Cipher):
    key_schedule = FealXKeySchedule if FEALX else FealKeySchedule
    encryption = FealEncryption
    rounds = 8

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.encryption.set_rounds(new_rounds)
        cls.key_schedule.set_rounds(new_rounds)

    @classmethod
    def test(cls):
        """Test FEAL with official test vectors."""
        # Extracted from "The FEAL Cipher Family"
        old_rounds = cls.rounds
        cls.set_rounds(8)

        if not FEALX:
            plaintext = (0, 0, 0, 0, 0, 0, 0, 0)
            key = (0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF)
            ciphertext = (0xCE, 0xEF, 0x2C, 0x86, 0xF2, 0x49, 0x07, 0x52)
            assert cls(plaintext, key) == ciphertext
        else:
            plaintext = (0, 0, 0, 0, 0, 0, 0, 0)
            key = (0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF)
            key += key  # double the size
            ciphertext = (0x92, 0xBE, 0xB6, 0x5D, 0x0E, 0x93, 0x82, 0xFB)
            assert cls(plaintext, key) == ciphertext

        cls.set_rounds(old_rounds)
arxpy/bitvector/operation.py
class Concat(Operation):
    """Concatenation operation.

    Given the bit-vectors :math:`(x_{n-1}, \dots, x_0)` and
    :math:`(y_{m-1}, \dots, y_0)`, ``Concat(x, y)`` returns the bit-vector
    :math:`(x_{n-1}, \dots, x_0, y_{m-1}, \dots, y_0)`.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import Concat
        >>> Concat(Constant(0x12, 8), Constant(0x345, 12))
        0x12345
        >>> Concat(Variable("x", 8), Variable("y", 8))
        x :: y

    """

    arity = [2, 0]
    is_symmetric = False
    infix_symbol = "::"

    @classmethod
    def output_width(cls, x, y):
        return x.width + y.width

    @classmethod
    def eval(cls, x, y):
        def doit(x, y):
            """Concatenation when both operands are int."""
            return int(x.bin() + y.bin()[2:], 2)

        if isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return core.Constant(doit(x, y), cls.output_width(x, y))
        elif isinstance(x, core.Constant) and isinstance(y, Concat) and \
                isinstance(y.args[0], core.Constant):
            return Concat(Concat(x, y.args[0]), y.args[1])
        elif isinstance(y, core.Constant) and isinstance(x, Concat) and \
                isinstance(x.args[1], core.Constant):
            return Concat(x.args[0], Concat(x.args[1], y))
        elif isinstance(x, Extract) and isinstance(y, Extract):
            # x[5:4] concat x[3:2] = x[5:2]
            if x.args[0] == y.args[0] and x.args[2] == y.args[1] + 1:
                return Extract(x.args[0], x.args[1], y.args[2])
arxpy/differential/difference.py
class XorDiff(Difference):
    """Represent XOR differences.

    The XOR difference of two `Term` is given by the XOR
    of the terms. In other words, the *difference operation*
    of `XorDiff` is the `BvXor` (see `Difference`).

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.differential.difference import XorDiff
        >>> x, y = Constant(0b000, 3), Constant(0b000, 3)
        >>> alpha = XorDiff.from_pair(x, y)
        >>> alpha
        XorDiff(0b000)
        >>> alpha.get_pair_element(x)
        0b000
        >>> x, y = Constant(0b010, 3), Constant(0b101, 3)
        >>> alpha = XorDiff.from_pair(x, y)
        >>> alpha
        XorDiff(0b111)
        >>> alpha.get_pair_element(x)
        0b101
        >>> k = Variable("k", 8)
        >>> alpha = XorDiff.from_pair(k, k)
        >>> alpha
        XorDiff(0x00)
        >>> alpha.get_pair_element(k)
        k
    """

    diff_op = operation.BvXor
    inv_diff_op = operation.BvXor

    @classmethod
    def derivative(cls, op, input_diff):
        """Return the derivative of ``op`` at the point ``input_diff``.

        See `Difference.derivative` for more information.

            >>> from arxpy.bitvector.core import Variable, Constant
            >>> from arxpy.bitvector.operation import BvAdd, BvXor, RotateLeft, BvSub
            >>> from arxpy.bitvector.extraop import make_partial_operation
            >>> from arxpy.differential.difference import XorDiff
            >>> d1, d2 = XorDiff(Variable("d1", 8)), XorDiff(Variable("d2", 8))
            >>> XorDiff.derivative(BvXor, [d1, d2])
            XorDiff(d1 ^ d2)
            >>> Xor1 = make_partial_operation(BvXor, tuple([None, Constant(1, 8)]))
            >>> XorDiff.derivative(Xor1, d1)
            XorDiff(d1)
            >>> Rotate1 = make_partial_operation(RotateLeft, tuple([None, 1]))
            >>> XorDiff.derivative(Rotate1, d1)
            XorDiff(d1 <<< 1)
            >>> XorDiff.derivative(BvAdd, [d1, d2])
            XDA(XorDiff(d1), XorDiff(d2))
            >>> XorDiff.derivative(BvSub, [d1, d2])
            XDS(XorDiff(d1), XorDiff(d2))
            >>> CteAdd1 = make_partial_operation(BvAdd, tuple([None, Constant(1, 8)]))
            >>> XorDiff.derivative(CteAdd1, d1)
            XDCA_0x01(XorDiff(d1))

        """
        input_diff = _tuplify(input_diff)
        assert len(input_diff) == sum(op.arity)

        msg = "invalid arguments: op={}, input_diff={}".format(
            op.__name__,
            [d.vrepr() if isinstance(d, core.Term) else d for d in input_diff])

        if not all(isinstance(diff, cls) for diff in input_diff):
            raise ValueError(msg)

        if op == operation.BvNot:
            return input_diff[0]

        if op == operation.BvXor:
            return cls(op(*[d.val for d in input_diff]))

        if op == operation.Concat:
            return cls(op(*[d.val for d in input_diff]))

        if op == operation.BvAdd:
            from arxpy.differential import derivative
            return derivative.XDA(input_diff)

        if op == operation.BvSub:
            from arxpy.differential import derivative
            return derivative.XDS(input_diff)

        if issubclass(op, extraop.PartialOperation):
            if op.base_op == operation.BvXor:
                assert len(input_diff) == 1
                d1 = input_diff[0]
                val = op.fixed_args[0] if op.fixed_args[0] is not None else op.fixed_args[1]
                d2 = cls.from_pair(val, val)
                input_diff = [d1, d2]
                return cls(op.base_op(*[d.val for d in input_diff]))

            if op.base_op == operation.BvAnd:
                assert len(input_diff) == 1
                d1 = input_diff[0]
                val = op.fixed_args[0] if op.fixed_args[0] is not None else op.fixed_args[1]
                if isinstance(val, core.Constant):
                    return cls(op.base_op(d1.val, val))

            if op.base_op in [operation.RotateLeft, operation.RotateRight]:
                if op.fixed_args[0] is None and op.fixed_args[1] is not None:
                    assert len(input_diff) == 1
                    d = input_diff[0]
                    return cls(op.base_op(d.val, op.fixed_args[1]))
                else:
                    raise ValueError(msg)

            if op.base_op in [operation.BvShl, operation.BvLshr]:
                if op.fixed_args[0] is None and op.fixed_args[1] is not None:
                    assert len(input_diff) == 1
                    d = input_diff[0]
                    return cls(op.base_op(d.val, op.fixed_args[1]))
                else:
                    raise ValueError(msg)

            if op.base_op == operation.Extract:
                if op.fixed_args[0] is None and op.fixed_args[1] is not None and op.fixed_args[2] is not None:
                    assert len(input_diff) == 1
                    d = input_diff[0]
                    return cls(op.base_op(d.val, op.fixed_args[1], op.fixed_args[2]))
                else:
                    raise ValueError(msg)

            if op.base_op == operation.Concat:
                assert len(input_diff) == 1
                d1 = input_diff[0]
                if op.fixed_args[0] is not None:
                    val = op.fixed_args[0]
                    input_diff = [cls.from_pair(val, val), d1]
                else:
                    val = op.fixed_args[1]
                    input_diff = [d1, cls.from_pair(val, val)]
                return cls(op.base_op(*[d.val for d in input_diff]))

            if op.base_op == operation.BvAdd:
                assert len(input_diff) == 1
                d = input_diff[0]
                cte = op.fixed_args[0] if op.fixed_args[0] is not None else op.fixed_args[1]
                from arxpy.differential import derivative
                return derivative.XDCA(d, cte)
            else:
                raise ValueError(msg)

        if hasattr(op, "xor_derivative"):
            return op.xor_derivative(input_diff)

        raise ValueError(msg)
arxpy/primitives/xtea.py
class XteaCipher(Cipher):
    key_schedule = XteaKeySchedule
    encryption = XteaEncryption
    rounds = 64

    @classmethod
    def set_rounds(cls, new_rounds):
        # assert new_rounds >= 2
        cls.rounds = new_rounds
        cls.encryption.set_rounds(new_rounds)
        cls.key_schedule.set_rounds(new_rounds)

    @classmethod
    def test(cls):
        """Test Xtea with official test vectors."""
        # https://go.googlesource.com/crypto/+/master/xtea/xtea_test.go
        plaintext = (0x41424344, 0x45464748)
        key = (0, 0, 0, 0)
        assert cls(plaintext, key) == (0xa0390589, 0xf8b8efa5)

        plaintext = (0x41424344, 0x45464748)
        key = (0x00010203, 0x04050607, 0x08090A0B, 0x0C0D0E0F)
        assert cls(plaintext, key) == (0x497df3d0, 0x72612cb5)
arxpy/bitvector/operation.py
class BvComp(Operation):
    """Equality operator.

    Provides Automatic Constant Conversion. See `Operation` for more
    information.

        >>> from arxpy.bitvector.core import Constant, Variable
        >>> from arxpy.bitvector.operation import BvComp
        >>> BvComp(Constant(1, 8), Constant(2, 8))
        0b0
        >>> BvComp(Constant(1, 8), 2)
        0b0
        >>> BvComp(Constant(1, 8), Variable("y", 8))
        0x01 == y

    The operator == is used for exact structural equality testing and
    it returns either True or False. On the other hand, BvComp
    performs symbolic equality testing and it leaves the relation unevaluated
    if it cannot prove the objects are equal (or unequal).

        >>> Variable("x", 8) == Variable("y", 8)
        False
        >>> BvComp(Variable("x", 8), Variable("y", 8))  # symbolic equality
        x == y

    """

    arity = [2, 0]
    is_symmetric = True
    is_simple = True
    infix_symbol = "=="

    @classmethod
    def condition(cls, x, y):
        return x.width == y.width

    @classmethod
    def output_width(cls, x, y):
        return 1

    @classmethod
    def eval(cls, x, y):
        zero = core.Constant(0, 1)
        one = core.Constant(1, 1)

        if x is y:
            return one
        elif isinstance(x, core.Constant) and isinstance(y, core.Constant):
            return one if x.val == y.val else zero
arxpy/primitives/speck.py
class SpeckInstance(enum.Enum):
    class SpeckKeySchedule(KeySchedule):
    class SpeckEncryption(Encryption):
    class SpeckCipher(Cipher):
def get_Speck_instance(speck_instance):
    def rf(x, y, k):
        def set_rounds(cls, new_rounds):
        def eval(cls, *master_key):
        def set_rounds(cls, new_rounds):
        def eval(cls, x, y):
        def set_rounds(cls, new_rounds):
        def test(cls):
arxpy/primitives/simon.py
class SimonRF(Operation):
class XDSimonRF(Derivative):
class SimonInstance(enum.Enum):
    class SimonKeySchedule(KeySchedule):
    class SimonEncryption(Encryption):
    class SimonCipher(Cipher):
    def output_width(cls, x):
    def eval(cls, x):
    def xor_derivative(cls, input_diff):
    def is_possible(self, output_diff):
        def is_even(x):
    def has_probability_one(self, output_diff):
    def weight(self, output_diff):
    def max_weight(self):
    def exact_weight(self, output_diff):
    def num_frac_bits(self):
    def error(self):
def get_Simon_instance(simon_instance):
        def set_rounds(cls, new_rounds):
        def eval(cls, *master_key):
        def set_rounds(cls, new_rounds):
        def eval(cls, x, y):
        def set_rounds(cls, new_rounds):
        def test(cls):
arxpy/smt/tests/test_search.py
def test_search_ch_skch(bvf_cipher, diff_type, initial_weight, solver_name, rounds, der_mode, search_mode, check,
                        verbose_level, filename):
    smart_print = _get_smart_print(filename)

    if rounds is not None:
        bvf_cipher.set_rounds(rounds)

    if issubclass(bvf_cipher, BvFunction):
        num_inputs = len(bvf_cipher.input_widths)
        input_diff_names = ["dp" + str(i) for i in range(num_inputs)]
        ch = BvCharacteristic(bvf_cipher, diff_type, input_diff_names)
    else:
        assert issubclass(bvf_cipher, Cipher)
        ch = SingleKeyCh(bvf_cipher, diff_type)

    if verbose_level >= 1:
        str_rounds = "" if rounds is None else "{} rounds".format(rounds)
        smart_print(str_rounds, bvf_cipher.__name__, diff_type.__name__, type(ch).__name__)
        if verbose_level >= 2:
            smart_print("Characteristic:")
            smart_print(ch)

    if issubclass(bvf_cipher, BvFunction):
        problem = SearchCh(ch, der_mode=der_mode)
    else:
        problem = SearchSkCh(ch, der_mode=der_mode)

    if verbose_level >= 1:
        smart_print(type(problem).__name__, der_mode, search_mode, solver_name,
                    "size:", problem.formula_size())
        if verbose_level >= 2:
            smart_print(problem.hrepr(verbose_level >= 3))

    sol = problem.solve(initial_weight,
                        solver_name=solver_name,
                        search_mode=search_mode,
                        check=check,
                        verbose_level=verbose_level,
                        filename=filename)

    if verbose_level >= 1:
        if sol is None:
            smart_print("\nUnsatisfiable")
        else:
            smart_print("\nSolution:")
            smart_print(sol)
            if verbose_level >= 2:
                if isinstance(sol, collections.abc.Sequence):
                    # for search_mode TopDifferentials
                    smart_print(sol[0].vrepr())
                else:
                    smart_print(sol.vrepr())
        smart_print()

    return sol
arxpy/primitives/tea.py
class TeaCipher(Cipher):
    key_schedule = TeaKeySchedule
    encryption = TeaEncryption
    rounds = 32

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.encryption.set_rounds(new_rounds)

    @classmethod
    def test(cls):
        """Test tea with official test vectors."""
        cls.set_rounds(32)

        plaintext = (0, 0)
        key = (0, 0, 0, 0)
        assert cls(plaintext, key) == (0x41EA3A0A, 0x94BAA940)

        plaintext = (0x01020304, 0x05060708)
        key = (0x00112233, 0x44556677, 0x8899AABB, 0xCCDDEEFF)
        assert cls(plaintext, key) == (0xDEB1C0A2, 0x7E745DB3)
arxpy/primitives/shacal2.py
class Shacal2Cipher(Cipher):
    key_schedule = Shacal2KeySchedule
    encryption = Shacal2Encryption
    rounds = 64

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.key_schedule.set_rounds(new_rounds)
        cls.encryption.set_rounds(new_rounds)

    # noinspection SpellCheckingInspection
    @classmethod
    def test(cls):
        # https://www.cosic.esat.kuleuven.be/nessie/testvectors/
        # key =
        # 80000000000000000000000000000000
        # 00000000000000000000000000000000
        # 00000000000000000000000000000000
        # 00000000000000000000000000000000
        # plain =
        # 00000000000000000000000000000000
        # 00000000000000000000000000000000
        # cipher =
        # 361AB632 2FA9E7A7 BB23818D 839E01BD
        # DAFDF473 05426EDD 297AEDB9 F6202BAE

        old_rounds = cls.rounds
        cls.set_rounds(64)

        global REFERENCE_VERSION

        for ref_v in [True, False]:
            REFERENCE_VERSION = ref_v

            key = [Constant(0x80000000, 32)]
            key.extend([Constant(0, 32) for _ in range(1, N)])
            pt = [Constant(0, 32) for _ in range(8)]
            ct = [
                Constant(0x361AB632, 32),
                Constant(0x2FA9E7A7, 32),
                Constant(0xBB23818D, 32),
                Constant(0x839E01BD, 32),
                Constant(0xDAFDF473, 32),
                Constant(0x05426EDD, 32),
                Constant(0x297AEDB9, 32),
                Constant(0xF6202BAE, 32),
            ]
            assert cls(pt, key) == tuple(ct)

            if N == 16:
                # key =
                # 00010203 04050607 08090A0B 0C0D0E0F
                # 10111213 14151617 18191A1B 1C1D1E1F
                # 20212223 24252627 28292A2B 2C2D2E2F
                # 30313233 34353637 38393A3B 3C3D3E3F
                # plain =
                # 00112233 44556677 8899AABB CCDDEEFF
                # 10213243 54657687 98A9BACB DCEDFE0F
                # cipher =
                # 1A6B234A 20EAD408 C2D83B35 8AC81D7A
                # 648ED25D 01B7C9EC 9CC4C9E2 5CFA813E

                key = [
                    Constant(0x00010203, 32), Constant(0x04050607, 32), Constant(0x08090A0B, 32), Constant(0x0C0D0E0F, 32),
                    Constant(0x10111213, 32), Constant(0x14151617, 32), Constant(0x18191A1B, 32), Constant(0x1C1D1E1F, 32),
                    Constant(0x20212223, 32), Constant(0x24252627, 32), Constant(0x28292A2B, 32), Constant(0x2C2D2E2F, 32),
                    Constant(0x30313233, 32), Constant(0x34353637, 32), Constant(0x38393A3B, 32), Constant(0x3C3D3E3F, 32),
                ]
                pt = [
                    Constant(0x00112233, 32),
                    Constant(0x44556677, 32),
                    Constant(0x8899AABB, 32),
                    Constant(0xCCDDEEFF, 32),
                    Constant(0x10213243, 32),
                    Constant(0x54657687, 32),
                    Constant(0x98A9BACB, 32),
                    Constant(0xDCEDFE0F, 32),
                ]
                ct = [
                    Constant(0x1A6B234A, 32),
                    Constant(0x20EAD408, 32),
                    Constant(0xC2D83B35, 32),
                    Constant(0x8AC81D7A, 32),
                    Constant(0x648ED25D, 32),
                    Constant(0x01B7C9EC, 32),
                    Constant(0x9CC4C9E2, 32),
                    Constant(0x5CFA813E, 32),
                ]
                assert cls(pt, key) == tuple(ct)

        cls.set_rounds(old_rounds)
arxpy/primitives/hight.py
class HightCipher(Cipher):
    key_schedule = HightKeySchedule
    encryption = HightEncryption
    rounds = 32

    @classmethod
    def set_rounds(cls, new_rounds):
        cls.rounds = new_rounds
        cls.key_schedule.set_rounds(new_rounds)
        cls.encryption.set_rounds(new_rounds)

    @classmethod
    def test(cls):
        """Test Hight with official test vectors."""
        # https://tools.ietf.org/html/draft-kisa-hight-00#section-5
        old_rounds = cls.rounds
        cls.set_rounds(32)

        plaintext = (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00)
        key = (0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
               0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff)
        assert cls(plaintext, key) == (0x00, 0xf4, 0x18, 0xae, 0xd9, 0x4f, 0x03, 0xf2)

        plaintext = (0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77)
        key = (0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x00)
        assert cls(plaintext, key) == (0x23, 0xce, 0x9f, 0x72, 0xe5, 0x43, 0xe6, 0xd8)

        cls.set_rounds(old_rounds)
arxpy/smt/search.py
class DerMode(enum.Enum):
    """Represent the different constraints available for the derivative weights of a characteristic.

    Attributes:
        Default: no additional constraint is added
        ProbabilityOne: fix all derivative weights to zero
        Valid: only the validity constraint is added (weight is ignored)
        XDCA_Approx: for the weights of `XDCA`, the ``precision`` is set to ``0``

    """
    Default = enum.auto()
    ProbabilityOne = enum.auto()
    Valid = enum.auto()
    XDCA_Approx = enum.auto()
arxpy/smt/search.py
class ChSearchMode(enum.Enum):
    """Represent the different options for searching `BvCharacteristic`.

    A characteristic is said to be *valid* if its weight
    :math:`w = - \log_2(p)`, where *p* is the probability of the characteristic,
    is lower than the bit-size of the input difference.

    A derivative weight is said to be *non-exact* if its weight
    does not match its exact weight.

    Attributes:
        FirstCh: returns the 1st characteristic found by iteratively searching with increasing weight.
            The characteristic found is returned as a `ChFound` object, but it may not have
            optimal theoretical weight if it contains a non-exact derivative weight
            or it may be empirically invalid. If no characteristic is found,
            ``None`` is returned.
        FirstChValid: similar to `FirstCh`, but if the characteristic found is
            empirically invalid (after computing its empirical weight), the search continues.
        Optimal: similar to `FirstCh`, but several characteristics are found to ensure
            that the one returned has the optimal theoretical weight.
            (only differs from `FirstCh` if there is a non-exact derivative weight).
        OptimalDifferential: similar to `Optimal`, but once a characteristic is found,
            only characteristics with different input/output differences are searched.
        AllOptimal: similar to `Optimal`, but the search never stops.
        TopDifferentials: searches for `MAX_CH_FOUND` characteristics
            (as in `FirstCh`) and return the differentials obtained
            by gathering those characteristics.
        AllValid: a non-stop search of valid characteristics with no
            additional constraints.

    """
    FirstCh = enum.auto()
    FirstChValid = enum.auto()
    Optimal = enum.auto()
    OptimalDifferential = enum.auto()
    AllOptimal = enum.auto()
    AllValid = enum.auto()
    TopDifferentials = enum.auto()


Question: import math
import collections
import functools
import os
import unittest
from datetime import datetime
from arxpy.bitvector.operation import Concat, BvComp
from arxpy.differential.difference import XorDiff
from arxpy.smt.search import DerMode, ChSearchMode, _get_smart_print
from arxpy.smt.tests.test_search import test_search_ch_skch
from arxpy.primitives.chaskey import ChaskeyPi
from arxpy.primitives.picipher import PiPermutation
from arxpy.primitives import speck
from arxpy.primitives import simon
from arxpy.primitives import simeck
from arxpy.primitives.hight import HightCipher
from arxpy.primitives.lea import LeaCipher
from arxpy.primitives.shacal1 import Shacal1Cipher
from arxpy.primitives.shacal2 import Shacal2Cipher
from arxpy.primitives.feal import FealCipher
from arxpy.primitives.tea import TeaCipher
from arxpy.primitives.xtea import XteaCipher
from arxpy.primitives import cham
"""Tests for cryptographic primitives."""





# from arxpy.primitives.threefish import ThreefishCipher


OUTPUT_FILE = False
VERBOSE_LEVEL = 0
# 0: quiet
# 1: basic info
# 2: + ssa, model
# 3: + hrepr
# 4: + full hrepr
CHECK = True


# *_rounds should have trivial characteristics but *_rounds + 1 not (with ProbabilityOne)
BvFunction = collections.namedtuple('BVF', ['function', 'rounds'])


Speck32 = speck.get_Speck_instance(speck.SpeckInstance.speck_32_64)
Simon32 = simon.get_Simon_instance(simon.SimonInstance.simon_32_64)
Simeck32 = simeck.get_Simeck_instance(simeck.SimeckInstance.simeck_32_64)

Answer: Cham64 = cham.get_Cham_instance(cham.ChamInstance.cham_64_128)