"""Shacal-2 cipher (based on SHA-256)."""
from arxpy.bitvector.core import Constant
from arxpy.bitvector.operation import RotateRight as ROR
from arxpy.primitives.primitives import KeySchedule, Encryption, Cipher
from arxpy.primitives.shacal1 import BvIf, BvMaj
N = 4 # number of key words, at least 4 for 128-bit, at most 16
assert N >= 4
# N == 16, optimized for default single-key
# N == 4, ref for linear related-key characteristics
# N == 4, optimized for non-linear related-key characteristics
REFERENCE_VERSION = False # ctes added in the encryption
k_ctes = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
]
k_ctes = [Constant(k, 32) for k in k_ctes]
[docs]class Shacal2KeySchedule(KeySchedule):
"""Key schedule function."""
rounds = 64
input_widths = [32 for _ in range(N)]
output_widths = [32 for _ in range(64 - (16 - N))]
[docs] @classmethod
def set_rounds(cls, new_rounds):
cls.rounds = new_rounds
cls.output_widths = [32 for _ in range(N + max(new_rounds - 16, 0))]
[docs] @classmethod
def eval(cls, *W): # w0, w1, ...
rk = list(W)
for i in range(N, 16):
rk.append(Constant(0, 32))
sigma0 = lambda x: ROR(x, 7) ^ ROR(x, 18) ^ (x >> Constant(3, 32))
sigma1 = lambda x: ROR(x, 17) ^ ROR(x, 19) ^ (x >> Constant(10, 32))
for i in range(16, cls.rounds):
rk.append(sigma1(rk[i-2]) + rk[i-7] + sigma0(rk[i-15]) + rk[i-16])
if REFERENCE_VERSION:
pass
else:
for i in range(cls.rounds):
rk[i] += k_ctes[i]
for i in range(cls.rounds):
if hasattr(cls, "skip_rounds") and i in cls.skip_rounds:
if i < N:
rk[i] = W[0]
elif N <= i < 16:
pass # rk[i] = 0
else:
rk[i] = W[0]
return rk[:N] + rk[16:cls.rounds]
# noinspection PyPep8Naming
[docs]class Shacal2Encryption(Encryption):
"""Encryption function."""
rounds = 64
input_widths = [32 for _ in range(8)]
output_widths = [32 for _ in range(8)]
round_keys = None
[docs] @classmethod
def set_rounds(cls, new_rounds):
cls.rounds = new_rounds
@classmethod
def round_function(cls, A, B, C, D, E, F, G, H, i):
if i < N:
W = cls.round_keys[i]
elif N <= i < 16:
if REFERENCE_VERSION:
W = Constant(0, 32)
else:
W = k_ctes[i]
else:
W = cls.round_keys[i - (16 - N)]
delta0 = lambda x: ROR(x, 2) ^ ROR(x, 13) ^ ROR(x, 22)
delta1 = lambda x: ROR(x, 6) ^ ROR(x, 11) ^ ROR(x, 25)
if REFERENCE_VERSION:
T1 = H + delta1(E) + BvIf(E, F, G) + W + k_ctes[i] # ref
else:
T1 = H + delta1(E) + BvIf(E, F, G) + W # optimized
T2 = delta0(A) + BvMaj(A, B, C)
return [
T1 + T2,
A,
B,
C,
D + T1,
E,
F,
G
]
[docs] @classmethod
def eval(cls, A, B, C, D, E, F, G, H):
cls.round_inputs = []
for i in range(cls.rounds):
cls.round_inputs.append([A, B, C, D, E, F, G, H])
if hasattr(cls, "skip_rounds") and i in cls.skip_rounds:
continue
A, B, C, D, E, F, G, H = cls.round_function(A, B, C, D, E, F, G, H, i)
return A, B, C, D, E, F, G, H
[docs]class Shacal2Cipher(Cipher):
key_schedule = Shacal2KeySchedule
encryption = Shacal2Encryption
rounds = 64
[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
# 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)