Перейти к содержимому
getnextpdf.com

Enterprise редакция

Стеганография — глубокий справочник

Этот детальный справочник описывает стеганографический канал NextPDF Enterprise. Канал скрывает зашифрованную полезную нагрузку внутри числовых корректировок кернинга в текстопоказывающем массиве TJ. У него четыре публичных символа: SteganographyEncoder, SteganographyDecoder, SteganographyConfig и SteganographyCapacity. Кодировщик выводит ключ через HKDF-SHA-256, шифрует полезную нагрузку AEAD-шифром и возвращает попозиционные смещения кернинга. Декодировщик обращает процесс, исходя из наблюдаемых корректировок или из необработанного потока содержимого.

Канал предназначен для внутреннего отслеживания утечек документов. Это не стеганография противодействующего класса. Закодированные данные могут быть уничтожены печатью с последующим сканированием, конвертацией PDF, повторной линеаризацией, перезаписью потока содержимого или любой операцией, нормализующей кернинг. NextPDF не имеет никакой сертификации для этого канала и не предоставляет её. Эта страница заявляет о возможности, а не о соответствии.

Эта возможность поставляется в составе NextPDF Enterprise (nextpdf/enterprise) и активируется лицензионным конвертом уровня Enterprise. Развёртывание без этого права не загружает классы данной возможности. Сравните редакции и получите лицензию.

Канал предоставляет четыре класса final. Все точки входа — public static, кроме конструктора SteganographyConfig и его аксессора effectiveMaxOffset. Вспомогательный 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Отклоняет ключ короче порога.voidInvalidArgumentException (ключ ниже порога)Общая проверка пути записи, продублированная на пути чтения.
SteganographyEncoder::MIN_SECRET_KEY_LENGTHконстантаПорог длины ключа в 128 бит, в байтах.int (16)НеприменимоБиблиотека контролирует длину, а не энтропию.
SteganographyDecoder::decode$observedAdjustments, $text, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Проверяет стойкость ключа; квантует отклонения; восстанавливает blob; расшифровывает AEAD.`stringnull(полезная нагрузка илиnull` при неверном ключе либо отсутствии нагрузки)InvalidArgumentException (ключ ниже порога)
SteganographyDecoder::decodeFromContentStream$contentStream, $fontKey, $metrics (FontMetrics), $secretKey, $config (SteganographyConfig)Токенизирует поток, восстанавливает текст и корректировки из массивов TJ, затем делегирует в decode.`stringnull(полезная нагрузка илиnull, когда нет текста TJ` либо расшифровка не удалась)InvalidArgumentException (ключ ниже порога, через decode)
SteganographyConfig::__construct$bitDepth, $maxAdjustmentEmRatio, $cipher, $requirePdfACompatibilityПроверяет область допустимости каждого аргумента; создаёт неизменяемый объект-значение.Экземпляр SteganographyConfigInvalidArgumentException (недопустимый $bitDepth, $maxAdjustmentEmRatio или $cipher)Класс readonly; четыре аргумента — это публичные продвинутые свойства.
SteganographyConfig::effectiveMaxOffsetнетВозвращает $maxAdjustmentEmRatio * 1000, делённое пополам при запросе совместимости с PDF/A.float (смещение в 1/1000 em)НеприменимоДеление пополам снижает риск обнаружения по расхождению ширины.
SteganographyConfig::CRYPTO_OVERHEADконстантаФиксированные накладные расходы шифрования на одну полезную нагрузку, в байтах.int (32)Неприменимо4-байтовая длина, 12-байтовый nonce, 16-байтовый тег.
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(),
): 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 истинно. Естественный кернинг читается из $metrics через FontMetrics::getKernPair. Возвращаемая карта разрежена: позиция, итоговая корректировка которой ровно нулевая, опускается.

Шифрование использует HKDF-SHA-256 для вывода 32-байтового ключа. Соль HKDF — это несекретный $fontKey, а метка info — фиксированная константа. Поэтому $secretKey вызывающей стороны — единственная граница конфиденциальности. AEAD-шифр — это aes-256-gcm или chacha20-poly1305, выбираемый через $config->cipher, выполняемый через openssl_encrypt со свежим 12-байтовым nonce и 16-байтовым тегом. Сериализованный blob — это 4-байтовая длина в формате big-endian, 12-байтовый nonce, шифртекст и 16-байтовый тег; эти фиксированные накладные расходы — 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 короче двух символов вызывает OverflowException в encode (пустая полезная нагрузка замыкается на [] до проверки длины); тот же текст даёт null в decode и ноль в SteganographyCapacity::calculate.
  • $payload крупнее ёмкости текста вызывает OverflowException до того, как будет выдана какая-либо корректировка.
  • $secretKey короче MIN_SECRET_KEY_LENGTH (16 байт) вызывает InvalidArgumentException на пути и записи, и чтения. Это нарушение контракта, отличное от обычного промаха по неверному ключу.
  • Неверный ключ, повреждённый набор корректировок или усечённый blob заставляют decode вернуть null через сбой AEAD-аутентификации, а не исключение.
  • Позиции, отсутствующие в разреженной карте $observedAdjustments, при извлечении трактуются как нулевое отклонение.
  • decodeFromContentStream возвращает null, когда поток не содержит текста TJ.
  • Канал хрупок по замыслу. Печать с последующим сканированием, конвертация PDF, повторная линеаризация, перезапись потока содержимого или нормализация кернинга могут уничтожить закодированные данные. Он непригоден для противодействующего или архивного использования.

Канал использует HKDF-SHA-256 для вывода ключа и один AEAD-шифр для конфиденциальности и целостности. NextPDF не имеет валидации FIPS для этого канала и не заявляет о ней. Модуль не обеспечивает соблюдение профиля FIPS; выбор шифра — это решение вызывающей стороны через $config->cipher. aes-256-gcm — это AES в режиме Galois/Counter, аутентифицирующий режим шифрования, построенный на одобренном 128-битном блочном шифре, соответствие которого валидируется в рамках CMVP, согласно NIST SP 800-38D §2. chacha20-poly1305 не определён рекомендацией NIST по режимам работы, поэтому OpenSSL-провайдер с ограничением FIPS отклоняет его; 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 соответствует режиму GCM из NIST SP 800-38D §2. Эта ссылка идентифицирует алгоритм; это не валидация данного канала.

Все пункты изложены пересказом; NextPDF не воспроизводит нормативный текст. NextPDF не делает никаких заявлений о соответствии в области стеганографии, криптографии или PDF для этого канала. Структурное выравнивание с моделью позиционирования TJ — это заявление о возможности, а не сертификация. Раскрытие об устойчивости остаётся в силе: канал предназначен для внутреннего отслеживания утечек и не относится к противодействующему классу.

  • Точки входа — методы public static в NextPDF\Enterprise\Security\Steganography, кроме конструктора 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.
  • Документированный since — 3.1.0 для агрегированной поверхности Enterprise. SteganographyEncryptionException расширяет RuntimeException, поэтому места вызова, перехватывающие обобщённый runtime-тип, продолжают работать.

Эта страница документирует только внешне наблюдаемое поведение и поддерживаемую публичную поверхность API. Внутренние пути пространств имён, вспомогательные классы, таблицы механизмов, имена файлов runbook и префиксы тикетов вне области рассмотрения.