# Technology Stack

**Project:** encrypted_archive (Custom Encrypted Archiver)
**Researched:** 2026-02-24
**Overall confidence:** MEDIUM (versions from training data — verify before use)

---

## Critical Constraint: Three-Platform Compatibility

Every technology choice is constrained by the weakest link: **busybox shell**. The Rust archiver can use any library, but the format it produces must be decodable by:
1. Kotlin on Android 13 (javax.crypto / java.security)
2. busybox shell (openssl, dd, xxd)

This constraint eliminates many otherwise-superior choices.

---

## Recommended Stack

### Encryption

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `aes` crate | ^0.8 | AES-256-CBC block cipher | busybox openssl supports `aes-256-cbc` — the critical three-way constraint. Android javax.crypto supports AES natively. | HIGH |
| `cbc` crate | ^0.1 | CBC mode of operation | Standard mode: openssl CLI `aes-256-cbc`, javax.crypto `AES/CBC/PKCS5Padding` | HIGH |
| `hmac` + `sha2` | ^0.12 / ^0.10 | HMAC-SHA256 integrity | Encrypt-then-MAC. busybox `openssl dgst -sha256 -hmac`. Kotlin `Mac("HmacSHA256")` | HIGH |

**Why AES-256-CBC over AES-GCM:** busybox openssl does NOT support AEAD modes (GCM/CCM) in `openssl enc`. AES-CBC + HMAC-SHA256 (encrypt-then-MAC) provides equivalent security. CBC is the only AES mode reliably available across all three platforms.

**Why NOT ChaCha20-Poly1305:** busybox openssl does not support ChaCha20. Android javax.crypto has no standard ChaCha20 support. Would require native libraries, violating constraints.

**Why NOT aes-gcm crate:** Not decodable via `openssl enc` in busybox.

### Compression

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `flate2` crate | ^1.0 | gzip compression | busybox `gunzip` works natively. Android `GZIPInputStream` works natively. Simplest cross-platform path. | HIGH |

**Why gzip-wrapped DEFLATE:** busybox `gunzip` handles it. Android `GZIPInputStream` handles it. flate2 `GzEncoder`/`GzDecoder` produces standard gzip.

**Why NOT zstd/lz4/brotli:** busybox has no decompressors for any of these.

### CLI Framework

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `clap` | ^4 | CLI argument parsing | De facto Rust standard. Derive macros. Subcommands (`pack`/`unpack`/`inspect`). | HIGH |

### Binary Format

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| Manual byte-level I/O | N/A | Custom binary format | Using bincode/serde would make format recognizable by forensic tools. Manual bytes give full control for obfuscation. | HIGH |

### Hashing / Integrity

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `sha2` crate | ^0.10 | SHA-256 checksums | busybox `sha256sum`, Android `MessageDigest("SHA-256")` | HIGH |

### Random / IV Generation

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `rand` | ^0.8 | Random IV generation | CSPRNG for AES-CBC initialization vectors. Each archive gets unique IV. | HIGH |

### Error Handling

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| `anyhow` | ^1 | Application errors | CLI app, not library. Ergonomic error chains. | HIGH |
| `thiserror` | ^2 | Typed format errors | Specific errors for format validation, decryption, integrity. | MEDIUM |

### Testing

| Technology | Version | Purpose | Why | Confidence |
|------------|---------|---------|-----|------------|
| Built-in `#[test]` | N/A | Unit tests | Round-trip pack/unpack/compare | HIGH |
| `assert_cmd` | ^2 | CLI integration tests | Test actual binary | MEDIUM |
| `tempfile` | ^3 | Temp dirs | Clean test isolation | HIGH |

---

## Kotlin/Android Decompressor Stack (Zero External Dependencies)

| Technology | Source | Purpose |
|------------|--------|---------|
| `javax.crypto.Cipher` | Android SDK | AES-256-CBC: `Cipher.getInstance("AES/CBC/PKCS5Padding")` |
| `javax.crypto.spec.SecretKeySpec` | Android SDK | 32-byte hardcoded key |
| `javax.crypto.spec.IvParameterSpec` | Android SDK | IV from archive header |
| `javax.crypto.Mac` | Android SDK | HMAC-SHA256: `Mac.getInstance("HmacSHA256")` |
| `java.util.zip.GZIPInputStream` | Android SDK | Gzip decompression |
| `java.security.MessageDigest` | Android SDK | SHA-256 integrity |
| `java.nio.ByteBuffer` | Android SDK | Little-endian parsing |

---

## Busybox Shell Decompressor Stack

| Tool | Purpose | Key Flags |
|------|---------|-----------|
| `dd` | Extract byte ranges | `bs=1 skip=N count=M` |
| `xxd` | Hex encode/decode keys | `xxd -p` |
| `openssl enc` | AES-256-CBC decrypt | `-d -aes-256-cbc -K HEX -iv HEX -nosalt` |
| `openssl dgst` | HMAC-SHA256 verify | `-sha256 -hmac KEY -binary` |
| `gunzip` | Gzip decompress | Standard input/output |
| `sha256sum` | Integrity check | `-c checksums` |

**Critical:** busybox `openssl enc` uses EVP_BytesToKey by default. MUST pass `-K` (hex key) + `-iv` (hex IV) + `-nosalt` for raw key mode. IV must be in cleartext header.

---

## Cross-Platform Compatibility Matrix

| Rust | Android SDK | busybox | Notes |
|------|-------------|---------|-------|
| `aes`+`cbc` (PKCS7) | `Cipher("AES/CBC/PKCS5Padding")` | `openssl enc -aes-256-cbc` | PKCS5=PKCS7 for 16-byte blocks |
| `hmac`+`sha2` | `Mac("HmacSHA256")` | `openssl dgst -sha256 -hmac` | Raw key, not password |
| `flate2` (GzEncoder) | `GZIPInputStream` | `gunzip` | Standard gzip |
| `sha2` | `MessageDigest("SHA-256")` | `sha256sum` | Hex comparison |

---

## Alternatives Considered

| Category | Recommended | Alternative | Why Not |
|----------|-------------|-------------|---------|
| Encryption | AES-256-CBC + HMAC | AES-256-GCM | busybox openssl lacks GCM |
| Encryption | AES-256-CBC + HMAC | ChaCha20-Poly1305 | Not in busybox/Android SDK |
| Compression | flate2 (gzip) | zstd | No busybox decompressor |
| Compression | flate2 (gzip) | lz4 | No busybox decompressor |
| Format | Manual bytes | bincode/serde | Recognizable patterns |
| Crypto ecosystem | RustCrypto (aes+cbc) | ring | ring bundles C code |
| Crypto ecosystem | RustCrypto (aes+cbc) | openssl-rs | Unnecessary system dep |

---

## Cargo.toml

```toml
[package]
name = "encrypted_archive"
version = "0.1.0"
edition = "2021"

[dependencies]
aes = "0.8"
cbc = "0.1"
hmac = "0.12"
sha2 = "0.10"
flate2 = "1.0"
clap = { version = "4", features = ["derive"] }
rand = "0.8"
anyhow = "1"
thiserror = "2"

[dev-dependencies]
assert_cmd = "2"
tempfile = "3"
```

**WARNING: Versions from training data (cutoff May 2025). Verify with `cargo search CRATE --limit 1` before use.**

---

## Gaps Requiring Verification

1. Exact latest crate versions (could not verify via crates.io)
2. Confirm target busybox build includes `openssl` applet
3. Confirm `xxd` availability in target busybox (fallback: `od`)
4. Test PKCS7 padding round-trip across all three platforms
5. Test flate2 GzEncoder output with busybox `gunzip` and Android `GZIPInputStream`
