File size: 3,283 Bytes
76552d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
033e4ff
76552d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
Shared utility functions for the ML-based cryptanalysis project.
"""
import numpy as np
import torch
import os
import json
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import seaborn as sns

# Reproducibility
SEED = 42
np.random.seed(SEED)
torch.manual_seed(SEED)

DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# Project paths
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

def int_to_bits(val, nbits):
    """Convert integer to numpy bit array (MSB first)."""
    return np.array([(val >> i) & 1 for i in range(nbits - 1, -1, -1)], dtype=np.float32)

def bits_to_int(bits):
    """Convert bit array back to integer."""
    val = 0
    for b in bits:
        val = (val << 1) | int(b)
    return val

def int_to_words(val, word_size, num_words):
    """Convert integer to word-level array."""
    mask = (1 << word_size) - 1
    words = []
    for i in range(num_words - 1, -1, -1):
        words.append((val >> (i * word_size)) & mask)
    return np.array(words, dtype=np.float32)

def hamming_weight(val, nbits):
    """Compute Hamming weight of an integer."""
    return bin(val & ((1 << nbits) - 1)).count('1')

def xor(a, b, nbits):
    """XOR two integers with bit masking."""
    return (a ^ b) & ((1 << nbits) - 1)

def rotate_left(val, r, nbits):
    """Left rotation."""
    return ((val << r) | (val >> (nbits - r))) & ((1 << nbits) - 1)

def rotate_right(val, r, nbits):
    """Right rotation."""
    return ((val >> r) | (val << (nbits - r))) & ((1 << nbits) - 1)

def save_results(results, filepath):
    """Save results dictionary to JSON."""
    os.makedirs(os.path.dirname(filepath), exist_ok=True)
    # Convert numpy types
    def convert(o):
        if isinstance(o, (np.integer,)):
            return int(o)
        if isinstance(o, (np.floating,)):
            return float(o)
        if isinstance(o, np.ndarray):
            return o.tolist()
        return o
    with open(filepath, 'w') as f:
        json.dump(results, f, indent=2, default=convert)
#load results from JSON, converting lists back to numpy arrays where appropriate
def load_results(filepath):
    """Load results dictionary from JSON."""
    with open(filepath, 'r') as f:
        return json.load(f)

def setup_plot_style():
    """Set up consistent, publication-quality plot style."""
    plt.style.use('seaborn-v0_8-whitegrid')
    plt.rcParams.update({
        'font.size': 12,
        'axes.titlesize': 14,
        'axes.labelsize': 12,
        'xtick.labelsize': 10,
        'ytick.labelsize': 10,
        'legend.fontsize': 10,
        'figure.figsize': (10, 6),
        'figure.dpi': 150,
        'savefig.dpi': 150,
        'savefig.bbox': 'tight',
    })

# Cipher registry
CIPHER_REGISTRY = {}

def register_cipher(name):
    """Decorator to register a cipher class."""
    def decorator(cls):
        CIPHER_REGISTRY[name] = cls
        return cls
    return decorator

def get_cipher(name):
    """Get a cipher class by name."""
    if name not in CIPHER_REGISTRY:
        raise ValueError(f"Unknown cipher: {name}. Available: {list(CIPHER_REGISTRY.keys())}")
    return CIPHER_REGISTRY[name]

def list_ciphers():
    """List all registered ciphers."""
    return list(CIPHER_REGISTRY.keys())