Sunlight v0.8.1 security release

195 views
Skip to first unread message

Filippo Valsorda

unread,
Jun 30, 2026, 3:29:30 PM (13 hours ago) Jun 30
to Certificate Transparency Policy
Hello fellow humans, AIs, and super-intelligent shades of the color blue,

Today we released Sunlight v0.8.1, a security fix for v0.8.0.


Summary. The Sunlight deduplication cache used 128-bit keys, which potentially allowed obtaining SCTs for forged certificates. In the WebPKI, this would have required a colluding, compromised, or separately vulnerable CA. Tuscolo, Willow, and Sycamore have rolled out the patch. Gouda is scheduled to roll out the patch soon.

Details. Submissions were deduplicated by looking them up in a SQLite database using a SHA-256 hash truncated to 128 bits. If a match is found in the deduplication cache, Sunlight signs an SCT over the stored (old) timestamp and index, and the submitted (pre-)certificate. With 128-bit keys, an attacker could compute a collision offline in 2⁶⁴ work (costing in the order of tens of thousands of dollars), submit an honest certificate, and then obtain an SCT for the colliding certificate, without the latter making it into the log.

Mitigating factors. This is a collision attack, so it requires selecting a pair of certificates which happen to randomly have the same truncated SHA-256 hash from a large pool of 2⁶⁴ candidates. In the WebPKI, this attack would require a colluding, compromised, or separately vulnerable CA, because the TLS BRs require unpredictable entropy in the serial number, making it impossible to compute the collision offline.

Remediation. Sunlight v0.8.1 now only inserts new entries in the cache using their full SHA-256 hash, in a new "cache256" table. Existing 128-bit keys are still queried, to avoid a wave of cache misses when upgrading from v0.8.0 (or earlier) to v0.8.1 (or later). This is relatively safe because a collision attack requires control over both entries. Either the attack was executed in the past, in which case the attacker already obtained a forged SCT, or the existing 128-bit keys are honest. With approximately 2³² honest entries in the cache, a multi-target second preimage attack that searches for a new certificate that matches one of them would require 2⁹⁶ work (taking in the order of a couple years with the same energy consumption as the whole Bitcoin network).

For extra safety, operators can optionally run the new recompute-cache command to rebuild the cache with 256-bit keys from the backend storage (with verification of the STH, not to trust the backend). Once recompute-cache has completed, running

sqlite3 cache.db "ALTER TABLE cache RENAME TO cache_legacy;"

disables the fallback. This is safe to run concurrently with the log. (Do not use DROP as that would be a long write transaction, which would block the sequencer. Ask me how I know. Poor Navigli.)

The Tuscolo logs have rolled out the patch, regenerated the cache, and disabled the fallback. I am told the Willow and Sycamore logs are now running v0.8.1, and that the Gouda rollout is planned for 22:30 CEST. I'd like to thank the Sunlight operators for their rapid response.

Retrospective. The deduplication cache implementation was probably too clever. It uses deterministic ECDSA to produce byte-identical SCTs without storing the signatures. I felt pressure to limit the on-disk size of this cache, because it needs to live locally, even in setups that use object storage for the log. That was probably over-indexed in retrospect: the Tuscolo2025h2 cache is 70GB. I also apparently did think about collisions, because the cache type had a comment saying

// birthday bound of 2⁴⁸ entries with collision chance 2⁻³²

but I failed to realize that intentional collisions were both possible and valuable to an attacker. Besides being less clever with the cache, it's not clear how else this could have been prevented without hindsight. Suggestions are welcome.

Timeline.
  • 2026-06-10: the issue is spontaneously reported by Anthropic, along with lower severity issues in Sunlight and age. The issue is reported as severity LOW. Upon a first skim, I fail to recognize the actual severity, and defer it.
  • 2026-06-25: upon triaging the issue, I identify its severity, and coordinate with other Sunlight operators on a suitable release date. (No details of the vulnerability were disclosed to the operators.) Tuesday is selected to avoid US and Canada holidays.
  • 2026-06-27: fix deployed to the Navigli staging logs.
  • 2026-06-28: fix deployed to the Tuscolo production logs.
  • 2026-06-30: public release, fix deployed to Willow and Sycamore; Gouda deployment planned for 22:30 CEST.
Credit. This vulnerability was discovered by Claude, Anthropic's AI assistant, and triaged by the Anthropic security team in collaboration with Anthropic Research.

Alla prossima (well, hopefully not),
Filippo

Matthew McPherrin

unread,
Jun 30, 2026, 3:56:00 PM (12 hours ago) Jun 30
to Filippo Valsorda, Certificate Transparency Policy
Let’s Encrypt has updated our logs to 0.8.1.

Thanks to Filippo for your hard work on maintaining Sunlight and working with us on coordinating schedules.

Our logs were down for a few minutes each while we deployed updated software.

--
You received this message because you are subscribed to the Google Groups "Certificate Transparency Policy" group.
To unsubscribe from this group and stop receiving emails from it, send an email to ct-policy+...@chromium.org.
To view this discussion visit https://groups.google.com/a/chromium.org/d/msgid/ct-policy/83d45f12-e70f-4733-9a7d-3378087f1088%40app.fastmail.com.

Andrew Ayer

unread,
Jun 30, 2026, 4:55:58 PM (11 hours ago) Jun 30
to Filippo Valsorda, Certificate Transparency Policy
Hi Filippo,

By remarkable coincidence I filed <https://github.com/transparency-dev/tessera/issues/1018> in Tessera/Tesseract last week. It's the same class of issue - a fault in the dedupe cache leading to an unincorporated SCT.

I made the observation that it's fragile for the input to the SCT signing operation to come from two places (the submitted certificate and the dedupe cache). Tesseract is going to address that by retrieving the entire entry from storage (instead of just the timestamp) and signing over that instead of an amalgamation.

I think it would be a very good idea for Sunlight to also do that, or to store the SCT signature in the cache. Either change would make the correct operation of the log not dependent on the correct operation of the dedupe cache.

By the way, v0.8.1 is not currently part of any branch in the sunlight repo.

Regards,
Andrew

Jeroen Massar

unread,
Jun 30, 2026, 5:44:10 PM (10 hours ago) Jun 30
to Filippo Valsorda, Certificate Transparency Policy


> On 30 Jun 2026, at 21:29, Filippo Valsorda <fil...@ml.filippo.io> wrote:
>
> Hello fellow humans, AIs, and super-intelligent shades of the color blue,
>
> Today we released Sunlight v0.8.1, a security fix for v0.8.0.
>
> https://github.com/FiloSottile/sunlight/releases/tag/v0.8.1
> https://github.com/FiloSottile/sunlight/compare/v0.8.0...v0.8.1
>
> Summary. The Sunlight deduplication cache used 128-bit keys, which potentially allowed obtaining SCTs for forged certificates. In the WebPKI, this would have required a colluding, compromised, or separately vulnerable CA. Tuscolo, Willow, and Sycamore have rolled out the patch. Gouda is scheduled to roll out the patch soon.

IPng Rennet was upgraded at 20:30 CEST and Gouda has been updated since 22:30 CEST. Both running fine.

Cache regeneration is in progress.


Thanks Filippo for all the great work on many thanks and for the timely heads up so we could be prepped for this quick and painless upgrade.

Regards,
Jeroen

Filippo Valsorda

unread,
Jun 30, 2026, 5:49:51 PM (10 hours ago) Jun 30
to Andrew Ayer, Certificate Transparency Policy
2026-06-30 22:55 GMT+02:00 Andrew Ayer <ag...@andrewayer.name>:
I made the observation that it's fragile for the input to the SCT signing operation to come from two places (the submitted certificate and the dedupe cache). Tesseract is going to address that by retrieving the entire entry from storage (instead of just the timestamp) and signing over that instead of an amalgamation.

I think it would be a very good idea for Sunlight to also do that, or to store the SCT signature in the cache.  Either change would make the correct operation of the log not dependent on the correct operation of the dedupe cache.

I considered retrieving the entry from storage (at least to upgrade the 128-bit key to the 256-bit version), but that would be a large change in the performance profile: serving a submission of a known certificate, which can be resubmitted an unlimited number of times (in the version where we do this for robustness, not for migration), would require retrieving 256 certificates from object storage, plus ~ 4 x 256 hashes to authenticate it. (Sunlight does not consider the object storage trusted, only the local cache and the lock storage.) Caching will not help against the worst case. I am kinda surprised TesseraCT can make that work.

The alternative is indeed storing the signature instead of recomputing it. That would roughly triple the size of the cache, which we just doubled. I had over-indexed on cache size, but 6x might actually matter?

The code is pretty easy to audit at this point, the cache is

SHA-256(entry_type || issuer_key_hash? || certificate) => timestamp, index

and the ECDSA message is

SHA-256(0 || 0 || timestamp || entry_type || issuer_key_hash? || certificate || index_extension)

I'll need to ponder it a bit. Thank you for the input.

By the way, v0.8.1 is not currently part of any branch in the sunlight repo.

Yeah, I forked off v0.8.0 to minimize the diff and ensure the rollout wouldn't require a rollback. I'll merge it in main as I go on developing v0.9.0.
Reply all
Reply to author
Forward
0 new messages