Files
tea3-py/src/tea3/cli.py
T

349 lines
11 KiB
Python

from tea3.pretty_print import pretty_print
from tea3.cliutils import prompt_int, prompt_choice, prompt_list
from tea3.tea3model import Tea3Model
from tea3.utils import set_bits, set_int_bits
from tea3.variable_search import run_exhaustive, run_exhaustive_staircase, run_exhaustive_staircase2, run_exhaustive_bp
from tea3.sbox import run_sbox
from tea3.variable_xor import run_variable_xor, run_exhaustive_xor
from tea3.f31f32 import run_f31f32
from tea3.cube_attack import run_cube_attack
from tea3.cube_attack_blackbox import run_cube_attack_offline_tea3
from tea3.precompute import run_precompute_cli, run_load_cli
def run_classic_cli():
print("\nR registers are indexed 0-7; bits within each register are 0-7.")
print("Enter -1 to print all registers, or all bits.")
steps = prompt_int("How many steps do you want to run? (1-100): ", 1, 100)
reg = prompt_int("Which R register do you want to inspect? (-1 or 0-7): ", -1, 7)
bit = prompt_int("Which bit of that register? (-1 or 0-7): ", -1, 7)
print("-" * 50)
model = Tea3Model()
for i in range(steps):
model.step()
print(f"\n[Step {i + 1}]")
regs = range(8) if reg == -1 else [reg]
bits = range(8) if bit == -1 else [bit]
for r in regs:
for b in bits:
poly = model.R_bits[r][b]
print(f"R_bits[{r}][{b}]")
print(len(poly.monomials()), "monomials")
print(pretty_print(poly))
print()
print("\n" + "=" * 50)
print("Done.")
def run_advanced_cli():
print("\nR registers are indexed 0-7; bits within each register are 0-7.")
print("x and r registers are indexed 0-4; bits within each register are 0-7.")
steps = prompt_int("How many steps do you want to run? (1-100): ", 1, 100)
reg = prompt_int("Which R register do you want to inspect? (-1 or 0-7): ", -1, 7)
bit = prompt_int("Which bit of that register? (-1 or 0-7): ", -1, 7)
raw = input("Bits to set to 0 (space-separated): ").strip()
zero_bits = raw.split() if raw else []
raw = input("Bits to set to 1 (space-separated): ").strip()
one_bits = raw.split() if raw else []
print("-" * 50)
model = Tea3Model()
try:
# 1 overrides 0
set_bits(model, zero_bits, 0)
set_bits(model, one_bits, 1)
except ValueError as e:
print(f"Error: {e}")
return
for i in range(steps):
model.step()
print(f"\n[Step {i + 1}]")
regs = range(8) if reg == -1 else [reg]
bits = range(8) if bit == -1 else [bit]
for r in regs:
for b in bits:
poly = model.R_bits[r][b]
print(f"R_bits[{r}][{b}]")
print(len(poly.monomials()), "monomials")
print(pretty_print(poly))
print()
print("\n" + "=" * 50)
print("Done.")
def run_exhaustive_cli():
print("\nChoose the variable-change family:")
print(" 1) First-row matrix")
print(" 2) Staircase matrix")
print(" 3) Staircase 2 matrix")
print(" 4) BP matrix")
family = prompt_choice("Your choice (1, 2, 3 or 4): ", {1, 2, 3, 4})
print("\nR registers are indexed 0-7; bits within each register are 0-7.")
print("Enter -1 to print all bits in the chosen register.")
steps = prompt_int("How many steps? (1-100): ", 1, 100)
target_reg = prompt_int("Target register (0-7): ", 0, 7)
target_bit = prompt_int("Target bit (-1 or 0-7): ", -1, 7)
print("-" * 50)
if family == 1:
run_exhaustive(steps, target_reg, target_bit)
elif family == 2:
run_exhaustive_staircase(steps, target_reg, target_bit)
elif family == 3:
run_exhaustive_staircase2(steps, target_reg, target_bit)
else:
run_exhaustive_bp(steps, target_reg, target_bit)
print("\n" + "=" * 50)
print("Done.")
def run_variable_xor_cli():
print("\nR registers are indexed 0-7; bits within each register are 0-7.")
print("Enter the bit positions to XOR, e.g. `0 1`.")
steps = prompt_int("How many steps? (1-100): ", 1, 100)
target_reg = prompt_int("Target register (0-7): ", 0, 7)
bits_to_xor = prompt_list(
"Bits to XOR (0-7, separated by spaces, ranges with '-'): ",
item_name="bit",
min_value=0,
max_value=7,
)
print("-" * 50)
run_variable_xor(steps, target_reg, bits_to_xor)
print("\n" + "=" * 50)
print("Done.")
def run_exhaustive_xor_cli():
print("\nR registers are indexed 0-7; bits within each register are 0-7.")
print("This mode searches all XOR combinations of a given size.")
steps = prompt_int("How many steps? (1-100): ", 1, 100)
target_reg = prompt_int("Target register (0-7): ", 0, 7)
xor_size = prompt_int("Number of bits to XOR (2-8): ", 2, 8)
print("-" * 50)
run_exhaustive_xor(steps=steps, target_reg=target_reg, xor_size=xor_size)
print("\n" + "=" * 50)
print("Done.")
def run_cube_attack_cli(model = None):
print("\nCube attack search on TEA3 model to search for low-degree superpolys.")
rounds = prompt_int("How many rounds? (1-100): ", 1, 100)
target_reg = prompt_int("Target R register (0-7): ", 0, 7)
target_bit = prompt_int("Target bit of that register (0-7): ", 0, 7)
raw = input("Bits to set to 0: ").strip()
fixed_zero_bits = raw.split() if raw else []
raw = input("Bits to set to 1: ").strip()
fixed_one_bits = raw.split() if raw else []
cube_size = prompt_int("Cube size (1-64): ", 1, 64)
print("\nSearch strategy:")
print(" 1) Exhaustive")
print(" 2) Random sampling")
strategy = prompt_choice("Your choice (1 or 2): ", {1, 2})
if strategy == 1:
mode = "exhaustive"
samples = 0
limit = prompt_int("How many results to keep? (1-100): ", 1, 100)
else:
mode = "random"
samples = prompt_int("How many random cubes to test? (1-200000): ", 1, 200000)
limit = prompt_int("How many results to keep? (1-100): ", 1, 100)
max_degree = prompt_int("Maximum accepted degree (0-5): ", 0, 5)
print("-" * 50)
run_cube_attack(
rounds=rounds,
target_reg=target_reg,
target_bit=target_bit,
cube_size=cube_size,
mode=mode,
samples=samples,
limit=limit,
max_degree=max_degree,
fixed_zero_bits=fixed_zero_bits,
fixed_one_bits=fixed_one_bits,
model=model,
)
print("\n" + "=" * 50)
print("Done.")
def run_cube_attack_oracle_cli():
print("\nBlack-box cube attack search on TEA3.")
print("This mode varies frame-number bits and queries the cipher directly.")
print("Frame-number bits are indexed 0-31, with 0 = least significant bit.")
base_frame_number = prompt_int("Base frame number (0-4294967295): ", 0, 0xFFFFFFFF)
raw = input("Frame bits to set to 0 (space-separated): ").strip()
fixed_zero_bits = raw.split() if raw else []
raw = input("Frame bits to set to 1 (space-separated): ").strip()
fixed_one_bits = raw.split() if raw else []
output_byte = prompt_int("Output keystream byte (0-63): ", 0, 63)
output_bit = prompt_int("Output bit within that byte (0-7): ", 0, 7)
cube_size = prompt_int("Cube size (1-32): ", 1, 32)
print("\nSearch strategy:")
print(" 1) Exhaustive")
print(" 2) Random sampling")
strategy = prompt_choice("Your choice (1 or 2): ", {1, 2})
if strategy == 1:
mode = "exhaustive"
samples = 0
limit = prompt_int("How many results to keep? (1-100): ", 1, 100)
else:
mode = "random"
samples = prompt_int("How many random cubes to test? (1-200000): ", 1, 200000)
limit = prompt_int("How many results to keep? (1-100): ", 1, 100)
raw = input(
"Enter 10 key bytes in hex or decimal, separated by spaces "
"(leave empty for all-zero key): "
).strip()
if raw:
try:
key_register = [int(x, 0) & 0xFF for x in raw.split()]
except ValueError as e:
print(f"Error: invalid key byte: {e}")
return
if len(key_register) != 10:
print("Error: TEA3 key register must contain exactly 10 bytes.")
return
else:
key_register = [0] * 10
try:
fixed_bits = {}
for b in fixed_zero_bits:
fixed_bits[int(b)] = 0
for b in fixed_one_bits:
fixed_bits[int(b)] = 1
except ValueError:
print("Error: frame bit indices must be integers.")
return
public_bits = [i for i in range(32) if i not in fixed_bits]
if not public_bits:
print("Error: no unfixed frame bits left to use as public variables.")
return
base_frame_number = set_int_bits(base_frame_number, fixed_zero_bits, 0)
base_frame_number = set_int_bits(base_frame_number, fixed_one_bits, 1)
print("-" * 50)
try:
run_cube_attack_offline_tea3(
key_register=key_register,
base_frame_number=base_frame_number,
public_bits=public_bits,
cube_size=cube_size,
mode=mode,
samples=samples,
limit=limit,
output_byte=output_byte,
output_bit=output_bit,
)
except ValueError as e:
print(f"Error: {e}")
print("\n" + "=" * 50)
print("Done.")
def main():
model = None
while True:
print("=" * 50)
print(" Tea3 Model ")
print("=" * 50)
print("\nChoose a mode:")
print(" 1) Classic inspection")
print(" 2) Advanced inspection with forced 0 or 1 bits")
print(" 3) Exhaustive variable-change search")
print(" 4) S box analysis")
print(" 5) Variable XOR")
print(" 6) F31, F32 analysis")
print(" 7) Exhaustive XOR search")
print(" 8) Cube attack search (symbolic)")
print(" 9) Cube attack search (oracle)")
print(" 10) Precompute and save model")
print(" 11) Load model")
print(" 0) Exit")
mode = prompt_choice(
"Your choice (0-11): ",
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
)
if mode == 0:
print("\nGoodbye")
break
elif mode == 1:
run_classic_cli()
elif mode == 2:
run_advanced_cli()
elif mode == 3:
run_exhaustive_cli()
elif mode == 4:
run_sbox()
elif mode == 5:
run_variable_xor_cli()
elif mode == 6:
run_f31f32()
elif mode == 7:
run_exhaustive_xor_cli()
elif mode == 8:
run_cube_attack_cli(model)
elif mode == 9:
run_cube_attack_oracle_cli()
elif mode == 10:
run_precompute_cli()
elif mode == 11:
model = run_load_cli()
input("\nPress Enter to return to the main menu...")
print()
main()