Kotlin-OHTTP
Kotlin/Android implementation of Oblivious HTTP (RFC 9458) with optional chunked mode (draft-ietf-ohai-chunked-ohttp) plus a minimal sample app. It uses Tink to provide HPKE primitives, which means that EngineFactory will resolve HMAC/cipher/EC to Conscrypt on devices which have Conscrypt available.
kotlin-ohttp-library: Android library that builds and decapsulates OHTTP requests/responses using HPKE (X25519/HKDF-SHA256/AES-128-GCM). Includes helpers for Binary HTTP encode/decode plus chunked encapsulation/decapsulation with per-chunk callbacks for streaming display.example-client: Android app that fetches keys fromhttp://10.0.2.2/ohttp-keys, relays throughhttp://10.0.2.2:8080/ohttp, and displays the response from a target URL. When “Chunked” is enabled for POST, decrypted chunks are appended live to the UI. All inputs are configurable, and the client supports GET or POST, as well as additional headers
clone the repo, open in Android studio. Fire up an emulator (tested with Pixel 9) and run. You will need a OHTTP config endpoint to get a key from and a OHTTP relay to send traffic to.
Gradle wrapper metadata is present; if gradle/wrapper/gradle-wrapper.jar is missing locally run:
gradle wrapper --gradle-version 8.6Then build or install:
./gradlew :example-client:installDebugOr if you prefer, open the cloned repo folder in your local Android Studio
The Android Studio emulator-friendly loopback address (10.0.2.2) is used by default in the app. Adjust the URLs in the example app if your relay or key endpoint differs. The example client permits cleartext HTTP requests to 10.0.2.2 for debugging purposes. You probably dont want cleartext HTTP and only want HTTPS in a real world scenario.
There are debug flags available in Hpke.kt and OhttpClient.kt giving enhanced visibility into operations, the output will be in your logcat in Android Studio. Ensure these are set to false outside of closed testing and debug environments.
object HpkeDebug {
const val ENABLED: Boolean = false
}
object OhttpDebug {
const val ENABLED: Boolean = false
}
Currently this library blindly trusts the integrity of the fetched key, which is not RFC 9548 compliant (compliance requires Key Integrity mechanisms). If you are going to use this anywhere, please ensure you implement suitable key integrity methods. Pull requests always welcomed.
The library does not select any specific authentication scheme. This is down to the reader to decide what to use in their situation, though many real world implementations of OHTTP are using PrivacyPass tokens.
- The library surfaces an
onChunkcallback inOhttpClient.execute/getthat fires for each decrypted chunk in chunked responses. The sample app appends these chunks to the response view as they arrive. - Headers for indeterminate-length (chunked) responses are parsed incrementally; trailers are read after the final chunk.
- Key integrity
- Test: BHTTP encoder/decoder for known-length and chunked framing (including indeterminate-length headers/trailers).
- Test: Varint encode/decode edge cases (1/2/4/8-byte encodings).
- Test: round-trip HPKE encapsulation/decapsulation against a test gateway for both standard and chunked requests; assert decrypted BHTTP matches expectations.
- UI/behavior: emulator test that toggles GET/POST chunked modes, verifies headers render correctly, and that
onChunkis invoked before the final chunk (e.g., via an IdlingResource or log assertion).