ipodsync 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ """Runtime for the transpiled generate_* functions of hashAB.
2
+
3
+ `U32` reproduces the semantics of C's 32-bit unsigned arithmetic: every operation
4
+ is masked to 2^32, `>>` is a logical shift (in the source all shifts are unsigned),
5
+ and `__index__` lets a value be used as a list index. The transpiler
6
+ (tools/transpile_hashab.py) emits nearly literal Python where every literal is
7
+ U32(...), and this class holds the semantics. The generate_buffer_from_state_mixing
8
+ helpers are ported by hand (they're short).
9
+ """
10
+ from __future__ import annotations
11
+
12
+ MASK = 0xFFFFFFFF
13
+
14
+
15
+ def _v(x) -> int:
16
+ return x.v if isinstance(x, U32) else (x & MASK)
17
+
18
+
19
+ # --- narrow C casts (NOT identity: truncation/sign extension) --------------------
20
+ def u8c(x): return U32(_v(x) & 0xFF)
21
+ def u16c(x): return U32(_v(x) & 0xFFFF)
22
+
23
+
24
+ def i8c(x):
25
+ b = _v(x) & 0xFF
26
+ return U32(b - 0x100 if b & 0x80 else b)
27
+
28
+
29
+ def i16c(x):
30
+ w = _v(x) & 0xFFFF
31
+ return U32(w - 0x10000 if w & 0x8000 else w)
32
+
33
+
34
+ # --- byte_helpers.h macros ------------------------------------------------------
35
+ def LOBYTE(x): return U32(_v(x) & 0xFF)
36
+ def BYTE1(x): return U32((_v(x) >> 8) & 0xFF)
37
+ def BYTE2(x): return U32((_v(x) >> 16) & 0xFF)
38
+ def HIBYTE(x): return U32((_v(x) >> 24) & 0xFF)
39
+ def LOWORD(x): return U32(_v(x) & 0xFFFF)
40
+ def HIWORD(x): return U32((_v(x) >> 16) & 0xFFFF)
41
+
42
+
43
+ class U32:
44
+ __slots__ = ("v",)
45
+
46
+ def __init__(self, v: int):
47
+ self.v = (v.v if type(v) is U32 else v) & MASK
48
+
49
+ # arithmetic (all results mod 2^32; sign doesn't matter for + - * ^ & | ~ <<)
50
+ def __add__(s, o): return U32(s.v + _v(o))
51
+ def __radd__(s, o): return U32(_v(o) + s.v)
52
+ def __sub__(s, o): return U32(s.v - _v(o))
53
+ def __rsub__(s, o): return U32(_v(o) - s.v)
54
+ def __mul__(s, o): return U32(s.v * _v(o))
55
+ def __rmul__(s, o): return U32(_v(o) * s.v)
56
+ def __and__(s, o): return U32(s.v & _v(o))
57
+ def __rand__(s, o): return U32(s.v & _v(o))
58
+ def __or__(s, o): return U32(s.v | _v(o))
59
+ def __ror__(s, o): return U32(s.v | _v(o))
60
+ def __xor__(s, o): return U32(s.v ^ _v(o))
61
+ def __rxor__(s, o): return U32(s.v ^ _v(o))
62
+ def __lshift__(s, o): return U32(s.v << _v(o))
63
+ def __rlshift__(s, o): return U32(_v(o) << s.v)
64
+ def __rshift__(s, o): return U32(s.v >> _v(o)) # logical (s.v >= 0)
65
+ def __rrshift__(s, o): return U32(_v(o) >> s.v)
66
+ def __floordiv__(s, o): return U32(s.v // _v(o)) # unsigned division
67
+ def __rfloordiv__(s, o): return U32(_v(o) // s.v)
68
+ def __truediv__(s, o): return U32(s.v // _v(o))
69
+ def __rtruediv__(s, o): return U32(_v(o) // s.v)
70
+ def __mod__(s, o): return U32(s.v % _v(o))
71
+ def __rmod__(s, o): return U32(_v(o) % s.v)
72
+ def __neg__(s): return U32(-s.v)
73
+ def __invert__(s): return U32(~s.v)
74
+
75
+ def __index__(s): return s.v
76
+ def __int__(s): return s.v
77
+ def __eq__(s, o): return s.v == _v(o)
78
+ def __hash__(s): return hash(s.v)
79
+ def __repr__(s): return f"U32(0x{s.v:08x})"
80
+
81
+
82
+ # --- InputSeed + make_seed (from generate_buffer_from_state_mixing.c) ------------
83
+ class InputSeed:
84
+ __slots__ = ("raw", "doubled", "masked_and", "masked_or", "init")
85
+
86
+
87
+ def make_seed(value) -> InputSeed:
88
+ value = U32(value)
89
+ doubled = value * 2 + U32(0xD1EB870E)
90
+ s = InputSeed()
91
+ s.raw = value
92
+ s.doubled = doubled
93
+ s.masked_and = doubled & U32(0xA50FF288)
94
+ s.masked_or = doubled | U32(0xA50FF288)
95
+ s.init = U32(0x44824335) + (U32(-0x2D7806BC) ^ (U32(-0x170A3C79) + value))
96
+ return s
97
+
98
+
99
+ # --- helper functions (ported by hand; they contain no shifts/divisions) --------
100
+ _SLOT_MUL = U32(-0x362C9C11)
101
+ _SLOT_INV = U32(0x362C9C11)
102
+ _COEFF_XOR = U32(0x49D363EF)
103
+ _SLOT_INNER_MUL = U32(-0x1E6C2B0F)
104
+ _COEFF_AND = U32(0x6C593822)
105
+ _INNER_CONST_AND = U32(-0x2E079947)
106
+
107
+
108
+ def slot_mix_add(slot_value, mix_value, mix_mask, offset, tweak):
109
+ diff = (mix_mask - mix_value) * 2 + tweak
110
+ gate = diff & (slot_value * U32(0x3CD8561E) + U32(0x5C0F3290))
111
+ return (offset - slot_value) + mix_value * _SLOT_MUL + mix_mask * _SLOT_INV + gate * _SLOT_MUL
112
+
113
+
114
+ def slot_mix_or(slot_value, term1, term2, offset):
115
+ return slot_value + offset + term1 + (term2 | (slot_value * U32(-0x3CD8561E) + U32(0xA3F0CD6F))) * _SLOT_MUL
116
+
117
+
118
+ def slot_mix_xor(slot_value, term1, term2, offset, xor_const, inner_const):
119
+ return offset - _COEFF_XOR * (xor_const ^ (inner_const + term1 + slot_value * _SLOT_INNER_MUL + term2 * 2))
120
+
121
+
122
+ def slot_mix_and(slot_value, term1_outer, term1_inner, term2, offset, mask):
123
+ return (offset + slot_value + term1_outer + (_COEFF_AND * term2)
124
+ - _COEFF_AND * (mask & (_INNER_CONST_AND + term1_inner + slot_value * _SLOT_INNER_MUL + term2 * 2)))
125
+
126
+
127
+ def masked_step(value, addend, bias, mask):
128
+ nxt = value + addend
129
+ gated = (value * 2 + bias) & mask
130
+ return nxt - gated
131
+
132
+
133
+ def masked_gate(value, gate_bias, mask):
134
+ return (value * 2 + gate_bias) & mask
ipodsync/_hashab_py.py ADDED
@@ -0,0 +1,285 @@
1
+ """Pure-Python port of hashAB (white-box AES) — WITHOUT a native lib.
2
+
3
+ Ported from dstaley/hashab (public domain). The tables (433 KB) live in
4
+ `_hashab_tables.bin` (see tools/gen_hashab_tables.py); the logic is here.
5
+ C overflow semantics are reproduced exactly: everything is computed in Python int
6
+ and masked (u32/u8); signed shifts/divisions are marked explicitly.
7
+
8
+ The port is validated against instrumented C and 100/100 test vectors
9
+ (tests/test_hashab*.py). The orchestrator (phase1/round_state/phase2/final) is here;
10
+ generate_key_material/generate_initial_buffer are in _hashab_gen2 (built by
11
+ tools/build_gen2.py); generate_buffer_from_state_mixing is in _hashab_gen.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import struct
16
+ from importlib import resources
17
+
18
+ from ._hashab_tables import TABLES
19
+
20
+ # --- table loading ---------------------------------------------------------------
21
+ _BLOB = resources.files(__package__).joinpath("_hashab_tables.bin").read_bytes()
22
+
23
+
24
+ def _t(name: str) -> bytes:
25
+ off, ln = TABLES[name]
26
+ return _BLOB[off:off + ln]
27
+
28
+
29
+ INPUT_SBOX = _t("INPUT_SBOX")
30
+ NIBBLE_SBOX_EVEN = _t("NIBBLE_SBOX_EVEN")
31
+ NIBBLE_SBOX_ODD = _t("NIBBLE_SBOX_ODD")
32
+ NIBBLE_SBOX_MAIN = _t("NIBBLE_SBOX_MAIN")
33
+ FINAL_SBOX = _t("FINAL_SBOX")
34
+ OUTPUT_SBOX = _t("OUTPUT_SBOX")
35
+ ROUND_KEYS = _t("ROUND_KEYS")
36
+ MIXCOL_STATE = _t("MIXCOL_STATE")
37
+ MIXCOL_MULT = _t("MIXCOL_MULT")
38
+ FINAL_PERM = _t("FINAL_PERM")
39
+ WB_STATE_EXTRACT = _t("WB_STATE_EXTRACT")
40
+ WB_INPUT_A = _t("WB_INPUT_A")
41
+ WB_INPUT_B = _t("WB_INPUT_B")
42
+ WB_MIX_A = _t("WB_MIX_A")
43
+ WB_MIX_B = _t("WB_MIX_B")
44
+ WB_T_TABLES = _t("WB_T_TABLES")
45
+ WB_T_MIX = _t("WB_T_MIX")
46
+ WB_FINAL_SBOX = _t("WB_FINAL_SBOX")
47
+
48
+ # inline phase 2 tables (from calcHashAB.c)
49
+ LOW_SUB = bytes.fromhex("06030c09020f08050e0b04010a07000d")
50
+ NIBBLE_LO_TABLE = bytes.fromhex(
51
+ "cec0c2c4c7c9cbcacccec1c3c5c4c6c8cbcdcfc1c0c2c4c7c9cbcacccec1c3c5c4c6c8cacd"
52
+ "cfc1c0c2c4c7c9cbcacccec1c3c5c7c6c8cacdcfc1c0c2c4c7c9cbcacccec0c3c5c7c6c8ca"
53
+ "cdcfc1c0c2c4c7c9cbcdcccec0c3c5c7c6c8cacdcfc1c0c2c4c6c9cbcdcccec0c3c5c7c6c8"
54
+ "cacdcfc1c3c2c4c6c9cbcdcccec0c3c5c7c6c8cacccfc1c3c2c4c6c9cbcdcccec0c3c5c7c9"
55
+ "c8cacccfc1c3c2c4c6c9cbcdcccec0c2c5c7c9c8cacccfc1c3c2c4c6c9cbcdcfcec0c2c5c7"
56
+ "c9c8cacccfc1c3c2c4c6c8cbcdcfcec0c2c5c7c9c8cacccfc1c3c5c4c6c8cbcdcfcec0c2c5"
57
+ "c7c9c8cacccec1c3c5c4c6c8cbcdcfcec0c2c5c7c9cbcacccec1c3c5c4c6c8cbcdcf")
58
+ NIBBLE_HI_TABLE = bytes.fromhex(
59
+ "9d78573209e4c3deb5906f4a213c1bf6cda887627954330ee5c0dfba916c4b263d18f7d2a9"
60
+ "84637e55300feac1dcbb966d48270219f4d3ae85607f5a310cebc6ddb897724924031ef5d0"
61
+ "af8a617c5b360de8c7a2b994734e25001ffad1ac8b667d583712e9c4a3be95704f2a011cfb"
62
+ "d6ad886742593413eec5a0bf9a714c2b061df8d7b28964435e3510efcaa1bc9b764d2807e2"
63
+ "f9d4b38e65405f3a11eccba6bd9877522904e3fed5b08f6a415c3b16edc8a7829974532e05"
64
+ "e0ffdab18c6b465d3817f2c9a4839e75502f0ae1fcdbb68d6847223914f3cea5809f7a512c"
65
+ "0be6fdd8b7926944233e15f0cfaa819c7b562d08e7c2d9b4936e45203f1af1ccab86")
66
+ PERMUTATION_TABLE = bytes.fromhex(
67
+ "06181107130d0e09151d021f01041c1a10140b1e030a1b19050f16001217080c")
68
+
69
+ # --- inline constants (from calcHashAB.c) --------------------------------------
70
+ AES_SHIFT_ROWS = (0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11)
71
+ MIXCOL_OFFSETS = (0, 1024, 2048, 3072)
72
+ KEY_STATE_INIT = (0xC9, 0xA1, 0x5C, 0x44, 0x17, 0x4B, 0xFA, 0xCD,
73
+ 0xF3, 0x32, 0x85, 0x94, 0x00, 0x5C, 0x0E, 0x5E)
74
+
75
+
76
+ # --- helpers: exact C uint semantics ---------------------------------------------
77
+ def u8(x: int) -> int:
78
+ return x & 0xFF
79
+
80
+
81
+ def u32(x: int) -> int:
82
+ return x & 0xFFFFFFFF
83
+
84
+
85
+ # --- phase 1: CBC-MAC-like compression (5 blocks of 16 bytes) -------------------
86
+ def _apply_sbox_pair(table: bytes, inp: int, key: int) -> int:
87
+ high = table[(inp & 0xF0) + (key >> 4)]
88
+ low = table[256 + u8((key & 0xF) + 16 * inp)]
89
+ return u8((high << 4) | low)
90
+
91
+
92
+ def _mix_column_byte(ms: bytes, idx0: int, idx1: int, idx2: int, idx3: int, j: int) -> int:
93
+ base = j * 1536
94
+ a = ms[idx0 + j]
95
+ b = ms[idx1 + j]
96
+ c = ms[idx2 + j]
97
+ d = ms[idx3 + j]
98
+ t1 = MIXCOL_MULT[(d & 0xF) + base + 768 + u8(c * 16)] & 0xF
99
+ t2 = MIXCOL_MULT[(b & 0xF) + base + 256 + u8(a * 16)]
100
+ low = MIXCOL_MULT[t1 + base + 1280 + u8(t2 * 16)] & 0xF
101
+ u1 = MIXCOL_MULT[(b >> 4) + (a & 0xF0) + base]
102
+ u2 = MIXCOL_MULT[(c & 0xF0) + base + 512 + (d >> 4)] & 0xF
103
+ high = MIXCOL_MULT[u8(u1 * 16) + base + 1024 + u2]
104
+ return u8(low + 16 * high)
105
+
106
+
107
+ def phase1(input_data: bytes) -> bytes:
108
+ """input_data — 80 bytes (uuid8+sha20+rnd23+padding29). -> phase1_output[80]."""
109
+ key_state = list(KEY_STATE_INIT)
110
+ round_key = ROUND_KEYS
111
+ out = bytearray(80)
112
+
113
+ for block in range(5):
114
+ boff = block * 16
115
+ first_sbox = NIBBLE_SBOX_ODD if block else NIBBLE_SBOX_EVEN
116
+
117
+ cipher_state = [0] * 16
118
+ for i in range(16):
119
+ ti = INPUT_SBOX[input_data[boff + i]]
120
+ cipher_state[i] = _apply_sbox_pair(first_sbox, ti, key_state[i])
121
+
122
+ round_output = [_apply_sbox_pair(NIBBLE_SBOX_MAIN, cipher_state[i], round_key[i])
123
+ for i in range(16)]
124
+
125
+ for rnd in range(1, 10):
126
+ rk = round_key[rnd * 16:]
127
+ perm = [4 * round_output[AES_SHIFT_ROWS[i]] + MIXCOL_OFFSETS[i % 4]
128
+ for i in range(16)]
129
+ for col in range(4):
130
+ idx0 = perm[col * 4 + 0]
131
+ idx1 = perm[col * 4 + 1]
132
+ idx2 = perm[col * 4 + 2]
133
+ idx3 = perm[col * 4 + 3]
134
+ for j in range(4):
135
+ cipher_state[col * 4 + j] = _mix_column_byte(
136
+ MIXCOL_STATE, idx0, idx1, idx2, idx3, j)
137
+ round_output = [_apply_sbox_pair(NIBBLE_SBOX_MAIN, cipher_state[i], rk[i])
138
+ for i in range(16)]
139
+
140
+ permuted = [round_output[AES_SHIFT_ROWS[i]] for i in range(16)]
141
+ for i in range(16):
142
+ cipher_state[i] = _apply_sbox_pair(
143
+ NIBBLE_SBOX_MAIN, FINAL_SBOX[permuted[i]], round_key[160 + i])
144
+
145
+ key_state = cipher_state[:]
146
+ for i in range(16):
147
+ out[boff + i] = OUTPUT_SBOX[cipher_state[i]]
148
+
149
+ return bytes(out)
150
+
151
+
152
+ def build_input_data(sha1: bytes, uuid: bytes, rnd: bytes) -> bytes:
153
+ """80 bytes: uuid(8)+sha1(20)+rnd(23)+padding(29 x 0xA5)."""
154
+ return bytes(uuid) + bytes(sha1) + bytes(rnd) + b"\xa5" * 29
155
+
156
+
157
+ # --- round_state: permutation of phase1_output -> 8 uint32 ----------------------
158
+ def derive_round_state(phase1_output: bytes) -> tuple[list[int], bytes]:
159
+ """-> (round_state[8 int], cipher_block[32 bytes])."""
160
+ cb = bytes(phase1_output[PERMUTATION_TABLE[i] + 44] for i in range(32))
161
+ rs = list(struct.unpack("<8I", cb))
162
+ return rs, cb
163
+
164
+
165
+ def init_target_rnd(rnd: bytes) -> bytearray:
166
+ """target[57]: 03 00 + rnd(23) with mixing + room for phase2."""
167
+ target = bytearray(57)
168
+ target[0] = 3
169
+ target[1] = 0
170
+ target[2:25] = rnd
171
+ for i in range(23):
172
+ x = target[i + 2]
173
+ target[i + 2] = u8(69 * x + 118 * (x & 0x5D) + 17)
174
+ return target
175
+
176
+
177
+ # --- phase 2: white-box AES (2 blocks of 16 bytes) ------------------------------
178
+ def _nibble_lo(x: int) -> int:
179
+ return NIBBLE_LO_TABLE[x]
180
+
181
+
182
+ def _nibble_hi(x: int) -> int:
183
+ return NIBBLE_HI_TABLE[x]
184
+
185
+
186
+ def _phase2_transform(rs: list[int], inp: bytes, tab_in: bytes, tab_mix: bytes) -> bytearray:
187
+ rs_bytes = [0] * 16
188
+ for w in range(4):
189
+ for b in range(4):
190
+ byte_val = (rs[w] >> (b * 8)) & 0xFF
191
+ rs_bytes[w * 4 + b] = WB_STATE_EXTRACT[byte_val + (b << 8) + (w << 10)]
192
+ input_trans = [tab_in[inp[i] + i * 256] for i in range(16)]
193
+ out = bytearray(16)
194
+ for i in range(16):
195
+ base = i * 512
196
+ rs_b = rs_bytes[i]
197
+ in_b = input_trans[i]
198
+ out[i] = u8(16 * tab_mix[(rs_b & 0xF0) + base + (in_b >> 4)]
199
+ + (tab_mix[u8(16 * rs_b + (in_b & 0xF)) + base + 256] & 0xF))
200
+ return out
201
+
202
+
203
+ def phase2(round_state: list[int], ibuff_result: bytes, cipher_block0: bytes) -> bytes:
204
+ """2 iterations of WB-AES. cipher_block0 — the round_state bytes (input to the 2nd iteration).
205
+ -> 32 bytes (into target[25:57])."""
206
+ output = bytearray(32)
207
+ cipher_block = bytearray(cipher_block0)
208
+ for it in range(2):
209
+ inp = ibuff_result if it == 0 else bytes(cipher_block)
210
+ tab_in = WB_INPUT_A if it == 0 else WB_INPUT_B
211
+ tab_mix = WB_MIX_A if it == 0 else WB_MIX_B
212
+ rs = round_state[it * 4: it * 4 + 4]
213
+
214
+ phase2_out = _phase2_transform(rs, inp, tab_in, tab_mix)
215
+ cipher_block[:16] = phase2_out
216
+ mix_state = bytearray(phase2_out)
217
+
218
+ for rnd in range(9):
219
+ round_offset = rnd * 4096
220
+ perm = [4 * (round_offset + mix_state[AES_SHIFT_ROWS[i]] + i * 256)
221
+ for i in range(16)]
222
+ for col in range(4):
223
+ co = col * 4
224
+ idx0 = perm[co + 1]
225
+ idx1 = perm[co + 2]
226
+ idx2 = perm[co + 3]
227
+ idx3 = perm[co]
228
+ for bi in range(4):
229
+ block = rnd * 16 + col * 4 + bi
230
+ t0 = WB_T_TABLES[idx1 + bi]
231
+ t1 = WB_T_TABLES[idx3 + bi]
232
+ t2 = WB_T_TABLES[idx0 + bi]
233
+ t3 = WB_T_TABLES[idx2 + bi]
234
+ hi_nib_t0 = _nibble_hi(t0)
235
+ hi_nib_t1 = _nibble_hi(t1)
236
+ lo_nib_t2 = _nibble_lo(t2)
237
+ lo_nib_t3 = _nibble_lo(t3)
238
+ t0_x27 = u8(27 * t0)
239
+ mixed_t1 = u8(128 + (122 ^ u8(-27 * t1)))
240
+ i1 = block * 1536 + 0 * 256 + ((((hi_nib_t1 >> 4) ^ 0x09) & 0x0F) << 4) | ((lo_nib_t2 ^ 0x0E) & 0x0F)
241
+ i3 = block * 1536 + 1 * 256 + ((((mixed_t1 & 0x0F) ^ 0x0C) << 4) | (((5 * t2 & 0x0F) ^ 0x06) & 0x0F))
242
+ i2 = block * 1536 + 2 * 256 + ((((hi_nib_t0 >> 4) ^ 0x09) & 0x0F) << 4) | ((lo_nib_t3 ^ 0x0E) & 0x0F)
243
+ i0 = block * 1536 + 3 * 256 + (((((t0_x27 & 0x0F) + 7) & 0x0F) ^ 0x1) << 4) | LOW_SUB[t3 & 0x0F]
244
+ i4 = block * 1536 + 4 * 256 + ((WB_T_MIX[i1] & 0x0F) << 4) | (WB_T_MIX[i2] & 0x0F)
245
+ i5 = block * 1536 + 5 * 256 + ((WB_T_MIX[i3] & 0x0F) << 4) | (WB_T_MIX[i0] & 0x0F)
246
+ cipher_block[co + bi] = u8(16 * WB_T_MIX[i4] + (WB_T_MIX[i5] & 0xF))
247
+ if rnd < 8:
248
+ mix_state = bytearray(cipher_block[:16])
249
+
250
+ temp = bytearray(16)
251
+ for i in range(16):
252
+ temp[i] = WB_FINAL_SBOX[cipher_block[AES_SHIFT_ROWS[i]] + i * 256]
253
+ cipher_block[:16] = temp
254
+ output[it * 16: it * 16 + 16] = cipher_block[:16]
255
+ return bytes(output)
256
+
257
+
258
+ def final_permute(target: bytearray) -> bytearray:
259
+ """Final permutation (FINAL_PERM) + nonlinear mixing of bytes 2..56."""
260
+ perm = struct.unpack("<55I", FINAL_PERM)
261
+ for i in range(55):
262
+ p = perm[i] + 2
263
+ target[p], target[i + 2] = target[i + 2], target[p]
264
+ for i in range(55):
265
+ x = target[i + 2]
266
+ y = u8(93 ^ u8(13 * x))
267
+ if x % 2 == 1:
268
+ y ^= 0x80
269
+ target[i + 2] = y
270
+ return target
271
+
272
+
273
+ # --- full pure-Python hashAB -----------------------------------------------------
274
+ def calc_hashab(sha1: bytes, uuid: bytes, rnd: bytes) -> bytes:
275
+ """Pure-Python calcHashAB: SHA1(20)+FirewireGuid(8)+rnd(23) -> 57-byte signature."""
276
+ from ._hashab_gen2 import generate_initial_buffer, generate_key_material
277
+
278
+ p1 = phase1(build_input_data(sha1, uuid, rnd))
279
+ round_state, cipher_block = derive_round_state(p1)
280
+ target = init_target_rnd(rnd)
281
+ expanded = generate_key_material(p1)
282
+ ibuff = generate_initial_buffer(expanded)
283
+ target[25:57] = phase2(round_state, ibuff, cipher_block)
284
+ final_permute(target)
285
+ return bytes(target)
Binary file
@@ -0,0 +1,31 @@
1
+ """hashAB tables manifest: name -> (offset, length) in _hashab_tables.bin.
2
+
3
+ Generated by tools/gen_hashab_tables.py from dstaley/hashab (public domain).
4
+ The bytes are loaded by hashab_py.py via importlib.resources.
5
+ """
6
+
7
+ TABLES = {
8
+ 'FINAL_PERM': (0, 220),
9
+ 'FINAL_SBOX': (220, 256),
10
+ 'GEN190_SBOX': (476, 2048),
11
+ 'GEN190_STATE_TABLE': (2524, 2048),
12
+ 'IBUFF_KEY_TABLE': (4572, 256),
13
+ 'IBUFF_MIX_SEED_A': (4828, 256),
14
+ 'IBUFF_MIX_SEED_B': (5084, 256),
15
+ 'INPUT_SBOX': (5340, 256),
16
+ 'MIXCOL_MULT': (5596, 6144),
17
+ 'MIXCOL_STATE': (11740, 4096),
18
+ 'NIBBLE_SBOX_EVEN': (15836, 512),
19
+ 'NIBBLE_SBOX_MAIN': (16348, 512),
20
+ 'NIBBLE_SBOX_ODD': (16860, 512),
21
+ 'OUTPUT_SBOX': (17372, 256),
22
+ 'ROUND_KEYS': (17628, 176),
23
+ 'WB_FINAL_SBOX': (17804, 4096),
24
+ 'WB_INPUT_A': (21900, 4096),
25
+ 'WB_INPUT_B': (25996, 4096),
26
+ 'WB_MIX_A': (30092, 8192),
27
+ 'WB_MIX_B': (38284, 8192),
28
+ 'WB_STATE_EXTRACT': (46476, 4096),
29
+ 'WB_T_MIX': (50572, 245760),
30
+ 'WB_T_TABLES': (296332, 147456),
31
+ }
@@ -0,0 +1,16 @@
1
+ """Row templates for adding a track to an EMPTY .itlp library.
2
+
3
+ SYNTHETIC: all string fields are blanked (personal data removed); pid/order are
4
+ overwritten in library.add_track. Regenerate with tools/gen_lib_templates.py
5
+ (from a local backup — the backup is not part of the public repo).
6
+ """
7
+
8
+ ITEM_TEMPLATE = {'pid': -9216401694829997841, 'revision_level': None, 'media_kind': 1, 'is_song': 1, 'is_audio_book': 0, 'is_music_video': 0, 'is_movie': 0, 'is_tv_show': 0, 'is_home_video': 0, 'is_ringtone': 0, 'is_tone': 0, 'is_voice_memo': 0, 'is_book': 0, 'is_rental': 0, 'is_itunes_u': 0, 'is_digital_booklet': 0, 'is_podcast': 0, 'date_modified': 758845134, 'year': 2013, 'content_rating': 0, 'content_rating_level': 0, 'is_compilation': 0, 'is_user_disabled': 0, 'remember_bookmark': 0, 'exclude_from_shuffle': 0, 'part_of_gapless_album': 0, 'chosen_by_auto_fill': 0, 'artwork_status': 0, 'artwork_cache_id': 0, 'start_time_ms': 0.0, 'stop_time_ms': 0.0, 'total_time_ms': 224888.0, 'total_burn_time_ms': None, 'track_number': 3, 'track_count': 0, 'disc_number': 0, 'disc_count': 0, 'bpm': 0, 'relative_volume': 0, 'eq_preset': None, 'radio_stream_status': None, 'genius_id': 0, 'genre_id': 12, 'category_id': 0, 'album_pid': -8778576643414911648, 'artist_pid': 3083228396371532061, 'composer_pid': 0, 'title': '', 'artist': '', 'album': '', 'album_artist': '', 'composer': None, 'sort_title': '', 'sort_artist': '', 'sort_album': '', 'sort_album_artist': '', 'sort_composer': None, 'title_order': 89300, 'artist_order': 53900, 'album_order': 32300, 'genre_order': 4700, 'composer_order': 200, 'album_artist_order': 44400, 'album_by_artist_order': None, 'series_name_order': 100, 'comment': None, 'grouping': None, 'description': None, 'description_long': None, 'collection_description': None, 'copyright': None, 'track_artist_pid': 539, 'physical_order': 1245, 'has_lyrics': 0, 'date_released': 0}
9
+
10
+ ARTIST_TEMPLATE = {'pid': -9211478403244814981, 'kind': 2, 'artwork_status': 0, 'artwork_album_pid': 0, 'name': '', 'name_order': 26600, 'sort_name': '', 'is_unknown': 0, 'has_songs': 1, 'has_music_videos': 0, 'has_non_compilation_tracks': 1, 'album_count': 1}
11
+
12
+ ALBUM_TEMPLATE = {'pid': -9152521762437827186, 'kind': 2, 'artwork_status': 0, 'artwork_item_pid': 0, 'artist_pid': -8213557128273776184, 'user_rating': 0, 'name': '', 'name_order': 4600, 'all_compilations': 0, 'feed_url': None, 'season_number': 0, 'is_unknown': 0, 'has_songs': 1, 'has_music_videos': 0, 'sort_order': 4600, 'artist_order': 2600, 'has_any_compilations': 0, 'sort_name': '', 'artist_count_calc': 1, 'has_movies': 0, 'item_count': 1, 'min_volume_normalization_energy': 2438}
13
+
14
+ TRACK_ARTIST_TEMPLATE = {'pid': 1, 'name': '', 'name_order': 100, 'sort_name': '', 'has_songs': 1, 'has_music_videos': 0, 'has_non_compilation_tracks': 1, 'is_unknown': 0, 'album_count': 1}
15
+
16
+ LOCATION_TEMPLATE = {'item_pid': -7383142142304564027, 'sub_id': 0, 'base_location_id': 1, 'location_type': 1179208773, 'location': '', 'extension': 1297101600, 'kind_id': 1, 'date_created': 737588774, 'file_size': 2192482, 'file_creator': None, 'file_type': None, 'num_dir_levels_file': None, 'num_dir_levels_lib': None}