
Strumento di steganografia per immagini JPG
# jdvrif
***jdvrif*** (*JPG Data Vehicle*) è uno strumento da riga di comando per la steganografia veloce e facile da usare, che consente di occultare ed estrarre qualsiasi tipo di file tramite un'immagine **JPG**. ***Solo Linux***.
Il file di dati viene compresso con ***libdeflate/zlib***, poi cifrato con ***XChaCha20-Poly1305*** (secretstream di ***libsodium***) sotto una chiave derivata tramite ***Argon2id*** da un ***PIN di recupero*** generato casualmente, e infine incorporato nell'immagine di copertura. Il PIN viene mostrato una sola volta, al termine di ***conceal***, e non viene mai memorizzato da nessuna parte: senza di esso il file occultato non può essere recuperato.
Usando la ***modalità conceal predefinita***, è possibile occultare qualsiasi tipo di file, con l'immagine finale limitata a ***2GiB***. Le altre modalità conceal per piattaforme e i siti di social media compatibili (*elencati di seguito*) hanno i propri limiti di dimensione ***molto più ridotti*** e altri requisiti.
Esiste una [***edizione Web***](https://cleasbycode.co.uk/jdvrif/app/) di ***jdvrif***, utilizzabile immediatamente come comoda alternativa al download e alla compilazione del codice sorgente della CLI. I caricamenti di file via Web sono limitati a **20MiB**.
È inoltre disponibile un port sperimentale in ***Rust*** [***jdvrif-rs***](https://github.com/CleasbyCode/jdvrif-rs) per chi è interessato a quel linguaggio. È compatibile a livello di formato: una build può recuperare le immagini prodotte dall'altra.
***jdvrif*** deriva in parte dalla ***[tecnica implementata](https://www.vice.com/en/article/bj4wxm/tiny-picture-twitter-complete-works-of-shakespeare-steganography)*** dal ricercatore di sicurezza ***[David Buchanan](https://www.da.vidbuchanan.co.uk/).***

*Immagine dimostrativa: **"A place of concealment"** / ***PIN: 7912872750612437051****
## Come jdvrif occulta i dati
A differenza del comune metodo di steganografia [***LSB***](https://ctf101.org/forensics/what-is-stegonagraphy/) (*Least Significant Bit*) che occulta i dati nei pixel di un'immagine di copertura, ***jdvrif*** nasconde per lo più i dati all'interno dei ***segmenti applicativi*** di un'immagine ***JPG*** (ICC, EXIF, XMP, ecc.).
| Modalità conceal | Dove finiscono i dati | Condivisione dell'immagine di output su |
| --- | --- | --- |
| *(nessuna opzione)* | segmenti APP2/profilo ICC | X-Twitter, Tumblr, Mastodon, Pixelfed, PostImage, ImgBB, ImgPile, Flickr |
| ***-b*** | segmenti EXIF / Photoshop / XMP incorniciati | ***solo Bluesky*** |
| ***-r*** | coefficienti DCT JPEG (***QIM***) | ***solo Reddit*** |
| ***-x*** | coefficienti DCT JPEG (***J-UNIWARD/STC***) | ***solo X-Twitter*** |
Le due eccezioni di piattaforma al metodo di archiviazione predefinito nei segmenti sono ***Reddit*** e ***X-Twitter***. Entrambe hanno una propria modalità conceal, e nessuna di queste modalità utilizza affatto i segmenti di metadati: ***-r*** e ***-x*** trasportano il payload nei coefficienti DCT dell'immagine ***JPG***.
***Reddit*** ricodifica le immagini caricate e scarta i segmenti di metadati su cui si basa la modalità predefinita, quindi ***-r*** è l'unica modalità che funziona lì. ***X-Twitter*** conserva invece un singolo e piccolo segmento ICC, quindi la modalità predefinita funziona ancora su quella piattaforma, ma solo per un payload minuscolo.
Per la modalità conceal di ***Reddit*** (***-r***), utilizziamo il [***metodo di steganografia QIM***](https://www.google.com/search?q=QIM+steganography+method&sourceid=chrome&ie=UTF-8&source=chrome.ctxt) (*Quantization Index Modulation nel dominio DCT JPEG*), poiché è l'unico metodo di archiviazione che attualmente funziona per ***Reddit***. La copertura viene transcodificata in baseline Q75 4:2:0 e il payload viene trasportato nei suoi blocchi DCT di luminanza.
Per massimizzare la capacità di archiviazione per la piattaforma ***Reddit***, utilizzare un'immagine di copertura con dimensioni elevate, **2048x2048**, **4096x4096**, **8192x8192 (max)**, ecc.
La qualità dell'immagine di copertura non è importante per questo metodo e dovrebbe essere mantenuta basilare per le dimensioni più grandi, per contribuire a ridurre al minimo la dimensione del file dell'immagine di copertura.
Sebbene la piattaforma ***X-Twitter*** possa utilizzare il metodo predefinito fornito da ***jdvrif***, in cui i dati vengono occultati all'interno dei segmenti APP2/ICC, ***X-Twitter*** limita questo a un singolo segmento ICC con una dimensione massima di appena **~10KiB**.
Per trasportare più di **~10KiB**, usa la modalità di occultamento della piattaforma ***X-Twitter*** (***-x***). Abbandona completamente i segmenti di metadati — nulla viene scritto in un profilo ICC — e utilizza invece il [***metodo di steganografia adattivo J-UNIWARD con Syndrome-Trellis Coding (STC)***](https://www.google.com/search?q=adaptive+J-UNIWARD+steganography+method+with+Syndrome-Trellis+Coding+(STC)). La cover viene transcodificata in progressivo 4:2:0 alla sua qualità derivata dalla sorgente (limitata a **Q97**).
Per massimizzare la capacità di archiviazione per la piattaforma ***X-Twitter***, usa un'immagine di copertura di alta qualità/dettaglio con dimensioni elevate, **1024x1024**, **2048x2048**, **4096x4096 (max)**, ecc.
I carrier ***-r*** e ***-x*** cifrano i tuoi dati e ne vincolano la posizione al tuo PIN di recupero usando ***Argon2id***, ma l'incorporamento altera comunque le statistiche DCT dell'immagine, quindi una steganalisi dedicata potrebbe rivelare che un'immagine contiene dati nascosti.
Le immagini ***-r*** e ***-x*** create con ***jdvrif v9.0*** restano recuperabili, ma ***v9.0*** non è in grado di recuperare le immagini ***-r*** o ***-x*** create con ***v9.1***.
Entrambe le modalità DCT trasportano molti meno dati rispetto alla modalità predefinita, quindi usa ***capsize*** per misurare un'immagine di copertura prima di scegliere un payload (vedi [Verifica della capacità](#checking-capacity-with-capsize)).
## Requisiti e compilazione (Linux)
La compilazione richiede **CMake 3.20 o superiore**, `flock` da **util-linux** e **Ninja** (preferito) oppure **Make**. Il compilatore deve essere **GCC 14 o superiore**, oppure **Clang 18 o superiore** abbinato a una libreria standard C++23 che implementi funzionalità come `std::format` e `std::print`. Le librerie native richieste sono **libsodium**, **libjpeg-turbo** (*sia le API `turbojpeg` che `libjpeg`*), **zlib** e **libdeflate**. **OpenMP** è opzionale: quando presente, parallelizza la fase di costo J-UNIWARD di ***-x***.
```console
$ sudo apt update
$ sudo apt install g++ cmake ninja-build util-linux libsodium-dev libturbojpeg0-dev libjpeg-dev zlib1g-dev libdeflate-dev
$ g++ --version # confirm the reported version is 14 or newer
$ chmod +x compile_jdvrif.sh
$ ./compile_jdvrif.sh
$ sudo cp jdvrif /usr/bin
```
Se la tua distribuzione fornisce GCC 14 come pacchetto con versione, installa `g++-14` e compila con `CXX=g++-14 ./compile_jdvrif.sh`.
Il wrapper mantiene i file oggetto tracciati dalle dipendenze sotto `src/build/`, così le invocazioni successive ricompilano solo ciò che è cambiato, e sostituisce il binario `jdvrif` pubblicato solo dopo una compilazione completa e riuscita. Imposta `JDVRIF_JOBS=<count>` per modificare il limite di parallelismo (predefinito: numero di CPU, con un massimo di 8), `JDVRIF_BUILD_DIR=<path>` per usare una cache di compilazione separata, oppure `BUILD_MODE=sanitize` per una build ASan/UBSan.
## Utilizzo
```console
$ jdvrif
Usage: jdvrif conceal [-b|-r|-x] <cover_image> <secret_file>
jdvrif recover <cover_image>
jdvrif capsize [-r|-x] <cover_image>
jdvrif --info
```
Esegui `jdvrif --info` per la guida integrata completa su modalità, opzioni di piattaforma e limiti di dimensione.
```console
$ jdvrif conceal your_cover_image.jpg your_secret_file.doc
Platform compatibility for output image:-
✓ X-Twitter
✓ Tumblr
✓ Mastodon
✓ Pixelfed
✓ PostImage
✓ ImgBB
✓ ImgPile
✓ Flickr
Recovery PIN: [***2166776980318349924***]
Important: Keep your PIN safe, so that you can extract the hidden file.
Saved "file-embedded" JPG image: jrif_4e87c566c.jpg (143029 bytes).
Complete!
$ jdvrif recover jrif_4e87c566c.jpg
PIN: *******************
Extracted hidden file: your_secret_file.doc (6165 bytes).
Complete! Please check your file.
```
argomenti di ***mode*** di jdvrif:
***conceal*** - Comprime, cifra e incorpora il tuo file di dati segreto all'interno di un'immagine di copertina ***JPG***.
***recover*** - Decifra, decomprime ed estrae il file di dati nascosto da un'immagine di copertina ***JPG*** (*è richiesto il PIN di recupero*).
***capsize*** - Riporta la capacità di trasporto di un'immagine di copertina per la modalità ***-r*** o ***-x***. Nessuna immagine viene salvata.
Requisiti per l'immagine di copertina:
● Solo JPEG, almeno **400x400** pixel, e in scala di grigi o a colori YCbCr — le immagini CMYK/YCCK devono essere prima convertite in RGB.
● Le dimensioni massime predefinite e per ***-b*** sono **4096x4096px**; ***-r*** usa **8192x8192px**, mentre ***-x*** è anch'esso limitato a **4096x4096px**.
● La modalità predefinita (*nessuna opzione*) rifiuta anche le immagini di copertina la cui qualità JPEG stimata è superiore a **Q97**; se l'immagine viene rifiutata, salvala di nuovo a una qualità inferiore.
Requisiti per il file di dati segreto:
● Il nome file incorporato non deve superare i **20 byte**, non deve iniziare con `.` o `-` né terminare con uno spazio o `.`, e deve essere UTF-8 valido senza caratteri di controllo o di formattazione bidirezionale né alcuno dei caratteri `/ \ : * ? " < > |`. Il nome viene memorizzato nell'immagine e ripristinato in fase di ***recover***.
● Il tuo file di dati viene compresso prima della cifratura, tranne per i tipi di file riconosciuti come già compressi (`.zip`, `.7z`, `.mp4`, `.jpg`, `.png`, ecc.). Nelle modalità predefinita e ***-b*** questi tipi saltano la compressione solo se più grandi di **10MiB**; nelle modalità ***-r*** e ***-x*** la saltano sempre. Per qualsiasi altro file destinato a un limite ridotto di una piattaforma, valuta di comprimerlo tu stesso prima (*zip, rar, 7z, ecc.*) così da conoscerne la dimensione esatta memorizzata.
## Piattaforme compatibili
\******************
Nota: ***Bluesky*** ora salva le immagini come ***WEBP*** per impostazione predefinita.
Per salvare un'immagine come ***JPG***, così da poter ancora recuperare i dati nascosti con ***jdvrif***:-
Per prima cosa fai clic sull'immagine nel post per aprirla, poi fai clic con il tasto destro sull'immagine. Dal menu, seleziona ***Apri immagine in una nuova scheda***.
Seleziona la nuova scheda e, nella barra degli indirizzi, spostati alla fine dell'indirizzo e aggiungi ***@jpg***, quindi premi invio.
Fai clic con il tasto destro sull'immagine e dal menu seleziona ***Salva immagine con nome...***
La tua immagine dovrebbe ora essere scaricata come ***JPG***, che funzionerà ora con ***jdvrif***.
Se desideri uno strumento per nascondere dati usando immagini ***WEBP*** da pubblicare su ***Bluesky***, puoi usare il mio strumento CLI di steganografia per ***WEBP*** ***[wbpdv](https://github.com/CleasbyCode/wbpdv)***
\******************
*Limite di dimensione per la pubblicazione misurato dalla dimensione ***combinata*** di ***immagine di copertina*** + ***file di dati compresso:****
● ***Flickr*** (**200MiB**), ***ImgPile*** (**100MiB**), ***ImgBB*** (**32MiB**), ***PostImage*** (**32MiB**), ***Pixelfed*** (**15MiB**).
*Limite di dimensione misurato ***solo*** dalla ***dimensione del file di dati compresso:****
● ***Mastodon*** (**~6MiB**), ***Tumblr*** (**~64KiB**), ***X-Twitter*** (**~10KiB / metodo predefinito**).
Ad esempio, con ***Mastodon***, se la tua immagine di copertina è **1MiB** puoi comunque incorporare un file di dati fino al limite di **~6MiB**.
**Altro: piattaforme con una propria modalità di occultamento:**
● ***Bluesky*** (***opzione -b***). Il ***JPG*** finito con "*file incorporato*" non deve superare **2.000.000 di byte (~1,9MiB)**, quindi l'immagine di copertina e il file di dati compresso condividono un unico budget. Il file di dati compresso da solo non deve superare **~171KiB**. Un'immagine di copertina già a 2.000.000 di byte non lascia alcuno spazio, quindi mantieni la copertina più piccola del limite almeno della dimensione del tuo file di dati compresso. Lo script "***create_bsky_post.py***" è necessario per pubblicare queste immagini su ***Bluesky***. *Maggiori informazioni su questo script più avanti nella pagina.*
● ***Reddit*** (***opzione -r***). L'immagine di copertina e il file di dati non devono essere più grandi di **20MiB** ciascuno, ma la capacità di trasporto effettiva dell'immagine di copertina è ***molto più piccola*** e dipende dalle sue dimensioni. Usa `jdvrif capsize -r` per misurarla.
● ***X-Twitter*** (***opzione -x***). L'immagine di copertina e il file di dati non devono superare ciascuno i **5MiB**, e la copertina non deve superare i **4096x4096** pixel. La capacità effettiva del carrier è ***molto inferiore*** e dipende dalla qualità e dalle dimensioni dell'immagine. Usa `jdvrif capsize -x` per misurarla.
Nelle modalità predefinita e ***-b***, l'immagine di copertina viene anche ottimizzata senza perdita prima dell'uso e non deve superare i **4MiB** dopo tale passaggio.
Per piattaforme come ***X-Twitter***, ***Reddit*** e ***Tumblr***, che hanno limiti ridotti sulla dimensione dei dati, potresti voler privilegiare dati che si comprimono bene, come file di testo, ecc.
https://github.com/user-attachments/assets/af330d09-f65d-4f75-8290-883078be34b3
https://github.com/user-attachments/assets/fc454d42-0240-4864-b44b-ce5ef7cfd94c
## Verifica della capacità con capsize
***capsize*** prepara l'immagine di copertina esattamente come farebbe ***conceal***, poi riporta quanto payload cifrato può trasportare. Nulla viene scritto su disco. Usa ***-r*** per il carrier ***Reddit*** e ***-x*** per il carrier ***X-Twitter*** (***-x*** è l'opzione predefinita se non ne viene specificata nessuna).
```console
$ jdvrif capsize -r basic_img_large_dims.jpg
Reddit capacity check for conceal -r mode only.
Cover Image: 384KiB, 8192x8192, Baseline YCbCr 4:2:0, Standard Q75 quantization (C3).
Theoretical C3 capacity limit for this cover image: 436906 bytes (~426KiB).
Conservative maximum compressed capacity with a 20-character filename: 436792 bytes (~426KiB).
Recommended maximum compressed capacity with a 20-character filename: 435768 bytes (~425KiB).
```
La cifra riportata è la capacità totale dell'***envelope*** cifrato, non un limite grezzo del file segreto: il nome del file, i metadati di cifratura e di recupero consumano da 95 a 114 byte per un payload a frame singolo, e i payload più grandi aggiungono overhead di framing. Non puntare al limite teorico — dove la capacità lo consente, mantieni il payload compresso almeno **1KiB** al di sotto del massimo conservativo. Il controllo di dimensione eseguito da ***conceal*** è quello autorevole.
## Opzioni di piattaforma per la modalità conceal
Per creare immagini ***JPG*** compatibili con "*data-concealed*" da pubblicare sulla piattaforma ***Reddit***, è necessario utilizzare l'opzione ***-r*** con la modalità ***conceal***.
```console
$ jdvrif conceal -r my_image.jpg hidden.doc
```
Queste immagini sono compatibili solo per la pubblicazione su ***Reddit***. Il tuo file di dati incorporato andrà perso se pubblicato su una piattaforma diversa.
Quando salvi/scarichi un'immagine da ***Reddit*** assicurati di cliccare sull'immagine all'interno del post per espanderla completamente prima di salvarla.
https://github.com/user-attachments/assets/ead3c34c-dd9a-4018-9ea3-0746fbdfdb8b
Per creare immagini ***JPG*** compatibili "*data-concealed*" da pubblicare sulla piattaforma ***X-Twitter*** utilizzando il metodo di steganografia J-UNIWARD, devi usare l'opzione ***-x*** con la modalità ***conceal***.
```console
$ jdvrif conceal -x my_image.jpg hidden.doc
```
Queste immagini sono compatibili solo per la pubblicazione su ***X-Twitter***. Il tuo file di dati incorporato andrà perso se pubblicato su una piattaforma diversa.
Quando salvi/scarichi un'immagine da ***X-Twitter*** assicurati di cliccare sull'immagine all'interno del post per espanderla completamente prima di salvarla.
Per creare immagini ***JPG*** compatibili "*file-embedded*" per la pubblicazione sulla piattaforma ***Bluesky***, devi usare l'opzione ***-b*** con la modalità ***conceal***.
```console
$ jdvrif conceal -b my_image.jpg hidden.doc
```
Queste immagini sono compatibili solo per la pubblicazione su ***Bluesky***. Il file di dati incorporato verrà rimosso se pubblicato su una piattaforma diversa.
È inoltre necessario utilizzare lo script Python [create_bsky_post.py](https://github.com/CleasbyCode/jdvrif/blob/main/src/bsky/create_bsky_post.py) (presente nella cartella ***src/bsky*** del repository) per pubblicare l'immagine su ***Bluesky***.
Non funzionerà se pubblichi le immagini su ***Bluesky*** tramite il sito web del browser o l'app mobile.
Per utilizzare lo script, dovrai creare una [***app password***](https://bsky.app/settings/app-passwords) dal tuo account ***Bluesky***. Passa le tue credenziali tramite l'ambiente anziché sulla riga di comando, dove sarebbero visibili ad altri utenti locali tramite strumenti come `ps`:
```console
$ pip install -r bsky/requirements.txt
$ export ATP_AUTH_HANDLE='you.bsky.social'
$ read -rsp 'Bluesky app password: ' ATP_AUTH_PASSWORD && export ATP_AUTH_PASSWORD
$ python3 bsky/create_bsky_post.py \
--image jrif_4e87c566c.jpg \
--alt-text "alt-text here [optional]" \
"standard post text here [required]"
$ unset ATP_AUTH_PASSWORD
```
Consulta [src/bsky/README.md](https://github.com/CleasbyCode/jdvrif/blob/main/src/bsky/README.md) per l'insieme completo di opzioni (*immagini multiple, risposte, post con citazione, link card*) e per sapere da cosa protegge il fork rafforzato dello script.
https://github.com/user-attachments/assets/1daef508-d304-491f-bfe2-2cdbb5d62081
## Test
Ciascuno degli script di test in `src/tests` riceve come argomento il binario da esercitare:
```console
$ bash tests/run_golden_tests.sh --bin ./jdvrif # recover pre-built images with known PINs
$ bash tests/run_roundtrip_tests.sh --bin ./jdvrif # fresh conceal/recover round-trips
$ bash tests/run_security_smoke.sh --bin ./jdvrif # negative-path and boundary checks
$ bash tests/run_reddit_tests.sh --bin ./jdvrif # -r QIM carrier
$ bash tests/run_twitter_tests.sh --bin ./jdvrif # -x J-UNIWARD/STC carrier
$ bash tests/run_io_security_tests.sh --bin ./jdvrif # input-size bounds, PIN signal/job-control handling
$ bash tests/run_jpeg_safety_tests.sh # JPEG parser/decoder safety (builds its own test binary)
$ bash tests/run_bsky_tests.sh # Bluesky posting helper
```
## Software e risorse di terze parti
### Applicazioni principali
- [libsodium](https://github.com/jedisct1/libsodium) — generazione casuale crittografica, derivazione della chiave Argon2id e flussi segreti XChaCha20-Poly1305. Collegata dinamicamente come libreria di sistema.
Licenza: [ISC License](https://github.com/jedisct1/libsodium/blob/master/LICENSE)
Copyright (c) 2013–2026 Frank Denis.
- [libjpeg-turbo](https://github.com/libjpeg-turbo/libjpeg-turbo) — elaborazione JPEG e trasformazione senza perdita. Collegata dinamicamente come libreria di sistema.
Questo software si basa in parte sul lavoro dell'Independent JPEG Group.
Licenze: [Independent JPEG Group License, Modified BSD 3-Clause License,
and zlib License](https://github.com/libjpeg-turbo/libjpeg-turbo/blob/2.1.5/LICENSE.md).
Copyright © 1991–2020 Thomas G. Lane e Guido Vollbeding.
Copyright © 2009–2023 D. R. Commander. Tutti i diritti riservati.
Copyright © 2015 Viktor Szathmáry. Tutti i diritti riservati.
- [zlib](https://github.com/madler/zlib) — compressione e decompressione zlib in streaming. Collegata dinamicamente come libreria di sistema.
Licenza: [zlib License](https://github.com/madler/zlib/blob/develop/LICENSE)
Copyright (C) 1995–2026 Jean-loup Gailly e Mark Adler.
- [libdeflate](https://github.com/ebiggers/libdeflate) — compressione rapida dell'intero buffer in formato zlib. Collegata dinamicamente come libreria di sistema.
Licenza: [MIT](https://github.com/ebiggers/libdeflate/blob/master/COPYING)
Copyright 2016 Eric Biggers.
Copyright 2024 Google LLC.
### Codice e risorse incorporati
- [Compact ICC Profiles](https://github.com/saucecontrol/Compact-ICC-Profiles) — profilo ICC
compatibile Adobe incorporato.
Licenza: [CC0 1.0 Universal](https://github.com/saucecontrol/Compact-ICC-Profiles/blob/master/license)
### Helper opzionale per la pubblicazione su Bluesky
- Bryan Newbold / ATProto Hacker Cookbook — create_bsky_post.py — Base per l'helper [forked](https://gist.github.com/CleasbyCode/1eb678ca1fa1975b1c1e20aeec33637e) per la pubblicazione su Bluesky (src/bsky/create_bsky_post.py).
Per riferimento vedere la [copia del Cookbook](https://github.com/bluesky-social/cookbook/blob/main/python-bsky-post/create_bsky_post.py)
Licenza: [CC0 1.0 Universal](https://github.com/bluesky-social/cookbook/blob/main/LICENSE-CC0).
- Requests — richieste HTTP e API Bluesky.
Licenza: [Apache 2.0](https://github.com/psf/requests/blob/main/LICENSE) / [NOTICE](https://github.com/psf/requests/blob/main/NOTICE)
Copyright 2019 Kenneth Reitz.
- Beautiful Soup 4 — analisi dei metadati HTML e Open Graph.
Licenza: [MIT](https://pypi.org/project/beautifulsoup4/)
Copyright (c) Leonard Richardson.
- Pillow — validazione delle immagini, dimensioni e gestione delle proporzioni.
Licenza: [MIT-CMU](https://github.com/python-pillow/Pillow/blob/main/LICENSE)
Copyright PIL © 1997–2011 Secret Labs AB e © 1995–2011 Fredrik Lundh e collaboratori.
Copyright Pillow © 2010 Jeffrey “Alex” Clark e collaboratori.
##