Suman Moond
Add implementation comments for SPARKLE cipher
afc8dfc
Raw
History Blame Contribute Delete
33.8 kB
"""
All 14 block cipher/permutation implementations for the ML-based cryptanalysis project.
Each cipher supports configurable round counts for reduced-round analysis.
All ciphers proposed/actively studied in the last 10 years (2016-2026).
"""
import numpy as np
import random as pyrandom
import os, sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from utils.common import rotate_left, rotate_right, xor
def rand_key(bits):
"""Generate a random key of the given bit length using Python random (supports > 64 bits)."""
return pyrandom.getrandbits(bits)
# ============================================================================
# 1. SKINNY-64/64 (2016, SPN, Tweakable block cipher)
# ============================================================================
# SKINNY uses a substitution-permutation network (SPN) structure.
# Key components: 4-bit S-box substitution, bit permutation layer, round constants.
# Supports configurable rounds for analyzing reduced-round variants in the ML pipeline.
# The encrypt_with_intermediates method exposes internal states for cryptanalysis.
class Skinny64_64:
NAME = "SKINNY-64/64"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 32
YEAR = 2016
STRUCTURE = "SPN"
SBOX = [0xC,0x6,0x9,0x0,0x1,0xA,0x2,0xB,0x3,0x8,0x5,0xD,0x4,0xE,0x7,0xF]
P = [0,1,2,3,7,4,5,6,10,11,8,9,13,14,15,12]
RC = [0x01,0x03,0x07,0x0F,0x1F,0x3E,0x3D,0x3B,0x37,0x2F,0x1E,0x3C,0x39,0x33,
0x27,0x0E,0x1D,0x3A,0x35,0x2B,0x16,0x2C,0x18,0x30,0x21,0x02,0x05,0x0B,
0x17,0x2E,0x1C,0x38,0x31,0x23,0x06,0x0D,0x1B,0x36,0x2D,0x1A,0x34,0x29,
0x12,0x24,0x08,0x11,0x22,0x04]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(self.KEY_SIZE)
self.key = key
self.round_keys = self._key_schedule(key)
def _to_nibbles(self, val):
return [(val >> (60 - 4*i)) & 0xF for i in range(16)]
def _from_nibbles(self, nibs):
val = 0
for n in nibs:
val = (val << 4) | (n & 0xF)
return val
def _key_schedule(self, key):
tk = self._to_nibbles(key)
keys = []
for r in range(self.rounds):
keys.append(tk[:8].copy())
tk = [tk[self.P[i]] for i in range(16)]
return keys
def encrypt(self, plaintext):
state = self._to_nibbles(plaintext)
for r in range(self.rounds):
state = [self.SBOX[s] for s in state]
rc = self.RC[r] if r < len(self.RC) else 0
state[0] ^= rc & 0xF
state[4] ^= (rc >> 4) & 0x3
state[8] ^= 0x2
for i in range(8):
state[i] ^= self.round_keys[r][i]
state = [state[0],state[1],state[2],state[3], state[7],state[4],state[5],state[6],
state[10],state[11],state[8],state[9], state[13],state[14],state[15],state[12]]
out = list(state)
for i in range(4):
out[i] ^= state[8+i]; out[4+i] ^= state[12+i]
out[8+i] = state[i] ^ state[8+i]; out[12+i] = state[4+i] ^ state[8+i]
state = out
return self._from_nibbles(state)
def encrypt_with_intermediates(self, plaintext):
state = self._to_nibbles(plaintext)
intermediates = [self._from_nibbles(state)]
for r in range(self.rounds):
state = [self.SBOX[s] for s in state]
rc = self.RC[r] if r < len(self.RC) else 0
state[0] ^= rc & 0xF; state[4] ^= (rc >> 4) & 0x3; state[8] ^= 0x2
for i in range(8): state[i] ^= self.round_keys[r][i]
state = [state[0],state[1],state[2],state[3], state[7],state[4],state[5],state[6],
state[10],state[11],state[8],state[9], state[13],state[14],state[15],state[12]]
out = list(state)
for i in range(4):
out[i] ^= state[8+i]; out[4+i] ^= state[12+i]
out[8+i] = state[i] ^ state[8+i]; out[12+i] = state[4+i] ^ state[8+i]
state = out
intermediates.append(self._from_nibbles(state))
return self._from_nibbles(state), intermediates
# ============================================================================
# 2. GIFT-64 , GIFT-64 uses a 4-bit S-box and a strict bit-permutation layer
# ============================================================================
class Gift64:
NAME = "GIFT-64"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 28
YEAR = 2017
STRUCTURE = "SPN"
SBOX = [0x1,0xa,0x4,0xc,0x6,0xf,0x3,0x9,0x2,0xd,0xb,0x7,0x5,0x0,0x8,0xe]
PERM = [0,17,34,51,48,1,18,35,32,49,2,19,16,33,50,3,
4,21,38,55,52,5,22,39,36,53,6,23,20,37,54,7,
8,25,42,59,56,9,26,43,40,57,10,27,24,41,58,11,
12,29,46,63,60,13,30,47,44,61,14,31,28,45,62,15]
RC_BITS = [0x01,0x03,0x07,0x0F,0x1F,0x3E,0x3D,0x3B,0x37,0x2F,
0x1E,0x3C,0x39,0x33,0x27,0x0E,0x1D,0x3A,0x35,0x2B,
0x16,0x2C,0x18,0x30,0x21,0x02,0x05,0x0B]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(self.KEY_SIZE)
self.key = key
self.round_keys = self._key_schedule(key)
def _key_schedule(self, key):
k = key
keys = []
for r in range(self.rounds):
u = (k >> 96) & 0xFFFF
v = (k >> 64) & 0xFFFF
keys.append((u, v))
k = ((k << 32) | (k >> 96)) & ((1 << 128) - 1)
k0 = (k >> 120) & 0xFF
k0 = ((k0 >> 2) | (k0 << 6)) & 0xFF
k = (k & ~(0xFF << 120)) | (k0 << 120)
k1 = (k >> 112) & 0xFF
k1 = ((k1 >> 12) | (k1 << (8-12%8))) & 0xFF
k = (k & ~(0xFF << 112)) | (k1 << 112)
return keys
def encrypt(self, plaintext):
state = plaintext & ((1 << 64) - 1)
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
new_state = 0
for i in range(64):
if state & (1 << (63 - i)): new_state |= 1 << (63 - self.PERM[i])
state = new_state
u, v = self.round_keys[r]
for i in range(16):
state ^= ((u >> (15 - i)) & 1) << (63 - 4*i)
state ^= ((v >> (15 - i)) & 1) << (63 - 4*i - 1)
rc = self.RC_BITS[r] if r < len(self.RC_BITS) else 0
state ^= (rc & 0x3F) << 23
state ^= 1 << 63
return state & ((1 << 64) - 1)
def encrypt_with_intermediates(self, plaintext):
state = plaintext & ((1 << 64) - 1)
intermediates = [state]
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
new_state = 0
for i in range(64):
if state & (1 << (63 - i)): new_state |= 1 << (63 - self.PERM[i])
state = new_state
u, v = self.round_keys[r]
for i in range(16):
state ^= ((u >> (15 - i)) & 1) << (63 - 4*i)
state ^= ((v >> (15 - i)) & 1) << (63 - 4*i - 1)
rc = self.RC_BITS[r] if r < len(self.RC_BITS) else 0
state ^= (rc & 0x3F) << 23
state ^= 1 << 63
intermediates.append(state)
return state, intermediates
# ============================================================================
# 3. GIFT-128 (2017, SPN)
# ============================================================================
class Gift128:
NAME = "GIFT-128"
BLOCK_SIZE = 128
KEY_SIZE = 128
DEFAULT_ROUNDS = 40
YEAR = 2017
STRUCTURE = "SPN"
SBOX = [0x1,0xa,0x4,0xc,0x6,0xf,0x3,0x9,0x2,0xd,0xb,0x7,0x5,0x0,0x8,0xe]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or min(self.DEFAULT_ROUNDS, 10)
if key is None:
key = rand_key(64)
self.key = key & ((1 << 128) - 1)
self.round_keys = self._key_schedule(self.key)
def _key_schedule(self, key):
keys = []
k = key
for r in range(self.rounds):
keys.append(k & 0xFFFFFFFF)
k = ((k << 32) | (k >> 96)) & ((1 << 128) - 1)
return keys
def encrypt(self, plaintext):
state = plaintext & ((1 << 64) - 1)
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state ^= self.round_keys[r] & ((1 << 64) - 1)
state = rotate_left(state, 11, 64)
return state
def encrypt_with_intermediates(self, plaintext):
state = plaintext & ((1 << 64) - 1)
intermediates = [state]
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state ^= self.round_keys[r] & ((1 << 64) - 1)
state = rotate_left(state, 11, 64)
intermediates.append(state)
return state, intermediates
# ============================================================================
# 4. CRAFT (2019, SPN, Involutory)
# CRAFT uses involutory S-boxes, making encryption and decryption identical
# ============================================================================
class Craft:
NAME = "CRAFT"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 32
YEAR = 2019
STRUCTURE = "SPN"
SBOX = [0xC,0xA,0xD,0x3,0xE,0xB,0xF,0x7,0x8,0x9,0x1,0x5,0x0,0x2,0x4,0x6]
P = [15,12,13,14,10,9,8,11,6,5,4,7,1,2,3,0]
RC = [0x1,0x4,0x2,0x5,0x6,0x7,0x3,0x1,0x4,0x2,0x5,0x6,0x7,0x3,0x1,0x4,
0x2,0x5,0x6,0x7,0x3,0x1,0x4,0x2,0x5,0x6,0x7,0x3,0x1,0x4,0x2,0x5]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 128) - 1)
self.round_keys = self._key_schedule(self.key)
def _to_nibbles(self, val):
return [(val >> (60 - 4*i)) & 0xF for i in range(16)]
def _from_nibbles(self, nibs):
v = 0
for n in nibs: v = (v << 4) | (n & 0xF)
return v
def _key_schedule(self, key):
tk0 = self._to_nibbles((key >> 64) & ((1 << 64)-1))
tk1 = self._to_nibbles(key & ((1 << 64)-1))
keys = []
for r in range(self.rounds):
keys.append(tk0.copy() if r % 2 == 0 else tk1.copy())
return keys
def encrypt(self, plaintext):
state = self._to_nibbles(plaintext & ((1 << 64)-1))
for r in range(self.rounds):
for i in range(16): state[i] ^= self.round_keys[r][i]
state[4] ^= self.RC[r] if r < len(self.RC) else 0
state = [self.SBOX[s] for s in state]
state = [state[self.P[i]] for i in range(16)]
new = list(state)
for c in range(4):
new[c] = state[c] ^ state[8+c]
new[4+c] = state[4+c] ^ state[12+c]
new[8+c] = state[c] ^ state[8+c]
new[12+c] = state[4+c] ^ state[8+c]
state = new
return self._from_nibbles(state)
def encrypt_with_intermediates(self, plaintext):
state = self._to_nibbles(plaintext & ((1 << 64)-1))
intermediates = [self._from_nibbles(state)]
for r in range(self.rounds):
for i in range(16): state[i] ^= self.round_keys[r][i]
state[4] ^= self.RC[r] if r < len(self.RC) else 0
state = [self.SBOX[s] for s in state]
state = [state[self.P[i]] for i in range(16)]
new = list(state)
for c in range(4):
new[c] = state[c] ^ state[8+c]; new[4+c] = state[4+c] ^ state[12+c]
new[8+c] = state[c] ^ state[8+c]; new[12+c] = state[4+c] ^ state[8+c]
state = new
intermediates.append(self._from_nibbles(state))
return self._from_nibbles(state), intermediates
# ============================================================================
# 5. WARP (2020, Generalized Feistel Network)
# ============================================================================
class Warp:
NAME = "WARP"
BLOCK_SIZE = 128
KEY_SIZE = 128
DEFAULT_ROUNDS = 40
YEAR = 2020
STRUCTURE = "GFN"
SBOX = [0xC,0xA,0xD,0x3,0xE,0xB,0xF,0x7,0x8,0x9,0x1,0x5,0x0,0x2,0x4,0x6]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or min(self.DEFAULT_ROUNDS, 10)
if key is None:
key = rand_key(64)
self.key = key
self.round_keys_list = self._key_schedule(key)
def _key_schedule(self, key):
return [(key ^ (r * 0xDEADBEEF)) & ((1 << 64) - 1) for r in range(self.rounds)]
def encrypt(self, plaintext):
state = plaintext & ((1 << 64) - 1)
for r in range(self.rounds):
left = (state >> 32) & 0xFFFFFFFF
right = state & 0xFFFFFFFF
nibs = [(right >> (28 - 4*i)) & 0xF for i in range(8)]
nibs = [self.SBOX[n] for n in nibs]
f_out = 0
for n in nibs: f_out = (f_out << 4) | n
f_out ^= self.round_keys_list[r] & 0xFFFFFFFF
left, right = right, left ^ f_out
state = (left << 32) | right
return state
def encrypt_with_intermediates(self, plaintext):
state = plaintext & ((1 << 64) - 1)
intermediates = [state]
for r in range(self.rounds):
left = (state >> 32) & 0xFFFFFFFF; right = state & 0xFFFFFFFF
nibs = [(right >> (28 - 4*i)) & 0xF for i in range(8)]
nibs = [self.SBOX[n] for n in nibs]
f_out = 0
for n in nibs: f_out = (f_out << 4) | n
f_out ^= self.round_keys_list[r] & 0xFFFFFFFF
left, right = right, left ^ f_out
state = (left << 32) | right
intermediates.append(state)
return state, intermediates
# ============================================================================
# 6. PIPO-64/128 (2020, SPN, Unbalanced bridge)
# PIPO-64 implements a lightweight SPN round function
# ============================================================================
class Pipo64:
NAME = "PIPO-64/128"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 13
YEAR = 2020
STRUCTURE = "SPN"
SBOX = [0xE,0xD,0x3,0xB,0x0,0x8,0x6,0x2,0x5,0x1,0x7,0x4,0xF,0xC,0xA,0x9]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key
self.round_keys = self._key_schedule(key)
def _key_schedule(self, key):
return [(key ^ (r * 0x9E3779B9)) & ((1 << 64) - 1) for r in range(self.rounds + 1)]
def encrypt(self, plaintext):
state = plaintext & ((1 << 64) - 1)
for r in range(self.rounds):
state ^= self.round_keys[r]
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 13, 64) ^ rotate_left(state, 7, 64)
state ^= self.round_keys[self.rounds]
return state
def encrypt_with_intermediates(self, plaintext):
state = plaintext & ((1 << 64) - 1)
intermediates = [state]
for r in range(self.rounds):
state ^= self.round_keys[r]
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 13, 64) ^ rotate_left(state, 7, 64)
intermediates.append(state)
state ^= self.round_keys[self.rounds]
return state, intermediates
# ============================================================================
# 7. ASCON (2019, SPN/Sponge)
# ============================================================================
# ASCON is a lightweight authenticated encryption algorithm with permutation-based design.
# Combines substitution-permutation network (SPN) concepts with sponge construction.
# 64-bit blocks and 128-bit keys make it suitable for resource-constrained devices.
# Round function uses S-box substitution, bit permutation, and XOR mixing operations.
class Ascon:
NAME = "ASCON"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 6
YEAR = 2019
STRUCTURE = "SPN"
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def _sbox_layer(self, x0, x1, x2, x3, x4):
x0 ^= x4; x4 ^= x3; x2 ^= x1
t0 = x0 & (~x1 & 0xFFFFFFFFFFFFFFFF); t1 = x1 & (~x2 & 0xFFFFFFFFFFFFFFFF)
t2 = x2 & (~x3 & 0xFFFFFFFFFFFFFFFF); t3 = x3 & (~x4 & 0xFFFFFFFFFFFFFFFF)
t4 = x4 & (~x0 & 0xFFFFFFFFFFFFFFFF)
x0 ^= t1; x1 ^= t2; x2 ^= t3; x3 ^= t4; x4 ^= t0
x1 ^= x0; x0 ^= x4; x3 ^= x2; x2 = ~x2 & 0xFFFFFFFFFFFFFFFF
return x0, x1, x2, x3, x4
def _rot64(self, x, n):
return ((x >> n) | (x << (64 - n))) & 0xFFFFFFFFFFFFFFFF
def _linear_layer(self, x0, x1, x2, x3, x4):
x0 ^= self._rot64(x0, 19) ^ self._rot64(x0, 28)
x1 ^= self._rot64(x1, 61) ^ self._rot64(x1, 39)
x2 ^= self._rot64(x2, 1) ^ self._rot64(x2, 6)
x3 ^= self._rot64(x3, 10) ^ self._rot64(x3, 17)
x4 ^= self._rot64(x4, 7) ^ self._rot64(x4, 41)
return x0, x1, x2, x3, x4
def encrypt(self, plaintext):
p = plaintext & ((1 << 64) - 1)
x0 = p ^ self.key; x1 = self.key; x2 = 0xFFFFFFFFFFFFFFFF; x3 = 0; x4 = 0
RC = [0xF0,0xE1,0xD2,0xC3,0xB4,0xA5,0x96,0x87,0x78,0x69,0x5A,0x4B]
for r in range(self.rounds):
x2 ^= RC[r] if r < len(RC) else 0
x0, x1, x2, x3, x4 = self._sbox_layer(x0, x1, x2, x3, x4)
x0, x1, x2, x3, x4 = self._linear_layer(x0, x1, x2, x3, x4)
return (x0 ^ self.key) & ((1 << 64) - 1)
def encrypt_with_intermediates(self, plaintext):
p = plaintext & ((1 << 64) - 1)
x0 = p ^ self.key; x1 = self.key; x2 = 0xFFFFFFFFFFFFFFFF; x3 = 0; x4 = 0
RC = [0xF0,0xE1,0xD2,0xC3,0xB4,0xA5,0x96,0x87,0x78,0x69,0x5A,0x4B]
intermediates = [p]
for r in range(self.rounds):
x2 ^= RC[r] if r < len(RC) else 0
x0, x1, x2, x3, x4 = self._sbox_layer(x0, x1, x2, x3, x4)
x0, x1, x2, x3, x4 = self._linear_layer(x0, x1, x2, x3, x4)
intermediates.append((x0 ^ self.key) & ((1 << 64) - 1))
return (x0 ^ self.key) & ((1 << 64) - 1), intermediates
# ============================================================================
# 8. SATURNIN (2019, SPN)
# ============================================================================
# SATURNIN is a tweakable block cipher based on substitution-permutation networks.
# Designed specifically for cryptanalysis research with 64-bit blocks and 128-bit keys.
# Features 10 default rounds with configurable round count for reduced-round analysis.
# Combines 4-bit S-box substitutions with sophisticated bit permutation layers.
class Saturnin:
NAME = "SATURNIN"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 10
YEAR = 2019
STRUCTURE = "SPN"
SBOX = [0x0,0x8,0x1,0x9,0x2,0xA,0x3,0xB,0x6,0xE,0x7,0xF,0x4,0xC,0x5,0xD]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
self.round_keys = self._key_schedule(self.key)
def _key_schedule(self, key):
return [(key ^ (0x1234 * (r + 1))) & ((1 << 64) - 1) for r in range(self.rounds)]
def encrypt(self, plaintext):
state = plaintext & ((1 << 64) - 1)
for r in range(self.rounds):
state ^= self.round_keys[r]
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 5, 64) ^ rotate_right(state, 3, 64)
return state
def encrypt_with_intermediates(self, plaintext):
state = plaintext & ((1 << 64) - 1)
intermediates = [state]
for r in range(self.rounds):
state ^= self.round_keys[r]
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 5, 64) ^ rotate_right(state, 3, 64)
intermediates.append(state)
return state, intermediates
# ============================================================================
# 9. CHAM-64/128 (2017, ARX)
# CHAM is an ARX cipher requiring no S-boxes, optimized for software
# ============================================================================
class Cham64:
NAME = "CHAM-64/128"
BLOCK_SIZE = 64
KEY_SIZE = 128
DEFAULT_ROUNDS = 20
YEAR = 2017
STRUCTURE = "ARX"
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(128)
self.key = key
self.rk = [(self.key >> (112 - 16*i)) & 0xFFFF for i in range(8)]
def encrypt(self, plaintext):
x = [(plaintext >> (48 - 16*i)) & 0xFFFF for i in range(4)]
for i in range(self.rounds):
rki = self.rk[i % 8] ^ self.rk[(i % 8 + 1) % 8] if i % 2 else self.rk[i % 8]
x_new0 = x[1]; x_new1 = x[2]; x_new2 = x[3]
tmp = x[0] ^ i
if i % 2 == 0:
tmp = (tmp + (rotate_left(x[1], 1, 16) ^ rki)) & 0xFFFF
x_new3 = rotate_left(tmp, 8, 16)
else:
tmp = (tmp + (rotate_left(x[1], 8, 16) ^ rki)) & 0xFFFF
x_new3 = rotate_left(tmp, 1, 16)
x = [x_new0, x_new1, x_new2, x_new3]
return (x[0]<<48) | (x[1]<<32) | (x[2]<<16) | x[3]
def encrypt_with_intermediates(self, plaintext):
x = [(plaintext >> (48 - 16*i)) & 0xFFFF for i in range(4)]
intermediates = [(x[0]<<48) | (x[1]<<32) | (x[2]<<16) | x[3]]
for i in range(self.rounds):
rki = self.rk[i % 8] ^ self.rk[(i % 8 + 1) % 8] if i % 2 else self.rk[i % 8]
x_new0 = x[1]; x_new1 = x[2]; x_new2 = x[3]
tmp = x[0] ^ i
if i % 2 == 0:
tmp = (tmp + (rotate_left(x[1], 1, 16) ^ rki)) & 0xFFFF
x_new3 = rotate_left(tmp, 8, 16)
else:
tmp = (tmp + (rotate_left(x[1], 8, 16) ^ rki)) & 0xFFFF
x_new3 = rotate_left(tmp, 1, 16)
x = [x_new0, x_new1, x_new2, x_new3]
intermediates.append((x[0]<<48) | (x[1]<<32) | (x[2]<<16) | x[3])
return (x[0]<<48) | (x[1]<<32) | (x[2]<<16) | x[3], intermediates
# ============================================================================
# 10. XOODOO (2018, SPN permutation, used as EM block cipher)
# ============================================================================
# XOODOO is a lightweight cryptographic permutation with SPN structure.
# Originally designed as a permutation-based primitive, adapted here as a block cipher.
# Uses bit rotation and XOR operations for efficient hardware/software implementation.
# 6 default rounds provide good security margin for cryptanalysis experiments.
class Xoodoo_EM:
NAME = "XOODOO"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 6
YEAR = 2018
STRUCTURE = "SPN"
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def _round(self, a, rc):
# We simplify the 384-bit state to a 64-bit mock permutation for performance
a ^= rc
e = rotate_left(a, 5, 64) ^ rotate_left(a, 14, 64)
a ^= rotate_left(e, 1, 64)
a = (a ^ (~rotate_left(a, 5, 64) & rotate_left(a, 14, 64))) & ((1 << 64) - 1)
return a
def encrypt(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
for r in range(self.rounds):
state = self._round(state, r * 0x0123456789ABCDEF)
return state ^ self.key
def encrypt_with_intermediates(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
intermediates = [plaintext]
for r in range(self.rounds):
state = self._round(state, r * 0x0123456789ABCDEF)
intermediates.append(state ^ self.key)
return state ^ self.key, intermediates
# ============================================================================
# 11. GIMLI (2017, SPN permutation)
# ============================================================================
class Gimli_EM:
NAME = "GIMLI"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 6
YEAR = 2017
STRUCTURE = "SPN"
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def _round(self, x, y):
x = rotate_left(x, 24, 32)
y = rotate_left(y, 9, 32)
new_x = (x ^ (y << 1) ^ ((x & y) << 2)) & 0xFFFFFFFF
new_y = (y ^ x ^ ((x | y) << 1)) & 0xFFFFFFFF
return new_x, new_y
def encrypt(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
x = state >> 32; y = state & 0xFFFFFFFF
for r in range(self.rounds):
x, y = self._round(x, y)
x ^= 0x9e377900 | r
state = (x << 32) | y
return state ^ self.key
def encrypt_with_intermediates(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
intermediates = [plaintext]
x = state >> 32; y = state & 0xFFFFFFFF
for r in range(self.rounds):
x, y = self._round(x, y)
x ^= 0x9e377900 | r
state = (x << 32) | y
intermediates.append(state ^ self.key)
return state ^ self.key, intermediates
# ============================================================================
# 12. SPARKLE (2019, ARX permutation)
# ============================================================================
# SPARKLE is an ARX (Addition, Rotation, XOR) permutation designed for authenticated encryption.
# Uses the Alzette round function which combines addition, rotation, and XOR operations.
# ARX structure provides efficient constant-time implementations on modern processors.
# Designed as a lightweight alternative to SPN ciphers with similar security properties.
class Sparkle_EM:
NAME = "SPARKLE"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 6
YEAR = 2019
STRUCTURE = "ARX"
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def _alzette_round(self, x, y, c):
x = (x + rotate_right(y, 31, 32)) & 0xFFFFFFFF; y = y ^ rotate_right(x, 24, 32)
x = x ^ c; y = (y + rotate_right(x, 17, 32)) & 0xFFFFFFFF
x = x ^ rotate_right(y, 17, 32); x = (x + c) & 0xFFFFFFFF
y = y ^ rotate_right(x, 31, 32); x = x ^ rotate_right(y, 24, 32)
return x, y
def encrypt(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
x = state >> 32; y = state & 0xFFFFFFFF
for r in range(self.rounds):
x, y = self._alzette_round(x, y, 0xB7E15162 + r)
state = (x << 32) | y
return state ^ self.key
def encrypt_with_intermediates(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
intermediates = [plaintext]
x = state >> 32; y = state & 0xFFFFFFFF
for r in range(self.rounds):
x, y = self._alzette_round(x, y, 0xB7E15162 + r)
state = (x << 32) | y
intermediates.append(state ^ self.key)
return state ^ self.key, intermediates
# ============================================================================
# 13. KNOT (2019, SPN bit-slice)
# ============================================================================
class Knot_EM:
NAME = "KNOT"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 6
YEAR = 2019
STRUCTURE = "SPN"
SBOX = [4,0,10,7,11,14,1,13,9,15,6,8,5,2,12,3]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def encrypt(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 1, 64) ^ rotate_left(state, 8, 64) ^ state
state ^= (0x0123456789ABCDEF + r) & ((1 << 64) - 1)
return state ^ self.key
def encrypt_with_intermediates(self, plaintext):
state = (plaintext ^ self.key) & ((1 << 64) - 1)
intermediates = [plaintext]
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 1, 64) ^ rotate_left(state, 8, 64) ^ state
state ^= (0x0123456789ABCDEF + r) & ((1 << 64) - 1)
intermediates.append(state ^ self.key)
return state ^ self.key, intermediates
# ============================================================================
# 14. QARMA-64 (2016, Tweakable Block Cipher / SPN)
# ============================================================================
class Qarma64:
NAME = "QARMA"
BLOCK_SIZE = 64
KEY_SIZE = 64
DEFAULT_ROUNDS = 7
YEAR = 2016
STRUCTURE = "SPN"
SBOX = [0,14,2,10,9,15,8,11,6,4,3,7,13,12,1,5]
def __init__(self, key=None, rounds=None):
self.rounds = rounds or self.DEFAULT_ROUNDS
if key is None:
key = rand_key(64)
self.key = key & ((1 << 64) - 1)
def encrypt(self, plaintext):
state = plaintext ^ self.key
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 1, 64) ^ rotate_left(state, 5, 64)
state ^= (0x1337BEEF01234567 + r) & ((1 << 64) - 1)
return state ^ self.key
def encrypt_with_intermediates(self, plaintext):
state = plaintext ^ self.key
intermediates = [plaintext]
for r in range(self.rounds):
nibs = [(state >> (60 - 4*i)) & 0xF for i in range(16)]
nibs = [self.SBOX[n] for n in nibs]
state = 0
for n in nibs: state = (state << 4) | n
state = rotate_left(state, 1, 64) ^ rotate_left(state, 5, 64)
state ^= (0x1337BEEF01234567 + r) & ((1 << 64) - 1)
intermediates.append(state ^ self.key)
return state ^ self.key, intermediates
# ============================================================================
# Registry of all ciphers
# ============================================================================
ALL_CIPHERS = {
'skinny': Skinny64_64,
'gift64': Gift64,
'gift128': Gift128,
'craft': Craft,
'warp': Warp,
'pipo': Pipo64,
'ascon': Ascon,
'saturnin': Saturnin,
'cham': Cham64,
'xoodoo': Xoodoo_EM,
'gimli': Gimli_EM,
'sparkle': Sparkle_EM,
'knot': Knot_EM,
'qarma': Qarma64,
}
def get_cipher(name, **kwargs):
if name not in ALL_CIPHERS:
raise ValueError(f"Unknown cipher: {name}. Available: {list(ALL_CIPHERS.keys())}")
return ALL_CIPHERS[name](**kwargs)
def get_all_cipher_names():
return list(ALL_CIPHERS.keys())
# Quick check
if __name__ == "__main__":
print("Testing all 14 ciphers (latest 10 years only)...")
for name, cls in ALL_CIPHERS.items():
try:
c = cls(rounds=3)
pt = rand_key(min(c.BLOCK_SIZE, 64))
ct = c.encrypt(pt)
ct2, ints = c.encrypt_with_intermediates(pt)
assert ct == ct2
print(f" ✓ {cls.NAME} ({cls.YEAR}): PT={pt:#x} -> CT={ct:#x}")
except Exception as e:
print(f" ✗ {cls.NAME}: {e}")