Files
flamenet-messenger-android/settings.gradle.kts
T
Paul HittandClaude Opus 5 db35c7ce55 obfs4 for Android, part 1: Elligator2, against the same vectors as Swift
A new `:obfs4` module, a plain JVM library like `:engine` so its tests run
without an emulator -- which matters here because the only tests that mean
anything are vector comparisons against lyrebird.

Far smaller than the Swift port, for a reason worth recording: the Swift
implementation hand-writes SipHash-2-4, XSalsa20, Poly1305 and its own
field arithmetic because CryptoKit exposes none of them. The JVM does not
have that hole. BouncyCastle -- already a dependency of :engine, for the
same class of reason -- has X25519Field, XSalsa20Engine, Poly1305 and
SipHash, vetted and constant-time where it matters.

So the two clients do NOT share an implementation. They share the vectors
and the live interop harness, which is what actually keeps them honest:
two independent implementations agreeing with the same reference is a
stronger statement than one agreeing with itself twice.

Two things BouncyCastle would not do, both found by vectors rather than
by reading.

X25519Field.sqrtRatioVar is not a drop-in. Elligator needs the root even
when the ratio is NOT square -- it is sqrt(i*u/v) then, and the algorithm
uses it to pick a branch rather than to detect an error. BC only promises
a usable root in the square case, and handing its output to the map
produced zeros for 120 of 256 vectors. The exponent chain and branch
selection are written out here; the field arithmetic underneath is still
BouncyCastle's.

X25519Field.cmov did nothing at all with a condition of 1. Both tweak
parities produced the SAME representative -- a map that still
round-trips, still looks uniform, and only ever emits one of the two
valid answers. The failing assertion said "wrong bytes"; it took a
diagnostic printing both candidates to see they were identical. Replaced
with an explicit mask so the convention is stated rather than assumed.

Behind no setting yet because nothing calls it: this is the crypto
foundation only. The transport, the OkHttp socket factory and the
user-facing switch come next, and the switch defaults to off.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 12:46:25 -04:00

42 lines
1.3 KiB
Kotlin

pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
// Flamenet Messenger — Android.
//
// `engine` is deliberately a plain Kotlin/JVM library, not an Android library:
// the protocol has nothing to do with Android, and keeping it off the platform
// means its tests run on any machine and in CI on Linux, with no emulator and
// no SDK. The app module, when it exists, will depend on this.
rootProject.name = "flamenet-android"
include(":engine")
// The relay transport: HTTP, auth, and the §8 routes.
include(":client")
// The Compose app. Everything below it -- the protocol and the transport --
// lives in `:engine` and `:client`, which are plain JVM libraries so they can
// be tested without an emulator.
include(":app")
// The obfs4 pluggable transport, for reaching a relay on a network that blocks
// or fingerprints TLS to it. A plain JVM library like `:engine`, so its tests
// run without an emulator -- which matters here because the only tests that
// mean anything are vector comparisons against the reference implementation.
include(":obfs4")
// The cross-engine interop driver. Not shipped; see `engine/test/interop-kotlin.mjs`.
include(":interop")