File size: 5,352 Bytes
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
"""
Input representation strategies for ML-based cryptanalysis.
Supports the 10 representations required in the project.
"""
import numpy as np

def _bits(arr, block_size):
    """Convert integer array to bit array (shape: [N, block_size])."""
    if block_size > 64:
        # Slow but safe path for >64 bits
        bits = np.zeros((len(arr), block_size), dtype=np.float32)
        for i, val in enumerate(arr):
            for j in range(block_size):
                bits[i, block_size - 1 - j] = (val >> j) & 1
        return bits
    else:
        masks = 1 << np.arange(block_size - 1, -1, -1, dtype=np.uint64)
        if not isinstance(arr, np.ndarray):
            arr = np.array(arr, dtype=np.uint64)
        else:
            arr = arr.astype(np.uint64)
        bits = (arr[:, None] & masks) > 0
        return bits.astype(np.float32)

def prepare_representation(dataset, rep_type_id, block_size=None):
    """
    Format a dataset into one of 10 representations.
    
    1: Raw Ciphertext Pairs (C, C')
    2: Ciphertext Difference (ΔC = C ⊕ C')
    3: Concatenated (C || C')
    4: Bit-Sliced (each bit as a feature channel)
    5: Word-Level
    6: Intermediate Differences (White-Box)
    7: Masked / Noisy
    8: Joint Plaintext-Ciphertext (P, C, P⊕ΔP, C')
    9: Statistical Feature Vectors (Hamming weight, etc.)
    10: Sequential / Round-Wise (for recurrent models)
    """
    C = dataset['C'].astype(np.uint64)
    C_p = dataset['C_prime'].astype(np.uint64)
    P = dataset['P'].astype(np.uint64)
    P_p = dataset['P_prime'].astype(np.uint64)
    N = len(C)
    bs = block_size or dataset.get('block_size', 64)
    
    if rep_type_id == 1:
        # 1: Raw Ciphertext Pairs as bits: [N, 2, block_size]
        bits_c = _bits(C, bs)
        bits_cp = _bits(C_p, bs)
        return np.stack([bits_c, bits_cp], axis=1)
        
    elif rep_type_id == 2:
        # 2: Ciphertext Difference ΔC = C ⊕ C'
        diff = C ^ C_p
        return _bits(diff, bs)
        
    elif rep_type_id == 3:
        # 3: Concatenated Representation (C || C'): [N, 2 * block_size]
        bits_c = _bits(C, bs)
        bits_cp = _bits(C_p, bs)
        return np.concatenate([bits_c, bits_cp], axis=1)
        
    elif rep_type_id == 4:
        # 4: Bit-Sliced Representation: [N, 2, block_size]
        # Treat ciphertext bits as separate feature channels
        bits_c = _bits(C, bs)
        bits_cp = _bits(C_p, bs)
        return np.stack([bits_c, bits_cp], axis=1)
        
    elif rep_type_id == 5:
        # 5: Word-Level (assume 16-bit words)
        words_c = np.stack([(C >> (i * 16)) & 0xFFFF for i in range(bs//16)], axis=1)
        words_cp = np.stack([(C_p >> (i * 16)) & 0xFFFF for i in range(bs//16)], axis=1)
        # Normalize arbitrarily by max word val to be ML-friendly
        return np.concatenate([words_c, words_cp], axis=1).astype(np.float32) / 65535.0
        
    elif rep_type_id == 6:
        # 6: Intermediate Differences
        if 'intermediates_P' not in dataset:
            raise ValueError("Dataset does not contain intermediates.")
        ints_p = np.array(dataset['intermediates_P'], dtype=np.uint64)
        ints_pp = np.array(dataset['intermediates_P_prime'], dtype=np.uint64)
        diff_ints = ints_p ^ ints_pp
        # Flatten differences across all rounds
        out = []
        for i in range(diff_ints.shape[1]):
            out.append(_bits(diff_ints[:, i], bs))
        return np.concatenate(out, axis=1)
        
    elif rep_type_id == 7:
        # 7: Masked / Noisy Representation (add 10% bit flip noise to diff)
        diff = _bits(C ^ C_p, bs)
        noise = (np.random.rand(*diff.shape) < 0.1).astype(np.float32)
        return np.abs(diff - noise)  # logical XOR for float 1/0
        
    elif rep_type_id == 8:
        # 8: Joint Plaintext-Ciphertext (P, C, P⊕ΔP, C')
        bp = _bits(P, bs)
        bc = _bits(C, bs)
        bpp = _bits(P_p, bs)
        bcp = _bits(C_p, bs)
        return np.concatenate([bp, bc, bpp, bcp], axis=1)
        
    elif rep_type_id == 9:
        # 9: Statistical Feature Vectors
        diff = _bits(C ^ C_p, bs)
        # Features: Hamming weight, HW(C), HW(C_p)
        hw_diff = np.sum(diff, axis=1, keepdims=True) / bs
        hw_c = np.sum(_bits(C, bs), axis=1, keepdims=True) / bs
        hw_cp = np.sum(_bits(C_p, bs), axis=1, keepdims=True) / bs
        # simple bit correlations (e.g., bit i == bit i+1)
        diff_shift = np.roll(diff, 1, axis=1)
        correl = np.sum(diff == diff_shift, axis=1, keepdims=True) / bs
        return np.concatenate([hw_diff, hw_c, hw_cp, correl], axis=1)
        
    elif rep_type_id == 10:
        # 10: Sequential / Round-Wise (for RNNs): [N, num_rounds, block_size]
        if 'intermediates_P' not in dataset:
            raise ValueError("Dataset does not contain intermediates.")
        ints_p = np.array(dataset['intermediates_P'], dtype=np.uint64)
        ints_pp = np.array(dataset['intermediates_P_prime'], dtype=np.uint64)
        diff_ints = ints_p ^ ints_pp
        # Shape: [N, rounds, block_size]
        N, R = diff_ints.shape
        out = np.zeros((N, R, bs), dtype=np.float32)
        for i in range(R):
            out[:, i, :] = _bits(diff_ints[:, i], bs)
        return out
        
    else:
        raise ValueError(f"Unknown representation ID: {rep_type_id}")