Linear: https://linear.app/engineereddev/issue/ENG-248
What's in this PR:
dev.pneumo.app.crypto(javax.crypto only): MasterKey (NFC, PBKDF2WithHmacSHA256), RFC 5869 HKDF, deriveKeys and keyCheck, PushCrypto (§6.5), streaming FileCrypto with the §6.6 lookahead plusciphertextSize, and PairingUri (§6.7).dev.pneumo.app.protocol: kotlinx.serialization models for every DTO and frame (sealed frames ontype,ignoreUnknownKeys, required fields stay required, refinements validated).dev.pneumo.app.net.ApiClient(OkHttp) mapsErrorBodyto a typedApiError.dev.pneumo.app.data.SecureStorewraps secrets with an Android Keystore AES-GCM key (pneumo_wrap) and stores them in DataStore, behind aKeyWrapperinterface.Uuidv7helper.- The
syncSharedFixturesGradle task copies the frozen vectors and fixtures into the unit-test resources underbuild/.
Cross-language result: every vector in packages/crypto/vectors/v1.json (contract locked at 323bca9) and all 90 valid / 111 invalid protocol fixtures pass in Kotlin. Extra Kotlin tests cover the gate's vector gaps: wrong version byte, final block under 16 bytes, and empty last chunk when n > 1.
Verify (lead, forced re-runs):
./gradlew spotlessCheck detekt lint testDebugUnitTest verifyRoborazziDebug --rerun-tasks → exit 0 · Lint found no errors or warnings · 46 executed · JVM tests=54 failures=0
./gradlew pixel6Api34DebugAndroidTest --rerun → exit 0 · managed device tests pass (incl. real Keystore SecureStore round trip)
git diff origin/main...HEAD -- packages → empty (frozen contract untouched)
Deviations:
- The
INTERNETpermission is added to the manifest (spec §14.2), and the serialization plugin is registered in the rootbuild.gradle.kts. Both are outside the listed Touches. - PBKDF2 rejects iteration counts above
Int.MAX_VALUE, the JCAPBEKeySpeclimit; this is documented in the Android README.