Sign inSign up

timriling/qzip

By timriling

•Updated 26 days ago

QuantumZip ("qZip") (Personal) to Protect Volume or Container Data via Encrypted Backup and Restore

Image
Security
Developer tools
Databases & storage
0

1.4K

timriling/qzip repository overview

qZip Volume Manager ("qzv")

Protect Kubernetes and Docker volumes with post-quantum encryption using the same cryptographic primitives that power qZip (https://www.quantumzip.net/⁠)

⁠How It Works: The Decrypt Flow
  1. Magic detection: _is_qzv_file(source_path) reads the first 5 bytes and confirms they match QZV_MAGIC.

  2. Read manifest: The method reads the next 4 bytes (manifest length), then reads that many bytes and parses as JSON.

  3. Validate manifest: Calls VolumeManager._validate_manifest(manifest). If validation fails (wrong version, missing fields, bad shard boundaries), raises ValueError("Invalid .qzv manifest: ...").

  4. Compute archive MAC key: Uses Argon2id with fixed salt b"qzip-qzv-v2-archive-mac-salt" and info string, then HKDF-SHA3-256 to derive the 32-byte footer key.

  5. Verify footer: Computes HMAC-SHA3-256 over the canonical manifest + all chunk bytes, compares with the 32-byte footer.

  6. Decrypt chunks: For each chunk:

    • Read its .qz data from the archive stream.
    • Determine chunk format (v1, v2, or v3) from the 4-byte magic.
    • If v1 or v2, reject with ValueError (only .qz v3 is supported as chunks).
    • If v3, delegate to VolumeManager.decrypt_chunk().
  7. Decrypt key if needed: If the first chunk fails with a "wrong passphrase" error, prompt for retry and re-attempt decryption.

  8. Extract tar: Use tarfile.extractall(filter="data") to extract the concatenated decrypted chunks as a tar archive.

  9. Write output: Write the extracted tar to output_path.

⁠Quick Start

⁠Python API
from qzv import VolumeManager

# Encrypt a directory — creates a v2 archive by default
mgr = VolumeManager("my_secure_passphrase")
mgr.encrypt_volume("/data/my-volume", "/backups/my-volume.qzv")

# Decrypt and restore — supports both v2 and legacy v1 archives
mgr.decrypt_volume("/backups/my-volume.qzv", "/data/restored")

# Inspect without decrypting
info = mgr.inspect_qzv("/backups/my-volume.qzv")
print(f"Format: {info['version']}, Chunks: {info['total_chunks']}, "
      f"Size: {info['total_tar_size']} bytes")
⁠CLI (qzv subcommand)
# Encrypt — produces a v2 archive by default
python -m qzv volume encrypt -s /data/volume -o backup.qzv -p "passphrase"

# Decrypt — auto-detects v2 or legacy v1 format from the magic bytes
python -m qzv volume decrypt -s backup.qzv -o /data/restored -p "passphrase"

# Inspect
python -m qzv volume inspect -s backup.qzv

# Kubernetes manifests (K8sVolumeProtector)
python -m qzv k8s volume snapshot -a my-app --source-pvc data-pvc --backup-pvc backup-pvc -p "pass"
python -m qzv k8s volume cronjob --schedule "0 2 * * *" ...

# Docker volumes (DockerVolumeProtector)
python -m qzv docker backup -v my_app_data -o /backups/my_app_data.qzv -p "pass"
python -m qzv docker restore -s /backups/my_app_data.qzv -v my_app_data_restored -p "pass"
⁠Kubernetes
from qzv import K8sVolumeProtector

k8s = K8sVolumeProtector("my_passphrase", namespace="production")

# Generate a snapshot Job
job = k8s.generate_snapshot_job("my-app", "data-pvc", "backup-pvc")
print(k8s.to_yaml(job))

# Generate a scheduled CronJob
cron = k8s.generate_cronjob("my-app", "data-pvc", "backup-pvc", schedule="0 2 * * *")
print(k8s.to_yaml(cron))

# Full pipeline (Secret + CronJob)
pipeline = k8s.generate_full_backup_pipeline("my-app", "data-pvc", "backup-pvc")
print(k8s.to_yaml_multi(pipeline))
⁠Docker
from qzv import DockerVolumeProtector

protector = DockerVolumeProtector("my_passphrase")

# Backup a Docker volume
protector.backup_volume("my_app_data", "/backups/my_app_data.qzv")

# Restore to a new volume
protector.restore_volume("/backups/my_app_data.qzv", "my_app_data_restored")

# List volumes
for vol in protector.list_volumes():
    print(vol["name"])


### Restore semantics

`qzv volume decrypt --source snap.qzv --output /dir` extracts the archive as
`/dir/<source-basename>/...` — a snapshot preserves the top-level directory
name of the encrypted source. The K8s snapshot Job mounts the source PVC at
`/source`, so a restore Job writing to `/target` yields `/target/source/...`.

Tag summary

Content type

Image

Digest

sha256:acaf8345c…

Size

60.6 MB

Last updated

26 days ago

docker pull timriling/qzip