
Beweisbar sichere linguistische Steganographie, die geheime Nachrichten in LLM-generierten Text durch Rotations-Range-Codierung einbettet. Enthält Einbettungs-, Extraktions- und Round-Trip-Verifizierungsskripte.
Rotation Range-Coding (RRC) Steganography — eine effiziente und nachweislich sichere linguistische steganographische Methode, die geheime Nachrichten in natürlichsprachlichen Text einbettet, der von großen Sprachmodellen generiert wird.
Paper: Efficient Provably Secure Linguistic Steganography via Range Coding (ACL 2026)
| Schritt | Beschreibung |
|---|---|
| Einbetten (Algorithmus 3) | Konvertiert die geheime Nachricht in einen Dezimalwert und rotiert ihn iterativ innerhalb eines schrumpfenden Intervalls, gesteuert durch die Wahrscheinlichkeitsverteilung des LM und einen PRNG-generierten Offset. Jeder Rotationsschritt erzeugt ein Token. |
| Extrahieren (Algorithmus 4) | Führt das LM erneut auf dem Stegotext aus, um die Intervallgrenzen wiederherzustellen, und rotiert dann zurück zum ursprünglichen Dezimalwert und binarisiert ihn. |
Der Rotationsmechanismus löst zwei Probleme der herkömmlichen Range-Coding-Steganographie:
.
├── 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
Der Encoder und der Decoder teilen sich rrc_core.py, anstatt separate
Kopien der Schleife zu halten. Die beiden Seiten müssen Bit für Bit übereinstimmen, und eine Abweichung
zwischen zwei unabhängig gepflegten Kopien zeigt sich nur als fehlgeschlagener Round
Trip, niemals bei der Inspektion.
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
Der Encoder zeichnet das eingebettete Geheimnis und die Token-IDs, die es trugen, auf, sodass die Extraktion eine Wiederherstellungsrate meldet. Zwei Empfängermodelle werden bewertet:
ids — der Empfänger erhält die Token-Sequenz. Dies isoliert das Codierungs-
schema; alles unter 100% ist ein numerischer oder logischer Fehler.text — der Empfänger erhält die detokenisierte Zeichenkette und tokenisiert sie erneut,
wie es ein Textkanal erfordert. Die Re-Tokenisierung reproduziert nicht immer die
Segmentierung des Senders, daher ist dies eine untere Schranke; die Lücke zwischen den beiden
Maßen zeigt die Tokenisierungsmehrdeutigkeit, nicht das Codierungsschema. Bei Llama-2-7b
mit 128 Bits: 100% für ids, ~91% für text.Wichtig: Sender und Empfänger müssen dasselbe Sprachmodell, denselben Schlüssel, dieselbe Bit- länge und dieselbe Top-k-Einstellung verwenden und außerdem dieselbe Hardware und dieselben Bibliotheks- versionen ausführen. Siehe Numerische Anforderungen.
Gemeinsam genutzt von RRC_embed.py und RRC_extract.py:
| Argument | Standard | Beschreibung |
|---|---|---|
--language_model | meta-llama/Llama-3.1-8B | HuggingFace-Modell-ID |
--bit_length | 128 | Länge der geheimen Nachricht in Bits |
--top_k | -1 | Top-k-Trunkierung; -1 = vollständiges Vokabular |
--key | 42 | Symmetrischer Schlüssel K (PRNG-Seed) |
--precision_margin | 40 | Dezimalstellen über die hinaus, die 2**bit_length benötigt |
--max_ctx_len | 3900 | KV-Cache-Fenster |
--device | auto | cuda oder cpu |
RRC_embed.py akzeptiert außerdem --prompts, --output, --limit, --seed,
--max_tokens, --resume, --no_verify und --part / --part_max zum
Sharding eines Prompt-Sets über Prozesse (--part_max 3 mit --part 0,1,2, einer
pro GPU).
RRC_extract.py akzeptiert außerdem --input_file, --mode {ids,text,both},
--limit und --verbose.
RRC verengt ein Intervall der Breite 2**l auf eine Breite von ~1, sodass der wiederhergestellte
Wert um einen Faktor von 2**l empfindlich auf relative Fehler reagiert: Bei 128 Bits verschiebt eine
relative Störung von 1e-16 irgendwo in der Kette das Ergebnis um
~1e22 gegenüber einer Toleranz von 0,5. Die Extraktion degradiert nicht sanft — sie
liefert entweder die Nachricht oder unzusammenhängende Bits. Gemessen auf Llama-2-7b:
| Einstellung | Relative Störung der Wahrscheinlichkeiten | Extraktion |
|---|---|---|
| Wiederholung desselben Batch-1-Decodes | 0 (bitweise identisch) | funktioniert |
| Dieselbe Zeile bei Batch 2 / 8 / 16 | ~1e-2 | schlägt fehl |
| Left-Padding des Prompts | ~1e-2 | schlägt fehl |
| Ein Teacher-Forced-Durchlauf statt inkrementellem Decoding | ~4e-3 | schlägt fehl |
Zwei Konsequenzen prägen die Implementierung. Batching ist nicht verfügbar, daher werden Sequenzen einzeln verarbeitet. Und der Empfänger wiederholt das inkrementelle Decoding des Senders Schritt für Schritt, anstatt den Stegotext in einem einzigen Forward-Pass zu bewerten, obwohl er alle Token im Voraus kennt. Beide Abkürzungen messen ~9x schneller und keine von beiden erhält die Wahrscheinlichkeiten.
Alles nach den Logits ist float64 und erzeugt bitweise identische Grenzen auf CPU und GPU, sodass der Forward-Pass der einzige Schritt ist, der einen Stegotext an seine Hardware bindet.
Die Präzision wird aus der Nachrichtenlänge abgeleitet als
ceil(bit_length * log10(2)) + margin und festgelegt, bevor irgendetwas anderes läuft. Sie
skaliert mit der Nachricht: 2**256 benötigt 78 Stellen und 2**8192 benötigt 2466.
Eine Präzision unterhalb dieser Schwelle schlägt nicht sauber fehl. Die niederwertigen Stellen des geheimen Werts werden weggerundet, das Intervall kollabiert innerhalb weniger Token und der Terminierungstest feuert zu früh, sodass ein Durchlauf abgeschlossen werden und Zahlen melden kann, die arithmetisch unerreichbar sind. Wenn die Präzision unabhängig von der Nachrichtenlänge bei 50 Stellen gehalten wird, ergibt sich:
| Bits | Auslastung bei 50 Stellen | abgeschlossene Stichproben | mit abgeleiteter Präzision |
|---|---|---|---|
| 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, kein Fehler ausgelöst | 100,87% |
| 8192 | 4766,49% | 1/2 | 99,07% |
--precision_margin passt den Spielraum über dem abgeleiteten Minimum an; das
Minimum selbst wird erzwungen.
Algorithmus 3 stoppt, wenn (L + R) / 2 - d_s in (-0.5, 0.5] fällt. Dieser Test
kann feuern, während das Intervall schmaler als 1 ist, und das Fenster
[mid - 0.5, mid + 0.5) ist dann breiter als das Intervall, das es trägt. Die Rückwärts-
rotation ist eine Isometrie modulo der Intervallbreite, sodass ein Fenster, das den
Schnitt überspannt, den Empfänger ein volles Intervall von d_s entfernt lässt.