File Transfer

Every file stripped, chunked, hashed, encrypted, and transferred with zero metadata leakage.

File Pipeline Overview


  SELECT FILE
      |
      v
  METADATA STRIP (EXIF, GPS, timestamps, camera info)
      |
      v
  BLAKE3 HASH (content-addressable integrity check)
      |
      v
  CHUNK (512 KB blocks)
      |
      v
  PER-CHUNK ENCRYPT (AES-256-GCM, unique key per file)
      |
      v
  TRANSFER (chunks sent over P2P via Tor)
      |
      v
  REASSEMBLE (recipient verifies BLAKE3 hash)
      |
      v
  DECRYPT → MEDIA CACHE (encrypted at rest)
  

Step 1: Metadata Stripping

Before any encryption or transmission, RVNT strips all metadata from the file. Metadata in images, videos, and documents can reveal information the sender did not intend to share.

What Is Stripped

Metadata TypeExample ContentRisk
EXIF GPS 37.7749, -122.4194 Reveals exact location where photo was taken
EXIF DateTime 2024:03:15 14:32:07 Reveals when photo was taken
Camera Model iPhone 15 Pro Max Reveals device type, narrows identity
Serial Number Camera or lens serial Unique device identifier
Thumbnail Embedded preview image May contain unedited original (even if photo was cropped)
Software Adobe Photoshop CC 2024 Reveals editing software, potential fingerprint
PDF Author John Smith Reveals creator identity
DOCX Revision Edit history, tracked changes Reveals editing history, contributor names
Stripping implementation by file type:

  Images (JPEG, PNG, HEIC, WebP):
    - Remove all EXIF data (exif crate)
    - Remove XMP metadata
    - Remove IPTC metadata
    - Remove embedded thumbnails
    - Preserve image data and ICC color profile only

  Videos (MP4, MOV, WebM):
    - Remove metadata atoms (moov/udta, moov/meta)
    - Remove GPS tracks
    - Remove creation/modification timestamps
    - Remove device information
    - Preserve video/audio streams only

  Documents (PDF):
    - Remove /Author, /Creator, /Producer metadata
    - Remove XMP metadata stream
    - Remove embedded JavaScript
    - Preserve document content

  All other files:
    - Sent as-is (no metadata stripping possible)
    - User warned: "This file type may contain metadata"
Metadata stripping is best-effort. New metadata formats and embedding techniques are continuously developed. RVNT strips all known metadata types, but novel or proprietary metadata may not be detected. For maximum safety, manually strip metadata before sending sensitive files.

Step 2: BLAKE3 Hashing

After metadata stripping, RVNT computes a BLAKE3 hash of the stripped file. This hash serves multiple purposes:

file_hash = BLAKE3(stripped_file_bytes)    // 32 bytes

Purposes:
  1. Integrity verification: recipient verifies hash after reassembly
  2. Deduplication: if the same file is sent twice, it is not re-transferred
  3. Content addressing: chunks are identified by file_hash + chunk_index
  4. Resume support: partially transferred files can resume from last chunk

BLAKE3 properties:
  - 256-bit output (32 bytes)
  - Faster than SHA-256 (~3x on modern CPUs)
  - Tree-hashing mode enables parallel computation
  - Cryptographically secure (collision + preimage resistant)

Step 3: Chunking

Files are split into fixed-size chunks for transfer. Chunking enables resume, parallel transfer, and limits memory usage.

Chunk parameters:
  Chunk size:      512 KB (524,288 bytes)
  Last chunk:      Variable (remainder of file)
  Max file size:   2 GB (4096 chunks maximum)
  Chunk numbering: 0-indexed, sequential

Example:
  File size: 2.5 MB (2,621,440 bytes)
  Chunks:    5
    Chunk 0: bytes [0 .. 524,287]       (512 KB)
    Chunk 1: bytes [524,288 .. 1,048,575]  (512 KB)
    Chunk 2: bytes [1,048,576 .. 1,572,863] (512 KB)
    Chunk 3: bytes [1,572,864 .. 2,097,151] (512 KB)
    Chunk 4: bytes [2,097,152 .. 2,621,439] (remainder, 512 KB)

Step 4: Per-Chunk Encryption

Each file is encrypted with a unique per-file key. Individual chunks are encrypted with keys derived from the file key and chunk index.

Key derivation:
  file_key = random_256bit()                    // 32 bytes, generated once per file

Per-chunk encryption:
  for each chunk_index in 0..num_chunks:
      chunk_key = HKDF-SHA256(
          ikm:  file_key,
          salt: file_hash,
          info: "rvnt-file-chunk-" || BE32(chunk_index),
          len:  32
      )
      nonce = BE32(chunk_index) || random_64bit()   // 96 bits
      encrypted_chunk = AES-256-GCM(
          key:   chunk_key,
          nonce: nonce,
          aad:   file_hash || BE32(chunk_index) || BE32(num_chunks),
          data:  chunk_bytes
      )
      secure_zero(chunk_key)

The file_key is sent to the recipient inside the message body
(which is itself Double Ratchet encrypted):

Attachment {
    hash:      file_hash,          // BLAKE3 of stripped file
    filename:  "photo.jpg",        // Original filename (optional)
    mime_type: "image/jpeg",
    size:      2621440,            // Stripped file size
    key:       file_key,           // Per-file encryption key
}

Step 5: Network Transfer

Transfer protocol:
  1. Sender sends message with Attachment metadata
  2. Recipient acknowledges and requests chunks
  3. Chunks transferred via libp2p stream:

  ChunkRequest {
      file_hash:    [32 bytes]
      chunk_index:  u32
  }

  ChunkResponse {
      file_hash:    [32 bytes]
      chunk_index:  u32
      total_chunks: u32
      encrypted_data: [variable, up to 512 KB + 16 byte GCM tag]
      nonce:        [12 bytes]
  }

  Transfer behavior:
    - Sequential by default (simple, reliable)
    - Parallel: up to 4 concurrent chunk transfers (optional)
    - Resume: if transfer is interrupted, resume from last
              successfully received chunk
    - Verification: each chunk verified by GCM tag on decrypt
    - Full file verification: BLAKE3(reassembled) == file_hash

  All chunk transfers route through Tor (same as messages)

Step 6: Reassembly and Verification

Recipient reassembly:
  1. Receive all chunks
  2. Decrypt each chunk with derived chunk_key
  3. Concatenate decrypted chunks in order
  4. Compute BLAKE3 hash of reassembled file
  5. Compare with file_hash from message metadata
  6. If match: file is intact and authentic
  7. If mismatch: file is corrupted or tampered, discard + alert user

Step 7: Media Cache

Decrypted files are stored in RVNT's encrypted media cache for display in the UI.

Media cache:
  Location:    {app_data}/media_cache/
  Encryption:  Each file re-encrypted with a cache-specific key
               derived from the database encryption key
  Thumbnails:  Generated locally, stored encrypted
  Max size:    Configurable (default: 1 GB)
  Eviction:    LRU (least recently used) when cache exceeds max
  Wipe:        Included in panic mode destruction sequence

Cache key derivation:
  cache_key = HKDF-SHA256(
      ikm:  database_key,
      info: "rvnt-media-cache",
      len:  32
  )

Files in cache are NOT the original files. They are:
  1. Metadata-stripped
  2. Re-encrypted with cache_key
  3. Stored with a random filename (no relationship to original name)

Supported File Types

CategoryTypesMax SizeMetadata Strip
ImagesJPEG, PNG, HEIC, WebP, GIF50 MBFull EXIF/XMP/IPTC
VideosMP4, MOV, WebM500 MBMetadata atoms, GPS
AudioMP3, AAC, OGG, FLAC100 MBID3 tags, comments
DocumentsPDF, TXT, CSV100 MBPDF metadata
OtherAny file type2 GBNone (warned)

Further Reading

  • Messaging -- Text message lifecycle and status system
  • How It Works -- Full encryption pipeline
  • Panic Mode -- How media cache is destroyed in an emergency

Last updated: 2026-04-12

RVNT Documentation — Post-quantum encrypted communications