cgaffga/phasmcore

GitHub: cgaffga/phasmcore

纯 Rust 实现的 JPEG 和 H.264 隐写术核心库,提供抗检测隐写、鲁棒嵌入和影子消息否认性等多种模式。

Stars: 9 | Forks: 0

# phasm-core [![crates.io](https://img.shields.io/crates/v/phasm-core.svg)](https://crates.io/crates/phasm-core) [![License: GPL-3.0](https://img.shields.io/badge/license-GPL--3.0-blue.svg)](LICENSE) [![Rust](https://img.shields.io/badge/rust-1.75%2B-orange.svg)](https://www.rust-lang.org) [![Platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20Android%20%7C%20Web%20%7C%20CLI-green.svg)](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, 信息安全, 信息隐藏, 加密, 加密通信, 可视化界面, 图像处理, 并行计算, 抗检测, 数字水印, 数据隐写, 机密通信, 漏洞扫描器, 网络安全, 网络安全, 网络流量审计, 视频处理, 通知系统, 隐写术, 隐私保护, 隐私保护