feat(android): device enrollment library + rotation (B4)

Hardware-backed (StrongBox/TEE) key + PKCS#10 CSR + /device/enroll client in
:api-client, presented via the existing X509KeyManager; renew body {csr}-only;
DeviceKeyProvider seam makes the orchestration JVM-testable. api-client tests +
koverVerify 80% gate pass.
This commit is contained in:
Yaojia Wang
2026-07-18 13:32:05 +02:00
parent 07bcbf0c08
commit 5e427dcf98
15 changed files with 1981 additions and 0 deletions

View File

@@ -29,17 +29,34 @@ android {
minSdk = 29
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
testOptions {
unitTests {
// The device-enroll orchestration commit logs via android.util.Log — let the JVM unit
// tests stub it (return 0) instead of throwing "not mocked". The security-critical paths
// (commit sequencing, error handling) run on the JVM with a software key double.
isReturnDefaultValues = true
}
}
}
kotlin {
jvmToolchain(17)
}
// JVM (local) unit tests use JUnit 5 (matching the pure modules); AGP's testDebug/ReleaseUnitTest
// tasks are `Test` tasks, so opt them into the JUnit Platform.
tasks.withType<Test>().configureEach {
useJUnitPlatform()
}
dependencies {
// Pure half: Pkcs12Parse (parse+validate), ClientKeyManagerLogic (alias truth table),
// CertificateSummary(Reader). `api` so :app sees the shared ParsedClientIdentity/summary types.
// (No :wire-protocol dep — nothing in src/main references wang.yaojia.webterm.wire*.)
api(project(":client-tls"))
// B4 device-enroll: the pure CSR encoder + login/enroll/renew client + HttpTransport seam live in
// :api-client (JVM-unit-tested); the framework HardwareBackedKey/DeviceEnroller drive them.
implementation(project(":api-client"))
implementation(libs.tink.android)
implementation(libs.okhttp)
// Mutex serializes the two-store rotation commit (single-commit invariant, A11).
@@ -50,4 +67,10 @@ dependencies {
androidTestImplementation(libs.androidx.test.core)
androidTestImplementation(libs.androidx.test.runner)
androidTestImplementation(libs.kotlinx.coroutines.core) // runBlocking for suspend mutators
// Local JVM unit tests (src/test) — the DeviceEnroller enroll/commit orchestration driven with a
// software P-256 key double + the shared FakeHttpTransport (no emulator, no AndroidKeyStore).
testImplementation(project(":test-support"))
testImplementation(libs.bundles.unit.test)
testRuntimeOnly(libs.junit.platform.launcher)
}