b7i6gf/Hide
GitHub: b7i6gf/Hide
一款使用 LSB 隐写术将加密文本隐藏于图像中的 Python 桌面应用,结合 AES-256-GCM 与 Argon2id 保障数据安全。
Stars: 0 | Forks: 0
# 🔐 Hide - 一款安全的 LSB 隐写术工具
[](https://www.python.org/downloads/)
[](https://github.com/b7i6gf/Hide/blob/main/LICENSE)
[](https://cryptography.io/en/46.0.6/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.algorithms.AES256)
[](https://cryptography.io/en/46.0.6/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.algorithms.AES256)
[](https://github.com/b7i6gf/Hide/releases/tag/v1.2.2)
一款用于使用**最低有效位 (LSB) 隐写术**将文本隐藏在图像中的桌面应用程序,结合了 **AES-256-GCM 认证加密**和 **Argon2id 密钥派生**。最终生成的图像与原图肉眼看起来完全一致,但却包含了一条加密且防篡改的隐藏信息。
## 功能
### 隐写术
- LSB 嵌入 RGB 像素通道 - 视觉上不可见
- 支持 PNG、JPEG、BMP、GIF、TIFF 作为输入
- 输出始终保存为**无损 PNG**,以防止压缩伪影破坏 payload
- 比特流中的 4 字节长度前缀 - 提取时准确读取正确数量的字节,无需猜测
### 加密
- **AES-256-GCM** 认证加密 - 在一个原语中实现机密性 + 完整性
- **Argon2id** 密钥派生(256 MB 内存 · 4 次迭代 · 4 条通道)- 抵抗 GPU 的密码哈希
- 每次操作都会重新生成 **128 位随机盐值** + **96 位随机 nonce** - 即使使用相同的密码和文本,两次嵌入的数据也不可能完全相同
- 在释放任何明文**之前**都会验证 GCM 身份验证标签 - 损坏或被篡改的图像会被拒绝
- 所有标记(起始/结束分隔符)都嵌入**在密文内部** - 原始图像数据中看不到任何结构
- 所有失败路径上的**统一错误消息** - 消除密码学预言机
### 密钥包(`.key` 文件)
- 密码和标记**随机生成** - 用户无需手动输入
- 使用 Argon2id 派生的主密钥通过 AES-256-GCM 加密整个密钥包
- 文件魔数头(`SKBX`)用作 **GCM 附加身份验证数据 (AAD)** - 可检测对文件头的篡改
- 没有主密码,该文件与随机字节无异
### 安全加固
- **解压炸弹保护** - 超过 100 MP(≈ 10 000 × 10 000 px)的图像在任何处理之前都会被拒绝
- 所有随机生成均使用 **`secrets.choice()`** - 密码学上无偏差,无取模偏差
- 使用 **`subprocess.run()`** 而不是 `os.system()` - 在 Windows 上没有 shell 注入向量
- 永远不会将明文密码存储到磁盘
### 性能与架构
- **NumPy 向量化**的位操作 - 比纯 Python 循环快 100-500 倍
- **线程安全的 GUI** - 所有处理都在 worker 线程中运行,主线程仅负责 UI;在处理大型图像时绝不会卡死
- 线程与 GUI 之间的通信完全通过 `queue.Queue` + `root.after()` 轮询进行
## 使用说明
### 隐藏文本
1. 打开 **Hide Text** 选项卡
2. 通过 **Browse** 选择源图像
3. 通过 **Save As** 选择输出路径
4. 输入或粘贴要隐藏的文本
5. 设置**密码**(可选 - 留空表示不加密嵌入)
6. 设置**起始标记**和**结束标记**(payload(即文本)的唯一标识符)
7. 点击 **Hide Text**
### 提取文本
1. 打开 **Extract Text** 选项卡
2. 选择隐写图像
3. 输入嵌入时使用的相同**密码**、**起始标记**和**结束标记**
4. 点击 **Extract Text**
### 生成密钥包
1. 打开 **Tools** 选项卡 → **Generate Key Bundle**
2. 输入**主密码**(这是你唯一需要记住的东西)
3. 确认主密码
4. 选择保存位置 - `.key` 文件将被加密写入(**请将其保存在一个非常非常安全的地方**)
在任何选项卡中使用 **Key File** 按钮加载密钥包。真实的凭据永远不会被显示。使用时必须重新输入**主密码**。
## 安装说明
本程序提供 .exe 或 .py 版本。
如需使用 .py 版本,需要满足以下安装要求。
### 前置条件
- Python 3.8 或更高版本
- pip
### 安装依赖
```
pip install Pillow numpy cryptography
```
### 运行
```
python hide.py
```
## 依赖项
| 包 | 版本 | 用途 |
|---------|---------|---------|
| [Pillow](https://python-pillow.org/) | ≥ 12.0 | 图像加载与保存 |
| [NumPy](https://numpy.org/) | ≥ 2.4 | 向量化位操作 |
| [cryptography](https://cryptography.io/) | ≥ 46.0 | AES-256-GCM, Argon2id |
# 适合那些想了解更多背景信息的人...
## 它是如何工作的? - 技术背景
## 1. 将文本隐藏在图像中 - LSB 隐写术
数字图像由数百万个像素组成。每个像素都有三个颜色通道:**红、绿、蓝 (RGB)**。每个通道都存储为 0 到 255 之间的数字——换言之,即 **8 位**。
```
Example: A slightly reddish pixel
Red: 198 → 1 1 0 0 0 1 1 0
Green: 87 → 0 1 0 1 0 1 1 1
Blue: 92 → 0 1 0 1 1 1 0 0
```
**最低有效位 (LSB)** 是最右边的位——即“最不重要”的那一位。改变它只会使颜色值偏移 ±1。在 0-255 的范围内,这种差异对人眼来说是完全不可见的。红色值 198 和 199 实际上是完全相同的。
### 核心思想
将要隐藏的信息转换为位序列:
```
Letter 'H' = 72 = 0 1 0 0 1 0 0 0
```
然后将这些位逐一写入图像中每个像素通道的 LSB:
```
Pixel 1, Red channel: 1100011[0] → 1100011[0] bit = 0
Pixel 1, Green channel: 0101011[1] → 0101011[1] bit = 1
Pixel 1, Blue channel: 0101110[0] → 0101110[0] bit = 0
Pixel 2, Red channel: 1001101[1] → 1001101[0] bit = 0 ← changed
...
```
结果就是:一张看起来与原图**像素级完美一致**的图像,但其不可见的低位中隐藏着一条完整的加密信息。
### 容量
每个像素可以存储 3 位(每个 RGB 通道 1 位)。一张典型的 1920 × 1080 像素的照片提供:
```
1920 × 1080 × 3 bits = 6,220,800 bits = 777,600 bytes ≈ 760 KB
```
这大约相当于 760,000 个字符的文本——比一整部小说还多。
### 为什么选 PNG 而不是 JPEG?
JPEG 压缩在数学上会改变像素值以减小文件大小。这样就会恰好破坏存储信息的 LSB。PNG 可以无损地保存像素——每一位都完好无损。
## 2. Argon2id - 为什么普通的哈希运算不够
### 弱密码带来的问题
如果有人发现了包含隐藏信息的图像,他们可能会尝试猜测密码。借助现代硬件——尤其是 GPU——每秒可以测试数十亿个密码。像 SHA-256 这样的简单算法在这里几乎提供不了什么保护:它的设计初衷就是为了运行快速,这意味着攻击者可以在一毫秒内尝试数千个密码。
### Argon2id 有何不同
**Argon2id** 是一种**内存困难型函数**——一种故意**消耗大量内存**的算法,目的是让同时测试多个密码变得昂贵且缓慢。
原因很简单:内存无法像原始计算能力那样轻易进行并行化扩展。一个 GPU 可能有数千个核心,但每个核心的内存非常有限。如果每一次密码尝试都需要 256 MB 的内存,那么一块 8 GB 内存的 GPU 只能并行运行 31 次尝试——而不是数十亿次。
### 本应用程序中使用的参数
| 参数 | 值 | 含义 |
|-----------|-------|---------|
| 内存消耗 | 256 MB | 每次解密尝试占用 256 MB 内存 |
| 迭代次数 | 4 | 算法在内存中进行 4 次遍历 |
| 并行度 | 4 lanes | 内部使用 4 个 CPU 线程 |
| 输出 | 32 字节 | 256 位 AES 密钥 |
在典型的台式机硬件上,单次尝试大约需要 **1-2 秒**。对于合法用户来说:几乎感觉不到。对于试图暴力破解数百万个密码的攻击者来说:实际上是徒劳的。
### "id" - 融合两者的优势
Argon2 有三种变体。**Argon2id** 结合了另外两种变体的优势:
- **Argon2d** - 通过数据依赖型的内存访问模式抵抗 GPU 攻击
- **Argon2i** - 通过数据无关型的内存访问模式抵抗侧信道攻击
- **Argon2id** - 前半部分行为类似于 Argon2i,后半部分类似于 Argon2d → 同时防御这两类攻击
Argon2id 在 2015 年赢得了**密码哈希竞赛 (Password Hashing Competition)**,如今已被 RFC 9106 推荐为密码哈希的标准。
### 本应用程序中的流程
```
Password + random Salt (16 bytes)
↓
Argon2id
(256 MB · 4 iter · 4 lanes)
↓
256-bit AES key
↓
AES-256-GCM encryption
```
**盐值**在这里至关重要:每次加密时都会重新随机生成,并存储在图像的 payload 中。即使两条信息使用相同的密码进行加密,每次也会派生出完全不同的密钥——这使得彩虹表和预计算攻击变得毫无意义。
# 关于 **Hide** 中使用的加密方式的清晰图表
## Payload 格式
### 加密的 payload
```
┌─────────────────────────────────────────────────────────────────────┐
│ 4 bytes Length prefix (big-endian uint32) │
├─────────────────────────────────────────────────────────────────────┤
│ 1 byte Version (0x01 = encrypted) │
│ 16 bytes Argon2id salt (random per operation) │
│ 12 bytes AES-GCM nonce (random per operation) │
│ N bytes AES-256-GCM ciphertext │
│ └─ start_marker + plaintext + end_marker (UTF-8) │
│ 16 bytes AES-GCM authentication tag │
└─────────────────────────────────────────────────────────────────────┘
```
### 未加密的 payload
```
┌─────────────────────────────────────────────────────────────────────┐
│ 4 bytes Length prefix (big-endian uint32) │
├─────────────────────────────────────────────────────────────────────┤
│ 1 byte Version (0x00 = plain) │
│ N bytes start_marker + plaintext + end_marker (UTF-8) │
└─────────────────────────────────────────────────────────────────────┘
```
整个流被写入扁平化的 RGB 像素数组的**最低有效位**中。
## 密钥包格式
```
┌──────────────────────────────────────────────────────────────────┐
│ 4 bytes Magic header: SKBX (also used as GCM AAD) │
│ 1 byte Version (0x01) │
│ 16 bytes Argon2id salt │
│ 12 bytes AES-GCM nonce │
│ N bytes AES-256-GCM ciphertext │
│ └─ [PW]>>password\n[mSEQ]>>magic\n[eSEQ]>>end\n │
│ 16 bytes AES-GCM authentication tag │
└──────────────────────────────────────────────────────────────────┘
```
## Argon2id 参数
| 参数 | 值 | 用途 |
|-----------|-------|---------|
| 内存消耗 | 262 144 KB (256 MB) | 增加拥有 GPU / ASIC 的攻击者的成本 |
| 迭代次数 | 4 | 时间硬化 |
| 并行度 | 4 lanes | 线程利用率 |
| 密钥长度 | 32 字节 | 256 位 AES 密钥 |
这些参数遵循 RFC 9106 的“离线”配置文件,以实现强大的抗暴力破解能力。在典型的桌面硬件上,密钥派生大约需要 **1-2 秒**——这是刻意为之的。
## 容量
图像容量取决于分辨率。每个像素通道存储 1 位,因此:
```
capacity (bytes) = (width × height × 3) / 8 − overhead
```
| 图像分辨率 | 大约容量 |
|-----------------|-----------------|
| 800 × 600 | ~175 KB |
| 1920 × 1080 | ~760 KB |
| 3840 × 2160 | ~3 MB |
在你输入时,应用程序会显示实时容量和使用进度条。
## 安全性注意事项
- **隐写可检测性**:顺序的 LSB 嵌入可以通过统计隐写分析工具(卡方检验、RS 分析)检测到。此工具优先考虑**密码学安全性**,而不是隐写的隐蔽性——发现隐藏数据的攻击者在没有密码的情况下仍然无法读取它。
- **密码强度**:Argon2id 提供了强大的抗暴力破解能力,但弱密码终究还是弱密码。请使用密钥包生成器以获得最大的熵。
- **仅限无损输出**:请始终使用 PNG 作为输出格式。JPEG 重压缩会破坏 LSB 数据。
## 免责声明
本软件(“Hide - 一款安全的 LSB 隐写术工具”)按“原样”提供,不附带任何明示或暗示的保证。
作者对以下情况不承担任何责任:
- 任何数据或文件的丢失、损坏或无法访问
- 密码、密钥文件或加密内容的丢失
- 因使用本程序而造成的硬件或软件损坏
- 因使用本软件而引发的法律后果
- 被第三方利用的安全漏洞
- 任何间接的、附带的或后果性的损害
使用本软件的风险完全由你自己承担。用户全权负责安全地存储密码和密钥文件。丢失的密码或密钥文件无法恢复。
本软件仅供合法用途。严禁使用它来绕过安全系统、隐藏非法内容或从事任何其他非法活动。
## 许可证
GNUv3 许可证 - 详情请参阅 [LICENSE](LICENSE)。
## 致谢
使用 Python 3.14 | tkinter | Pillow 12.0.0 | NumPy 2.4.2 | cryptography 46.0.5 构建
### 提取文本
1. 打开 **Extract Text** 选项卡
2. 选择隐写图像
3. 输入嵌入时使用的相同**密码**、**起始标记**和**结束标记**
4. 点击 **Extract Text**
### 生成密钥包
1. 打开 **Tools** 选项卡 → **Generate Key Bundle**
2. 输入**主密码**(这是你唯一需要记住的东西)
3. 确认主密码
4. 选择保存位置 - `.key` 文件将被加密写入(**请将其保存在一个非常非常安全的地方**)
在任何选项卡中使用 **Key File** 按钮加载密钥包。真实的凭据永远不会被显示。使用时必须重新输入**主密码**。
## 安装说明
本程序提供 .exe 或 .py 版本。
如需使用 .py 版本,需要满足以下安装要求。
### 前置条件
- Python 3.8 或更高版本
- pip
### 安装依赖
```
pip install Pillow numpy cryptography
```
### 运行
```
python hide.py
```
## 依赖项
| 包 | 版本 | 用途 |
|---------|---------|---------|
| [Pillow](https://python-pillow.org/) | ≥ 12.0 | 图像加载与保存 |
| [NumPy](https://numpy.org/) | ≥ 2.4 | 向量化位操作 |
| [cryptography](https://cryptography.io/) | ≥ 46.0 | AES-256-GCM, Argon2id |
# 适合那些想了解更多背景信息的人...
## 它是如何工作的? - 技术背景
## 1. 将文本隐藏在图像中 - LSB 隐写术
数字图像由数百万个像素组成。每个像素都有三个颜色通道:**红、绿、蓝 (RGB)**。每个通道都存储为 0 到 255 之间的数字——换言之,即 **8 位**。
```
Example: A slightly reddish pixel
Red: 198 → 1 1 0 0 0 1 1 0
Green: 87 → 0 1 0 1 0 1 1 1
Blue: 92 → 0 1 0 1 1 1 0 0
```
**最低有效位 (LSB)** 是最右边的位——即“最不重要”的那一位。改变它只会使颜色值偏移 ±1。在 0-255 的范围内,这种差异对人眼来说是完全不可见的。红色值 198 和 199 实际上是完全相同的。
### 核心思想
将要隐藏的信息转换为位序列:
```
Letter 'H' = 72 = 0 1 0 0 1 0 0 0
```
然后将这些位逐一写入图像中每个像素通道的 LSB:
```
Pixel 1, Red channel: 1100011[0] → 1100011[0] bit = 0
Pixel 1, Green channel: 0101011[1] → 0101011[1] bit = 1
Pixel 1, Blue channel: 0101110[0] → 0101110[0] bit = 0
Pixel 2, Red channel: 1001101[1] → 1001101[0] bit = 0 ← changed
...
```
结果就是:一张看起来与原图**像素级完美一致**的图像,但其不可见的低位中隐藏着一条完整的加密信息。
### 容量
每个像素可以存储 3 位(每个 RGB 通道 1 位)。一张典型的 1920 × 1080 像素的照片提供:
```
1920 × 1080 × 3 bits = 6,220,800 bits = 777,600 bytes ≈ 760 KB
```
这大约相当于 760,000 个字符的文本——比一整部小说还多。
### 为什么选 PNG 而不是 JPEG?
JPEG 压缩在数学上会改变像素值以减小文件大小。这样就会恰好破坏存储信息的 LSB。PNG 可以无损地保存像素——每一位都完好无损。
## 2. Argon2id - 为什么普通的哈希运算不够
### 弱密码带来的问题
如果有人发现了包含隐藏信息的图像,他们可能会尝试猜测密码。借助现代硬件——尤其是 GPU——每秒可以测试数十亿个密码。像 SHA-256 这样的简单算法在这里几乎提供不了什么保护:它的设计初衷就是为了运行快速,这意味着攻击者可以在一毫秒内尝试数千个密码。
### Argon2id 有何不同
**Argon2id** 是一种**内存困难型函数**——一种故意**消耗大量内存**的算法,目的是让同时测试多个密码变得昂贵且缓慢。
原因很简单:内存无法像原始计算能力那样轻易进行并行化扩展。一个 GPU 可能有数千个核心,但每个核心的内存非常有限。如果每一次密码尝试都需要 256 MB 的内存,那么一块 8 GB 内存的 GPU 只能并行运行 31 次尝试——而不是数十亿次。
### 本应用程序中使用的参数
| 参数 | 值 | 含义 |
|-----------|-------|---------|
| 内存消耗 | 256 MB | 每次解密尝试占用 256 MB 内存 |
| 迭代次数 | 4 | 算法在内存中进行 4 次遍历 |
| 并行度 | 4 lanes | 内部使用 4 个 CPU 线程 |
| 输出 | 32 字节 | 256 位 AES 密钥 |
在典型的台式机硬件上,单次尝试大约需要 **1-2 秒**。对于合法用户来说:几乎感觉不到。对于试图暴力破解数百万个密码的攻击者来说:实际上是徒劳的。
### "id" - 融合两者的优势
Argon2 有三种变体。**Argon2id** 结合了另外两种变体的优势:
- **Argon2d** - 通过数据依赖型的内存访问模式抵抗 GPU 攻击
- **Argon2i** - 通过数据无关型的内存访问模式抵抗侧信道攻击
- **Argon2id** - 前半部分行为类似于 Argon2i,后半部分类似于 Argon2d → 同时防御这两类攻击
Argon2id 在 2015 年赢得了**密码哈希竞赛 (Password Hashing Competition)**,如今已被 RFC 9106 推荐为密码哈希的标准。
### 本应用程序中的流程
```
Password + random Salt (16 bytes)
↓
Argon2id
(256 MB · 4 iter · 4 lanes)
↓
256-bit AES key
↓
AES-256-GCM encryption
```
**盐值**在这里至关重要:每次加密时都会重新随机生成,并存储在图像的 payload 中。即使两条信息使用相同的密码进行加密,每次也会派生出完全不同的密钥——这使得彩虹表和预计算攻击变得毫无意义。
# 关于 **Hide** 中使用的加密方式的清晰图表
## Payload 格式
### 加密的 payload
```
┌─────────────────────────────────────────────────────────────────────┐
│ 4 bytes Length prefix (big-endian uint32) │
├─────────────────────────────────────────────────────────────────────┤
│ 1 byte Version (0x01 = encrypted) │
│ 16 bytes Argon2id salt (random per operation) │
│ 12 bytes AES-GCM nonce (random per operation) │
│ N bytes AES-256-GCM ciphertext │
│ └─ start_marker + plaintext + end_marker (UTF-8) │
│ 16 bytes AES-GCM authentication tag │
└─────────────────────────────────────────────────────────────────────┘
```
### 未加密的 payload
```
┌─────────────────────────────────────────────────────────────────────┐
│ 4 bytes Length prefix (big-endian uint32) │
├─────────────────────────────────────────────────────────────────────┤
│ 1 byte Version (0x00 = plain) │
│ N bytes start_marker + plaintext + end_marker (UTF-8) │
└─────────────────────────────────────────────────────────────────────┘
```
整个流被写入扁平化的 RGB 像素数组的**最低有效位**中。
## 密钥包格式
```
┌──────────────────────────────────────────────────────────────────┐
│ 4 bytes Magic header: SKBX (also used as GCM AAD) │
│ 1 byte Version (0x01) │
│ 16 bytes Argon2id salt │
│ 12 bytes AES-GCM nonce │
│ N bytes AES-256-GCM ciphertext │
│ └─ [PW]>>password\n[mSEQ]>>magic\n[eSEQ]>>end\n │
│ 16 bytes AES-GCM authentication tag │
└──────────────────────────────────────────────────────────────────┘
```
## Argon2id 参数
| 参数 | 值 | 用途 |
|-----------|-------|---------|
| 内存消耗 | 262 144 KB (256 MB) | 增加拥有 GPU / ASIC 的攻击者的成本 |
| 迭代次数 | 4 | 时间硬化 |
| 并行度 | 4 lanes | 线程利用率 |
| 密钥长度 | 32 字节 | 256 位 AES 密钥 |
这些参数遵循 RFC 9106 的“离线”配置文件,以实现强大的抗暴力破解能力。在典型的桌面硬件上,密钥派生大约需要 **1-2 秒**——这是刻意为之的。
## 容量
图像容量取决于分辨率。每个像素通道存储 1 位,因此:
```
capacity (bytes) = (width × height × 3) / 8 − overhead
```
| 图像分辨率 | 大约容量 |
|-----------------|-----------------|
| 800 × 600 | ~175 KB |
| 1920 × 1080 | ~760 KB |
| 3840 × 2160 | ~3 MB |
在你输入时,应用程序会显示实时容量和使用进度条。
## 安全性注意事项
- **隐写可检测性**:顺序的 LSB 嵌入可以通过统计隐写分析工具(卡方检验、RS 分析)检测到。此工具优先考虑**密码学安全性**,而不是隐写的隐蔽性——发现隐藏数据的攻击者在没有密码的情况下仍然无法读取它。
- **密码强度**:Argon2id 提供了强大的抗暴力破解能力,但弱密码终究还是弱密码。请使用密钥包生成器以获得最大的熵。
- **仅限无损输出**:请始终使用 PNG 作为输出格式。JPEG 重压缩会破坏 LSB 数据。
## 免责声明
本软件(“Hide - 一款安全的 LSB 隐写术工具”)按“原样”提供,不附带任何明示或暗示的保证。
作者对以下情况不承担任何责任:
- 任何数据或文件的丢失、损坏或无法访问
- 密码、密钥文件或加密内容的丢失
- 因使用本程序而造成的硬件或软件损坏
- 因使用本软件而引发的法律后果
- 被第三方利用的安全漏洞
- 任何间接的、附带的或后果性的损害
使用本软件的风险完全由你自己承担。用户全权负责安全地存储密码和密钥文件。丢失的密码或密钥文件无法恢复。
本软件仅供合法用途。严禁使用它来绕过安全系统、隐藏非法内容或从事任何其他非法活动。
## 许可证
GNUv3 许可证 - 详情请参阅 [LICENSE](LICENSE)。
## 致谢
使用 Python 3.14 | tkinter | Pillow 12.0.0 | NumPy 2.4.2 | cryptography 46.0.5 构建标签:AES-256-GCM, Argon2id, DNS 反向解析, LSB隐写, Python, 密码学, 手动系统调用, 数据隐写, 无后门, 桌面应用, 逆向工具