Skip to content

File Format

Jonas Resch edited this page Nov 27, 2025 · 1 revision

File Format

QuantHide uses a custom binary format for storing encrypted data within images. This page documents the internal structure.

Overview

┌────────────────────────────────────────────────────────┐
│                    Encoded Image                       │
├────────────────────────────────────────────────────────┤
│  Image Pixels (RGB)                                    │
│  └── LSB contains: ┌─────────────────────────────────┐ │
│                    │ Header │ Encrypted Payload │Pad │ │
│                    └─────────────────────────────────┘ │
└────────────────────────────────────────────────────────┘

Data Structure

Header (Fixed Size)

Offset Size Field Description
0 4 bytes Magic 0x51 0x48 0x49 0x44 ("QHID")
4 1 byte Version Format version (currently 0x01)
5 1 byte Flags Bit flags for options
6 4 bytes Length Encrypted payload size (big-endian)
10 32 bytes Salt Argon2id salt
42 32 bytes Nonce ChaCha20-Poly1305 nonce

Total Header Size: 74 bytes

Flags Byte

Bit Meaning
0 Data type: 0 = text, 1 = file
1 Has decoy message
2 Is split part
3 Reserved
4-7 Reserved

Encrypted Payload

The payload is encrypted with ChaCha20-Poly1305:

┌─────────────────────────────────────────┐
│ Kyber Ciphertext (1568 bytes)           │
├─────────────────────────────────────────┤
│ Encrypted Data                          │
│ ├── [If file] Filename length (2 bytes) │
│ ├── [If file] Filename (UTF-8)          │
│ ├── [If file] File size (8 bytes)       │
│ └── Actual content (text or file data)  │
├─────────────────────────────────────────┤
│ Authentication Tag (16 bytes)           │
└─────────────────────────────────────────┘

Random Padding

After the payload, random bytes are appended to:

  • Hide the actual data size
  • Fill remaining image capacity
  • Prevent size-based analysis

LSB Encoding

Data is embedded in the Least Significant Bits of pixel color channels:

Original Pixel:  R: 10110100  G: 11001010  B: 01110011
                        ↓           ↓           ↓
Data bits:              1           0           1
                        ↓           ↓           ↓
Modified Pixel:  R: 10110101  G: 11001010  B: 01110011

Bit Distribution

  • Default: 2 bits per channel (6 bits per pixel)
  • Low capacity mode: 1 bit per channel
  • High capacity mode: 4 bits per channel (more visible)

Decoy Mode Format

When decoy mode is enabled:

┌─────────────────────────────────────────┐
│ Standard Header (decoy flag set)        │
├─────────────────────────────────────────┤
│ Decoy Message (encrypted with decoy pw) │
├─────────────────────────────────────────┤
│ Separator (encrypted marker)            │
├─────────────────────────────────────────┤
│ Real Message (encrypted with real pw)   │
├─────────────────────────────────────────┤
│ Random Padding                          │
└─────────────────────────────────────────┘

Split Encoding Format

For split images, each part contains:

┌─────────────────────────────────────────┐
│ Header (split flag set)                 │
├─────────────────────────────────────────┤
│ Part Index (1 byte)                     │
├─────────────────────────────────────────┤
│ Total Parts (1 byte)                    │
├─────────────────────────────────────────┤
│ Data Fragment (encrypted)               │
├─────────────────────────────────────────┤
│ Fragment Checksum (32 bytes)            │
└─────────────────────────────────────────┘

Security Properties

What's Protected

  • ✅ Message content (encrypted)
  • ✅ File content (encrypted)
  • ✅ Filename (encrypted)
  • ✅ Data length (padded)
  • ✅ Metadata (stripped)

What's Detectable

  • ⚠️ Presence of QuantHide data (magic bytes)
  • ⚠️ Format version
  • ⚠️ Whether it's text or file (flag bit)

Note: A forensic analyst with the right tools could detect that an image contains QuantHide data, but cannot read the content without the password.

Version History

Version Changes
0x01 Initial release, Kyber1024 + ChaCha20-Poly1305

Implementation Notes

For Developers

// Magic bytes check
const MAGIC: [u8; 4] = [0x51, 0x48, 0x49, 0x44]; // "QHID"

// Version check
const CURRENT_VERSION: u8 = 0x01;

// Minimum image size for header
const MIN_PIXELS: usize = 74 * 8 / 6; // ~99 pixels minimum

Compatibility

  • Forward compatible: newer versions can read older formats
  • Backward compatible: older versions reject newer formats gracefully
  • Cross-platform: identical format on Windows, macOS, Linux

See Also

Clone this wiki locally