"""SKINNY-64-64.
TK1 version, where the tweakey size is equal to the block size.
This implementation is based on `skinny128`, but a `WDTModel`
is used to model the XOR and linear models of the S-box.
"""
from decimal import Decimal
from math import inf
from cascada.bitvector.core import Constant
from cascada.bitvector.secondaryop import LutOperation
from cascada.abstractproperty.opmodel import log2_decimal
from cascada.differential.difference import XorDiff
from cascada.differential.opmodel import get_wdt_model as get_differential_wdt_model
from cascada.linear.opmodel import get_wdt_model as get_linear_wdt_model
from cascada.primitives import skinny128
default_num_rounds = 32
[docs]class SKINNYTweakeySchedule(skinny128.SKINNYTweakeySchedule):
"""Key schedule of SKINNY-64-64."""
num_rounds = default_num_rounds
input_widths = [4 for _ in range(4*4)]
output_widths = [4 for _ in range(4*2 * default_num_rounds)] # 2 rows per round
[docs] @classmethod
def set_num_rounds(cls, new_num_rounds):
cls.num_rounds = new_num_rounds
cls.output_widths = [4 for _ in range(4*2 * new_num_rounds)]
[docs]class SboxLut(LutOperation):
"""The 4-bit S-box of SKINNY-64-64."""
lut = [Constant(x, 4) for x in [12, 6, 9, 0, 1, 10, 2, 11, 3, 8, 5, 13, 4, 14, 7, 15]]
ddt = [
(16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0),
(0, 4, 0, 4, 0, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2),
(0, 0, 4, 0, 0, 0, 2, 2, 0, 0, 0, 4, 2, 2, 0, 0),
(0, 0, 4, 0, 0, 0, 2, 2, 0, 0, 4, 0, 2, 2, 0, 0),
(0, 2, 0, 2, 2, 0, 0, 2, 2, 0, 2, 0, 0, 2, 2, 0),
(0, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 2, 2, 0, 0, 2),
(0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2),
(0, 0, 0, 0, 4, 4, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2),
(0, 0, 0, 0, 0, 4, 4, 0, 2, 2, 2, 2, 0, 0, 0, 0),
(0, 4, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2),
(0, 0, 4, 0, 0, 0, 2, 2, 4, 0, 0, 0, 0, 0, 2, 2),
(0, 0, 4, 0, 0, 0, 2, 2, 0, 4, 0, 0, 0, 0, 2, 2),
(0, 2, 0, 2, 2, 0, 0, 2, 0, 2, 0, 2, 0, 2, 2, 0),
(0, 2, 0, 2, 2, 0, 0, 2, 2, 0, 2, 0, 2, 0, 0, 2)
]
lat = [
(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, -1 / 4, 1 / 4, 0, 0, -1 / 4, -1 / 4, -1 / 2, 1 / 2, -1 / 4, -1 / 4, 0, 0, 1 / 4, -1 / 4, 0),
(0, 1 / 2, 0, 0, 0, -1 / 2, 0, 0, 0, -1 / 2, 0, 0, 0, -1 / 2, 0, 0),
(0, -1 / 4, 1 / 4, 1 / 2, 0, -1 / 4, -1 / 4, 0, 0, 1 / 4, 1 / 4, 0, 1 / 2, -1 / 4, 1 / 4, 0),
(0, 0, 1 / 2, 0, 0, 0, 0, 1 / 2, 0, 0, -1 / 2, 0, 0, 0, 0, 1 / 2),
(0, -1 / 4, -1 / 4, 0, 0, -1 / 4, -1 / 4, 0, -1 / 2, -1 / 4, 1 / 4, 0, 0, 1 / 4, -1 / 4, 1 / 2),
(0, 0, -1 / 2, 1 / 2, 0, 0, 0, 0, 0, 0, -1 / 2, -1 / 2, 0, 0, 0, 0),
(0, 1 / 4, -1 / 4, 0, 0, 1 / 4, -1 / 4, 0, 0, -1 / 4, -1 / 4, 1 / 2, 1 / 2, 1 / 4, 1 / 4, 0),
(0, 1 / 4, 0, -1 / 4, 1 / 2, -1 / 4, 0, -1 / 4, 0, 1 / 4, 0, -1 / 4, 0, 1 / 4, 1 / 2, 1 / 4),
(0, 0, 1 / 4, -1 / 4, 0, 0, 1 / 4, -1 / 4, -1 / 2, 0, -1 / 4, -1 / 4, 1 / 2, 0, -1 / 4, -1 / 4),
(0, -1 / 4, 0, -1 / 4, -1 / 2, 1 / 4, 0, -1 / 4, 0, -1 / 4, 0, -1 / 4, 0, -1 / 4, 1 / 2, 1 / 4),
(0, 0, 1 / 4, 1 / 4, 0, 0, 1 / 4, 1 / 4, 0, -1 / 2, 1 / 4, -1 / 4, 0, 1 / 2, 1 / 4, -1 / 4),
(0, 1 / 4, 0, 1 / 4, -1 / 2, -1 / 4, 1 / 2, -1 / 4, 0, 1 / 4, 0, 1 / 4, 0, 1 / 4, 0, 1 / 4),
(0, 0, 1 / 4, 1 / 4, 0, 0, -1 / 4, -1 / 4, -1 / 2, 0, -1 / 4, 1 / 4, -1 / 2, 0, 1 / 4, -1 / 4),
(0, 1 / 4, 0, -1 / 4, -1 / 2, -1 / 4, -1 / 2, 1 / 4, 0, 1 / 4, 0, -1 / 4, 0, 1 / 4, 0, -1 / 4),
(0, 1 / 2, 1 / 4, 1 / 4, 0, 1 / 2, -1 / 4, -1 / 4, 0, 0, 1 / 4, -1 / 4, 0, 0, -1 / 4, 1 / 4)
]
SboxLut.xor_model = get_differential_wdt_model(
SboxLut, XorDiff, tuple([tuple(inf if x == 0 else -log2_decimal(x / Decimal(2 ** 4)) for x in row) for row in ddt]))
SboxLut.linear_model = get_linear_wdt_model(
SboxLut, tuple([tuple(inf if x == 0 else -log2_decimal(Decimal(abs(x))) for x in row) for row in lat]))
[docs]class SKINNYEncryption(skinny128.SKINNYEncryption):
"""Encryption function of SKINNY-64-64."""
num_rounds = default_num_rounds
cell_width = 4
ignore_first_sub_cells = False
sbox = SboxLut
logging_mode = skinny128.LoggingMode.Silent
[docs]class SKINNYCipher(skinny128.SKINNYCipher):
"""The block cipher SKINNY-64-64."""
key_schedule = SKINNYTweakeySchedule
encryption = SKINNYEncryption
@classmethod
def test(cls):
old_num_rounds = cls.num_rounds
old_ignore = cls.encryption.ignore_first_sub_cells
cls.encryption.ignore_first_sub_cells = False
cls.set_num_rounds(default_num_rounds)
"""
0xb, 0x3, 0x9, 0xd, 0xf, 0xb, 0x2, 0x4, 0x2, 0x9, 0xb, 0x8, 0xa, 0xc, 0x7, """
key = [0xf, 0x5, 0x2, 0x6,
0x9, 0x8, 0x2, 0x6,
0xf, 0xc, 0x6, 0x8,
0x1, 0x2, 0x3, 0x8]
plaintext = [0x0, 0x6, 0x0, 0x3,
0x4, 0xf, 0x9, 0x5,
0x7, 0x7, 0x2, 0x4,
0xd, 0x1, 0x9, 0xd]
assert list(cls(plaintext, key)) == [
0xb, 0xb, 0x3, 0x9,
0xd, 0xf, 0xb, 0x2,
0x4, 0x2, 0x9, 0xb,
0x8, 0xa, 0xc, 0x7]
cls.set_num_rounds(old_num_rounds)
cls.encryption.ignore_first_sub_cells = old_ignore