
Incorpora più messaggi segreti nelle scelte dei token delle chat LLM utilizzando codificatori steganografici aritmetici/Discop, con decodifica bit-exact e valutazione della steganalisi.
Codice di ricerca per la steganografia linguistica su dialoghi chat con LLM, con una baseline a singolo stream e un protocollo multi-stream (HiTMS) in batch che nasconde più messaggi segreti indipendenti contemporaneamente e sfrutta il batching GPU per una velocità di gran lunga maggiore.
Un modello Bob fa domande; un modello Alice risponde, codificando segretamente il payload nelle sue scelte di token tramite un codificatore steganografico; Bob riesegue il modello sulla risposta di Alice per recuperare i bit. Codifica e decodifica sono bit-esatte, quindi il segreto viene recuperato senza perdite.
arithmetic*.py)discop*.py)single_stream.py): nessun overhead di framing — ogni
bit del canale è payload (~100% di utilizzo).protocol.py): flussi segreti frammentati
attraverso i round, un mapping stream→slot guidato da PRF, slot esca, header
di lunghezza a 16 bit e un keystream di riempimento. Più risposte per round vengono generate in
, quindi la velocità scala con la dimensione del batch.Modelli usati negli esperimenti: meta-llama/Llama-3.2-3B-Instruct e
google/gemma-3-4b-it. Dataset: sottoinsiemi creativi/open-ended di
databricks/databricks-dolly-15k e HuggingFaceH4/no_robots.
| Percorso | Descrizione |
|---|---|
arithmetic.py, arithmetic_batch.py | Stego a codifica aritmetica (codificatore/decodificatore di stream singolo + in batch). |
discop.py, discop_batch.py | Codificatore stego Discop + PRG condiviso (singolo + in batch). |
utils.py | Helper condivisi (conversione bit/int, entropia, top-k, controlli di fine frase). |
protocol.py | Protocollo HiTMS multi-stream (mapping degli stream, esche, header, filler). |
single_stream.py | Protocollo a singolo stream (senza framing; tronca al L-esimo bit). |
round_trip.py, batch_round_trip.py | Demo/test round-trip minimi one-shot. |
multi_round_demo.py | Esecutore di prove multi-stream (run_trial) + demo dettagliata a singola prova. |
single_stream_demo.py | Esecutore di prove a singolo stream (run_single_trial) + demo. |
run_sweep.py | Sweep multi-stream su {modello}×{pool}×{codificatore}, riprendibile. |
run_single_sweep.py | Sweep counterpart a singolo stream. |
run_scaling_sweep.py | Sweep di scaling del numero di stream (x ∈ {1,2,4,8,16,32,64}), qualsiasi modello/pool/codificatore. |
run_x1_shards.py, run_x1_finish.sh, merge_x1_shards.py | Ablazione x=1: shard di una cella su più GPU, poi merge con controlli di copertura. |
judge_quality.py | Scorer di impercettibilità LLM-as-a-judge (una QA per chiamata, riprendibile). |
gen_cover.py | Generazione di testo "cover" senza payload (riferimento per la steganalisi). |
steganalysis_bert.py | Rilevatore cover-vs-stego (BERT / RoBERTa / DeBERTa-v3 / ELECTRA). |
export_data.py | Costruisce il mirror pubblicabile solo-riassunto in . |
data/ contiene i risultati di ogni esperimento del paper — capacità,
velocità, utilizzo, punteggi del giudice e AUROC del rilevatore — come un oggetto JSON
per prova. Vedi data/README.md per lo schema completo.
Lo stegotesto generato non è incluso: i log grezzi incorporano la
domanda e la risposta per ogni frammento, il che li rende ~1 GB, quindi
export_data.py rimuove quei campi e mantiene ogni misurazione (~24 MB). Tutti i
driver sono seedati, quindi rieseguire uno sweep rigenera il testo esattamente.
Le directory di output grezze (
sweep_logs/,single_sweep_logs/,scaling_logs/,judge_logs/,cover_logs/,run_logs/,steganalysis_logs/) sono gitignored — sono grandi (l'ultima contiene checkpoint di rilevatori addestrati multi-GB) e completamente rigenerabili.
conda create -n ems python=3.10 -y && conda activate ems
pip install torch transformers datasets numpy
pip install openai # needed only for judge_quality.py
È richiesta una GPU CUDA per eseguire gli LLM. I checkpoint Llama e Gemma sono
soggetti a gating sull'Hugging Face Hub, quindi esegui huggingface-cli login (con
accesso concesso a quei modelli) prima del primo utilizzo.
Costruisci i pool di domande (una volta):
python build_question_pool.py # -> dolly15k_creative_questions.json
python build_norobots_pool.py # -> norobots_creative_questions.json
Prova singola dettagliata (controllo di sanità):
# multi-stream
CUDA_VISIBLE_DEVICES=0 python multi_round_demo.py
# single-stream
CUDA_VISIBLE_DEVICES=0 python single_stream_demo.py
# override di modello / codificatore / pool tramite env
ROUND_TRIP_MODEL=google/gemma-3-4b-it STEGO_ALGORITHM=discop \
STEGO_QUESTION_POOL=norobots_creative_questions.json \
CUDA_VISIBLE_DEVICES=0 python multi_round_demo.py
Esperimenti completi (riprendibili — riesegui lo stesso comando per continuare):
# multi-stream (8 stream x 1024 bit), tutte le combinazioni modello/pool/codificatore, 500 prove
CUDA_VISIBLE_DEVICES=0 python run_sweep.py --trials 500
# baseline a singolo stream
CUDA_VISIBLE_DEVICES=0 python run_single_sweep.py --trials 500
# scaling del numero di stream (dolly + Llama + Discop)
CUDA_VISIBLE_DEVICES=0 python run_scaling_sweep.py --x-values 4 8 16 32 64
Valutazione dell'impercettibilità (richiede una chiave OpenAI in OPENAI_API_KEY o un
file OPENAI_API_key.txt locale, entrambi gitignored):
python judge_quality.py --dry-run # solo pianificazione, nessuna chiamata API
python judge_quality.py --limit 2 # piccolo smoke test live
python judge_quality.py # esecuzione completa (riprendibile)
python judge_quality.py --aggregate-only # ricalcola la tabella dei punteggi
Ogni driver fissa i propri seed (shuffling dei prompt, campionamento del payload, RNG di
campionamento, torch.manual_seed) e consuma i prompt da uno stream di prompt deterministico
e consapevole della passata, quindi uno sweep completo è riproducibile end-to-end e riprende
esattamente dal suo checkpoint dopo un'interruzione.
OPENAI_API_key.txt, *.key e .env sono ignorati.data/build_question_pool.py, build_norobots_pool.py | Costruiscono i pool di domande dai dataset HF. |
*_creative_questions.json | Pool di domande pre-costruiti. |
legacy/ | Script / pool precedenti, mantenuti come riferimento. |