Spaces:
Sleeping
Sleeping
| """ | |
| 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}") | |