10 KiB
Feature Landscape
Domain: Custom encrypted archiver with proprietary binary format Researched: 2026-02-24 Confidence: MEDIUM (based on domain knowledge of archive formats, encryption patterns, and Android constraints)
Table Stakes
Features that are mandatory for this product to function correctly. Missing any of these means the archive is either non-functional, insecure against casual inspection, or unreliable.
| Feature | Why Expected | Complexity | Notes |
|---|---|---|---|
| Multi-file packing/unpacking | Core purpose: bundle texts + APKs into one archive | Medium | Need file table/index structure; handle varied sizes from few KB to tens of MB |
| AES-256-CBC encryption | Without real encryption, any hex editor reveals content | Medium | busybox openssl supports aes-256-cbc; Android javax.crypto supports AES natively |
| HMAC-SHA256 integrity (encrypt-then-MAC) | Detect corruption and tampering | Medium | busybox openssl dgst -sha256 -hmac; Kotlin Mac("HmacSHA256") |
| Compression before encryption | Reduce archive size; compression after encryption is ineffective (encrypted data has max entropy) | Low | Use deflate/gzip; must compress BEFORE encrypt |
| Hardcoded key embedding | Project requirement: no user-entered passwords, key baked into dearchiver code | Low | Key in Kotlin code and shell script; rotate key means new build |
| Custom magic bytes | Standard magic bytes (PK, 7z, etc.) let file/binwalk identify format; custom bytes prevent this | Low | Use random-looking bytes, not human-readable strings; avoid patterns that match known formats |
| Round-trip fidelity | Unpacked files must be byte-identical to originals | Low | Verified via checksums; critical for APKs (signature breaks if single byte changes) |
| CLI interface for packing | Archiver runs on Linux/macOS developer machine | Low | Standard CLI: encrypted_archive pack -o output.bin file1.txt file2.apk |
| Kotlin unpacker (Android 13) | Primary dearchiver path on target device | High | Pure JVM, no native libs; must handle javax.crypto for AES |
| Busybox shell unpacker (fallback) | Backup when Kotlin app unavailable | High | Only dd, xxd, openssl, sh; format must be simple enough for positional extraction |
| File metadata preservation (name, size) | Unpacker must know which bytes belong to which file and what to name them | Low | Stored in file table; at minimum: filename, original size, compressed size, offset |
Differentiators
Features that exceed baseline expectations and provide meaningful protection or usability improvements. Not all are needed for MVP, but they strengthen the product.
| Feature | Value Proposition | Complexity | Notes |
|---|---|---|---|
| Format obfuscation: fake headers | Misleads automated analysis tools (binwalk, foremost) that scan for patterns | Medium | Insert decoy headers resembling JPEG, PNG, or random formats at predictable offsets; casual user sees "corrupted image" not "encrypted archive" |
| Format obfuscation: shuffled blocks | File data blocks stored out-of-order with a scramble map | Medium | Prevents sequential extraction even if encryption is somehow bypassed; adds complexity to busybox unpacker |
| Format obfuscation: randomized padding | Variable-length random padding between blocks | Low | Makes block boundaries unpredictable to static analysis; minimal implementation cost |
| Version field in header | Forward-compatible format evolution | Low | Single byte version; unpackers check version and reject incompatible archives gracefully |
| Per-file encryption with derived keys | Each file encrypted with unique key derived from master key + file index/salt | Medium | Limits damage if one file's plaintext is known (known-plaintext attack on specific block) |
| Progress reporting during pack/unpack | UX for large archives (tens of MB of APKs) | Low | CLI progress bar; Kotlin callback for UI integration |
| Dry-run / validation mode | Check archive integrity without full extraction | Low | Verify checksums and structure without writing files to disk; useful for debugging on device |
| Configurable compression level | Trade speed vs size for different content types (APKs are already compressed, texts compress well) | Low | APKs benefit little from compression; allow per-file or global setting |
| Salt / IV per archive | Each archive uses random IV/nonce even with same key; prevents identical plaintext producing identical ciphertext | Low | Standard crypto practice; 16-byte IV for AES-CBC; must be stored in archive header (unencrypted) |
| Error messages that do not leak format info | Unpacker errors say "invalid archive" not "checksum mismatch at block 3 offset 0x4A2" | Low | Defense in depth: even error messages should not help reverse engineering |
Anti-Features
Features to explicitly NOT build. Each would add complexity without matching the project's threat model or constraints.
| Anti-Feature | Why Avoid | What to Do Instead |
|---|---|---|
| Password-based key derivation (PBKDF2/Argon2) | Project explicitly uses hardcoded key; password entry UX is unwanted on car head unit | Embed key directly in Kotlin/shell code; accept that key extraction from APK is possible for determined attackers |
| GUI for archiver | Scope creep; CLI is sufficient for developer workflow (pack on laptop, deploy to device) | Well-designed CLI with clear flags and help text |
| Windows archiver support | Out of scope per project constraints; Rust cross-compiles easily IF needed later | Linux/macOS only; document that WSL works if Windows user needs it |
| Streaming/pipe support | Files are small enough (KB to tens of MB) to fit in memory; streaming adds format complexity that breaks busybox compatibility | Load entire file into memory for pack/unpack; document max file size assumption |
| Nested/recursive archives | No use case: archive contains flat list of texts and APKs | Single-level file list only |
| File permissions / ownership metadata | Android target manages its own permissions; Unix permissions from build machine are irrelevant | Store only filename and size; ignore mode/owner/timestamps |
| Compression algorithm selection at runtime | Over-engineering; one good default is sufficient | Use deflate/gzip -- available everywhere: Rust, Kotlin, busybox; hardcode the choice |
| Public-key / asymmetric encryption | Massive complexity increase for no benefit given hardcoded key model | Symmetric encryption only (AES-256) |
| Self-extracting archives | Target is Android, not desktop; shell script IS the extractor | Separate archive file + separate unpacker (Kotlin app or shell script) |
| DRM or license enforcement | Not the purpose; this is content bundling protection, not DRM | Simple encryption is sufficient for the threat model |
| File deduplication within archive | Archive contains distinct files (texts and different APKs); dedup adds complexity with near-zero benefit | Pack files as-is |
| Encryption of filenames in file table | Nice in theory but busybox shell unpacker needs to know filenames to extract; encrypting the file table massively complicates the shell path | Store filenames inside the encrypted payload (entire payload is encrypted, so filenames are protected by archive-level encryption) |
Feature Dependencies
Compression --> Encryption --> Format Assembly (compression MUST happen before encryption)
|
v
Integrity Checks (HMAC over encrypted blocks)
Custom Magic Bytes --> Format Header Design
Version Field --> Format Header Design
Salt/IV Storage --> Format Header Design
File Metadata (name, size) --> File Table Structure --> Format Assembly
Format Assembly --> CLI Packer (Rust)
Format Specification --> Kotlin Unpacker
Format Specification --> Busybox Shell Unpacker
Per-file Key Derivation --> Requires Format Specification to include file index/salt
Fake Headers --> Requires Format Assembly to insert decoys at correct positions
Shuffled Blocks --> Requires File Table to store block ordering map
Critical dependency chain:
Format Spec (on paper)
--> Rust Packer (implements spec)
--> Kotlin Unpacker (reads spec)
--> Shell Unpacker (reads spec)
--> Round-trip tests (validates all three agree)
The format specification must be finalized BEFORE any implementation begins, because three independent implementations (Rust, Kotlin, shell) must produce identical results.
MVP Recommendation
Prioritize (Phase 1 -- must ship):
- Format specification document -- Define header, file table, block layout, magic bytes, version field, IV/salt placement
- Compression + Encryption pipeline -- Compress with gzip, encrypt with AES-256-CBC, authenticate with HMAC-SHA256
- Rust CLI packer -- Pack multiple files into the custom format
- Integrity verification via HMAC-SHA256 -- Encrypt-then-MAC for both integrity and authenticity
- Kotlin unpacker -- Primary extraction path on Android 13. Pure JVM using javax.crypto
- Busybox shell unpacker -- Fallback extraction. This constrains the format to be simple
- Round-trip tests -- Verify Rust-pack, Kotlin-unpack, shell-unpack all produce identical output
Defer (Phase 2 -- after MVP works):
- Fake headers / decoy data -- Obfuscation layer; adds no functional value, purely anti-analysis
- Shuffled blocks -- Significant complexity, especially for busybox
- Progress reporting -- Nice UX but not blocking
- Configurable compression -- Start with one setting that works; optimize later
- Dry-run / validation mode -- Useful for debugging but not for initial delivery
- Per-file derived keys -- Defense-in-depth for later
Key MVP constraint: The busybox shell unpacker is the most constraining component. Every format decision must be validated against "can busybox dd/xxd/openssl do this?" If the answer is no, the feature must be deferred or redesigned.
Sources
- Domain knowledge of archive format design (ZIP, tar, 7z format specifications)
- Domain knowledge of cryptographic best practices (NIST, libsodium documentation patterns)
- Domain knowledge of Android crypto APIs (javax.crypto, OpenSSL CLI)
- Domain knowledge of busybox utility capabilities