Pular para o conteúdo
getnextpdf.com

Enterprise edição

Esteganografia — Referência Profunda

Esta referência detalhada documenta o canal esteganográfico do NextPDF Enterprise. O canal esconde um payload criptografado dentro dos ajustes numéricos de kern de uma array de exibição de texto TJ. Ele tem quatro símbolos públicos: SteganographyEncoder, SteganographyDecoder, SteganographyConfig e SteganographyCapacity. O codificador deriva uma chave com HKDF-SHA-256, criptografa o payload com uma cifra AEAD e retorna deslocamentos de kern por posição. O decodificador reverte o processo a partir de ajustes observados ou de um content stream bruto.

O canal foi projetado para rastreamento interno de vazamento de documentos. Ele não é esteganografia de grau adversarial. Dados codificados podem ser destruídos por impressão-e-digitalização, conversão de PDF, relinearização, reescrita de content stream ou qualquer operação que normalize o kerning. O NextPDF não possui nenhuma certificação para este canal e não concede nenhuma. Esta página declara capacidade, não conformidade.

Este recurso é distribuído no NextPDF Enterprise (nextpdf/enterprise) e é ativado com um envelope de licença de nível Enterprise. Uma implantação sem essa habilitação não carrega as classes do recurso. Compare as edições e obtenha uma licença.

O canal expõe quatro classes final. Todos os pontos de entrada são public static, exceto o construtor de SteganographyConfig e seu acessador effectiveMaxOffset. A classe de suporte NextPDF\Enterprise\Security\Steganography\SteganographyEncryptionException é lançada pelo codificador; ela não é um tipo construído pelo chamador.

SímboloParâmetrosComportamento padrãoRetornaLança ou falha comNotas
SteganographyEncoder::encode$payload, $text, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Um $payload vazio retorna []; verifica a força da chave; criptografa; calcula deslocamentos de kern por posição.array<int, float> (posição => ajuste em 1/1000 em, convenção AFM)InvalidArgumentException (chave abaixo do piso); OverflowException (texto com menos de 2 caracteres, ou payload acima da capacidade); SteganographyEncryptionException (falha AEAD)Passe o resultado para NextPDF\Content\TextRenderer::buildTjArrayOperator(). A API retorna ajustes na convenção AFM; buildTjArrayOperator() realiza a conversão numérica do TJ do PDF (a ISO 32000-2 subtrai o número da posição atual). Escritores manuais de content stream devem preservar essa convenção de sinal.
SteganographyEncoder::assertSecretKeyStrength$secretKeyRejeita uma chave mais curta que o piso.voidInvalidArgumentException (chave abaixo do piso)Proteção compartilhada do caminho de escrita, espelhada no caminho de leitura.
SteganographyEncoder::MIN_SECRET_KEY_LENGTHconstanteO piso de comprimento de chave de 128 bits em bytes.int (16)Não aplicávelA biblioteca impõe comprimento, não entropia.
SteganographyDecoder::decode$observedAdjustments, $text, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Verifica a força da chave; quantiza desvios; reconstrói o blob; decripta com AEAD.`stringnull(payload, ounull` em chave errada ou sem payload)InvalidArgumentException (chave abaixo do piso)
SteganographyDecoder::decodeFromContentStream$contentStream, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Tokeniza o stream, reconstrói texto e ajustes a partir de arrays TJ, e então delega para decode.`stringnull(payload, ounullquando não há textoTJ` ou a decriptação falha)InvalidArgumentException (chave abaixo do piso, via decode)
SteganographyConfig::__construct$bitDepth, $maxAdjustmentEmRatio, $cipher, $requirePdfACompatibilityValida o domínio de cada argumento; produz um value object imutável.Instância de SteganographyConfigInvalidArgumentException ($bitDepth, $maxAdjustmentEmRatio ou $cipher inválidos)Classe readonly; os quatro argumentos são propriedades públicas promovidas.
SteganographyConfig::effectiveMaxOffsetnenhumRetorna $maxAdjustmentEmRatio * 1000, reduzido à metade quando a compatibilidade com PDF/A é solicitada.float (deslocamento em 1/1000 em)Não aplicávelA redução à metade diminui o risco de detecção por incompatibilidade de largura.
SteganographyConfig::CRYPTO_OVERHEADconstanteO overhead fixo de criptografia por payload em bytes.int (32)Não aplicável4 bytes de comprimento, 12 bytes de nonce, 16 bytes de tag.
SteganographyCapacity::calculate$text, $config (SteganographyConfig)Calcula os bytes de payload utilizáveis para o texto, após o overhead.int (0 quando o texto é curto demais)Não aplicávelA capacidade é positions * bitDepth / 8 menos o overhead.
SteganographyCapacity::minimumTextLength$payloadBytes, $config (SteganographyConfig)Calcula a contagem mínima de caracteres UTF-8 para um payload.int (contagem de caracteres)Não aplicávelInverso de calculate.

As assinaturas verbatim seguem, cada uma com a proveniência do código-fonte.

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

O codificador divide $text em caracteres UTF-8 e forma uma posição por par consecutivo de caracteres. Cada posição carrega $config->bitDepth bits, que é um ou dois. O payload é primeiro criptografado, depois serializado em um blob e então convertido em uma sequência de bits. Cada posição codifica seus bits como um pequeno deslocamento não negativo adicionado ao valor de kern natural daquele par de caracteres.

O deslocamento é uma fração do deslocamento máximo efetivo. O deslocamento máximo efetivo é $maxAdjustmentEmRatio * 1000 unidades de design, reduzido à metade quando $requirePdfACompatibility é verdadeiro. O kern natural é lido de $metrics através de FontMetrics::getKernPair. O mapa retornado é esparso: uma posição cujo ajuste final é exatamente zero é omitida.

A criptografia usa HKDF-SHA-256 para derivar uma chave de 32 bytes. O salt do HKDF é o $fontKey não secreto e o rótulo de info é uma constante fixa. Portanto, a $secretKey do chamador é a única fronteira de confidencialidade. A cifra AEAD é aes-256-gcm ou chacha20-poly1305, selecionada por $config->cipher, executada através de openssl_encrypt com um novo nonce de 12 bytes e uma tag de 16 bytes. O blob serializado é um comprimento big-endian de 4 bytes, o nonce de 12 bytes, o ciphertext e a tag de 16 bytes; esse overhead fixo é CRYPTO_OVERHEAD, que é 32 bytes.

O decodificador reverte a transformação. Ele calcula o desvio de cada ajuste observado em relação ao kern natural, normaliza pelo deslocamento máximo efetivo e quantiza para o nível mais próximo. Ele reagrupa o blob, valida o cabeçalho de comprimento e chama openssl_decrypt. Uma chave errada, um payload ausente ou ajustes corrompidos fazem a autenticação AEAD falhar, e o decodificador retorna null. decodeFromContentStream primeiro tokeniza o stream bruto com NextPDF\Pro\Projection\ContentProjectionWriter::tokenize, reconstrói o texto e os ajustes numéricos de cada array TJ, e então delega para decode.

SteganographyCapacity::calculate informa o tamanho de payload utilizável para um texto e uma configuração, após subtrair CRYPTO_OVERHEAD; retorna zero quando o texto é curto demais. SteganographyCapacity::minimumTextLength é o inverso: a menor contagem de caracteres UTF-8 que admite um payload do tamanho solicitado.

  • Um $payload vazio retorna um mapa vazio de encode; nenhum byte é escrito, e a proteção de força da chave não é alcançada.
  • Para um payload não vazio, um $text com menos de dois caracteres levanta OverflowException em encode (um payload vazio faz curto-circuito para [] antes da verificação de comprimento); o mesmo texto resulta em null em decode e zero em SteganographyCapacity::calculate.
  • Um $payload maior que a capacidade do texto levanta OverflowException antes que qualquer ajuste seja emitido.
  • Uma $secretKey mais curta que MIN_SECRET_KEY_LENGTH (16 bytes) levanta InvalidArgumentException tanto no caminho de escrita quanto no de leitura. Isso é uma violação de contrato, distinta de um erro normal de chave errada.
  • Uma chave errada, um conjunto de ajustes corrompido ou um blob truncado fazem decode retornar null por falha de autenticação AEAD, não uma exceção.
  • Posições ausentes de um mapa $observedAdjustments esparso são tratadas como um desvio zero durante a extração.
  • decodeFromContentStream retorna null quando o stream não contém texto TJ.
  • O canal é frágil por design. Impressão-e-digitalização, conversão de PDF, relinearização, reescrita de content stream ou normalização de kerning podem destruir os dados codificados. Ele é inadequado para uso adversarial ou de arquivamento.

O canal usa HKDF-SHA-256 para derivação de chave e uma cifra AEAD para confidencialidade e integridade. O NextPDF não possui nenhuma validação FIPS para este canal e não faz tal alegação. O módulo não impõe um perfil FIPS; a seleção da cifra é a decisão do chamador através de $config->cipher. aes-256-gcm é AES em Galois/Counter Mode, um modo de criptografia autenticada construído sobre uma cifra de bloco de 128 bits aprovada, cuja conformidade é validada sob o CMVP, conforme NIST SP 800-38D §2. chacha20-poly1305 não é definido por uma recomendação de modo de operação do NIST, então um provedor OpenSSL restrito a FIPS o rejeita; openssl_encrypt então retorna false e o codificador levanta SteganographyEncryptionException. Se uma implantação atende a um requisito FIPS é a determinação do operador contra o seu provedor validado, não uma afirmação do NextPDF.

A incorporação escreve elementos numéricos em uma array de exibição de texto TJ. Conforme a ISO 32000-2:2020 §9.4.3, uma array TJ mostra texto e permite que um elemento numérico ajuste a posição do glifo; o número é expresso em milésimos de uma unidade de espaço de texto e é subtraído da posição atual. Depois que um glifo é pintado, a matriz de texto é transladada pelo deslocamento combinado, de modo que um número de posicionamento desloca a colocação dos glifos subsequentes — ISO 32000-2:2020 §9.4.4. O canal adiciona seus deslocamentos aos valores de kern natural na mesma convenção de 1/1000 em (AFM), onde um valor negativo aperta o espaçamento.

O embasamento AEAD limita-se à seleção da primitiva: aes-256-gcm corresponde ao modo GCM do NIST SP 800-38D §2. Essa referência identifica um algoritmo; ela não é uma validação deste canal.

Todas as cláusulas são parafraseadas; o NextPDF não reproduz texto normativo. O NextPDF não faz nenhuma alegação de conformidade de esteganografia, criptografia ou PDF para este canal. O alinhamento estrutural com o modelo de posicionamento TJ é uma declaração de capacidade, não uma certificação. A divulgação de robustez permanece: o canal é para rastreamento interno de vazamento, e não é de grau adversarial.

  • Os pontos de entrada são métodos public static em NextPDF\Enterprise\Security\Steganography, exceto o construtor de SteganographyConfig e effectiveMaxOffset.
  • SteganographyConfig é um value object final readonly. Suas quatro propriedades são imutáveis após a construção, e os domínios de seus argumentos são validados no construtor: $bitDepth é 1 ou 2, $maxAdjustmentEmRatio está em (0, 0.05], e $cipher é aes-256-gcm ou chacha20-poly1305.
  • A saída do encode é consumida por NextPDF\Content\TextRenderer::buildTjArrayOperator. Os pares de kern vêm de NextPDF\Typography\FontMetrics. A decodificação de content stream lê através de NextPDF\Pro\Projection\ContentProjectionWriter e não muta o stream.
  • O piso de comprimento de chave é imposto no ponto de entrada e reafirmado na fronteira criptográfica privada, de modo que nenhum caminho interno pode alcançar o HKDF com uma chave fraca. A biblioteca impõe comprimento, não entropia; fornecer material de chave de alta entropia é responsabilidade do integrador.
  • CRYPTO_OVERHEAD (32 bytes) é o custo fixo por payload e já é subtraído por SteganographyCapacity::calculate.
  • O since documentado é 3.1.0 para a superfície agregada do Enterprise. SteganographyEncryptionException estende RuntimeException, então os call sites que capturam o tipo runtime genérico continuam a funcionar.

Esta página documenta apenas o comportamento observável externamente e a superfície da API pública suportada. Caminhos de namespace internos, classes auxiliares, tabelas de mecanismos, nomes de arquivos de runbook e prefixos de tickets estão fora de escopo.