Skip to content

[dart:io] Use Conscrypt APEX root store on Android 14+ - #64696

Closed
LucasHild wants to merge 1 commit into
dart-lang:mainfrom
LucasHild:use-conscrypt-apex-root-store-on-android-14
Closed

LucasHild wants to merge 1 commit into
dart-lang:mainfrom
LucasHild:use-conscrypt-apex-root-store-on-android-14

Conversation

@LucasHild

Copy link
Copy Markdown
Contributor

On Android 14+ (API 34), the system root store moved into the updatable Conscrypt APEX module, so Google can add or distrust root CAs through Google Play system updates. Recent CA incidents (e.g. eMudhra compromise) show why it matters that these changes reach devices quickly. Currently, they never reach dart:io.

Current behavior: dart:io always reads roots from /system/etc/security/cacerts, which is only updated when the manufacturer ships an OTA. On Android 14+ this differs from the platform TLS stack (Conscrypt), which prefers the APEX store, so Dart apps and native Android code on the same device can trust different roots.

Proposed behavior: dart:io reads roots from /apex/com.android.conscrypt/cacerts when it exists, and falls back to /system/etc/security/cacerts otherwise. This is the same order Conscrypt uses.

I validated the change on two emulators:

  • API 36 emulator: roots loaded from /apex/com.android.conscrypt/cacerts. HTTPS requests to google.com and dart.dev succeeded, self-signed.badssl.com was rejected with CERTIFICATE_VERIFY_FAILED, and reading the APEX directory caused no SELinux denials.
  • API 33 emulator: the APEX directory doesn't exist, so roots were loaded from /system/etc/security/cacerts. Same results.

  • I’ve reviewed the contributor guide and applied the relevant portions to this PR.
Contribution guidelines:
  • See our contributor guide for general expectations for PRs.
  • Larger or significant changes should be discussed in an issue before creating a PR.
  • Contributions to our repos should follow the Dart style guide and use dart format.

Note that this repository uses Gerrit for code reviews. Your pull request will be automatically converted into a Gerrit CL and a link to the CL written into this PR. The review will happen on Gerrit but you can also push additional commits to this PR to update the code review.

@copybara-service

Copy link
Copy Markdown

Thank you for your contribution! This project uses Gerrit for code reviews. Your pull request has automatically been converted into a code review at:

https://dart-review.googlesource.com/c/sdk/+/567800

Please wait for a developer to review your code review at the above link; you can speed up the review if you sign into Gerrit and manually add a reviewer that has recently worked on the relevant code. See CONTRIBUTING.md to learn how to upload changes to Gerrit directly.

Additional commits pushed to this PR will update both the PR and the corresponding Gerrit CL. After the review is complete on the CL, your reviewer will merge the CL (automatically closing this PR).

@copybara-service

Copy link
Copy Markdown

Gerrit CL has been approved, please wait for a reviewer to merge it.

There are also new comments on the CL, please review them and respond if necessary because reviewer might have requested clarifications or possibly some final changes and will not merge the CL until their questions have been answered.

@copybara-service

Copy link
Copy Markdown

Gerrit CL has been approved, please wait for a reviewer to merge it.

There are also new comments on the CL, please review them and respond if necessary because reviewer might have requested clarifications or possibly some final changes and will not merge the CL until their questions have been answered.

copybara-service Bot pushed a commit that referenced this pull request Oct 22, 2026
Closes #64696

GitOrigin-RevId: 2ae2217

Issue description: dart:io on Android 14+ reads root CAs from /system/etc/security/cacerts instead of the updatable Conscrypt APEX store, so CA changes delivered via Google Play system updates never reach Dart apps.
What is the fix: Prefer /apex/com.android.conscrypt/cacerts when present, falling back to /system/etc/security/cacerts.
Why cherry-pick: Lets Dart/Flutter apps on stable pick up root CA distrusts promptly.
Risk: Low, small change confined to Android root store lookup, landed on main.
Issue link(s): #64696

[email protected]

Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/567800
Change-Id: I9a100821e182e6746ddd7f729e9ce089e4a1bfb6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/568260
Reviewed-by: Slava Egorov <[email protected]>
copybara-service Bot pushed a commit that referenced this pull request Oct 22, 2026
Closes #64696

GitOrigin-RevId: 2ae2217

Issue description: dart:io on Android 14+ reads root CAs from /system/etc/security/cacerts instead of the updatable Conscrypt APEX store, so CA changes delivered via Google Play system updates never reach Dart apps.
What is the fix: Prefer /apex/com.android.conscrypt/cacerts when present, falling back to /system/etc/security/cacerts.
Why cherry-pick: Lets Dart/Flutter apps on beta pick up root CA distrusts promptly.
Risk: Low, small change confined to Android root store lookup, landed on main.
Issue link(s): #64696

[email protected]

Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/567800
Change-Id: Ia49a7e11e1d123e2559118fbba8b40239d71cf77
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/568221
Reviewed-by: Slava Egorov <[email protected]>
Commit-Queue: Alexander Thomas <[email protected]>
fa0311 pushed a commit to fa0311/flutter-sdk-eMudhra that referenced this pull request Oct 23, 2026
Closes dart-lang#64696

GitOrigin-RevId: 2ae2217
Change-Id: I61b524070ba141afe7ea05a37b10841272ac74b6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/567800
Reviewed-by: Martin Kustermann <[email protected]>
Reviewed-by: Slava Egorov <[email protected]>
Commit-Queue: Slava Egorov <[email protected]>
(cherry picked from commit 3ee784c)

Co-Authored-By: Claude Fable 5.1 <[email protected]>
fa0311 pushed a commit to fa0311/flutter-sdk-eMudhra that referenced this pull request Oct 23, 2026
Closes dart-lang#64696

GitOrigin-RevId: 2ae2217
Change-Id: I61b524070ba141afe7ea05a37b10841272ac74b6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/567800
Reviewed-by: Martin Kustermann <[email protected]>
Reviewed-by: Slava Egorov <[email protected]>
Commit-Queue: Slava Egorov <[email protected]>
(cherry picked from commit 3ee784c)

Co-Authored-By: Claude Fable 5.1 <[email protected]>
2ZeroSix pushed a commit to 2ZeroSix/sdk that referenced this pull request Oct 24, 2026
Closes dart-lang#64696

GitOrigin-RevId: 2ae2217

Issue description: dart:io on Android 14+ reads root CAs from /system/etc/security/cacerts instead of the updatable Conscrypt APEX store, so CA changes delivered via Google Play system updates never reach Dart apps.
What is the fix: Prefer /apex/com.android.conscrypt/cacerts when present, falling back to /system/etc/security/cacerts.
Why cherry-pick: Lets Dart/Flutter apps on stable pick up root CA distrusts promptly.
Risk: Low, small change confined to Android root store lookup, landed on main.
Issue link(s): dart-lang#64696

[email protected]

Cherry-pick: https://dart-review.googlesource.com/c/sdk/+/567800
Change-Id: I9a100821e182e6746ddd7f729e9ce089e4a1bfb6
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/568260
Reviewed-by: Slava Egorov <[email protected]>
(cherry picked from commit a9e26a2)
apooverma pushed a commit to apooverma/flutter that referenced this pull request Oct 26, 2026
This PR automates the synchronization of `CHANGELOG.md` from the
`stable` release branch (`flutter-3.47-candidate.0`,
[flutter#195179](flutter#195179)) to the
`master` branch for the `3.47.8` stable hotfix
([flutter#195000](flutter#195000)).

### Added `3.47.8` Changelog Entry

### [3.47.8](https://github.com/flutter/flutter/releases/tag/3.47.8)
#### Security advisory
[GHSA-frr6-h895-5pxp](GHSA-frr6-h895-5pxp):
- [dart/568000](https://dart-review.googlesource.com/c/sdk/+/568000)
Updates BoringSSL in the Dart runtime (3.13.7) to block compromised
eMudhra intermediate CA certificates.
- [dart/64696](dart-lang/sdk#64696) Updates
`dart:io` on Android 14+ to use the updatable Conscrypt APEX root
certificate store.
- Developers shipping Android, Windows, or Linux applications should
upgrade to Flutter 3.47.8 and rebuild their apps.
---
- [flutter/190235](flutter#190235)
When running flutter pub get or building desktop apps with existing or
broken plugin symlink entries, flutter_tools no longer crashes with a
PathExistsException.
- [flutter/192934](flutter#192934)
When dumping the focus tree or layer tree from flutter run on any
platform before the UI isolate finishes initializing or after teardown,
flutter_tools crashed with a null check operator exception.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
chrisbobbe added a commit to zulip/zulip-flutter that referenced this pull request Oct 26, 2026
This brings in Dart 3.15.0-71.0.dev, with two changes to how dart:io
checks TLS certificates:

- Its BoringSSL (at c7eb007fd106, which includes BoringSSL commit
  dd682072baab) rejects a certificate chain if any certificate in it
  has one of the eight compromised eMudhra intermediate CA keys.
  See dart-lang/sdk#64644 and https://emudhra.we-have-a-problem.com/ .

- On Android 14+, it reads the root store Android itself uses, which
  Google Play system updates can change, instead of the fixed copy in
  /system. See dart-lang/sdk#64696.

Before this, the Android app accepted a test server's certificate
issued by one of those compromised intermediates (tested 2026-10-22 on
a Pixel 8 with Android 17, against
https://dv-g3b-emudhra.we-have-a-problem.com/ ). On the same phone,
Android's own TLS stack, which Kotlin and Java apps typically use,
rejected it: Google had distributed a block on those keys through
Google Play services, and dart:io doesn't consult that.

This fixes these eight keys specifically. dart:io still doesn't
consult Android's blocklist, so a similar incident would again need
an SDK update to reach the app. The alternative is to send the app's
HTTP traffic through Android's own TLS stack, with a client like
Cronet; that's been discussed in #461.

Co-Authored-By: Claude Fable 5.1 <[email protected]>
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
This PR automates the synchronization of `CHANGELOG.md` from the
`stable` release branch (`flutter-3.47-candidate.0`,
[flutter#195179](flutter#195179)) to the
`master` branch for the `3.47.8` stable hotfix
([flutter#195000](flutter#195000)).

### Added `3.47.8` Changelog Entry

### [3.47.8](https://github.com/flutter/flutter/releases/tag/3.47.8)
#### Security advisory
[GHSA-frr6-h895-5pxp](GHSA-frr6-h895-5pxp):
- [dart/568000](https://dart-review.googlesource.com/c/sdk/+/568000)
Updates BoringSSL in the Dart runtime (3.13.7) to block compromised
eMudhra intermediate CA certificates.
- [dart/64696](dart-lang/sdk#64696) Updates
`dart:io` on Android 14+ to use the updatable Conscrypt APEX root
certificate store.
- Developers shipping Android, Windows, or Linux applications should
upgrade to Flutter 3.47.8 and rebuild their apps.
---
- [flutter/190235](flutter#190235)
When running flutter pub get or building desktop apps with existing or
broken plugin symlink entries, flutter_tools no longer crashes with a
PathExistsException.
- [flutter/192934](flutter#192934)
When dumping the focus tree or layer tree from flutter run on any
platform before the UI isolate finishes initializing or after teardown,
flutter_tools crashed with a null check operator exception.

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [AI contribution guidelines] and understand my
responsibilities, or I am not using AI tools.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant in-code documentation (doc comments with
`///`).
- [x] If this PR introduces a new feature or capability, I created and
linked a website documentation issue or PR in [flutter/website] (or
verified none is needed).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[AI contribution guidelines]:
https://github.com/flutter/flutter/blob/main/CONTRIBUTING.ai.md
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[flutter/website]: https://github.com/flutter/website
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant