Source code for cascada.primitives.picipher

"""Permutation :math:`\pi` of :math:`\pi`-Cipher (16-bit version)."""
from cascada.bitvector.core import Constant
from cascada.bitvector.operation import RotateLeft as ROL

from cascada.bitvector.ssa import RoundBasedFunction


N = 4  # small version with N = 2

PI_MU_CONST_0 = Constant(0xF0E8, 16)
PI_MU_CONST_1 = Constant(0xE4E2, 16)
PI_MU_CONST_2 = Constant(0xE1D8, 16)
PI_MU_CONST_3 = Constant(0xD4D2, 16)

PI_MU_ROT_CONST_0 = 1
PI_MU_ROT_CONST_1 = 4
PI_MU_ROT_CONST_2 = 9
PI_MU_ROT_CONST_3 = 11

PI_NY_CONST_0 = Constant(0xD1CC, 16)
PI_NY_CONST_1 = Constant(0xCAC9, 16)
PI_NY_CONST_2 = Constant(0xC6C5, 16)
PI_NY_CONST_3 = Constant(0xC3B8, 16)

PI_NY_ROT_CONST_0 = 2
PI_NY_ROT_CONST_1 = 5
PI_NY_ROT_CONST_2 = 7
PI_NY_ROT_CONST_3 = 13

PI_CONST = [
    [0xB4B2, 0xB1AC, 0xAAA9, 0xA6A5],
    [0xA39C, 0x9A99, 0x9695, 0x938E],
    [0x8D8B, 0x8778, 0x7472, 0x716C],
    [0x6A69, 0x6665, 0x635C, 0x5A59],
    [0x5655, 0x534E, 0x4D4B, 0x473C],
    [0x3A39, 0x3635, 0x332E, 0x2D2B],
    # 0x271E, 0x1D1B, 0x170F, 0xF0E8,
    # 0xE4E2, 0xE1D8, 0xD4D2, 0xD1CC,
]
PI_CONST = [[Constant(x_i, 16) for x_i in x] for x in PI_CONST]


[docs]class PiPermutation(RoundBasedFunction): """The permutation :math:`\pi` as a bit-vector function.""" num_rounds = 3 * 2 input_widths = [16 for _ in range(N * 4)] output_widths = [16 for _ in range(N * 4)]
[docs] @classmethod def set_num_rounds(cls, new_num_rounds): cls.num_rounds = new_num_rounds
@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
[docs] @classmethod def eval(cls, *args): # p0, p1, ... x = list(args) for i in range(cls.num_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) cls.add_round_outputs(*x) return x
@classmethod def test(cls): old_num_rounds = cls.num_rounds cls.set_num_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_num_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_num_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_num_rounds(old_num_rounds)