cgaffga/phasmcore
GitHub: cgaffga/phasmcore
纯 Rust 实现的 JPEG 和 H.264 隐写术核心库,提供抗检测隐写、鲁棒嵌入和影子消息否认性等多种模式。
Stars: 9 | Forks: 0
# phasm-core
[](https://crates.io/crates/phasm-core)
[](LICENSE)
[](https://www.rust-lang.org)
[](https://phasm.app)
纯 Rust 隐写术引擎,用于在 JPEG 照片中隐藏加密文本消息。
这是 [Phasm](https://phasm.app) 背后的核心库——可在 [iOS](https://apps.apple.com/app/phasm-steganography/id6759446274)、Android 和 [Web](https://phasm.app) 上使用。
## 安装
使用默认功能将库添加到您的 `Cargo.toml` 中(根据 [Via LA AVC](https://www.via-la.com/licensing-2/avc-h-264/) 专利义务,不包含 H.264 编码器):
```
cargo add phasm-core
```
或者安装 CLI 二进制文件:
```
cargo install phasm-cli
```
### 可选的 Cargo 特性
| Feature | 描述 |
|---------|-------------|
| `parallel` | 用于 J-UNIWARD 成本计算、STC 嵌入和 Armor 解码扫描的 [Rayon](https://github.com/rayon-rs/rayon) 并行处理。推荐用于原生构建。 |
| `video` | H.264 视频隐写管道(解码器 + 比特流解析)。 |
| `simd` | 针对 H.264 运动补偿和 SATD 的手工调优 SIMD 内联函数(NEON / AVX2 / WASM SIMD128)。 |
| `h264-encoder` | 纯 Rust 洁净室 H.264 编码器。**实验性——仅限研究使用**;生产环境默认为 OpenH264 (`openh264-backend`)。**默认关闭——仅在根据当地司法管辖区的 [Via LA AVC](https://www.via-la.com/licensing-2/avc-h-264/) 专利池条款进行分发时启用。**仅源码消费免版税;超过免费额度(目前为 100,000 单位/年)的二进制分发则不免。 |
```
cargo add phasm-core --features parallel
cargo install phasm-cli --features h264-encoder # H.264 video stego, source-build only
```
## 两种嵌入模式
### Ghost (隐秘)
针对**不可检测性**进行优化。使用 [J-UNIWARD](https://phasm.app/blog/uerd-vs-juniwird-detection-benchmarks) 成本函数为每个 DCT 系数分配失真成本,然后通过 [Syndrome-Trellis Codes (STC)](https://phasm.app/blog/syndrome-trellis-codes-practical-guide) 进行嵌入以最小化总失真。其结果能够抵抗最先进的隐写分析检测器(SRNet、XedroudjNet)在典型嵌入率下的检测。
当隐写图像将在不重新压缩的情况下存储或传输时,请使用 Ghost 模式——该嵌入无法在 JPEG 重编码后存活。
Ghost 模式还支持**文件附件**(Brotli 压缩,多文件)。
#### 影子消息 (合理的否认性)
Ghost 模式支持**[影子消息](https://phasm.app/blog/shadow-messages-plausible-deniability-steganography)**——隐藏在单张图像中的多条消息,每条消息使用不同的密码。如果被胁迫要求交出密码,您可以提供影子密码而不是主密码。对手解码出一条诱饵消息,并且无法检测是否还存在其他消息。
影子使用带有 Reed-Solomon 纠错的 **Y 通道直接 LSB 嵌入**。核心设计特性:
- **成本池位置选择**——影子位置从成本最低的 UNIWARD 成本区域中通过两层过滤(前 N% 的成本池 + 密钥相关的 ChaCha20 置换)提取,确保修改落在纹理区域以实现最大隐蔽性
- **∞ 成本保护**——当主消息使用动态 w ≥ 2 时,影子位置在 STC Viterbi 网格中获得 `f32::INFINITY` 成本,引导主编码器绕过它们,实现 **BER ≈ 0%**
- **无请求头暴力破解解码**——没有魔术字节或约定参数。“首块窥探”为每个 (分数, 奇偶校验) 组合仅解码第一个 RS 块,以读取 `plaintext_len` 并推导出精确的帧数据长度——**大约 30 次 RS 块解码**,而不是扫描数千个 FDL 值。一个小的 FDL 回退机制负责处理微小消息(单个不完整的 RS 块)。AES-256-GCM-SIV 身份验证是唯一的验证器
- **隐写成本验证**——在 STC 嵌入之后,编码器在隐写图像上重新运行 UNIWARD 以验证影子 BER。如果验证失败,**升级级联**会自动增加 RS 奇偶校验 (4 → 8 → 16 → … → 128),直到影子存活或容量耗尽
`smart_decode` 会自动尝试在主 Ghost 解码之后回退到影子解码。
#### SI-UNIWARD (深度隐藏)
当编码器能够访问原始未压缩像素(例如转换为 JPEG 的 PNG、HEIC 或 RAW 输入)时,**SI-UNIWARD** (Side-Informed UNIWARD) 利用 JPEG 量化舍入误差来大幅降低嵌入成本。那些处于两个量化级别之间“接近边界”的系数可以以接近零的成本被翻转,并且修改方向被选择为*朝向*量化前的值,而不是默认的 nsF5 方向。
结果是:在相同检测风险下拥有**约 43% 的更高容量**,或者在相同容量下具有显著更低的失真。解码器保持完全不变——`ghost_decode` 在标准和 SI-UNIWARD 隐写图像上的工作方式相同。
当原始像素与 JPEG 载体一起可用时,请使用 `ghost_encode_si` / `ghost_capacity_si`。
### Armor (鲁棒)
针对**存活能力**进行优化。使用扩频变换抖动调制 (STDM) 将比特嵌入到稳定的低频 DCT 系数中,并受到[自适应 Reed-Solomon ECC](https://phasm.app/blog/surviving-jpeg-recompression) 的保护,通过[对数似然比的软判决解码](https://phasm.app/blog/soft-majority-voting-llr-concatenated-codes) 实现。[DFT 幅度模板](https://phasm.app/blog/dft-template-geometric-resilience-steganography) 提供针对旋转、缩放和裁剪的几何同步。
Armor 是所有平台上的**默认模式**。
#### Fortress 子模式
对于短消息,Armor 会自动激活 **Fortress**——一种 [BA-QIM (块平均量化索引调制)](https://phasm.app/blog/watson-perceptual-masking-qim-steganography) 方案,它将每个 8x8 块的 1 个比特嵌入到块平均亮度中。Fortress 利用了 [JPEG 重压缩的三个不变量](https://phasm.app/blog/jpeg-recompression-invariants)——块平均值、亮度排序和系数符号——这些不变量即使在激进的重编码后也会持续存在。
Fortress 能够在 WhatsApp 重压缩(QF ~62,分辨率缩放到 1600px)后存活。有关全面的平台分析,请参阅 [15 个消息平台如何处理您的照片](https://phasm.app/blog/how-15-platforms-process-your-photos)。
[Watson 感知掩蔽](https://phasm.app/blog/watson-perceptual-masking-qim-steganography) 根据局部纹理能量调整每个块的嵌入强度,确保修改在纹理区域不可见的同时保护平滑区域。
### 载体图像优化器
`optimize_cover` 在 JPEG 压缩之前预处理原始像素,以提高嵌入质量和容量。每种模式都有不同的管道:
- **Ghost**:纹理自适应的 4 阶段管道(噪声注入、微对比度、虚光蒙版、平滑区域抖动)。逐像素的 5×5 方差图使强度适应现有纹理——避免降低预先优化的图像质量。
- **Armor**:用于 STDM 鲁棒性的轻度管道(块边界平滑、DC 稳定)。
- **Fortress**:最小化(仅块边界平滑),用于稳定 BA-QIM 的 DC 平均值。
**“无损”保证**:如果优化降低了平均梯度能量(隐写容量的代理指标),则返回未更改的原始像素。修改难以察觉 (PSNR > 44 dB, SSIM > 0.993)。
```
use phasm_core::{optimize_cover, OptimizerConfig, OptimizerMode};
let optimized = optimize_cover(&raw_pixels_rgb, width, height, &OptimizerConfig {
strength: 0.85,
seed: [0u8; 32], // ChaCha20 seed for deterministic noise
mode: OptimizerMode::Ghost,
});
```
### 解码自动检测
`smart_decode` 会自动尝试所有模式——Ghost 主消息 → Ghost 影子 → Armor → Fortress——解码端不需要模式选择器。
### 我应该使用哪种模式?
| | Ghost | Armor | Fortress |
|---|---|---|---|
| **目标** | 不可检测 | 在重压缩后存活 | 在 WhatsApp 后存活 |
| **算法** | J-UNIWARD + STC | STDM + Reed-Solomon | BA-QIM + Watson 掩蔽 |
| **最适用场景** | 直接分享、AirDrop、电子邮件、“作为文件发送” | 社交媒体、云存储、跨平台 | WhatsApp、具有激进压缩的消息应用 |
| **容量** | 高(每 12 MP 约 1 KB) | 中 | 低(仅限短消息) |
| **抵抗检测** | 是(抗隐写分析) | 中等 | 中等 |
| **抵抗重压缩** | 否 | 是(在轻度 QF 下降时 >95%) | 是(在 QF ~62 下存活) |
| **合理的否认性** | 是(影子消息) | 否 | 否 |
| **文件附件** | 是(高达 2 MB) | 否 | 否 |
| **激活方式** | `--mode ghost` | 默认 | 自动(Armor 中的短消息) |
**经验法则:**如果图像保持完整 → Ghost。如果会被重压缩 → Armor。如果会通过 WhatsApp 传输 → Fortress 自动激活。
## 快速开始
```
use phasm_core::{ghost_encode, ghost_decode};
let cover = std::fs::read("photo.jpg").unwrap();
let stego = ghost_encode(&cover, "secret message", "passphrase").unwrap();
let decoded = ghost_decode(&stego, "passphrase").unwrap();
assert_eq!(decoded.text, "secret message");
```
```
use phasm_core::{armor_encode, armor_decode};
let cover = std::fs::read("photo.jpg").unwrap();
let stego = armor_encode(&cover, "secret message", "passphrase").unwrap();
let (decoded, quality) = armor_decode(&stego, "passphrase").unwrap();
assert_eq!(decoded.text, "secret message");
println!("Integrity: {:.0}%", quality.integrity_percent);
```
```
use phasm_core::{ghost_encode_si, ghost_decode};
// SI-UNIWARD: when you have original uncompressed pixels + the JPEG cover
let raw_pixels_rgb = /* RGB pixel data from PNG/HEIC/RAW */;
let cover = std::fs::read("photo.jpg").unwrap();
let stego = ghost_encode_si(
&cover, raw_pixels_rgb, width, height, "secret message", "passphrase"
).unwrap();
// Decode is identical — no special decoder needed
let decoded = ghost_decode(&stego, "passphrase").unwrap();
assert_eq!(decoded.text, "secret message");
```
```
use phasm_core::{ghost_encode_with_shadows, ghost_decode, ghost_shadow_decode, ShadowLayer};
let cover = std::fs::read("photo.jpg").unwrap();
let shadows = vec![
ShadowLayer {
message: "decoy message".into(),
passphrase: "decoy_pass".into(),
files: vec![],
},
];
let stego = ghost_encode_with_shadows(
&cover, "real secret", &[], "real_pass", &shadows, None
).unwrap();
// Primary message — with the real passphrase
let primary = ghost_decode(&stego, "real_pass").unwrap();
assert_eq!(primary.text, "real secret");
// Shadow message — with the decoy passphrase
let shadow = ghost_shadow_decode(&stego, "decoy_pass").unwrap();
assert_eq!(shadow.text, "decoy message");
```
## API
```
// Ghost mode (stealth)
ghost_encode(jpeg_bytes, message, passphrase) -> Result>
ghost_encode_with_quality(jpeg_bytes, message, passphrase) -> Result<(Vec, EncodeQuality)>
ghost_decode(jpeg_bytes, passphrase) -> Result
ghost_encode_with_files(jpeg_bytes, message, files, passphrase) -> Result>
ghost_encode_with_files_quality(jpeg_bytes, message, files, passphrase) -> Result<(Vec, EncodeQuality)>
ghost_capacity(jpeg_image) -> Result
// Ghost shadow messages (plausible deniability)
ghost_encode_with_shadows(jpeg_bytes, message, files, passphrase, shadows, si) -> Result>
ghost_encode_with_shadows_quality(jpeg_bytes, message, files, passphrase, shadows, si) -> Result<(Vec, EncodeQuality)>
ghost_encode_si_with_shadows(jpeg_bytes, raw_rgb, width, height, message, files, passphrase, shadows) -> Result>
ghost_encode_si_with_shadows_quality(jpeg_bytes, raw_rgb, width, height, message, files, passphrase, shadows) -> Result<(Vec, EncodeQuality)>
ghost_shadow_decode(stego_bytes, passphrase) -> Result
shadow_capacity(jpeg_bytes) -> Result
estimate_shadow_capacity(jpeg_image) -> Result
ghost_capacity_with_shadows(jpeg_bytes, shadows) -> Result
// Ghost SI-UNIWARD (stealth + side-informed, ~43% more capacity)
ghost_encode_si(jpeg_bytes, raw_rgb, width, height, message, passphrase) -> Result>
ghost_encode_si_with_quality(jpeg_bytes, raw_rgb, width, height, message, passphrase) -> Result<(Vec, EncodeQuality)>
ghost_encode_si_with_files(jpeg_bytes, raw_rgb, width, height, message, files, passphrase) -> Result>
ghost_encode_si_with_files_quality(jpeg_bytes, raw_rgb, width, height, message, files, passphrase) -> Result<(Vec, EncodeQuality)>
ghost_capacity_si(jpeg_image) -> Result
// Armor mode (robust)
armor_encode(jpeg_bytes, message, passphrase) -> Result>
armor_encode_with_quality(jpeg_bytes, message, passphrase) -> Result<(Vec, EncodeQuality)>
armor_decode(jpeg_bytes, passphrase) -> Result<(PayloadData, DecodeQuality)>
armor_capacity(jpeg_image) -> Result
// Unified decode (auto-detects mode)
smart_decode(jpeg_bytes, passphrase) -> Result<(PayloadData, DecodeQuality)>
// Cover image optimizer (preprocessing before JPEG compression)
optimize_cover(pixels_rgb, width, height, config) -> Vec
// Capacity estimation with Brotli compression
compressed_payload_size(text, files) -> usize
// Image dimension validation
validate_encode_dimensions(width, height) -> Result<()>
// Real-time progress tracking
progress::init(total) // reset and set total steps
progress::advance() // increment step (capped at total)
progress::finish() // mark complete (step = total)
progress::get() -> (u32, u32) // read (step, total)
progress::cancel() // request cancellation
progress::check_cancelled() -> Result<()> // returns Err(Cancelled) if set
```
### 类型
- **`PayloadData`**——解码后的消息文本 + 可选文件附件
- **`FileEntry`**——文件附件(文件名 + 内容字节)
- **`ShadowLayer`**——用于合理否认性的影子消息(消息 + 密码 + 可选文件)
- **`EncodeQuality`**——编码时间分数 (0-100)、上下文提示键、模式(Ghost 的隐秘性,Armor 的鲁棒性)
- **`DecodeQuality`**——信号完整性百分比、RS 错误计数/容量、fortress 标志
- **`ArmorCapacityInfo`**——按编码层级的容量细分 (Phase 1/2/3, Fortress)
- **`OptimizerConfig`**——优化器设置(强度、种子、模式)
- **`OptimizerMode`**——管道变体 (Ghost, Armor, Fortress)
SI-UNIWARD 函数 (`ghost_encode_si`, `ghost_encode_si_with_files`) 接受原始未压缩像素的额外参数(`raw_rgb: &[u8]`、`pixel_width: u32`、`pixel_height: u32`)。解码器不需要边信息——`ghost_decode` 和 `smart_decode` 对标准和 SI-UNIWARD 编码的图像均有效。
## 视频隐写术 (H.264)
C 阶段(于 2026-05-14 关闭)随两个编码器后端一起发布了 H.264 视频隐写功能。**OpenH264 后端是 v1.0 生产环境的默认选项**;纯 Rust 编码器仍作为选加入口的实验性路径提供,供无法提供 C++ 后端的研究和纯源码消费者使用。
### 编码器选择
| 后端 | CLI 标志 | 状态 | 1080p × 30f 编码 |
|---------|----------|--------|--------------------|
| OpenH264 (默认) | `--encoder open-h264` | 生产环境 | 1.3 s |
| 纯 Rust (实验性) | `--encoder rust-h264` | 实验性——仅限研究 | ~60 min (推算) |
根据 C.8.14 性能基准,OpenH264 隐写在 480p × 10f 下比纯 Rust **快 1365 倍**(86 ms 对 117 s),在 720p × 16f 下**快 4114 倍**(370 ms 对 25.4 分钟),并推算在纯 Rust 路径下 1080p × 30f 每次编码大约需要 60 分钟,而 OpenH264 为 1.3 秒。
提供纯 Rust 编码器的目的是为了透明度、可审查性以及 WASM/无 FFI 使用场景。它不适用于高于冒烟测试规模的生产工作流,并且与 OpenH264 相比存在已知的视觉质量和隐写指纹差距。仅在您需要纯 Rust 路径并接受性能和质量的权衡时才使用它。
### 比特流互换
这两个后端产生**比特流独特的隐写**(不同的载体集、不同的 CABAC 覆盖域),因此其中一个生成的文件不能在解码器之间互换使用。CLI 的 `phasm decode` 通过先尝试 OH264 再尝试 CABAC-v2 回退来自动检测(参见 `core/cli/src/decode.rs::decode_h264_cabac`)。
### 纯 Rust 管道
`stego/video/h264_pipeline.rs` 跨四个域(尾随一符号、级别后缀幅度 LSB、级别后缀符号 LSB、运动矢量差 LSB)将加密消息嵌入到 CAVLC 编码的 Baseline H.264 流中。纯 Rust H.264 编码器及其 CABAC 熵编码器是洁净室实现的——源自 ITU-T H.264 规 / ISO/IEC 14496-10 和已发表的学术文献;数值表转录自规范或引用自已发表的来源。
### OpenH264 后端 (v1.0 生产环境默认)
`openh264-backend` Cargo 特性接入了 Cisco OpenH264 编解码器的 [phasm-openh264](https://github.com/cgaffga/phasm-openh264) 分支 (BSD-2-Clause),该分支通过 `core-openh264-sys` 工作区成员进行静态链接。该分支在 `core/openh264-sys/build.rs` 中通过 SHA 锁定,并通过位于 `vendor/phasm-openh264` 的 git submodule 访问。phasmcore 的源镜像不包含该分支的子模块,因此 `cargo build --features openh264-backend` 将**根据快速失败的桩程序进行构建**,除非您单独检出该分支:
```
git clone https://github.com/cgaffga/phasmcore.git
cd phasmcore
git clone https://github.com/cgaffga/phasm-openh264.git vendor/phasm-openh264
git -C vendor/phasm-openh264 checkout $(grep -m1 -oE '[a-f0-9]{40}' core/openh264-sys/build.rs)
cargo build --features openh264-backend
```
如果缺少该子模块,构建将打印一条 `cargo:warning=...` 解释情况并指回此处。后端调用可以正常编译和链接,但在运行时返回 `-1` / `NULL`,以便调用者回退到纯 Rust 路径并得到清晰的错误。这对于希望使用 phasm-core 的 JPEG 路径或纯 Rust H.264 路径而没有真实后端所需的 C++ 工具链 (meson + ninja) 的下游消费者来说是一个有用的状态。
### 纯源码分发 (专利池立场)
编码器受 `h264-encoder` Cargo 特性控制,该特性**默认关闭**。发布到 GitHub Releases(位于本 README 底部的 `phasm` CLI 安装程序)的预构建二进制文件**不**捆绑 H.264 编码器——它们仅附带 JPEG 隐写术。想要使用 CLI 视频隐写功能的消费者必须自己从源代码重新构建:
```
cargo install phasm-cli --features h264-encoder
```
此立场符合 [Via LA 的 AVC 专利池](https://www.via-la.com/licensing-2/avc-h-264/) 对分发 H.264 编码器二进制文件的许可要求。仅源码分发允许免版税;超过免费额度(截至 2026 年为 100,000 单位/年)的二进制分发则不被允许。移动应用构建包含该编码器,并受适用于这些分发渠道(而非本 crate 的源码消费者)的专利池条款的约束。
`core/cli/Cargo.toml` 和 `core/Cargo.toml` 使此控制变得明确;在通过 GitHub Releases 或其他无视免费额度的渠道发布您打算发布的任何二进制文件之前,请勿启用 `h264-encoder`,除非您已首先审查了 Via LA AVC 许可条款。
生产就绪路径 (`openh264-backend`) 随 v1.0 CLI 和移动版本一同发布;纯 Rust `h264-encoder` 路径仍为实验性选项,可通过 `--encoder rust-h264` 启用。API 和默认特性集可能仍会演变;在此发布是为了透明度和审查。
## Cargo 特性
| Feature | 描述 |
|---------|-------------|
| `parallel` | 启用用于 J-UNIWARD 成本计算、STC 嵌入和 Armor 解码扫描的 [Rayon](https://github.com/rayon-rs/rayon) 并行处理。推荐用于原生构建。 |
| `video` | H.264 视频隐写管道(解码器 + 比特流解析)。请参阅上面的“视频隐写术 (H.264)”。 |
| `h264-encoder` | 用于视频隐写编码的纯 Rust H.264 编码器。隐含 `video`。**实验性——仅限研究使用**,在 480p × 10f 下比 OpenH264 慢约 1365 倍;通过 `--encoder rust-h264` 选择加入。仅限源码 (AVC 专利池)。 |
| `cabac-stego` | 在 H.264 High Profile + CABAC 启用编码时的 CABAC 隐写管道。隐含 `h264-encoder`。实验性。 |
| `openh264-backend` | **v1.0 生产环境默认**。通过 `phasm-openh264` 分支 (BSD-2-Clause) 的第二 H.264 路径,接入 `phasm decode` 作为自动检测的首次尝试。如果缺少 `vendor/phasm-openh264`,则根据快速失败的桩程序进行构建(在构建时发出警告,在运行时返回错误)。 |
| `wasm` | 通过 `wasm-bindgen` + `js-sys` 支持 WASM 桥接。 |
## CLI
`cli/` 目录中提供了命令行界面。
### 安装
```
# macOS / Linux
curl --proto '=https' --tlsv1.2 -LsSf https://github.com/cgaffga/phasmcore/releases/latest/download/phasm-cli-installer.sh | sh
# Windows PowerShell
powershell -c "irm https://github.com/cgaffga/phasmcore/releases/latest/download/phasm-cli-installer.ps1 | iex"
# 或者从源码构建
cargo install --path cli
```
### 使用方法
```
# Encode (Armor 模式,默认)
phasm encode photo.jpg -m "secret message" -p "passphrase"
# Encode (Ghost 模式)
phasm encode photo.jpg --mode ghost -m "secret" -p "pass" -o secret.jpg
# PNG 输入 → 自动 Deep Cover (SI-UNIWARD)
phasm encode photo.png --mode ghost -m "hello" -p "pass"
# Shadow 消息(合理推诿)
phasm encode photo.jpg --mode ghost -m "real" -p "main" --m2 "decoy" --p2 "shadow"
# 文件附件
phasm encode photo.jpg --mode ghost -m "see attached" -p "pass" --attach plans.pdf
# Decode(自动检测模式)
phasm decode stego.jpg -p "passphrase"
# 提取文件附件
phasm decode stego.jpg -p "pass" --extract ./output/
# 显示容量
phasm capacity photo.jpg
# 从 stdin 管道传入消息
echo "hello" | phasm encode photo.jpg -p "pass"
# Machine-readable 输出
phasm encode photo.jpg -m "hello" -p "pass" --json
phasm decode stego.jpg -p "pass" --quiet
```
有关所有选项,请参阅 `phasm encode --help`、`phasm decode --help`、`phasm capacity --help`。
## 构建与测试
```
cargo test -p phasm-core # default (single-threaded)
cargo test -p phasm-core --features parallel # with Rayon parallelism
```
### 示例
```
# Encode 和 decode 消息
cargo run -p phasm-core --example test_encode -- photo.jpg "Hello" "passphrase"
cargo run -p phasm-core --example test_encode -- --decode stego.jpg "passphrase"
# Timing 基准测试
cargo run -p phasm-core --example test_timing -- stego.jpg
# Quick decode 测试
cargo run -p phasm-core --example test_link -- stego.jpg
```
## 架构
### 设计原则
- **零 C FFI**——从 JPEG 解析到 AES 加密的纯 Rust,可编译为原生和 WASM
- **确定性**——使用[纯 IEEE 754 多项式数学模块](https://phasm.app/blog/deterministic-cross-platform-math-wasm)在 x86、ARM 和 WASM 上产生相同的输出(不使用 `f64::sin`/`cos`——它们在 WASM 中编译为不确定的 `Math.*`)
- **内存高效**——基于条带的小波计算、紧凑型位置(每个 8 字节)、1 位打包的 Viterbi 回溯指针(减少 32 倍)、用于大图像的分段检查点(O(√n) 内存)、`i8` 量化的 SI-UNIWARD 舍入误差(比 `f64` 小 8 倍)、逐条带亮度块计算、JPEG 输出前的策略性 `drop()` 调用。在约 1 GB 峰值内存下支持 **200 MP** 图像。
- **短消息**——针对 1 KB 以下的文本有效载荷进行了优化
- **隐写输出是原始的**——编码后的 JPEG 字节必须在不再重编码的情况下保存/共享
### JPEG 编解码器
[`jpeg`](src/jpeg/) 模块是一个[从头构建的 JPEG 系数编解码器](https://phasm.app/blog/pure-rust-jpeg-coefficient-codec)——除了 `std` 之外零依赖。它将 JPEG 文件解析为 DCT 系数网格,允许修改,并以**逐字节完全相同的往返保真度**将其写回。支持基线 (SOF0) 和渐进式 (SOF2) JPEG 输入,始终输出基线。
原始霍夫曼表从载体图像中保留(如果需要,可回退到重建)。
### 密码学
所有有效载荷在嵌入前都经过加密:
- **密钥派生**:Argon2id (RFC 9106)——两个层级:
- *结构密钥*(由密码 + 固定盐确定性派生):驱动系数置换和 STC 矩阵生成
- *加密密钥*(密码 + 随机盐):带有 nonce 误用抗性的 AES-256-GCM-SIV (RFC 8452)
- **PRNG**:ChaCha20 用于所有密钥派生的随机性(置换、扩频矢量、模板峰值)
- **有效载荷压缩**:Brotli (RFC 7932) 用于紧凑的有效载荷;标志字节指示压缩
### Ghost 管道
1. 将 JPEG 解析为 DCT 系数
2. 派生结构密钥 (Argon2id,在多核上与步骤 3 重叠) → 置换种子 + STC 种子
3. **逐条带**计算 [J-UNIWARD 成本](https://phasm.app/blog/uerd-vs-juniwird-detection-benchmarks) (Daubechies-8 小波,3 个子带,并行行+列滤波)——内联收集位置,不生成完整的 CostMap
4. *(仅 SI-UNIWARD)* 使用量化的舍入误差 (`i8`,精度损失 <2%) 内联调节成本:`cost *= (1 - 2|error|)`。亮度块以 50 个块行的条带计算,以最小化峰值内存。
5. 置换系数顺序 (使用 ChaCha20 的 Fisher-Yates)
6. 加密有效载荷 (AES-256-GCM-SIV) 并组帧 (长度 + CRC)
7. **具有动态 w 的短 STC**:仅嵌入实际的 `m` 个消息比特 (不零填充到 `m_max`)。编码器选择 `w = min(⌊n/m⌋, 10)`——对于小消息,这意味着与固定 `w` 相比**修改的系数减少了 2,500 倍**。解码器并行暴力尝试所有 1-10 的 `w` 值 (`rayon::find_map_first`);帧格式中的 CRC32 提供即时验证(在 2⁻³² 处的误报率约为 0)。
8. *(影子模式)* 将 `f32::INFINITY` 成本分配给影子位置 → Viterbi 绕过它们
9. 通过 [STC](https://phasm.app/blog/syndrome-trellis-codes-practical-guide) (h=7) 嵌入,最小化加权失真;SI-UNIWARD 使用知情的修改方向(朝向量化前的值)
10. 将修改后的系数写回 JPEG (按 MCU 行报告进度)
#### STC Viterbi 优化器
STC 编码器使用 Viterbi 风格的动态规划算法,在具有 2^h = 128 个状态的网格中寻找最小成本的修改序列。两个关键的内存优化使其对于大型图像 (32MP+ 照片,48MP 相机传感器) 变得实用:
**1 位打包的回溯指针**——标准 Viterbi 为每个载体步骤的每个网格状态存储一个前驱状态 (u32)。由于每个目标状态恰好有 2 个候选前驱 (stego_bit = 0 或 1),因此只需要 1 位。一个 `u128` (16 字节) 存储每个步骤所有 128 个状态的决定,取代了包含 128 个条目 (512 字节) 的 `Vec`。**内存减少 32 倍。**
前向过程迭代*目标状态*而不是源状态:
```
for s in 0..num_states {
let cost_0 = prev_cost[s] + rho_0; // stego=0, pred=s
let cost_1 = prev_cost[s ^ col] + rho_1; // stego=1, pred=s^col
if cost_1 < cost_0 {
curr_cost[s] = cost_1;
packed_bp |= 1u128 << s; // 1 bit records the choice
} else {
curr_cost[s] = cost_0;
}
}
```
回溯从单个位重建前驱:
```
let bit = ((back_ptr[j] >> s) & 1) as u8;
if bit == 1 { s ^= col; } // undo XOR to get predecessor
```
在消息块边界(移位步骤),移位前状态由已知的消息比特完全确定——不需要存储:
```
s = ((s << 1) | message_bit) & (num_states - 1); // invertible shift
```
**分段检查点 Viterbi**——对于具有超过 100 万可用位置的图像,即使是每步 16 字节也可能超过移动设备的内存 (例如,1 亿位置 × 16 字节 = 1.6 GB)。分段方法将内存减少到 O(√n):
- **阶段 A (正向扫描)**:运行完整的 Viterbi 而不存储回溯指针。每 K = ⌈√m⌉ 个消息块保存一次成本数组检查点(每个约 1 KB)。最大有效载荷的总检查点内存:约 1.5 MB。
- **阶段 B (段重计算)**:以相反顺序处理段。对于每个段,从其检查点重新运行前向过程,仅存储该段的回溯指针,然后回溯并释放。每个段的峰值内存:约 200 KB。
调度器自动选择最优路径:
```
const SEGMENTED_THRESHOLD: usize = 1_000_000;
if n_used <= SEGMENTED_THRESHOLD {
stc_embed_inline(...) // single pass, all back_ptr in memory
} else {
stc_embed_segmented(...) // checkpoint/recompute, O(√n) memory
}
```
两种路径产生**逐位完全相同的输出**(通过等效性测试验证)。分段路径用 O(√n) 内存换取 2 倍的计算时间——通常任何图像大小的总内存约为 3 MB。
| 图像 | n_used | 内联内存 | 分段内存 |
|-------|--------|--------------|-----------------|
| 12 MP 手机 | 2M | 32 MB | 3 MB |
| 32 MB PNG | 5M | 80 MB | 3 MB |
| 48 MP 相机 | 10M | 160 MB | 3 MB |
| 100 MP 中画幅 | 30M | 480 MB | 3 MB |
| 200 MP 旗舰机 | 59M | 944 MB | 3 MB |
#### 基于条带的 UNIWARD 与紧凑位置
J-UNIWARD 成本函数需要对整个解压缩图像进行小波分解——通过 Daubechies-8 滤波器生成三个方向子带 (LH、HL、HH)。对于 200 MP 图像,存储完整的像素阵列 + 3 个小波子带将需要大约 3.2 GB。
**基于条带的流式**方法消除了这一点:
1. 在 50 个块行(约 400 像素)的水平条带中处理图像
2. 每个条带解压缩其像素行(为 16 抽头小波滤波器边界提供 ±22px 填充),计算行/列滤波的小波子带,并评估每个系数的成本
3. 可嵌入位置被内联收集到一个紧凑的 `Vec` 中——从未生成完整的 CostMap
4. 条带内存在下一个条带开始之前被释放
`CoeffPos` 类型使用**紧凑表示**:`u32` 平面索引 + `f32` 成本 = 每个位置 8 字节(相比于使用 `usize` + `f64` 的 16 字节有所减少)。STC接受 `f32` 成本,仅在内部 Viterbi 累加时提升为 `f64`。
| 图像 | DctGrid | 位置 | 条带缓冲区 | **总峰值** |
|-------|---------|-----------|---------------|---------------|
| 12 MP 手机 | 24 MB | 48 MB | 12 MB | **84 MB** |
| 48 MP 相机 | 96 MB | 190 MB | 30 MB | **316 MB** |
| 100 MP 中画幅 | 200 MB | 400 MB | 50 MB | **650 MB** |
| 200 MP 旗舰机 | 400 MB | 800 MB | 170 MB | **~1 GB** |
容量估算几乎是即时的:它直接从 DctGrid 计算非零 AC 系数(不需要小波计算),因为 J-UNIWARD 为所有非零系数分配有限的成本。
### Armor 管道
1. 解析 JPEG,派生结构密钥
2. 计算稳定性图 (选择低频 AC 系数)
3. 使用自适应 RS 奇偶校验加密并组帧有效载荷
4. 通过带有扩频矢量 (ChaCha20 派生) 的 STDM 嵌入
5. 对于短消息:激活 Fortress (在块平均值上使用 [BA-QIM](https://phasm.app/blog/watson-perceptual-masking-qim-steganography) 和 [Watson 掩蔽](https://phasm.app/blog/watson-perceptual-masking-qim-steganography))
6. 嵌入 [DFT 幅度模板](https://phasm.app/blog/dft-template-geometric-resilience-steganography) 以实现几何弹性
7. 解码:具有[软判决级联码](https://phasm.app/blog/soft-majority-voting-llr-concatenated-codes)的三阶段并行扫描
### 进度报告
编码和解码管道均通过全局原子变量 (`progress::advance()`) 报告实时进度。这使得所有平台都能获得响应迅速的 UI 反馈:
- **Ghost 编码**:177 步——5 (解析) + 100 (UNIWARD 子步骤) + 50 (STC Viterbi 子步骤) + 2 (置换 + 密钥派生) + 20 (JPEG 写入 MCU 行)
- **带影子的 Ghost 编码**:277 步——为隐写成本检查增加 100 步 (验证 UNIWARD 过程)
- **Ghost 解码**:107 步——5 (解析) + 100 (UNIWARD) + 2 (STC 提取 + 解密)。动态 w 暴力破解并行运行。
- **Armor 编码**:6 步 (STDM 路径) 或 3 步 (Fortress 路径,在运行时自动调整)
- **Armor 解码**:基于候选数量的动态总数 (约 50 步)。阶段 1 增量扫描在约 21 个候选者之间并行化。
取消是协作式的:`progress::cancel()` 设置一个标志,该标志通过 `progress::check_cancelled()` 在自然循环边界处进行检查。
### FFT
使用确定性旋转因子 (`det_sincos()`) 的内部 Cooley-Tukey + Bluestein FFT 实现。没有外部 FFT crate——保证在所有平台上产生[逐位相同的结果](https://phasm.app/blog/deterministic-cross-platform-math-wasm)。
## 项目结构
```
src/
lib.rs Public API re-exports
det_math.rs Deterministic math (sin/cos/atan2/hypot/exp/powi)
jpeg/
mod.rs JpegImage: parse, modify, serialize
bitio.rs Bit-level reader/writer with JPEG byte stuffing
dct.rs DCT coefficient grids and quantization tables
error.rs JPEG parsing errors
frame.rs SOF frame info (dimensions, components, subsampling)
huffman.rs Huffman coding tables (two-level decode, encode)
marker.rs JPEG marker iterator
pixels.rs IDCT/DCT for pixel-domain operations (forward DCT, luma extraction)
scan.rs Entropy-coded scan reader/writer
tables.rs DQT/DHT table parsing
zigzag.rs Zigzag scan order mapping
stego/
mod.rs Ghost/Armor encode/decode entry points
pipeline.rs Ghost mode pipeline (J-UNIWARD + STC)
crypto.rs AES-256-GCM-SIV + Argon2id key derivation
frame.rs Payload framing (length, CRC, mode byte, salt, nonce)
payload.rs Payload serialization (Brotli, file attachments)
permute.rs Fisher-Yates coefficient permutation
side_info.rs SI-UNIWARD: rounding errors, cost modulation, direction selection
shadow.rs Shadow messages (Y-channel direct LSB + RS ECC, plausible deniability)
optimizer.rs Cover image optimizer (texture-adaptive preprocessing, do-no-harm)
quality.rs Encode quality scoring (stealth for Ghost, robustness for Armor)
capacity.rs Ghost capacity estimation
progress.rs Real-time progress tracking (atomics + WASM callback)
error.rs StegoError enum
cost/
mod.rs Cost function trait
uniward.rs J-UNIWARD (Daubechies-8 wavelet, 3 subbands)
uerd.rs UERD cost function (legacy)
stc/
mod.rs Syndrome-Trellis Codes
embed.rs STC Viterbi embedding (1-bit packed + segmented checkpoint)
extract.rs STC extraction (syndrome computation)
hhat.rs H-hat submatrix generation (ChaCha20-derived)
armor/
mod.rs Armor mode re-exports
pipeline.rs Armor encode/decode (STDM + Fortress + DFT template)
embedding.rs STDM embed/extract with adaptive delta
selection.rs Coefficient stability map
spreading.rs ChaCha20-derived spreading vectors
ecc.rs Reed-Solomon GF(2^8) encoder/decoder
repetition.rs Repetition coding with soft majority voting
capacity.rs Armor capacity estimation
fortress.rs BA-QIM block-average embedding + Watson masking
template.rs DFT magnitude template (geometric resilience)
dft_payload.rs DFT ring-based payload embedding
fft2d.rs 2D FFT (Cooley-Tukey + Bluestein)
resample.rs Bilinear resampling for geometric correction
cli/ Command-line interface (phasm binary)
test-vectors/ Synthetic JPEG test images
tests/ Integration tests (round-trip, cross-platform, geometry)
examples/ CLI tools (encode/decode, timing, diagnostics)
```
## 研究与出版物
phasm-core 中的算法在 [Phasm 博客](https://phasm.app/blog) 上有详细记录:
### 隐写术基础
- [低嵌入率下的自适应隐写术:UERD 与 J-UNIWARD 检测基准](https://phasm.app/blog/uerd-vs-juniwird-detection-benchmarks)
- [用于实用 JPEG 隐写术的 Syndrome-Trellis 码](https://phasm.app/blog/syndrome-trellis-codes-practical-guide)
- [在 JPEG 重压缩中存活:DCT 域鲁棒隐写术的定量分析](https://phasm.app/blog/surviving-jpeg-recompression)
### 鲁棒性与纠错
- [JPEG 重压缩的三个不变量:块平均值、亮度排序和系数符号](https://phasm.app/blog/jpeg-recompression-invariants)
- [隐写术的软解码:LLR 和级联码如何将 19% 的 BER 转化为零错误](https://phasm.app/blog/soft-majority-voting-llr-concatenated-codes)
- [感知掩蔽遇见 QIM:不可见鲁棒隐写术的自适应嵌入强度](https://phasm.app/blog/watson-perceptual-masking-qim-steganography)
### 几何弹性
- [傅里叶域模板嵌入:Phasm 如何在旋转、缩放和裁剪中存活](https://phasm.app/blog/dft-template-geometric-resilience-steganography)
### 合理的否认性
- [影子消息:Phasm 如何在单张照片中隐藏多个秘密](https://phasm.app/blog/shadow-messages-plausible-deniability-steganography)
### 实现
- [构建纯 Rust JPEG 系数编解码器](https://phasm.app/blog/pure-rust-jpeg-coefficient-codec)
- [当 f64::sin() 破坏你的加密时:为 WASM 隐写术构建确定性数学](https://phasm.app/blog/deterministic-cross-platform-math-wasm)
- [从 480 MB 到 3 MB:将 Viterbi 隐写术装入手机](https://phasm.app/blog/segmented-viterbi-memory-efficient-stc)
### 平台分析
- [15 个消息平台如何处理您的照片](https://phasm.app/blog/how-15-platforms-process-your-photos)
## Phasm 如何对比?
| 特性 | Phasm | OpenStego | Steghide | F5 |
|---------|-------|-----------|----------|----|
| **状态** | 活跃 (2025–至今) | 不再维护 | 不再维护 (2003) | 不再维护 |
| **语言** | Rust (原生 + WASM) | Java | C++ | Java |
| **加密** | AES-256-GCM-SIV + Argon2id | DES | Blowfish / AES | 无 |
| **嵌入** | J-UNIWARD + STC (h=7) | LSB | LSB-相邻 | DCT 直方图移位 |
| **抗隐写分析能力** | 高 (抵抗 SRNet、XedroudjNet) | 低 | 低 | 中等 |
| **在重压缩后存活** | 是 (Armor / Fortress) | 否 | 否 | 否 |
| **在 WhatsApp 后存活** | 是 (Fortress,短消息) | 否 | 否 | 否 |
| **合理的否认性** | 是 (影子消息) | 否 | 否 | 否 |
| **边信息模式** | 是 (SI-UNIWARD,+43% 容量) | 否 | 否 | 否 |
| **文件附件** | 是 (Ghost,高达 2 MB) | 否 | 是 | 否 |
| **几何弹性** | 是 (DFT 模板) | 否 | 否 | 否 |
| **平台** | iOS, Android, Web (WASM), CLI | 桌面 | Linux / Windows CLI | Java |
| **Web (无需安装)** | 是 | 否 | 否 | 否 |
| **图像格式** | JPEG (任何输入自动转换) | PNG, BMP | JPEG, BMP, WAV, AU | JPEG |
| **开源** | GPL-3.0 | GPL-2.0 | GPL-2.0 | MIT |
## 常见问题解答
**Phasm 可以与 WhatsApp 配合使用吗?**
可以。Fortress 子模式(在 Armor 模式下为短消息自动激活)能够在 WhatsApp 的默认重压缩管道中存活。对于较长的消息,请将图像作为文档/文件发送以完全绕过压缩。
**Ghost 模式的可检测性如何?**
在典型的嵌入率 (0.4 bpnzAC) 下,即使是像 SRNet 这样的深度学习隐写分析检测器在面对 J-UNIWARD 嵌入的图像时表现也接近随机猜测。SI-UNIWARD (对非 JPEG 输入自动激活) 进一步降低了检测风险。
**支持哪些图像尺寸?**
最小 200×200 像素,最大 8192×8192 / 16 兆像素。超大图像会自动下采样。引擎内部可处理高达 200 MP 的图像,STC 嵌入的峰值内存仅需约 3 MB。
**输出是 JPEG 还是 PNG?**
始终为 JPEG。输入可以是任何常见格式 (JPEG、PNG、WebP、HEIC)——非 JPEG 输入会自动转换,并在 Ghost 模式下启用 SI-UNIWARD 深度隐藏。
**我能隐藏文件而不仅仅是文本吗?**
可以,在 Ghost 模式下。除了文本消息外,还支持每个文件最大 2 MB 的文件附件。文件在嵌入前经过 Brotli 压缩。
**如果我忘记了密码怎么办?**
消息无法恢复。Phasm 使用带有 Argon2id 密钥派生的 AES-256-GCM-SIV——没有后门,没有恢复机制。只有正确的密码才能解密消息。
**有人能在没有密码的情况下证明消息存在吗?**
Ghost 模式旨在抵抗这一点。影子消息增加了合理的否认性——您可以提供一个诱饵密码,该密码会解码出一条无害的消息,而无法证明还存在其他消息。
**Phasm 可以离线工作吗?**
可以。所有处理均在设备上进行。Web 版本完全在 WebAssembly 中运行——无需服务器通信,无需帐户。
## 许可证
GPL-3.0-only。请参阅 [LICENSE](LICENSE)。
第三方依赖项许可证列在 [THIRD_PARTY_LICENSES](THIRD_PARTY_LICENSES) 中。
标签:AI工具, Android, DSL, H.264, iOS, JPEG, J-UNIWARD, meg, Rust, SIMD, STC, WebAssembly, 信息安全, 信息隐藏, 加密, 加密通信, 可视化界面, 图像处理, 并行计算, 抗检测, 数字水印, 数据隐写, 机密通信, 漏洞扫描器, 网络安全, 网络安全, 网络流量审计, 视频处理, 通知系统, 隐写术, 隐私保护, 隐私保护