The wrapper downloaded a 164 MB distribution from services.gradle.org
and executed it with no integrity check. Every dependency in this
repository is already pinned to an exact version, with no dynamic
versions anywhere, so the build tool itself was the unchecked part.
`distributionSha256Sum` is what Gradle provides for this.
Proven cold, because the first attempt proved nothing: setting the
checksum and running `./gradlew --version` succeeded with an all-zeros
value too, since Gradle verifies at download time and the distribution
was already extracted here. With the cache moved aside it refuses:
Verification of Gradle distribution failed!
Expected checksum: '0000...0000'
Actual checksum: 'acd53f1edaf02f1a8ff99879f8a34b302661a057d9b063ae9e35b552f804d20a'
That is both halves at once. The control is enforced, and Gradle's own
hash of the freshly downloaded artifact is the value pinned here, which
matches what services.gradle.org publishes. The correct checksum then
downloads and runs on the same cold cache.
What this does not defend against: the checksum comes from the same
origin as the distribution, so a compromise of that server right now
would serve both. It does catch a corrupted mirror, a poisoned cache,
and a tampered re-upload later.
Still unverified: the dependency artifacts themselves. Their versions
are exact but there is no `verification-metadata.xml`, so nothing checks
what comes back from the repositories.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Android does not exist, and the first thing it needs is not a screen. It
needs the protocol implemented a third time — which is only worth
anything if the third implementation is checked against the spec and the
published vectors rather than against a reading of our own Swift.
So `android/engine` is a plain Kotlin/JVM library with no Android in it:
no SDK, no emulator, no device, and therefore gateable in CI on Linux
like everything else. It reads
engine/test/vectors/protocol-v1.json from its one location rather than
carrying a copy, because a vector transcribed into the test that checks
it is a vector that agrees with itself.
Covered so far: safety numbers (§7) and key transparency verification
(§19) — RFC 6962 leaf and node hashing, the unbalanced split, and
bottom-up audit paths. Nine tests, all green against the same file the
JavaScript and Swift engines answer to.
The §19 tests are the ones worth having. A top-down reading of a
bottom-up audit path yields the correct root for every left-spine index,
so it passes any suite that only checks index 0 — the vectors pin a
right-descending index, and the tests assert both that the published
path verifies and that the same path reversed does not.
android/README.md lists what is missing — X25519, X3DH, the ratchet,
sealed sender, both post-quantum halves, the relay client, the app — as
a table rather than as an omission. Android stays honestly marked "not
started" on the site until that list is shorter.
Bytecode targets 17 without demanding a JDK 17 be installed: a
jvmToolchain(17) fails on any newer JDK, which is a poor trade for a
library with no JDK 17-specific behaviour.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>