跳转到内容
getnextpdf.com

Enterprise 版本

隐写术 — 深度参考

本深度参考记录了 NextPDF Enterprise 隐写通道。该通道将加密载荷隐藏在 TJ 文本显示数组的数值字距调整量之中。它有四个公开符号:SteganographyEncoderSteganographyDecoderSteganographyConfigSteganographyCapacity。编码器使用 HKDF-SHA-256 派生密钥,用 AEAD 密码算法加密载荷,并返回每个位置的字距偏移量。解码器则从观测到的调整量或原始内容流中逆转此过程。

该通道专为内部文档泄漏溯源而设计。它不是对抗级隐写术。编码数据可能被打印再扫描、PDF 转换、重新线性化、内容流重写或任何使字距归一化的操作所破坏。NextPDF 对此通道不持有任何认证,也不授予任何认证。本页陈述的是能力,而非合规性。

此能力随 NextPDF Enterprisenextpdf/enterprise)发布,并通过 Enterprise 级许可证信封激活。缺少该授权的部署不会加载此能力的类。比较版本并获取许可证

该通道暴露四个 final 类。除 SteganographyConfig 构造函数及其 effectiveMaxOffset 访问器外,所有入口点均为 public static。配套的 NextPDF\Enterprise\Security\Steganography\SteganographyEncryptionException 由编码器抛出;它不是调用方构造的类型。

符号参数默认行为返回值抛出或失败于说明
SteganographyEncoder::encode$payload$text$fontKey$metricsFontMetrics)、$secretKey$configSteganographyConfig$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拒绝短于下限的密钥。voidInvalidArgumentException(密钥低于下限)共享的写入路径守卫,在读取路径上有对应镜像。
SteganographyEncoder::MIN_SECRET_KEY_LENGTH常量以字节为单位的 128 位密钥长度下限。int(16)不适用库强制的是长度,而非熵。
SteganographyDecoder::decode$observedAdjustments$text$fontKey$metricsFontMetrics)、$secretKey$configSteganographyConfig断言密钥强度;量化偏差;重建 blob;AEAD 解密。`stringnull(载荷,或在密钥错误或无载荷时为 null`)InvalidArgumentException(密钥低于下限)
SteganographyDecoder::decodeFromContentStream$contentStream$fontKey$metricsFontMetrics)、$secretKey$configSteganographyConfig对流进行分词,从 TJ 数组重建文本与调整量,然后委托给 decode`stringnull(载荷,或在无 TJ文本或解密失败时为null`)InvalidArgumentException(密钥低于下限,经由 decode
SteganographyConfig::__construct$bitDepth$maxAdjustmentEmRatio$cipher$requirePdfACompatibility校验每个参数的取值域;产出一个不可变值对象。SteganographyConfig 实例InvalidArgumentException(无效的 $bitDepth$maxAdjustmentEmRatio$cipherreadonly 类;四个参数是公开提升属性。
SteganographyConfig::effectiveMaxOffset返回 $maxAdjustmentEmRatio * 1000,在请求 PDF/A 兼容性时减半。float(以 1/1000 em 为单位的偏移量)不适用减半可降低宽度不匹配的检测风险。
SteganographyConfig::CRYPTO_OVERHEAD常量每个载荷固定的加密开销字节数。int(32)不适用4 字节长度、12 字节 nonce、16 字节 tag。
SteganographyCapacity::calculate$text$configSteganographyConfig计算扣除开销后该文本可用的载荷字节数。int(文本过短时为 0)不适用容量为 positions * bitDepth / 8 减去开销。
SteganographyCapacity::minimumTextLength$payloadBytes$configSteganographyConfig计算某载荷所需的最小 UTF-8 字符数。int(字符数)不适用calculate 的逆运算。

逐字签名如下,每个均附带源代码出处。

public static function encode(
string $payload,
string $text,
string $fontKey,
FontMetrics $metrics,
string $secretKey,
SteganographyConfig $config = new SteganographyConfig(),
): array
public static function assertSecretKeyStrength(string $secretKey): void
public 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(),
): ?string
public static function decodeFromContentStream(
string $contentStream,
string $fontKey,
FontMetrics $metrics,
string $secretKey,
SteganographyConfig $config = new SteganographyConfig(),
): ?string
public function __construct(
public int $bitDepth = 1,
public float $maxAdjustmentEmRatio = 0.02,
public string $cipher = 'aes-256-gcm',
public bool $requirePdfACompatibility = false,
)
public function effectiveMaxOffset(): float
public const int CRYPTO_OVERHEAD = 32;
public static function calculate(
string $text,
SteganographyConfig $config = new SteganographyConfig(),
): int
public 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-gcmchacha20-poly1305,由 $config->cipher 选定,通过 openssl_encrypt 运行,配以新鲜的 12 字节 nonce 和 16 字节 tag。序列化后的 blob 由 4 字节大端长度、12 字节 nonce、密文以及 16 字节 tag 组成;此固定开销即 CRYPTO_OVERHEAD,为 32 字节。

解码器逆转该变换。它计算每个观测调整量相对于自然字距的偏差,按有效最大偏移量归一化,并量化到最近的级别。它重新组装 blob,校验长度头,并调用 openssl_decrypt。错误的密钥、缺失的载荷或损坏的调整量会导致 AEAD 认证失败,解码器返回 nulldecodeFromContentStream 首先用 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 转换、重新线性化、内容流重写或字距归一化都可能破坏编码数据。它不适用于对抗性或归档用途。

该通道使用 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] 内,$cipheraes-256-gcmchacha20-poly1305
  • encode 的输出由 NextPDF\Content\TextRenderer::buildTjArrayOperator 消费。字距对来自 NextPDF\Typography\FontMetrics。内容流解码经由 NextPDF\Pro\Projection\ContentProjectionWriter 读取,且不修改流。
  • 密钥长度下限在入口点强制执行,并在私有密码边界处重新断言,因此没有任何内部路径能以弱密钥到达 HKDF。库强制的是长度,而非熵;提供高熵密钥材料是集成方的责任。
  • CRYPTO_OVERHEAD(32 字节)是每个载荷的固定成本,且已被 SteganographyCapacity::calculate 扣除。
  • 聚合的 Enterprise 面所记录的 since 为 3.1.0SteganographyEncryptionException 继承自 RuntimeException,因此捕获通用运行时类型的调用点仍能继续工作。

本页仅记录可从外部观测的行为以及受支持的公开 API 面。内部命名空间路径、辅助类、机制表、运行手册文件名和工单前缀均不在范围内。