Pular para o conteúdo
getnextpdf.com

Enterprise edição

Marca d'água digital e incorporação esteganográfica

O NextPDF Enterprise incorpora um payload oculto e criptografado em um PDF gerado fazendo ajustes pequenos e controlados no espaçamento entre pares de letras. Você fornece um payload — tipicamente um identificador por destinatário — e uma chave secreta; o codificador grava o payload como desvios imperceptíveis do kerning natural do texto. Um decodificador correspondente, dada a mesma chave, recupera o payload. Esta página é no nível de comportamento: ela declara o que o codificador grava, a criptografia que ele usa e o limite da técnica.

O uso pretendido é o rastreamento interno de vazamento de documentos: quando um documento controlado vaza, o marcador recuperado identifica a cópia do destinatário. Não é esteganografia adversarial e não é uma garantia de sobrevivência a processamento arbitrário.

Os pré-requisitos estão declarados no front matter e repetidos em Pré-requisitos.

Esta capacidade é fornecida no NextPDF Enterprise (nextpdf/enterprise) e ativa-se com um envelope de licença de nível Enterprise. Uma implantação sem essa habilitação não carrega as classes da capacidade. A capacidade é executada inteiramente em processo durante a geração do documento; nenhum conteúdo de documento sai do host. Compare as edições e obtenha uma licença.

O texto PDF desenhado com um array de kerning carrega um ajuste numérico entre glifos. O codificador trata cada posição de ajuste como um portador de alguns bits:

  1. Ele criptografa o payload com uma cifra de criptografia autenticada com dados associados (AEAD) — AES-256-GCM por padrão, ou ChaCha20-Poly1305. A AEAD fornece tanto confidencialidade quanto integridade, de modo que um portador adulterado falha na descriptografia em vez de produzir silenciosamente um payload errado.
  2. Ele deriva a chave de criptografia de 32 bytes da sua chave secreta e da chave da fonte usando a função de derivação de chave baseada em HMAC (HKDF) com SHA-256. A HKDF extrai uma chave pseudoaleatória de comprimento fixo do material de chaveamento de entrada e, em seguida, a expande até o comprimento exigido, conforme RFC 5869 §2.
  3. Ele gera um vetor de inicialização (IV) aleatório novo de 12 bytes por criptografia. O AES-GCM exige que o IV seja único para uma dada chave, ou a garantia de autenticação é perdida, conforme NIST SP 800-38D §5.2.1.
  4. Ele mapeia os bytes criptografados para uma sequência de bits e distribui os bits pelas posições de par de letras disponíveis, codificando um ou dois bits por posição. O desvio que ele adiciona ao kerning natural é limitado por uma fração configurável do em — pequeno o suficiente para permanecer visualmente imperceptível.

O decodificador reverte o processo: ele lê os ajustes de kerning de um content stream, subtrai o kerning natural, quantiza os desvios de volta para bits, remonta o blob criptografado e o descriptografa com a mesma chave. Se a chave estiver errada ou o portador tiver sido destruído, a descriptografia não retorna nada em vez de um payload errado.

A capacidade escala com o comprimento do texto: cada posição de par de letras carrega um ou dois bits, então um payload deve caber nas posições que o texto fornece. O codificador lança um erro de overflow tipado quando o payload excede a capacidade.

Um modo de compatibilidade com PDF/A reduz pela metade o desvio máximo para ficar abaixo do limiar de tolerância de largura de um validador, trocando capacidade por conformidade mais estrita.

A escolha estrutural é ocultar o marcador no kerning em vez de em uma sobreposição visível ou em um campo de metadados. Um marcador de metadados é trivial de remover, e um carimbo visível altera a página. Os desvios de kerning, em vez disso, viajam dentro do texto que o destinatário precisa manter, e permanecem imperceptíveis. A criptografia autenticada é o segundo pilar: um portador adulterado ou parcial falha na autenticação, então o decodificador não retorna nada em vez de um destinatário errado. A chave é derivada por fonte com HKDF, vinculando o marcador ao contexto do documento, e não a um segredo compartilhado nu. A divulgação honesta de robustez decorre diretamente: o marcador sobrevive à redistribuição comum, mas não à reescrita deliberada do content stream, então o escopo declarado é o rastreamento interno de vazamento, não a esteganografia adversarial.

Contexto de projeto: A redação não é um retângulo preto.

  1. Instale o NextPDF Core e o pacote Enterprise, e tenha uma licença Enterprise ativa.
  2. Gere o documento com uma fonte que exponha métricas de pares de kerning; o codificador lê o kerning natural das métricas da fonte.
  3. Forneça a chave secreta a partir do seu secret manager, não do código-fonte. A mesma chave é necessária para decodificar.
  4. Decida a profundidade de bits (um ou dois bits por posição) e se a compatibilidade com PDF/A é necessária, com base nas suas necessidades de capacidade e conformidade.

A configuração de codificação é imutável e validada na construção:

  • Profundidade de bits — um ou dois bits por posição de par de letras. Maior profundidade dá mais capacidade, mas desvios maiores.
  • Razão máxima de ajuste — o teto de desvio como uma fração do em, dentro de uma faixa limitada. Valores maiores dão mais folga, mas correm risco de visibilidade.
  • Cifra — AES-256-GCM (padrão) ou ChaCha20-Poly1305. Ambas são AEAD.
  • Compatibilidade com PDF/A — quando ativada, reduz pela metade o desvio máximo efetivo.

Use a mesma configuração para codificar e decodificar; uma incompatibilidade não gera nenhum payload recuperado.

  1. Leia a chave secreta do seu secret manager.
  2. Construa a configuração de codificação (profundidade de bits, razão de desvio, cifra, sinalizador PDF/A).
  3. Compute os ajustes de kerning para o texto que você está prestes a renderizar, passando o payload, o texto, a chave da fonte, as métricas da fonte, a chave secreta e a configuração.
  4. Aplique os ajustes retornados quando você gravar a sequência de texto, para que o marcador seja incorporado durante a geração.
  5. Para rastrear uma cópia vazada, execute o decodificador sobre o content stream do documento suspeito com a mesma chave de fonte, métricas de fonte, chave secreta e configuração, e leia o payload recuperado.
examples/enterprise/steganography-capacity-guard.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
/**
* Reject a payload that cannot fit the carrier text before encoding.
*
* Each letter-pair position carries $bitDepth bits. Guarding capacity up
* front turns an unencodable payload into a clear caller-side error instead
* of relying on the encoder's overflow exception alone.
*
* @param non-empty-string $payload The bytes to embed (already minimal).
* @param positive-int $textLength The character count of the carrier text.
* @param int<1, 2> $bitDepth Bits encoded per letter-pair position.
*
* @throws \OverflowException When the payload cannot fit the available positions.
*/
function assertPayloadFits(string $payload, int $textLength, int $bitDepth): void
{
$positions = $textLength - 1;
$capacityBytes = \intdiv($positions * $bitDepth, 8);
if (\strlen($payload) > $capacityBytes) {
throw new \OverflowException(\sprintf(
'Payload of %d bytes exceeds carrier capacity of %d bytes.',
\strlen($payload),
$capacityBytes,
));
}
}
examples/enterprise/steganography-decode-guarded.php
<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Enterprise\Security\Steganography\SteganographyDecoder;
use NextPDF\Enterprise\Security\Steganography\SteganographyConfig;
use NextPDF\Typography\FontMetrics;
use Psr\Log\LoggerInterface;
final readonly class LeakTracer
{
public function __construct(private LoggerInterface $logger) {}
/**
* Recover the embedded marker from a suspect document's content stream.
*
* Decoding returns null on a wrong key or a destroyed carrier rather than
* a wrong payload, so the caller treats null as "no marker recovered".
*
* @param string $contentStream The suspect content-stream bytes.
* @param non-empty-string $fontKey The font key used at generation.
* @param FontMetrics $metrics Font metrics with kerning pairs.
* @param string $secretKey The same secret key used to encode.
* @param SteganographyConfig $config The same configuration used to encode.
*
* @return string|null The recovered marker, or null when none is found.
*/
public function trace(
string $contentStream,
string $fontKey,
FontMetrics $metrics,
string $secretKey,
SteganographyConfig $config,
): ?string {
$marker = SteganographyDecoder::decodeFromContentStream(
$contentStream,
$fontKey,
$metrics,
$secretKey,
$config,
);
if ($marker === null) {
$this->logger->info('No steganographic marker recovered from content stream.');
}
return $marker;
}
}
  1. Codifique um payload conhecido em uma sequência de texto conhecida e, em seguida, decodifique-o de volta com a mesma chave e configuração; confirme que o payload recuperado corresponde.
  2. Decodifique com uma chave deliberadamente errada e confirme que o resultado é nulo, não um payload errado — esta é a garantia de integridade da AEAD em ação.
  3. Inspecione a página renderizada e confirme que a mudança de espaçamento não é visualmente perceptível na razão de desvio configurada.
  4. Quando a compatibilidade com PDF/A é necessária, valide a saída contra o seu perfil PDF/A e confirme que a tolerância de largura não é ultrapassada.
  • Criptografia autenticada. O payload é criptografado com AES-256-GCM ou ChaCha20-Poly1305. Um portador adulterado ou truncado falha na autenticação durante a descriptografia; ele não gera um payload errado.
  • IV por criptografia. Um IV aleatório novo de 12 bytes é gerado para cada criptografia, satisfazendo o requisito de unicidade do AES-GCM conforme NIST SP 800-38D §5.2.1.
  • Chave derivada. A chave de criptografia é derivada com HKDF-SHA-256 a partir do seu segredo e da chave da fonte (RFC 5869 §2). Mantenha o segredo no seu secret manager; trate-o como qualquer segredo de assinatura.
  • O marcador é conteúdo do documento. Os bytes incorporados fazem parte do conteúdo da página, não de conteúdo de log. Não grave o payload nem a chave secreta em logs.

Esta página trata de incorporação criptográfica. Toda fonte normativa é parafraseada; nenhum texto normativo é reproduzido. ### Divulgação de robustez

O marcador é carregado nos ajustes de kerning. Ele pode ser destruído por impressão e reescaneamento, por ferramentas de conversão de PDF, por relinearização ou por qualquer reescrita de content stream que normalize o kerning. A técnica é mais adequada ao rastreamento interno de vazamento de documentos distribuídos em sua forma gerada. Não é esteganografia adversarial e não sobrevive a processamento arbitrário a jusante. Não confie nela como o único controle quando o modelo de ameaças inclui remoção deliberada.

  • Payload grande demais. O codificador lança um erro de overflow tipado quando o payload excede a capacidade do texto. Encurte o payload ou alongue o texto portador.
  • Texto portador insuficiente. Texto mais curto que dois caracteres não oferece nenhuma posição portadora e lança um erro.
  • Chave errada na decodificação. A decodificação retorna nulo. Trate o nulo como “nenhum marcador recuperado”, não como um resultado parcial.
  • Incompatibilidade de configuração. A codificação e a decodificação devem usar a mesma profundidade de bits, razão de desvio, cifra e sinalizador PDF/A; uma incompatibilidade não gera nenhum payload recuperado.

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