A memory-safe tool to backup and restore GPG keys using QR codes. Inspired by the paperkey utility.
Traditional GPG secret key backups are often stored on digital media, which can fail or be compromised. This tool allows you to print your secret key material as QR codes on physical paper, making it durable and offline.
- Backup: Extract secret material from GPG (stripping redundant public info) and generate a professional PDF with indexed QR codes and a human-readable hex dump.
- Restore: Scan QR codes from images or PDF to reassemble the GPG secret key.
- Manual Recovery: Guided line-by-line entry mode using the printed hex dump, with CRC-24 validation for 100% data integrity assurance even when QR codes are unreadable.
- Durability: Integrated Reed-Solomon error correction allows recovery even if some QR codes are lost or damaged.
- Memory Safety: Written in pure Rust.
- Cryptography: Uses
Sequoia-PGPwith the Nettle backend (constant-time primitives). - Anti-Forensics:
- Proactive Zeroization of all sensitive buffers in memory.
- Targeted Memory Protection: Uses
Zeroizing<T>wrappers to ensure sensitive data (GPG keys, QR images, PDF buffers) is immediately wiped from RAM after use. - In-Memory Processing: QR code images and PDF pages are generated and processed entirely in memory buffers, avoiding disk traces.
Cellulose is designed for forensic stability:
- Reproducible Builds: All official binaries are bit-for-bit identical to those built from source in our verified dev container.
- Deterministic PDFs: Backup documents are bit-for-bit identical when given identical input and a fixed timestamp via the
SOURCE_DATE_EPOCHenvironment variable.
To prevent resource exhaustion (DoS) and ensure QR code scannability, Cellulose enforces a hard 48KB limit on stdin ingestion.
A comprehensive manual page is available.
Generate the man page (requires pandoc):
just manView the man page without installing:
man ./cellulose.1- Install Rust and Cargo.
- Install the Nettle development library.
- Install just (task runner).
- Install Git LFS.
If you prefer not to install dependencies locally, you can use the pre-configured development container:
just devThis drops you into a shell with all prerequisites (including just, nettle, and cargo-deny) installed. It uses persistent container volumes for the Cargo cache for efficient builds and stores artifacts in target/container.
We use just to manage all development tasks.
Build the project in release mode:
just buildRun the full security and quality audit (Recommended):
# Performs fmt, clippy, cargo-deny, and cargo-audit in a verified container
just lintAuto-format code locally:
# Run this before committing to ensure style consistency
cargo fmt --allThe binary will be available at target/release/cellulose.
For reproducible environments and complex tasks, we provide several container-based workflows (compatible with podman and docker).
Our primary validation gate is a containerized auditing suite that ensures consistent results across all developer environments.
# Run all checks (rustfmt, clippy, cargo-deny, cargo-audit)
just lint
# Generate automated security advisory impact reports
# (Includes reachability analysis for ignored advisories)
just advisories
# Run a specific sub-task
just lint clippy
just lint deny
just lint auditTo update the vendored PDFium library (x86_64) with automated attestation verification:
# Update to default version
just update-pdfium
# Update to a specific version
just update-pdfium chromium/7825Run formal verification tools (Flux, Creusot, Hegel) without local setup:
just verify # Defaults to flux
just hegel # Run Hegel global soundness checks
just verify creusot # Run Creusot deductive verificationExecute intensive fuzz tests or generate a code coverage report in an isolated environment:
just fuzz # Runs all 8 specialized fuzzer targets
just coverageFor detailed command syntax and a full list of security features, consult the manual page.
1. Create a physical backup:
gpg --export-secret-key YOUR_KEY_ID | cellulose backup --output backup.pdf.age2. Verify backup integrity:
cellulose verify backup.pdf.age --pubkey my_public_key.asc3. Restore and import into GPG:
cellulose restore backup.pdf.age --pubkey my_public_key.asc --import4. Last-resort manual recovery:
# Guided line-by-line entry using the printed hex dump
cellulose restore --manual --pubkey my_public_key.asc --importWarning
Using the --output flag during restoration (instead of --import) will write your unencrypted secret key directly to the disk. Always prefer --import to keep secrets in RAM.
To ensure the integrity and authenticity of the build artifacts:
gh attestation verify cellulose --repo dawud/cellulosecosign verify-blob cellulose \
--signature cellulose.sig \
--certificate cellulose.pem \
--certificate-identity "https://github.com/dawud/cellulose/.github/workflows/ci.yml@refs/heads/main" \
--certificate-oidc-issuer "https://token.actions.githubusercontent.com"- Font: Carlito (SIL Open Font License 1.1)
- Library: PDFium (BSD 3-Clause)
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.