Skip to content

Commit b54818a

Browse files
Merge pull request #21 from appdevforall/fix/adb-keystore-revert-s3
fix(controller): restore ADB keystore capabilities broken by S3
2 parents a8a1d37 + 2b9389a commit b54818a

2 files changed

Lines changed: 23 additions & 9 deletions

File tree

‎controller/app/src/main/java/org/iiab/controller/IIABAdbManager.java‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
public class IIABAdbManager extends AbsAdbConnectionManager {
3131

3232
private static final String TAG = "IIABAdbManager";
33-
private static final String KEY_ALIAS = "iiab_adb_key_v3";
33+
private static final String KEY_ALIAS = "iiab_adb_key_v4";
3434

3535
// --- SINGLETON PATTERN ---
3636
private static IIABAdbManager instance;
@@ -48,19 +48,27 @@ private IIABAdbManager(Context context) {
4848
keyStore.load(null);
4949

5050
if (!keyStore.containsAlias(KEY_ALIAS)) {
51-
Log.i(TAG, "Generating new RSA V3 key pair in AndroidKeyStore...");
51+
Log.i(TAG, "Generating new RSA V4 key pair in AndroidKeyStore...");
5252
KeyPairGenerator kpg = KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_RSA, "AndroidKeyStore");
5353

54-
// S3: the ADB identity key is only ever used to sign/verify the
55-
// connection handshake (no Cipher uses this alias), so scope it to
56-
// SIGN | VERIFY. The previous spec also granted ENCRYPT | DECRYPT
57-
// with non-randomized encryption padding, which is unused attack
58-
// surface and a crypto anti-pattern.
54+
// IMPORTANT: these capabilities are REQUIRED, do not narrow them.
55+
// The ADB connection runs over TLS, and the libadb/conscrypt
56+
// handshake signs with this key via a raw RSA operation
57+
// (Cipher "RSA/ECB/NoPadding"). That needs PURPOSE_ENCRYPT plus
58+
// ENCRYPTION_PADDING_NONE (and PKCS1) and setRandomizedEncryptionRequired(false).
59+
// A prior change (tech-debt "S3") scoped this to SIGN|VERIFY only,
60+
// which made the keystore reject the handshake op with
61+
// INCOMPATIBLE_PADDING_MODE and broke every ADB connection on a
62+
// freshly generated key. Reverted here; alias bumped to v4 so the
63+
// broken v3 keys are regenerated.
5964
KeyGenParameterSpec spec = new KeyGenParameterSpec.Builder(
6065
KEY_ALIAS,
61-
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
66+
KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
67+
| KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
6268
.setDigests(KeyProperties.DIGEST_NONE, KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
6369
.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
70+
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE, KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
71+
.setRandomizedEncryptionRequired(false)
6472
.setKeySize(2048)
6573
.setCertificateSubject(new X500Principal("CN=" + this.deviceName))
6674
.setCertificateSerialNumber(BigInteger.ONE)

‎controller/docs/TECH_DEBT_PLAN.md‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,19 @@ _Last updated: 2026-06-17. Tracks remediation work against the findings below. I
6161
- `:app:syncNativeArtifacts` (`preBuild` dependency) called the GitHub API **unauthenticated on every build**; since `jniLibs/*.so` are gitignored, CI always downloads and hit GitHub's 60/hr unauthenticated limit -> intermittent **HTTP 403** ("tag not found"), failing `assembleDebug` on unrelated PRs.
6262
- Fix: (1) **cache-first** — check the local tracker/binaries/manifest before any network call, so builds with artifacts present skip the API entirely; (2) **fallback auth** — fetch release metadata UNAUTHENTICATED first (so forks without a token still build), and only on failure retry with `GITHUB_TOKEN`/`GH_TOKEN` from the env; (3) clearer rate-limit error. CI passes `secrets.GITHUB_TOKEN` to the gradle steps (job-level env). No token is ever *required*.
6363

64+
**S3 — ADB keystore scope: REVERTED / not real debt** (PR `fix/adb-keystore-revert-s3`)
65+
- S3 (PR #10) re-scoped the ADB identity key to `SIGN | VERIFY` only, assuming the `ENCRYPT|DECRYPT` + non-randomized PKCS1/NONE encryption paddings were unused attack surface. **That assumption was wrong.** The ADB connection runs over TLS and the libadb/conscrypt handshake signs with the key via a raw RSA op (`Cipher "RSA/ECB/NoPadding"`), which **requires** `PURPOSE_ENCRYPT` + `ENCRYPTION_PADDING_NONE/PKCS1` + `setRandomizedEncryptionRequired(false)`. On a freshly generated key the keystore rejected the op (`INCOMPATIBLE_PADDING_MODE`) and **every ADB connection broke** (confirmed via logcat).
66+
- Reverted: restored those capabilities and bumped the alias `iiab_adb_key_v3` -> `v4` so already-broken keys regenerate (one-time ADB re-pair). Added a comment marking the capabilities as required. **S3 is withdrawn from the register** (misdiagnosis, not debt).
67+
6468
**D11 — Archive path-traversal on extraction (Phase 1 security): DONE** (PR `fix/phase1-security-d11-archive-traversal`)
6569
- Closes **D11**: `TarExtractor` extracted with `tar -xf -C destDir` without validating member names. An **imported/restored backup is untrusted** (`Import backup` accepts any file and `restore` extracts it into `filesDir/rootfs`), so a crafted `.tar.gz` using `../` or absolute members could escape the destination and overwrite app files.
6670
- New pure domain rule `org.iiab.controller.deploy.domain.ArchiveEntry.escapesRoot(name)` (rejects absolute paths and `..` that climb above the root; allows benign `./` and internal `a/../b`). Unit-tested (`ArchiveEntryTest`).
6771
- `TarExtractor` now **pre-lists** entries (`tar -t`, gzip decompressed in Java) and **fails closed** if any member escapes — for *every* extraction (the verified rootfs install included, defense in depth). Also single-quoted the paths in the backup-creation `sh -c` pipe (the "unquoted backup pipe" half of D11).
6872
- Verify on a real install/restore that legitimate rootfs/backup members are relative (they are by convention) so the guard does not false-positive.
6973

70-
**Phase 1 — Security hardening: IN PROGRESS.** Done so far: **S1** (PR #9), **M4**, **S3** (PR #10), **D6** (PR #12), **D2** (PR #13), **D12** (PR #16), **D11**. Remaining: **S4**, **F15**.## 1. Executive summary
74+
**Phase 1 — Security hardening: IN PROGRESS.** Done so far: **S1** (PR #9), **M4** (PR #10), **D6** (PR #12), **D2** (PR #13), **D12** (PR #16), **D11** (PR #15). **S3 reverted** (see above). Remaining: **S4**, **F15**.
75+
76+
## 1. Executive summary
7177

7278
The Controller is functional and shows real security intent (it SHA256-audits native binaries at build time, scrubs the keystore in CI, and scopes most broadcasts). But it carries debt on four fronts that scale badly toward the README's "millions of users" goal:
7379

0 commit comments

Comments
 (0)