Enterprise 版本
隐写术 — 深度参考
本深度参考记录了 NextPDF Enterprise 隐写通道。该通道将加密载荷隐藏在 TJ 文本显示数组的数值字距调整量之中。它有四个公开符号:SteganographyEncoder、SteganographyDecoder、SteganographyConfig 和 SteganographyCapacity。编码器使用 HKDF-SHA-256 派生密钥,用 AEAD 密码算法加密载荷,并返回每个位置的字距偏移量。解码器则从观测到的调整量或原始内容流中逆转此过程。
该通道专为内部文档泄漏溯源而设计。它不是对抗级隐写术。编码数据可能被打印再扫描、PDF 转换、重新线性化、内容流重写或任何使字距归一化的操作所破坏。NextPDF 对此通道不持有任何认证,也不授予任何认证。本页陈述的是能力,而非合规性。
可用性与许可
标题为“可用性与许可”的章节此能力随 NextPDF Enterprise(nextpdf/enterprise)发布,并通过 Enterprise 级许可证信封激活。缺少该授权的部署不会加载此能力的类。比较版本并获取许可证。
公开 API 面
标题为“公开 API 面”的章节该通道暴露四个 final 类。除 SteganographyConfig 构造函数及其 effectiveMaxOffset 访问器外,所有入口点均为 public static。配套的 NextPDF\Enterprise\Security\Steganography\SteganographyEncryptionException 由编码器抛出;它不是调用方构造的类型。
| 符号 | 参数 | 默认行为 | 返回值 | 抛出或失败于 | 说明 |
|---|---|---|---|---|---|
SteganographyEncoder::encode | $payload、$text、$fontKey、$metrics(FontMetrics)、$secretKey、$config(SteganographyConfig) | 空 $payload 返回 [];断言密钥强度;加密;计算每个位置的字距偏移量。 | array<int, float>(位置 => 以 1/1000 em 为单位的调整量,AFM 惯例) | InvalidArgumentException(密钥低于下限);OverflowException(文本少于 2 个字符,或载荷超出容量);SteganographyEncryptionException(AEAD 失败) | 将结果传递给 NextPDF\Content\TextRenderer::buildTjArrayOperator()。API 返回 AFM 惯例的调整量;buildTjArrayOperator() 执行 PDF TJ 数值转换(ISO 32000-2 从当前位置减去该数值)。手动编写内容流者必须保留该符号惯例。 |
SteganographyEncoder::assertSecretKeyStrength | $secretKey | 拒绝短于下限的密钥。 | void | InvalidArgumentException(密钥低于下限) | 共享的写入路径守卫,在读取路径上有对应镜像。 |
SteganographyEncoder::MIN_SECRET_KEY_LENGTH | 常量 | 以字节为单位的 128 位密钥长度下限。 | int(16) | 不适用 | 库强制的是长度,而非熵。 |
SteganographyDecoder::decode | $observedAdjustments、$text、$fontKey、$metrics(FontMetrics)、$secretKey、$config(SteganographyConfig) | 断言密钥强度;量化偏差;重建 blob;AEAD 解密。 | `string | null(载荷,或在密钥错误或无载荷时为 null`) | InvalidArgumentException(密钥低于下限) |
SteganographyDecoder::decodeFromContentStream | $contentStream、$fontKey、$metrics(FontMetrics)、$secretKey、$config(SteganographyConfig) | 对流进行分词,从 TJ 数组重建文本与调整量,然后委托给 decode。 | `string | null(载荷,或在无 TJ文本或解密失败时为null`) | InvalidArgumentException(密钥低于下限,经由 decode) |
SteganographyConfig::__construct | $bitDepth、$maxAdjustmentEmRatio、$cipher、$requirePdfACompatibility | 校验每个参数的取值域;产出一个不可变值对象。 | SteganographyConfig 实例 | InvalidArgumentException(无效的 $bitDepth、$maxAdjustmentEmRatio 或 $cipher) | readonly 类;四个参数是公开提升属性。 |
SteganographyConfig::effectiveMaxOffset | 无 | 返回 $maxAdjustmentEmRatio * 1000,在请求 PDF/A 兼容性时减半。 | float(以 1/1000 em 为单位的偏移量) | 不适用 | 减半可降低宽度不匹配的检测风险。 |
SteganographyConfig::CRYPTO_OVERHEAD | 常量 | 每个载荷固定的加密开销字节数。 | int(32) | 不适用 | 4 字节长度、12 字节 nonce、16 字节 tag。 |
SteganographyCapacity::calculate | $text、$config(SteganographyConfig) | 计算扣除开销后该文本可用的载荷字节数。 | int(文本过短时为 0) | 不适用 | 容量为 positions * bitDepth / 8 减去开销。 |
SteganographyCapacity::minimumTextLength | $payloadBytes、$config(SteganographyConfig) | 计算某载荷所需的最小 UTF-8 字符数。 | int(字符数) | 不适用 | calculate 的逆运算。 |
逐字签名如下,每个均附带源代码出处。
public static function encode( string $payload, string $text, string $fontKey, FontMetrics $metrics, string $secretKey, SteganographyConfig $config = new SteganographyConfig(),): arraypublic static function assertSecretKeyStrength(string $secretKey): voidpublic const int MIN_SECRET_KEY_LENGTH = 16;public static function decode( array $observedAdjustments, string $text, string $fontKey, FontMetrics $metrics, string $secretKey, SteganographyConfig $config = new SteganographyConfig(),): ?stringpublic static function decodeFromContentStream( string $contentStream, string $fontKey, FontMetrics $metrics, string $secretKey, SteganographyConfig $config = new SteganographyConfig(),): ?stringpublic function __construct( public int $bitDepth = 1, public float $maxAdjustmentEmRatio = 0.02, public string $cipher = 'aes-256-gcm', public bool $requirePdfACompatibility = false,)public function effectiveMaxOffset(): floatpublic const int CRYPTO_OVERHEAD = 32;public static function calculate( string $text, SteganographyConfig $config = new SteganographyConfig(),): intpublic static function minimumTextLength( int $payloadBytes, SteganographyConfig $config = new SteganographyConfig(),): int行为契约
标题为“行为契约”的章节编码器将 $text 拆分为 UTF-8 字符,并为每一对相邻字符构成一个位置。每个位置承载 $config->bitDepth 个比特,即一个或两个。载荷首先被加密,然后序列化为 blob,再转换为比特序列。每个位置将其比特编码为一个加到该字符对自然字距值上的小的非负偏移量。
该偏移量是有效最大偏移量的一个分数。有效最大偏移量为 $maxAdjustmentEmRatio * 1000 个设计单位,当 $requirePdfACompatibility 为 true 时减半。自然字距通过 FontMetrics::getKernPair 从 $metrics 中读取。返回的映射是稀疏的:最终调整量恰好为零的位置会被省略。
加密使用 HKDF-SHA-256 派生一个 32 字节密钥。HKDF 盐值是非保密的 $fontKey,info 标签是一个固定常量。因此调用方的 $secretKey 是唯一的机密性边界。AEAD 密码算法为 aes-256-gcm 或 chacha20-poly1305,由 $config->cipher 选定,通过 openssl_encrypt 运行,配以新鲜的 12 字节 nonce 和 16 字节 tag。序列化后的 blob 由 4 字节大端长度、12 字节 nonce、密文以及 16 字节 tag 组成;此固定开销即 CRYPTO_OVERHEAD,为 32 字节。
解码器逆转该变换。它计算每个观测调整量相对于自然字距的偏差,按有效最大偏移量归一化,并量化到最近的级别。它重新组装 blob,校验长度头,并调用 openssl_decrypt。错误的密钥、缺失的载荷或损坏的调整量会导致 AEAD 认证失败,解码器返回 null。decodeFromContentStream 首先用 NextPDF\Pro\Projection\ContentProjectionWriter::tokenize 对原始流进行分词,从每个 TJ 数组重建文本与数值调整量,然后委托给 decode。
SteganographyCapacity::calculate 在扣除 CRYPTO_OVERHEAD 后,报告某文本与配置可用的载荷大小;文本过短时返回零。SteganographyCapacity::minimumTextLength 是其逆运算:能容纳所请求大小载荷的最小 UTF-8 字符数。
边界情形与失败模式
标题为“边界情形与失败模式”的章节- 空
$payload使encode返回一个空映射;不写入任何字节,且不会触及密钥强度守卫。 - 对于非空载荷,字符少于两个的
$text会在encode中引发OverflowException(空载荷会在长度检查之前短路返回[]);同样的文本在decode中产生null,在SteganographyCapacity::calculate中产生零。 - 大于文本容量的
$payload会在发出任何调整量之前引发OverflowException。 - 短于
MIN_SECRET_KEY_LENGTH(16 字节)的$secretKey在写入路径和读取路径上都会引发InvalidArgumentException。这是契约违规,不同于普通的密钥错误未命中。 - 错误的密钥、损坏的调整量集或截断的 blob 会通过 AEAD 认证失败使
decode返回null,而非抛出异常。 - 在提取过程中,稀疏
$observedAdjustments映射中缺失的位置被视为零偏差。 - 当流中不含
TJ文本时,decodeFromContentStream返回null。 - 该通道在设计上就是脆弱的。打印再扫描、PDF 转换、重新线性化、内容流重写或字距归一化都可能破坏编码数据。它不适用于对抗性或归档用途。
FIPS 模式行为
标题为“FIPS 模式行为”的章节该通道使用 HKDF-SHA-256 进行密钥派生,并使用一种 AEAD 密码算法保证机密性与完整性。NextPDF 对此通道不持有 FIPS 验证,也不作此宣称。 该模块不强制 FIPS 配置;密码算法的选择是调用方通过 $config->cipher 作出的决定。aes-256-gcm 是伽罗瓦/计数器模式下的 AES,一种构建在获批 128 位分组密码之上的认证加密模式,其合规性依据 NIST SP 800-38D §2 在 CMVP 下验证。chacha20-poly1305 未由任何 NIST 操作模式建议所定义,因此受 FIPS 约束的 OpenSSL 提供者会拒绝它;openssl_encrypt 随即返回 false,编码器引发 SteganographyEncryptionException。某部署是否满足 FIPS 要求,是运维方对照其已验证提供者作出的判定,而非 NextPDF 的断言。
合规性
标题为“合规性”的章节该嵌入将数值元素写入 TJ 文本显示数组。依据 ISO 32000-2:2020 §9.4.3,TJ 数组显示文本并允许数值元素调整字形位置;该数值以文本空间单位的千分之一表示,并从当前位置中减去。字形绘制之后,文本矩阵按合并位移进行平移,因此定位数值会移动后续字形的放置位置 — ISO 32000-2:2020 §9.4.4。该通道以相同的 1/1000 em(AFM)惯例将其偏移量加到自然字距值上,其中负值会收紧间距。
AEAD 的依据仅限于原语选择:aes-256-gcm 对应 NIST SP 800-38D §2 的 GCM 模式。该引用标识的是一种算法;它并非对此通道的验证。
所有条款均为释义;NextPDF 不复制规范性文本。NextPDF 对此通道不作任何隐写术、密码学或 PDF 合规性宣称。与 TJ 定位模型的结构对齐是一种能力陈述,而非认证。鲁棒性声明依然成立:该通道用于内部泄漏溯源,而非对抗级用途。
开发注记
标题为“开发注记”的章节- 入口点是
NextPDF\Enterprise\Security\Steganography中的public static方法,除SteganographyConfig构造函数和effectiveMaxOffset外。 SteganographyConfig是一个final readonly值对象。它的四个属性在构造后不可变,其参数取值域在构造函数中校验:$bitDepth为 1 或 2,$maxAdjustmentEmRatio在(0, 0.05]内,$cipher为aes-256-gcm或chacha20-poly1305。- encode 的输出由
NextPDF\Content\TextRenderer::buildTjArrayOperator消费。字距对来自NextPDF\Typography\FontMetrics。内容流解码经由NextPDF\Pro\Projection\ContentProjectionWriter读取,且不修改流。 - 密钥长度下限在入口点强制执行,并在私有密码边界处重新断言,因此没有任何内部路径能以弱密钥到达 HKDF。库强制的是长度,而非熵;提供高熵密钥材料是集成方的责任。
CRYPTO_OVERHEAD(32 字节)是每个载荷的固定成本,且已被SteganographyCapacity::calculate扣除。- 聚合的 Enterprise 面所记录的 since 为
3.1.0。SteganographyEncryptionException继承自RuntimeException,因此捕获通用运行时类型的调用点仍能继续工作。
另请参见
标题为“另请参见”的章节发布边界
标题为“发布边界”的章节本页仅记录可从外部观测的行为以及受支持的公开 API 面。内部命名空间路径、辅助类、机制表、运行手册文件名和工单前缀均不在范围内。