imagestego 0.1.0__tar.gz

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.
Files changed (27) hide show
  1. imagestego-0.1.0/LICENSE +0 -0
  2. imagestego-0.1.0/PKG-INFO +38 -0
  3. imagestego-0.1.0/README.md +21 -0
  4. imagestego-0.1.0/pyproject.toml +32 -0
  5. imagestego-0.1.0/setup.cfg +4 -0
  6. imagestego-0.1.0/setup.py +3 -0
  7. imagestego-0.1.0/src/ImageStego/__init__.py +0 -0
  8. imagestego-0.1.0/src/ImageStego/obfuscation/__init__.py +67 -0
  9. imagestego-0.1.0/src/ImageStego/obfuscation/arnold_cat_map.py +130 -0
  10. imagestego-0.1.0/src/ImageStego/obfuscation/bakers_map.py +244 -0
  11. imagestego-0.1.0/src/ImageStego/obfuscation/block_scramble.py +168 -0
  12. imagestego-0.1.0/src/ImageStego/obfuscation/gif_scramble.py +166 -0
  13. imagestego-0.1.0/src/ImageStego/obfuscation/henon_map.py +87 -0
  14. imagestego-0.1.0/src/ImageStego/obfuscation/mp4_scramble.py +82 -0
  15. imagestego-0.1.0/src/ImageStego/obfuscation/pixel_scramble.py +97 -0
  16. imagestego-0.1.0/src/ImageStego/steganography/__init__.py +16 -0
  17. imagestego-0.1.0/src/ImageStego/steganography/lsb.py +195 -0
  18. imagestego-0.1.0/src/ImageStego/steganography/lsb_edge.py +168 -0
  19. imagestego-0.1.0/src/ImageStego/utils.py +0 -0
  20. imagestego-0.1.0/src/gui/__init__.py +0 -0
  21. imagestego-0.1.0/src/gui/gui_demo.py +675 -0
  22. imagestego-0.1.0/src/imagestego.egg-info/PKG-INFO +38 -0
  23. imagestego-0.1.0/src/imagestego.egg-info/SOURCES.txt +25 -0
  24. imagestego-0.1.0/src/imagestego.egg-info/dependency_links.txt +1 -0
  25. imagestego-0.1.0/src/imagestego.egg-info/entry_points.txt +2 -0
  26. imagestego-0.1.0/src/imagestego.egg-info/requires.txt +4 -0
  27. imagestego-0.1.0/src/imagestego.egg-info/top_level.txt +2 -0
File without changes
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.1
2
+ Name: imagestego
3
+ Version: 0.1.0
4
+ Summary: Multimedia Obfuscation & Steganography Tool
5
+ Author-email: Equinox_Wangstu <wangstu@126.com>
6
+ License: MIT
7
+ Classifier: Programming Language :: Python :: 3
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.8
11
+ Description-Content-Type: text/markdown
12
+ License-File: LICENSE
13
+ Requires-Dist: pywebio
14
+ Requires-Dist: Pillow
15
+ Requires-Dist: moviepy
16
+ Requires-Dist: cryptography
17
+
18
+ # ImageStego
19
+ ImageStego may be a versatile multimedia processing tool, it features an intuitive web interface built with PyWebIO
20
+ ## Features
21
+
22
+ - **Image Obfuscation**
23
+ Multiple scrambling algorithms to distort images:
24
+ - Pixel Scramble
25
+ - Block Scramble
26
+ - Arnold Cat Map
27
+ - Baker's Map
28
+ - Henon Map
29
+
30
+ - **GIF Obfuscation**
31
+ Frame‑wise scrambling of animated GIFs
32
+
33
+ - **MP4 Obfuscation**
34
+ Video‑level scrambling using frame permutation.
35
+
36
+ - **Image Steganography (LSB)**
37
+ Hide secret messages inside PNG/JPG/BMP images:
38
+
@@ -0,0 +1,21 @@
1
+ # ImageStego
2
+ ImageStego may be a versatile multimedia processing tool, it features an intuitive web interface built with PyWebIO
3
+ ## Features
4
+
5
+ - **Image Obfuscation**
6
+ Multiple scrambling algorithms to distort images:
7
+ - Pixel Scramble
8
+ - Block Scramble
9
+ - Arnold Cat Map
10
+ - Baker's Map
11
+ - Henon Map
12
+
13
+ - **GIF Obfuscation**
14
+ Frame‑wise scrambling of animated GIFs
15
+
16
+ - **MP4 Obfuscation**
17
+ Video‑level scrambling using frame permutation.
18
+
19
+ - **Image Steganography (LSB)**
20
+ Hide secret messages inside PNG/JPG/BMP images:
21
+
@@ -0,0 +1,32 @@
1
+ [build-system]
2
+ requires = ["setuptools==69.5.1", "wheel==0.43.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "imagestego"
7
+ version = "0.1.0"
8
+ description = "Multimedia Obfuscation & Steganography Tool"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Equinox_Wangstu", email = "wangstu@126.com"},
12
+ ]
13
+ license = {text = "MIT"}
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+ requires-python = ">=3.8"
20
+ dependencies = [
21
+ "pywebio",
22
+ "Pillow",
23
+ "moviepy",
24
+ "cryptography",
25
+ ]
26
+
27
+ [project.scripts]
28
+ image-stego-gui = "gui.gui_demo:main"
29
+
30
+ [tool.setuptools.packages.find]
31
+ where = ["src"]
32
+ include = ["ImageStego*", "gui*"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,3 @@
1
+ from setuptools import setup
2
+
3
+ setup()
File without changes
@@ -0,0 +1,67 @@
1
+ # src/ImageStego/obfuscation/__init__.py
2
+
3
+ """
4
+ obfuscation 模块:提供各种图像混淆方法
5
+ """
6
+
7
+ ## 图片相关
8
+ # 像素级随机置换
9
+ from .pixel_scramble import (
10
+ obfuscate as pixel_obfuscate,
11
+ deobfuscate as pixel_deobfuscate,
12
+ )
13
+
14
+ # 块级随机置换
15
+ from .block_scramble import (
16
+ obfuscate as block_obfuscate,
17
+ deobfuscate as block_deobfuscate,
18
+ )
19
+
20
+ # ArnoldCat
21
+ from .arnold_cat_map import (
22
+ obfuscate as arnold_cat_obfuscate,
23
+ deobfuscate as arnold_cat_deobfuscate,
24
+ )
25
+
26
+ #BakerMap
27
+ from .bakers_map import (
28
+ obfuscate as bakers_obfuscate,
29
+ deobfuscate as bakers_deobfuscate,
30
+ )
31
+
32
+ #HenonMap
33
+ from .henon_map import (
34
+ obfuscate as henon_obfuscate,
35
+ deobfuscate as henon_deobfuscate,
36
+ )
37
+
38
+ ## 视频相关
39
+ #gif
40
+ from .gif_scramble import (
41
+ obfuscate as obfuscate_gif,
42
+ deobfuscate as deobfuscate_gif,
43
+ )
44
+
45
+ #mp4
46
+ from .mp4_scramble import (
47
+ obfuscate as obfuscate_mp4,
48
+ deobfuscate as deobfuscate_mp4,
49
+ )
50
+
51
+ # 可以加一个方便的 __all__,控制 from obfuscation import * 的行为
52
+ __all__ = [
53
+ "pixel_obfuscate",
54
+ "pixel_deobfuscate",
55
+ "block_obfuscate",
56
+ "block_deobfuscate",
57
+ "arnold_cat_obfuscate",
58
+ "arnold_cat_deobfuscate",
59
+ "bakers_obfuscate",
60
+ "bakers_deobfuscate",
61
+ "henon_obfuscate",
62
+ "henon_deobfuscate",
63
+ "obfuscate_gif",
64
+ "deobfuscate_gif",
65
+ "obfuscate_mp4",
66
+ "deobfuscate_mp4",
67
+ ]
@@ -0,0 +1,130 @@
1
+ import numpy as np
2
+ from PIL import Image, PngImagePlugin
3
+ import time
4
+ import hashlib
5
+ from typing import Optional, Tuple
6
+
7
+
8
+ def _generate_final_seed(seed: Optional[int], timestamp: Optional[int] = None) -> int:
9
+ parts = [str(seed) if seed is not None else "default"]
10
+ if timestamp is not None:
11
+ parts.append(str(timestamp))
12
+ combined = "_".join(parts)
13
+ return int(hashlib.sha256(combined.encode()).hexdigest(), 16) % (2**32)
14
+
15
+
16
+ def _pad_to_square(img: Image.Image) -> Tuple[Image.Image, int, int, int, int]:
17
+ width, height = img.size
18
+ side = max(width, height)
19
+ delta_w = side - width
20
+ delta_h = side - height
21
+ pad_left = delta_w // 2
22
+ pad_top = delta_h // 2
23
+ pad_right = delta_w - pad_left
24
+ pad_bottom = delta_h - pad_top
25
+ padded_img = Image.new("RGB", (side, side), (128, 128, 128))
26
+ padded_img.paste(img, (pad_left, pad_top))
27
+ return padded_img, width, height, pad_left, pad_top
28
+
29
+
30
+ def obfuscate(
31
+ image_path: str,
32
+ output_path: str,
33
+ iterations: int = 50,
34
+ seed: Optional[int] = None,
35
+ use_timestamp: bool = True
36
+ ) -> Optional[int]:
37
+ img = Image.open(image_path).convert("RGB")
38
+ padded_img, orig_width, orig_height, pad_left, pad_top = _pad_to_square(img)
39
+ n = padded_img.size[0]
40
+
41
+ pixels = np.array(padded_img)
42
+
43
+ timestamp = int(time.time()) if use_timestamp else None
44
+ final_seed = _generate_final_seed(seed, timestamp)
45
+
46
+ # 正向迭代
47
+ for _ in range(iterations):
48
+ new_pixels = np.zeros_like(pixels)
49
+ for y in range(n):
50
+ for x in range(n):
51
+ new_x = (2 * x + y) % n
52
+ new_y = (x + y) % n
53
+ new_pixels[new_y, new_x] = pixels[y, x]
54
+ pixels = new_pixels
55
+
56
+ scrambled_img = Image.fromarray(pixels)
57
+
58
+ info = PngImagePlugin.PngInfo()
59
+ if timestamp is not None:
60
+ info.add_text("arnold_timestamp", str(timestamp))
61
+ info.add_text("arnold_iterations", str(iterations))
62
+ info.add_text("orig_width", str(orig_width))
63
+ info.add_text("orig_height", str(orig_height))
64
+ info.add_text("padded_size", str(n))
65
+ info.add_text("pad_left", str(pad_left))
66
+ info.add_text("pad_top", str(pad_top))
67
+
68
+ scrambled_img.save(output_path, "PNG", pnginfo=info)
69
+ print(f"Arnold 猫映射混淆完成,已保存到:{output_path}")
70
+ print(f"迭代次数: {iterations},方形边长: {n}")
71
+ if timestamp is not None:
72
+ print(f"timestamp 已嵌入: {timestamp}")
73
+
74
+ return timestamp
75
+
76
+
77
+ def deobfuscate(
78
+ obf_image_path: str,
79
+ output_path: str,
80
+ iterations: Optional[int] = None,
81
+ seed: Optional[int] = None,
82
+ timestamp: Optional[int] = None
83
+ ) -> None:
84
+ img = Image.open(obf_image_path).convert("RGB")
85
+ pixels = np.array(img)
86
+ n = pixels.shape[0]
87
+
88
+ # 读取元数据
89
+ ts_from_meta = None
90
+ ts_str = img.info.get("arnold_timestamp")
91
+ if ts_str:
92
+ ts_from_meta = int(ts_str)
93
+
94
+ iters_from_meta = None
95
+ iters_str = img.info.get("arnold_iterations")
96
+ if iters_str:
97
+ iters_from_meta = int(iters_str)
98
+
99
+ final_timestamp = ts_from_meta if ts_from_meta is not None else timestamp
100
+ final_iterations = iters_from_meta if iters_from_meta is not None else iterations
101
+
102
+ if final_iterations is None:
103
+ raise ValueError("必须提供 iterations 或从元数据读取")
104
+
105
+ orig_width = int(img.info.get("orig_width", n))
106
+ orig_height = int(img.info.get("orig_height", n))
107
+ padded_size = int(img.info.get("padded_size", n))
108
+ pad_left = int(img.info.get("pad_left", 0))
109
+ pad_top = int(img.info.get("pad_top", 0))
110
+
111
+ print(f"恢复参数:iterations={final_iterations}, timestamp={final_timestamp}")
112
+ print(f"原始尺寸: {orig_width}x{orig_height}, padded: {padded_size}")
113
+ print(f"填充位置: pad_left={pad_left}, pad_top={pad_top}")
114
+
115
+ # 逆向迭代
116
+ for _ in range(final_iterations):
117
+ new_pixels = np.zeros_like(pixels)
118
+ for y in range(n):
119
+ for x in range(n):
120
+ prev_x = (x - y) % n
121
+ prev_y = (-x + 2 * y) % n
122
+ new_pixels[prev_y, prev_x] = pixels[y, x]
123
+ pixels = new_pixels
124
+
125
+ # 裁剪回原始尺寸(从填充位置开始裁剪)
126
+ restored_pixels = pixels[pad_top:pad_top + orig_height, pad_left:pad_left + orig_width, :]
127
+ restored_img = Image.fromarray(restored_pixels)
128
+ restored_img.save(output_path, "PNG")
129
+ print(f"Arnold 猫映射恢复完成,已保存到:{output_path}")
130
+ print(f"最终尺寸: {orig_width}x{orig_height}")
@@ -0,0 +1,244 @@
1
+ import numpy as np
2
+ from PIL import Image, PngImagePlugin
3
+ import time
4
+ import hashlib
5
+ from typing import Optional, Tuple
6
+
7
+
8
+ def _pad_to_even_square(img: Image.Image) -> Tuple[Image.Image, int, int, int, int]:
9
+ """
10
+ 将图像填充为正方形,且边长为偶数。
11
+ 返回:(填充后的图像, 原始宽度, 原始高度, 左填充量, 上填充量)
12
+ """
13
+ width, height = img.size
14
+ side = max(width, height)
15
+ if side % 2 != 0:
16
+ side += 1 # 确保为偶数
17
+ delta_w = side - width
18
+ delta_h = side - height
19
+ left = delta_w // 2
20
+ top = delta_h // 2
21
+ # 右和下填充可能多一个像素(当差值不是偶数时)
22
+ right = delta_w - left
23
+ bottom = delta_h - top
24
+ padded_img = Image.new("RGB", (side, side), (128, 128, 128))
25
+ padded_img.paste(img, (left, top))
26
+ return padded_img, width, height, left, top
27
+
28
+
29
+ def _bakers_forward(pixels: np.ndarray, n: int) -> np.ndarray:
30
+ """
31
+ 离散 Baker 映射正向变换(混淆)。
32
+ pixels: (n, n, 3) 的 numpy 数组,n 为偶数。
33
+ """
34
+ new_pixels = np.zeros_like(pixels)
35
+ half = n // 2
36
+ for y in range(n):
37
+ for x in range(n):
38
+ if x < half:
39
+ new_x = 2 * x
40
+ new_y = y
41
+ else:
42
+ new_x = 2 * x - n + 1
43
+ new_y = y + half
44
+ # 理论上 new_x, new_y 都在 [0, n-1] 内,取模仅为边界安全
45
+ new_pixels[new_y % n, new_x % n] = pixels[y, x]
46
+ return new_pixels
47
+
48
+
49
+ def _bakers_inverse(pixels: np.ndarray, n: int) -> np.ndarray:
50
+ """
51
+ 离散 Baker 映射逆向变换(恢复)。
52
+ pixels: (n, n, 3) 的 numpy 数组,n 为偶数。
53
+ """
54
+ new_pixels = np.zeros_like(pixels)
55
+ half = n // 2
56
+ for y in range(n):
57
+ for x in range(n):
58
+ if x % 2 == 0:
59
+ orig_x = x // 2
60
+ orig_y = y
61
+ else:
62
+ orig_x = (x + n - 1) // 2
63
+ orig_y = y - half
64
+ # 对于正确生成的混淆图像,orig_x, orig_y 均在 [0, n-1] 内
65
+ new_pixels[orig_y % n, orig_x % n] = pixels[y, x]
66
+ return new_pixels
67
+
68
+
69
+ def _derive_key(password: Optional[str], seed: Optional[int]) -> Optional[bytes]:
70
+ """
71
+ 根据密码和种子派生密钥(SHA256)。
72
+ 如果两者均为 None,则返回 None。
73
+ """
74
+ if password is None and seed is None:
75
+ return None
76
+ material = ''
77
+ if password:
78
+ material += password
79
+ if seed is not None:
80
+ material += str(seed)
81
+ return hashlib.sha256(material.encode()).digest()
82
+
83
+
84
+ def _apply_xor(pixels: np.ndarray, key: bytes) -> np.ndarray:
85
+ """
86
+ 对像素数组应用逐字节异或(原地修改并返回)。
87
+ pixels: 形状为 (H, W, C) 的 uint8 数组。
88
+ key: 字节密钥。
89
+ """
90
+ key_array = np.frombuffer(key, dtype=np.uint8)
91
+ # 将密钥扩展为像素数组大小
92
+ repeats = (pixels.size + key_array.size - 1) // key_array.size
93
+ key_expanded = np.tile(key_array, repeats)[:pixels.size]
94
+ key_expanded = key_expanded.reshape(pixels.shape)
95
+ # 异或操作(numpy 自动按元素进行)
96
+ return np.bitwise_xor(pixels, key_expanded)
97
+
98
+
99
+ def obfuscate(
100
+ image_path: str,
101
+ output_path: str,
102
+ iterations: int = 20,
103
+ seed: Optional[int] = None,
104
+ password: Optional[str] = None,
105
+ use_timestamp: bool = True
106
+ ) -> Optional[int]:
107
+ """
108
+ 对图像进行 Baker 混淆(可选加密层),并保存为 PNG(含元数据)。
109
+ - image_path: 输入图像路径
110
+ - output_path: 输出混淆图像路径(PNG 格式)
111
+ - iterations: 混淆迭代次数
112
+ - seed: 种子,与 password 一起用于派生加密密钥(可选)
113
+ - password: 密码,用于派生加密密钥(可选)
114
+ - use_timestamp: 是否将当前时间戳嵌入元数据
115
+ 返回:嵌入的时间戳(若 use_timestamp=True),否则 None
116
+ """
117
+ img = Image.open(image_path).convert("RGB")
118
+ padded_img, orig_width, orig_height, left_pad, top_pad = _pad_to_even_square(img)
119
+ n = padded_img.size[0] # 此时 n 为偶数
120
+
121
+ pixels = np.array(padded_img)
122
+
123
+ # 若提供了密码或种子,则对像素进行异或加密
124
+ key = _derive_key(password, seed)
125
+ if key is not None:
126
+ pixels = _apply_xor(pixels, key)
127
+ encrypted_flag = "1"
128
+ else:
129
+ encrypted_flag = "0"
130
+
131
+ timestamp = int(time.time()) if use_timestamp else None
132
+
133
+ for _ in range(iterations):
134
+ pixels = _bakers_forward(pixels, n)
135
+
136
+ scrambled_img = Image.fromarray(pixels)
137
+
138
+ # 保存元数据
139
+ info = PngImagePlugin.PngInfo()
140
+ if timestamp is not None:
141
+ info.add_text("bakers_timestamp", str(timestamp))
142
+ info.add_text("bakers_iterations", str(iterations))
143
+ info.add_text("orig_width", str(orig_width))
144
+ info.add_text("orig_height", str(orig_height))
145
+ info.add_text("padded_size", str(n))
146
+ info.add_text("left_pad", str(left_pad))
147
+ info.add_text("top_pad", str(top_pad))
148
+ info.add_text("bakers_encrypted", encrypted_flag) # 标记是否加密
149
+
150
+ scrambled_img.save(output_path, "PNG", pnginfo=info)
151
+ print(f"Baker 混淆完成,已保存到:{output_path}")
152
+ print(f"迭代次数: {iterations},填充后边长: {n}")
153
+ if key is not None:
154
+ print("加密层已应用(使用 password/seed)")
155
+ if timestamp is not None:
156
+ print(f"时间戳已嵌入: {timestamp}")
157
+
158
+ return timestamp
159
+
160
+
161
+ def deobfuscate(
162
+ obf_image_path: str,
163
+ output_path: str,
164
+ iterations: Optional[int] = None,
165
+ seed: Optional[int] = None,
166
+ timestamp: Optional[int] = None,
167
+ password: Optional[str] = None
168
+ ) -> None:
169
+ """
170
+ 恢复 Baker 混淆的图像(若加密则需要提供相同的 password/seed)。
171
+ - obf_image_path: 混淆图像路径(PNG,应包含元数据)
172
+ - output_path: 输出恢复图像路径
173
+ - iterations: 若未提供,则从元数据读取
174
+ - seed: 种子,用于派生解密密钥(若图像加密)
175
+ - timestamp: 未使用(仅用于接口兼容)
176
+ - password: 密码,用于派生解密密钥(若图像加密)
177
+ """
178
+ img = Image.open(obf_image_path).convert("RGB")
179
+ pixels = np.array(img)
180
+ n = pixels.shape[0]
181
+
182
+ # 从元数据读取参数
183
+ iters_str = img.info.get("bakers_iterations")
184
+ if iters_str is None:
185
+ raise ValueError("无法从图像元数据中读取迭代次数")
186
+ final_iterations = int(iters_str)
187
+
188
+ orig_width = int(img.info.get("orig_width", n))
189
+ orig_height = int(img.info.get("orig_height", n))
190
+ padded_size = int(img.info.get("padded_size", n))
191
+ left_pad = int(img.info.get("left_pad", 0))
192
+ top_pad = int(img.info.get("top_pad", 0))
193
+ encrypted_flag = img.info.get("bakers_encrypted", "0")
194
+
195
+ # 边长校验
196
+ if n != padded_size:
197
+ print(f"警告: 当前图像边长 {n} 与元数据记录的填充边长 {padded_size} 不符,将使用当前边长 {n} 进行恢复")
198
+
199
+ ts_from_meta = img.info.get("bakers_timestamp")
200
+ print(f"恢复参数:iterations={final_iterations}, 元数据时间戳={ts_from_meta}, 加密标记={encrypted_flag}")
201
+
202
+ # 逆向 Baker 迭代
203
+ for _ in range(final_iterations):
204
+ pixels = _bakers_inverse(pixels, n)
205
+
206
+ # 如果图像加密了,则需要解密
207
+ if encrypted_flag == "1":
208
+ key = _derive_key(password, seed)
209
+ if key is None:
210
+ raise ValueError("图像已加密,但未提供 password 或 seed,无法解密")
211
+ pixels = _apply_xor(pixels, key)
212
+ print("解密层已应用")
213
+
214
+ # 根据保存的填充偏移量裁剪原始图像区域
215
+ restored_pixels = pixels[top_pad:top_pad + orig_height, left_pad:left_pad + orig_width, :]
216
+ restored_img = Image.fromarray(restored_pixels)
217
+ restored_img.save(output_path, "PNG")
218
+ print(f"Baker 恢复完成,已保存到:{output_path}")
219
+ print(f"最终尺寸: {orig_width} x {orig_height}")
220
+
221
+
222
+ # 可选:添加一个简单的测试函数,验证正向和逆向变换是否互逆(不包含加密层)
223
+ def test_bakers_map(n: int = 10):
224
+ """测试 n 为偶数时的互逆性"""
225
+ if n % 2 != 0:
226
+ n += 1
227
+ original = np.random.randint(0, 256, (n, n, 3), dtype=np.uint8)
228
+ forward = _bakers_forward(original, n)
229
+ inverse = _bakers_inverse(forward, n)
230
+ if np.array_equal(original, inverse):
231
+ print("测试通过:正向和逆向 Baker 变换互逆")
232
+ else:
233
+ print("测试失败:正向和逆向 Baker 变换不互逆")
234
+ diff = np.sum(original != inverse)
235
+ print(f"差异像素数:{diff}")
236
+
237
+
238
+ if __name__ == "__main__":
239
+ # 测试 Baker 映射本身(可选)
240
+ # test_bakers_map(128)
241
+
242
+ # 使用示例(混淆 + 密码)
243
+
244
+ pass