Aller au contenu
getnextpdf.com

Enterprise édition

Stéganographie — Référence détaillée

Cette référence détaillée documente le canal stéganographique de NextPDF Enterprise. Le canal dissimule une charge utile chiffrée dans les ajustements numériques de crénage d’un tableau d’affichage de texte TJ. Il expose quatre symboles publics : SteganographyEncoder, SteganographyDecoder, SteganographyConfig et SteganographyCapacity. L’encodeur dérive une clé avec HKDF-SHA-256, chiffre la charge utile avec un algorithme AEAD et renvoie des décalages de crénage par position. Le décodeur inverse le processus à partir des ajustements observés ou d’un flux de contenu brut.

Le canal est conçu pour le traçage interne des fuites de documents. Il ne s’agit pas d’une stéganographie de niveau adversarial. Les données encodées peuvent être détruites par une impression-puis-numérisation, une conversion PDF, une re-linéarisation, une réécriture du flux de contenu ou toute opération qui normalise le crénage. NextPDF ne détient aucune certification pour ce canal et n’en accorde aucune. Cette page énonce une capacité, non une conformité.

Cette capacité est livrée dans NextPDF Enterprise (nextpdf/enterprise) et s’active avec une enveloppe de licence de niveau Enterprise. Un déploiement dépourvu de ce droit ne charge pas les classes de la capacité. Compare les éditions et obtiens une licence.

Le canal expose quatre classes finales. Tous les points d’entrée sont public static, à l’exception du constructeur de SteganographyConfig et de son accesseur effectiveMaxOffset. La classe auxiliaire NextPDF\Enterprise\Security\Steganography\SteganographyEncryptionException est levée par l’encodeur ; il ne s’agit pas d’un type construit par l’appelant.

SymboleParamètresComportement par défautRenvoieLève ou échoue avecNotes
SteganographyEncoder::encode$payload, $text, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Un $payload vide renvoie [] ; vérifie la robustesse de la clé ; chiffre ; calcule les décalages de crénage par position.array<int, float> (position => ajustement en 1/1000 em, convention AFM)InvalidArgumentException (clé sous le plancher) ; OverflowException (texte de moins de 2 caractères, ou charge utile au-delà de la capacité) ; SteganographyEncryptionException (échec AEAD)Passe le résultat à NextPDF\Content\TextRenderer::buildTjArrayOperator(). L’API renvoie des ajustements en convention AFM ; buildTjArrayOperator() effectue la conversion numérique TJ du PDF (ISO 32000-2 soustrait le nombre de la position courante). Les rédacteurs manuels de flux de contenu doivent préserver cette convention de signe.
SteganographyEncoder::assertSecretKeyStrength$secretKeyRejette une clé plus courte que le plancher.voidInvalidArgumentException (clé sous le plancher)Garde-fou partagé du chemin d’écriture, répliqué sur le chemin de lecture.
SteganographyEncoder::MIN_SECRET_KEY_LENGTHconstanteLe plancher de longueur de clé de 128 bits, en octets.int (16)Sans objetLa bibliothèque impose la longueur, pas l’entropie.
SteganographyDecoder::decode$observedAdjustments, $text, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Vérifie la robustesse de la clé ; quantifie les écarts ; reconstruit le blob ; déchiffre en AEAD.`stringnull(charge utile, ounull` en cas de mauvaise clé ou d’absence de charge utile)InvalidArgumentException (clé sous le plancher)
SteganographyDecoder::decodeFromContentStream$contentStream, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Tokenise le flux, reconstruit le texte et les ajustements à partir des tableaux TJ, puis délègue à decode.`stringnull(charge utile, ounullen l'absence de texteTJ` ou en cas d’échec du déchiffrement)InvalidArgumentException (clé sous le plancher, via decode)
SteganographyConfig::__construct$bitDepth, $maxAdjustmentEmRatio, $cipher, $requirePdfACompatibilityValide le domaine de chaque argument ; produit un objet valeur immuable.instance SteganographyConfigInvalidArgumentException ($bitDepth, $maxAdjustmentEmRatio ou $cipher invalide)Classe readonly ; les quatre arguments sont des propriétés publiques promues.
SteganographyConfig::effectiveMaxOffsetaucunRenvoie $maxAdjustmentEmRatio * 1000, réduit de moitié lorsque la compatibilité PDF/A est demandée.float (décalage en 1/1000 em)Sans objetCette réduction de moitié abaisse le risque de détection par écart de largeur.
SteganographyConfig::CRYPTO_OVERHEADconstanteLe surcoût de chiffrement fixe par charge utile, en octets.int (32)Sans objetLongueur de 4 octets, nonce de 12 octets, étiquette de 16 octets.
SteganographyCapacity::calculate$text, $config (SteganographyConfig)Calcule les octets de charge utile utilisables pour le texte, après surcoût.int (0 lorsque le texte est trop court)Sans objetLa capacité vaut positions * bitDepth / 8 moins le surcoût.
SteganographyCapacity::minimumTextLength$payloadBytes, $config (SteganographyConfig)Calcule le nombre minimal de caractères UTF-8 pour une charge utile.int (nombre de caractères)Sans objetInverse de calculate.

Les signatures verbatim suivent, chacune avec sa provenance de source.

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

L’encodeur découpe $text en caractères UTF-8 et forme une position par paire de caractères consécutifs. Chaque position porte $config->bitDepth bits, soit un ou deux. La charge utile est d’abord chiffrée, puis sérialisée en un blob, puis convertie en une séquence de bits. Chaque position encode ses bits sous la forme d’un petit décalage non négatif ajouté à la valeur de crénage naturelle de cette paire de caractères.

Le décalage est une fraction du décalage maximal effectif. Le décalage maximal effectif vaut $maxAdjustmentEmRatio * 1000 unités de conception, réduit de moitié lorsque $requirePdfACompatibility vaut true. Le crénage naturel est lu depuis $metrics via FontMetrics::getKernPair. La carte renvoyée est creuse : une position dont l’ajustement final est exactement nul est omise.

Le chiffrement utilise HKDF-SHA-256 pour dériver une clé de 32 octets. Le sel HKDF est le $fontKey non secret et l’étiquette info est une constante fixe. Par conséquent, le $secretKey de l’appelant constitue la seule frontière de confidentialité. L’algorithme AEAD est aes-256-gcm ou chacha20-poly1305, sélectionné par $config->cipher, exécuté via openssl_encrypt avec un nonce frais de 12 octets et une étiquette de 16 octets. Le blob sérialisé se compose d’une longueur big-endian de 4 octets, du nonce de 12 octets, du texte chiffré et de l’étiquette de 16 octets ; ce surcoût fixe est CRYPTO_OVERHEAD, soit 32 octets.

Le décodeur inverse la transformation. Il calcule l’écart de chaque ajustement observé par rapport au crénage naturel, normalise par le décalage maximal effectif et quantifie au niveau le plus proche. Il réassemble le blob, valide l’en-tête de longueur et appelle openssl_decrypt. Une mauvaise clé, une charge utile absente ou des ajustements corrompus font échouer l’authentification AEAD, et le décodeur renvoie null. decodeFromContentStream tokenise d’abord le flux brut avec NextPDF\Pro\Projection\ContentProjectionWriter::tokenize, reconstruit le texte et les ajustements numériques à partir de chaque tableau TJ, puis délègue à decode.

SteganographyCapacity::calculate indique la taille de charge utile utilisable pour un texte et une configuration, après soustraction de CRYPTO_OVERHEAD ; elle renvoie zéro lorsque le texte est trop court. SteganographyCapacity::minimumTextLength en est l’inverse : le plus petit nombre de caractères UTF-8 admettant une charge utile de la taille demandée.

  • Un $payload vide renvoie une carte vide depuis encode ; aucun octet n’est écrit, et le garde-fou de robustesse de clé n’est pas atteint.
  • Pour une charge utile non vide, un $text de moins de deux caractères lève OverflowException dans encode (une charge utile vide court-circuite vers [] avant la vérification de longueur) ; le même texte produit null dans decode et zéro dans SteganographyCapacity::calculate.
  • Un $payload supérieur à la capacité du texte lève OverflowException avant qu’aucun ajustement ne soit émis.
  • Un $secretKey plus court que MIN_SECRET_KEY_LENGTH (16 octets) lève InvalidArgumentException sur le chemin d’écriture comme sur le chemin de lecture. Il s’agit d’une violation de contrat, distincte d’un échec normal dû à une mauvaise clé.
  • Une mauvaise clé, un ensemble d’ajustements corrompu ou un blob tronqué amène decode à renvoyer null par échec d’authentification AEAD, et non par une exception.
  • Les positions absentes d’une carte creuse $observedAdjustments sont traitées comme un écart nul lors de l’extraction.
  • decodeFromContentStream renvoie null lorsque le flux ne contient aucun texte TJ.
  • Le canal est fragile par conception. Une impression-puis-numérisation, une conversion PDF, une re-linéarisation, une réécriture du flux de contenu ou une normalisation du crénage peut détruire les données encodées. Il ne convient pas à un usage adversarial ou d’archivage.

Le canal utilise HKDF-SHA-256 pour la dérivation de clé et un algorithme AEAD pour la confidentialité et l’intégrité. NextPDF ne détient aucune validation FIPS pour ce canal et n’en revendique aucune. Le module n’impose pas de profil FIPS ; la sélection de l’algorithme relève de l’appelant via $config->cipher. aes-256-gcm est AES en mode Galois/Counter, un mode de chiffrement authentifié bâti sur un chiffrement par blocs de 128 bits approuvé dont la conformité est validée sous le CMVP, selon NIST SP 800-38D §2. chacha20-poly1305 n’est pas défini par une recommandation de mode d’opération du NIST, si bien qu’un fournisseur OpenSSL contraint par FIPS le rejette ; openssl_encrypt renvoie alors false et l’encodeur lève SteganographyEncryptionException. Déterminer si un déploiement satisfait une exigence FIPS relève de l’opérateur au regard de son fournisseur validé, et non d’une affirmation de NextPDF.

L’intégration écrit des éléments numériques dans un tableau d’affichage de texte TJ. Selon ISO 32000-2:2020 §9.4.3, un tableau TJ affiche du texte et permet à un élément numérique d’ajuster la position du glyphe ; le nombre est exprimé en millièmes d’une unité d’espace-texte et est soustrait de la position courante. Une fois un glyphe peint, la matrice de texte est translatée du déplacement combiné, de sorte qu’un nombre de positionnement décale le placement des glyphes suivants — ISO 32000-2:2020 §9.4.4. Le canal ajoute ses décalages aux valeurs de crénage naturelles dans la même convention 1/1000 em (AFM), où une valeur négative resserre l’espacement.

L’ancrage AEAD se limite à la sélection de primitive : aes-256-gcm correspond au mode GCM de NIST SP 800-38D §2. Cette référence identifie un algorithme ; elle ne constitue pas une validation de ce canal.

Toutes les clauses sont paraphrasées ; NextPDF ne reproduit pas le texte normatif. NextPDF ne formule aucune revendication de conformité stéganographique, cryptographique ou PDF pour ce canal. L’alignement structurel avec le modèle de positionnement TJ est un énoncé de capacité, non une certification. La mise en garde sur la robustesse demeure : le canal sert au traçage interne des fuites, et il n’est pas de niveau adversarial.

  • Les points d’entrée sont des méthodes public static dans NextPDF\Enterprise\Security\Steganography, à l’exception du constructeur de SteganographyConfig et de effectiveMaxOffset.
  • SteganographyConfig est un objet valeur final readonly. Ses quatre propriétés sont immuables après construction, et les domaines de ses arguments sont validés dans le constructeur : $bitDepth vaut 1 ou 2, $maxAdjustmentEmRatio est dans (0, 0.05], et $cipher est aes-256-gcm ou chacha20-poly1305.
  • La sortie de l’encodage est consommée par NextPDF\Content\TextRenderer::buildTjArrayOperator. Les paires de crénage proviennent de NextPDF\Typography\FontMetrics. Le décodage du flux de contenu lit à travers NextPDF\Pro\Projection\ContentProjectionWriter et ne mute pas le flux.
  • Le plancher de longueur de clé est imposé au point d’entrée et réaffirmé à la frontière cryptographique privée, si bien qu’aucun chemin interne ne peut atteindre HKDF avec une clé faible. La bibliothèque impose la longueur, pas l’entropie ; fournir un matériel de clé à forte entropie relève de la responsabilité de l’intégrateur.
  • CRYPTO_OVERHEAD (32 octets) est le coût fixe par charge utile et est déjà soustrait par SteganographyCapacity::calculate.
  • La version documentée since est 3.1.0 pour la surface Enterprise agrégée. SteganographyEncryptionException étend RuntimeException, de sorte que les sites d’appel qui interceptent le type d’exécution générique continuent de fonctionner.

Cette page documente uniquement le comportement observable de l’extérieur et la surface d’API publique prise en charge. Les chemins de namespace internes, les classes auxiliaires, les tables de mécanismes, les noms de fichiers de runbook et les préfixes de tickets sont hors périmètre.