"""Hight cipher."""
from arxpy.bitvector.core import Constant
from arxpy.bitvector.operation import RotateLeft as ROL
from arxpy.primitives.primitives import KeySchedule, Encryption, Cipher
[docs]class HightKeySchedule(KeySchedule):
"""Key schedule function."""
rounds = 34 # key whitening seen as a round
input_widths = [8 for _ in range(16)]
output_widths = [8 for _ in range(4 * 34)]
[docs] @classmethod
def set_rounds(cls, new_rounds):
cls.rounds = new_rounds
cls.output_widths = [8 for _ in range(4 * cls.rounds)]
[docs] @classmethod
def eval(cls, *mk):
mk = list(reversed(mk)) # mk[i] = mki
d = [
0x5a, 0x6d, 0x36, 0x1b, 0x0d, 0x06, 0x03, 0x41,
0x60, 0x30, 0x18, 0x4c, 0x66, 0x33, 0x59, 0x2c,
0x56, 0x2b, 0x15, 0x4a, 0x65, 0x72, 0x39, 0x1c,
0x4e, 0x67, 0x73, 0x79, 0x3c, 0x5e, 0x6f, 0x37,
0x5b, 0x2d, 0x16, 0x0b, 0x05, 0x42, 0x21, 0x50,
0x28, 0x54, 0x2a, 0x55, 0x6a, 0x75, 0x7a, 0x7d,
0x3e, 0x5f, 0x2f, 0x17, 0x4b, 0x25, 0x52, 0x29,
0x14, 0x0a, 0x45, 0x62, 0x31, 0x58, 0x6c, 0x76,
0x3b, 0x1d, 0x0e, 0x47, 0x63, 0x71, 0x78, 0x7c,
0x7e, 0x7f, 0x3f, 0x1f, 0x0f, 0x07, 0x43, 0x61,
0x70, 0x38, 0x5c, 0x6e, 0x77, 0x7b, 0x3d, 0x1e,
0x4f, 0x27, 0x53, 0x69, 0x34, 0x1a, 0x4d, 0x26,
0x13, 0x49, 0x24, 0x12, 0x09, 0x04, 0x02, 0x01,
0x40, 0x20, 0x10, 0x08, 0x44, 0x22, 0x11, 0x48,
0x64, 0x32, 0x19, 0x0c, 0x46, 0x23, 0x51, 0x68,
0x74, 0x3a, 0x5d, 0x2e, 0x57, 0x6b, 0x35, 0x5a
]
d = [Constant(d_i, 8) for d_i in d]
def sk_round_i(round_i):
assert round_i <= 31
sk = []
for i in range(8):
for j in range(8):
if 4*round_i <= 16 * i + j < 4*round_i + 4:
sk.append(mk[(j - i) % 8] + d[16 * i + j])
elif 4*round_i <= 16 * i + j + 8 < 4*round_i + 4:
sk.append(mk[((j - i) % 8) + 8] + d[16 * i + j + 8])
return sk
rk = []
for r in range(cls.rounds):
if hasattr(cls, "skip_rounds") and r in cls.skip_rounds:
rk.extend(mk[:4]) # cte outputs not supported
continue
if r == 0:
wk0, wk1, wk2, wk3 = [mk[i + 12] for i in range(4)]
rk.extend([wk0, wk1, wk2, wk3])
elif r < cls.rounds - 1:
sk0, sk1, sk2, sk3 = sk_round_i(r - 1)
rk.extend([sk0, sk1, sk2, sk3])
else:
assert r == cls.rounds - 1
if r == 33:
wk4, wk5, wk6, wk7 = [mk[i - 4] for i in range(4, 8)]
rk.extend([wk4, wk5, wk6, wk7])
else:
sk0, sk1, sk2, sk3 = sk_round_i(r - 1)
rk.extend([sk0, sk1, sk2, sk3])
return rk
[docs]class HightEncryption(Encryption):
"""Encryption function."""
rounds = 34
input_widths = [8 for _ in range(8)]
output_widths = [8 for _ in range(8)]
round_keys = None
[docs] @classmethod
def set_rounds(cls, new_rounds):
cls.rounds = new_rounds
@classmethod
def initial_transformation(cls, p, wk3, wk2, wk1, wk0):
x = [None for _ in range(len(p))]
x[0] = p[0] + wk0
x[1] = p[1]
x[2] = p[2] ^ wk1
x[3] = p[3]
x[4] = p[4] + wk2
x[5] = p[5]
x[6] = p[6] ^ wk3
x[7] = p[7]
return x
@classmethod
def round_function(cls, x, sk3, sk2, sk1, sk0): # SK4i+3,SK4i+2,SK4i+1,SK4i
def f0(bv):
return ROL(bv, 1) ^ ROL(bv, 2) ^ ROL(bv, 7)
def f1(bv):
return ROL(bv, 3) ^ ROL(bv, 4) ^ ROL(bv, 6)
# there is a typo in Section 2.4 of Hight paper; using Fig. 3 instead
y = [None for _ in range(len(x))]
y[1] = x[0]
y[3] = x[2]
y[5] = x[4]
y[7] = x[6]
y[0] = x[7] ^ (f0(x[6]) + sk3)
y[2] = x[1] + (f1(x[0]) ^ sk0) # sk2
y[4] = x[3] ^ (f0(x[2]) + sk1)
y[6] = x[5] + (f1(x[4]) ^ sk2) # sk0
return y
@classmethod
def final_transformation(cls, x, wk7, wk6, wk5, wk4):
c = [None for _ in range(len(x))]
c[0] = x[1] + wk4
c[1] = x[2]
c[2] = x[3] ^ wk5
c[3] = x[4]
c[4] = x[5] + wk6
c[5] = x[6]
c[6] = x[7] ^ wk7
c[7] = x[0]
return c
[docs] @classmethod
def eval(cls, *p): # p7,...,p0
x = list(reversed(p))
cls.round_inputs = []
for r in range(cls.rounds): # due to round_inputs, better all logic in for loop
cls.round_inputs.append(x)
if hasattr(cls, "skip_rounds") and r in cls.skip_rounds:
continue
if r == 0:
wk0, wk1, wk2, wk3 = cls.round_keys[4*r: 4*r + 4]
x = cls.initial_transformation(x, wk3, wk2, wk1, wk0)
elif r < cls.rounds - 1:
sk0, sk1, sk2, sk3 = cls.round_keys[4*r: 4*r + 4]
x = cls.round_function(x, sk3, sk2, sk1, sk0)
else:
assert r == cls.rounds - 1
if r == 33:
wk4, wk5, wk6, wk7 = cls.round_keys[4*r: 4*r + 4]
x = cls.final_transformation(x, wk7, wk6, wk5, wk4)
else:
sk0, sk1, sk2, sk3 = cls.round_keys[4*r: 4*r + 4]
x = cls.round_function(x, sk3, sk2, sk1, sk0)
return list(reversed(x))
[docs]class HightCipher(Cipher):
key_schedule = HightKeySchedule
encryption = HightEncryption
rounds = 34
max_rounds = 34
[docs] @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 set_skip_rounds(cls, skip_rounds):
cls.encryption.skip_rounds = skip_rounds
cls.key_schedule.skip_rounds = skip_rounds
[docs] @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(34)
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)