plugins { kotlin("jvm") } repositories { mavenCentral() } dependencies { testImplementation(kotlin("test")) // `org.json` is part of the Android framework, so on a phone this costs // zero shipped bytes and is `compileOnly` for that reason -- bundling a // second copy would shadow the platform's and is a classic duplicate-class // build failure. On the JVM (tests, the interop driver) it has to be a real // dependency. // // Safe here only because nothing security-relevant depends on key order: // Android's implementation preserves insertion order and the reference one // does not, and the two bytes-must-match encodings in this engine -- the // ratchet header and the canonical log leaf -- are both built by hand. compileOnly("org.json:json:20240303") testImplementation("org.json:json:20240303") // The engine's ONE real dependency, and it is here because the platform // leaves a hole rather than because it was convenient. // // Android serves Ed25519 key generation from `AndroidKeyStore` and nowhere // else -- probed on API 35, see ProviderProbeTest -- and Keystore only ever // makes keys it keeps. This engine needs Ed25519 keys it holds: a device // identity that lives in our own vault, and an account key derived from a // passphrase. Neither can be a Keystore key by definition. // // That left implementing RFC 8032 by hand, which for SIGNING means // non-constant-time arithmetic over a secret scalar, or taking a vetted // constant-time one. Only the low-level `math.ec.rfc8032.Ed25519` class is // used -- not the JCA provider, which is awkward to register on Android -- // so R8 shrinks nearly all of it away. implementation("org.bouncycastle:bcprov-jdk18on:1.79") } // Bytecode level 17, which is what Android's toolchain consumes, produced by // whatever JDK happens to be installed. A `jvmToolchain(17)` here would demand // a JDK 17 on every machine that builds this and fail on anything newer, which // is a poor trade for a library with no JDK 17-specific behaviour. kotlin { compilerOptions { jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17) } } java { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } tasks.test { useJUnitPlatform() testLogging { events("passed", "failed", "skipped") showStandardStreams = true } }