Skip to content

Add Android ECH support#9573

Draft
yschimke wants to merge 4 commits into
lysine-dev:masterfrom
yschimke:AndroidDns
Draft

Add Android ECH support#9573
yschimke wants to merge 4 commits into
lysine-dev:masterfrom
yschimke:AndroidDns

Conversation

@yschimke

Copy link
Copy Markdown
Collaborator

Initial version before dealing with NetworkSecurityPolicy,

instead just activated by developer using AndroidDns, and a server having HTTPS records with ECH.

@yschimke

Copy link
Copy Markdown
Collaborator Author

@swankjesse my biggest question is how to deal with NetworkSecurityPolicy for both
a) activation - is that AndroidDns? is that just whether to include for a specific host
b) error handling / fallback

I previously ended up with EchModes (disabled, strict and so on) read from the policy

https://github.com/lysine-dev/okhttp/pull/9383/changes#diff-62659f553c144abdf5c3d578b9d14e96bf069b4ef9e7f93549b04ff38e4fcbc9

@swankjesse swankjesse left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

THIS IS IT

private val network: Network? = null,
/**
* True to also query the `HTTPS` record for service metadata such as ECH. Set this to false to
* save a query when only IP addresses are needed.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if there’s a way to phrase this documentation to encourage people to keep it on.

‘Set this to false to omit the service metadata query. This will disable privacy features in the HTTPS call.’

@yschimke yschimke Jul 21, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think maybe we end up defaulting to the networkSecurityConfig, but let developers opt-out, or opt-in.

For now, likely opt-in while we confirm it works.

GREASE means it should never cause failures just by being enabled.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll ask tomorrow if that mode still exists.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I'll update the wording. But also think if we want this we should make AndroidDns the default on API 37+ and when network security policy isn't globally disabled?

I think effectively the modes are (potentially per host)

UNSPECIFIED
DISABLED
ENABLED

So maybe that default once we are confident is the right option?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt Outdated
Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt
Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt
Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt
Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt
Comment thread okhttp/src/androidMain/kotlin/okhttp3/android/AndroidDns.kt
val echConfig =
try {
EchConfigList.fromBytes(echConfigList.toByteArray())
} catch (_: InvalidEchDataException) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ain’t great. Reducing privacy on misconfigured data is a bad look.

Just let this throw all the way to the caller instead?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is (or was) a policy in Android.

They removed the required mode? So maybe enabled is now required?

https://cs.android.com/android/_/android/platform/frameworks/base/+/14b56d11ef2f36ad3335d757110d1020bcd6d00b

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Throw for now until we have a policy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No wait, this was the one I meant.

https://issuetracker.google.com/issues/319957694

@swankjesse

Copy link
Copy Markdown
Collaborator

I previously ended up with EchModes (disabled, strict and so on) read from the policy

My preference is we assume DNS is where this should be configured. If there’s a ECH parameter on the TYPE_HTTPS record we should use it, and if there isn’t then we should assume the site operators don‘t care.

Configuring it on the client is too difficult for large organizations, because the team that ships services and apps is far detached from the team that manages DNS and HTTPS.

@yschimke

Copy link
Copy Markdown
Collaborator Author

I previously ended up with EchModes (disabled, strict and so on) read from the policy

My preference is we assume DNS is where this should be configured. If there’s a ECH parameter on the TYPE_HTTPS record we should use it, and if there isn’t then we should assume the site operators don‘t care.

Configuring it on the client is too difficult for large organizations, because the team that ships services and apps is far detached from the team that manages DNS and HTTPS.

But there is a developer API that we should honour.

https://developer.android.com/privacy-and-security/security-config#EncryptedClientHelloSummary

yschimke added 3 commits July 21, 2026 22:38
Initial version before dealing with NetworkSecurityPolicy,
instead just activated by developer using AndroidDns, and
a server having HTTPS records with ECH.
@yschimke

Copy link
Copy Markdown
Collaborator Author

Still tests to go

@yschimke

Copy link
Copy Markdown
Collaborator Author

Need to put this catch back

emulator-5554 - 17 Tests 34/29 completed. (5 skipped) (1 failed)
Tests on emulator-5554 - 17 failed: There was 1 failure(s).
Finished 34 tests on emulator-5554 - 17
Execute okhttp.android.test.OkHttpTest.testUnderscoreRequest: FAILED
The input does not conform to the STD 3 ASCII rules. line: java.lang.IllegalArgumentException: Invalid input to toASCII: example_underscore_123.s3.amazonaws.com
at java.net.IDN.toASCII(IDN.java:115)
at javax.net.ssl.SNIHostName.<init>(SNIHostName.java:99)
at com.android.org.conscrypt.Platform.getSSLParameters(Platform.java:269)
at com.android.org.conscrypt.ConscryptEngine.getSSLParameters(ConscryptEngine.java:425)
at com.android.org.conscrypt.ConscryptEngineSocket.getSSLParameters(ConscryptEngineSocket.java:193)
at okhttp3.internal.platform.android.Android17SocketAdapter.configureTlsExtensions(Android17SocketAdapter.kt:63)
at okhttp3.internal.platform.Android10Platform.configureTlsExtensions(Android10Platform.kt:86)
at okhttp3.internal.connection.ConnectPlan.connectTls(ConnectPlan.kt:349)

@swankjesse

Copy link
Copy Markdown
Collaborator

Where does Android implement the policy in that XML file? Presumably we act as if it's always enabled, and the XML config overrides our preferences?

@yschimke

Copy link
Copy Markdown
Collaborator Author

Where does Android implement the policy in that XML file? Presumably we act as if it's always enabled, and the XML config overrides our preferences?

But then we do extra work fetching ECH records that won't be used.

But I think the test is that we do the correct, secure, optimal thing with DnsOverHttps enabled? So it can't be inside AndroidDns then?

Not sure where exactly - one of these

https://cs.android.com/android/platform/superproject/+/android-latest-release:external/conscrypt/repackaged/platform/src/main/java/com/android/org/conscrypt/ConscryptNetworkSecurityPolicy.java

https://cs.android.com/android/platform/superproject/+/android-latest-release:external/conscrypt/nsc/src/android/security/NetworkSecurityPolicy.java

cc @swankjesse not sure if you see non threaded comments.

.filterIsInstance<Dns.Record.IpAddress>()
.map { it.address }

override fun newCall(request: Dns.Request): Dns.Call = AndroidDnsCall(request, includeServiceMetadata = includeServiceMetadata)

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@swankjesse should includeServiceMetadata move to Dns.Request? so we can honour NetworkSecurityPolicy?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants