
Provably secure linguistic steganography embedding secret messages into LLM-generated text via rotation range-coding. Includes embed, extract, and round-trip verification scripts. Esteganografia linguística comprovadamente segura que incorpora mensagens secretas em texto gerado por LLM por meio de codificação de intervalo por rotação. Inclui scripts de incorporação, extração e verificação de ida e volta.
Rotation Range-Coding (RRC) Steganography — um método esteganográfico linguístico eficiente e comprovadamente seguro que incorpora mensagens secretas em texto em linguagem natural gerado por grandes modelos de linguagem.
Artigo: Efficient Provably Secure Linguistic Steganography via Range Coding (ACL 2026)
| Etapa | Descrição |
|---|---|
| Incorporar (Algoritmo 3) | Converte a mensagem secreta em um valor decimal e iterativamente a rotaciona dentro de um intervalo que encolhe, guiado pela distribuição de probabilidade do LM e por um deslocamento gerado por PRNG. Cada etapa de rotação produz um token. |
| Extrair (Algoritmo 4) | Executa novamente o LM sobre o estegotexto para recuperar os limites do intervalo, então reverte a rotação de volta ao valor decimal original e o binariza. |
O mecanismo de rotação resolve dois problemas da esteganografia de codificação de intervalo convencional:
.
├── rrc_core.py # Algorithms 3 and 4, shared by every entry point
├── RRC_embed.py # Embedding CLI
├── RRC_extract.py # Extraction CLI (reports recovery accuracy)
├── test_roundtrip.py # Round-trip test across several message lengths
├── fastest_embed.py # Embedding-speed benchmark
├── 0.Prompts.tsv # 1000 prompts (first ~10 words of C4 sequences)
└── requirements.txt
O codificador e o decodificador compartilham rrc_core.py em vez de manter cópias
separadas do laço. Os dois lados devem concordar bit a bit, e uma divergência
entre duas cópias mantidas independentemente se manifesta apenas como uma falha no
round trip, nunca na inspeção.
pip install -r requirements.txt
python test_roundtrip.py # GPT-2, 32/128/512 bits
python test_roundtrip.py --model meta-llama/Llama-2-7b-hf
python RRC_embed.py \
--language_model meta-llama/Llama-2-7b-hf \
--bit_length 128 --key 42 --top_k -1
python RRC_extract.py \
--language_model meta-llama/Llama-2-7b-hf \
--bit_length 128 --key 42 \
--input_file 1.RC_decimal_Llama-2-7b-hf_bit128.tsv
O codificador registra o segredo que incorporou e os ids de token que o carregaram, de modo que a extração reporta uma taxa de recuperação. Dois modelos receptores são avaliados:
ids — o receptor recebe a sequência de tokens. Isso isola o esquema de
codificação; qualquer coisa abaixo de 100% é uma falha numérica ou lógica.text — o receptor recebe a string destokenizada e a tokeniza novamente,
como um canal de texto exige. A re-tokenização nem sempre reproduz a segmentação
do emissor, então este é um limite inferior; a diferença entre as duas
medidas indica ambiguidade de tokenização, não do esquema de codificação. No Llama-2-7b
com 128 bits: 100% para ids, ~91% para text.Importante: emissor e receptor devem compartilhar o modelo de linguagem, a chave, o comprimento de bits e a configuração de top-k, e também devem executar o mesmo hardware e versões de biblioteca. Veja Requisitos numéricos.
Compartilhados por RRC_embed.py e RRC_extract.py:
| Argumento | Padrão | Descrição |
|---|---|---|
--language_model | meta-llama/Llama-3.1-8B | Id do modelo HuggingFace |
--bit_length | 128 | Comprimento da mensagem secreta em bits |
--top_k | -1 | Truncamento top-k; -1 = vocabulário completo |
--key | 42 | Chave simétrica K (semente do PRNG) |
--precision_margin | 40 | Dígitos decimais além dos que 2**bit_length precisa |
--max_ctx_len | 3900 | Janela do cache KV |
--device | auto | cuda ou cpu |
RRC_embed.py também aceita --prompts, --output, --limit, --seed,
--max_tokens, --resume, --no_verify e --part / --part_max para
fragmentar um conjunto de prompts entre processos (--part_max 3 com --part 0,1,2, um
por GPU).
RRC_extract.py também aceita --input_file, --mode {ids,text,both},
--limit e --verbose.
O RRC estreita um intervalo de largura 2**l até a largura ~1, então o valor
recuperado é sensível ao erro relativo por um fator de 2**l: com 128 bits, uma
perturbação relativa de 1e-16 em qualquer ponto da cadeia desloca o resultado em
~1e22 contra uma tolerância de 0.5. A extração não degrada graciosamente — ela
ou retorna a mensagem ou retorna bits não relacionados. Medido no Llama-2-7b:
| Configuração | Perturbação relativa das probabilidades | Extração |
|---|---|---|
| Repetir a mesma decodificação batch-1 | 0 (bit a bit idêntica) | funciona |
| A mesma linha em batch 2 / 8 / 16 | ~1e-2 | falha |
| Preenchimento à esquerda do prompt | ~1e-2 | falha |
| Uma passagem teacher-forced em vez de decodificação incremental | ~4e-3 | falha |
Duas consequências moldam a implementação. O batching não está disponível, então as sequências são processadas uma de cada vez. E o receptor reproduz a decodificação incremental do emissor passo a passo em vez de pontuar o estegotexto em uma única passagem forward, mesmo tendo todos os tokens de antemão. Ambos os atalhos medem ~9x mais rápido e nenhum preserva as probabilidades.
Tudo após os logits é float64 e produz limites bit a bit idênticos em CPU e GPU, então a passagem forward é a única etapa que vincula um estegotexto ao seu hardware.
A precisão é derivada do comprimento da mensagem como
ceil(bit_length * log10(2)) + margin e definida antes de qualquer outra coisa executar. Ela
escala com a mensagem: 2**256 precisa de 78 dígitos e 2**8192 precisa de 2466.
Precisão abaixo desse limiar não falha de forma limpa. Os dígitos de ordem inferior do valor secreto são arredondados, o intervalo colapsa em poucos tokens e o teste de terminação dispara cedo, de modo que uma execução pode completar e reportar números que são aritmeticamente inalcançáveis. Manter a precisão em 50 dígitos independentemente do comprimento da mensagem resulta em:
| bits | utilização com 50 dígitos | amostras concluídas | com precisão derivada |
|---|---|---|---|
| 256 | 157.05% | 8/20 | 100.17% |
| 512 | 299.67% | 9/20 | 102.48% |
| 1024 | 660.91% | 7/10 | 102.58% |
| 2048 | 1046.40% | 1/5 | 98.18% |
| 4096 | 2483.92% | 3/3, nenhum erro levantado | 100.87% |
| 8192 | 4766.49% | 1/2 | 99.07% |
--precision_margin ajusta a folga acima do mínimo derivado; o
mínimo em si é imposto.
O Algoritmo 3 para quando (L + R) / 2 - d_s cai em (-0.5, 0.5]. Esse teste
pode disparar enquanto o intervalo é mais estreito que 1, e a janela
[mid - 0.5, mid + 0.5) é então mais larga que o intervalo que a carrega. A rotação
reversa é uma isometria módulo a largura do intervalo, então uma janela que atravessa o
corte deixa o receptor a um intervalo inteiro de distância de d_s.
O emissor, portanto, reproduz a rotação reversa do receptor antes de se comprometer
a parar e emite outro token se a reprodução não retornar o segredo. A
reprodução é trabalho puro de Decimal (~0.05 ms contra ~20 ms para uma passagem forward), então
a guarda não custa nada mensurável. --no_verify a desabilita.
Medido no Llama-2-7b: