Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 15 additions & 9 deletions controller/app/src/main/java/org/iiab/controller/Aria2Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ public void startDownload(Context context, String url, DownloadListener listener
if (!caCertFile.exists()) {
extractAsset(context, "cacert.pem", caCertFile);
}
// D6: fail closed. The downloaded rootfs is extracted and executed as
// root, so we must never fall back to an unverified TLS connection
// where a MITM could swap it. If the CA bundle is unavailable, abort.
if (!caCertFile.exists()) {
throw new Exception("Secure download aborted: CA certificate bundle (cacert.pem) is unavailable.");
}

Log.d(TAG, "Executing Native Aria2c...");
Log.d(TAG, "Target URL: " + url);
Expand All @@ -83,20 +89,20 @@ public void startDownload(Context context, String url, DownloadListener listener
command.add("--max-connection-per-server=4");
command.add("--split=4");
command.add("--follow-metalink=mem");
// D6: verify the SHA-256 checksums embedded in the .meta4 (Metalink)
// while downloading. On mismatch aria2 exits non-zero, so onError fires
// and the archive is never extracted/executed.
command.add("--check-integrity=true");
command.add("--enable-dht=true");
command.add("--dht-file-path=" + dhtFile.getAbsolutePath());
command.add("--bt-enable-lpd=true");
command.add("--seed-time=0");

// --- Apply SSL Certificate Validation ---
if (caCertFile.exists()) {
Log.d(TAG, "SSL certificates found. Enforcing strict validation.");
command.add("--check-certificate=true");
command.add("--ca-certificate=" + caCertFile.getAbsolutePath());
} else {
Log.w(TAG, "cacert.pem not found! Falling back to insecure connection.");
command.add("--check-certificate=false");
}
// --- Apply SSL Certificate Validation (D6: always strict; cacert
// presence was already enforced above, so there is no insecure path) ---
Log.d(TAG, "Enforcing strict TLS certificate validation.");
command.add("--check-certificate=true");
command.add("--ca-certificate=" + caCertFile.getAbsolutePath());

command.add("--console-log-level=warn");
command.add("--summary-interval=1");
Expand Down
8 changes: 7 additions & 1 deletion controller/docs/TECH_DEBT_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,13 @@ _Last updated: 2026-06-17. Tracks remediation work against the findings below. I
- **S3** (`IIABAdbManager` key spec): the ADB identity key was created with `ENCRYPT | DECRYPT` + non-randomized encryption padding on top of sign/verify. Verified no `Cipher` uses this alias, so the spec is now `SIGN | VERIFY` only (encryption paddings / `setRandomizedEncryptionRequired(false)` removed). Alias kept at `v3` (non-disruptive: hardens new key generation without forcing existing devices to re-pair ADB).
- No new unit tests: both are framework-bound legacy line-fixes with no extractable pure logic; verified by inspection + compile + CI lint.

**Phase 1 — Security hardening: IN PROGRESS.** Done so far: **S1** (PR #9), **M4**, **S3**. Remaining: **D2**, **D6**, **D12**, **S4**, **D11**.
**D6 — Download integrity + TLS fail-closed (Phase 1 security): DONE** (PR `fix/phase1-security-d6-download-integrity`)
- Closes **D6**: the rootfs (`latest_*.meta4` -> `.tar.gz`) is extracted and executed as root, but downloads had no integrity check and `Aria2Manager` silently fell back to `--check-certificate=false` when `cacert.pem` was missing.
- **TLS fail-closed**: `cacert.pem` is now required (fail with a clear error if it cannot be provisioned) and `--check-certificate=true` is always passed — the insecure fallback is removed. Applies to all aria2 downloads (rootfs + ZIM).
- **Integrity**: `--check-integrity=true` makes aria2 verify the SHA-256 checksums published in the `.meta4` (confirmed present: file-level + per-piece) during download; on mismatch aria2 exits non-zero, `onError` fires and the archive is never extracted. Uses the server's published hash + verified TLS, so no redundant on-device re-hash of the ~1.2 GB file.
- Follow-up (separate items): ZIM content integrity (Kiwix publishes no embedded hash today; needs `.sha256` sidecars) and the cleartext OTA APK path in `MainActivity` (**F15**).

**Phase 1 — Security hardening: IN PROGRESS.** Done so far: **S1** (PR #9), **M4**, **S3** (PR #10), **D6**. Remaining: **D2**, **D12**, **S4**, **D11**.

## 1. Executive summary

Expand Down
Loading