What Happened
On the morning the news broke, Scott Helme published a technical write-up of a key-generation incident at eMudhra, an Indian commercial Certificate Authority. The headline: eight of eMudhra's intermediate CAs - six for TLS issuance (DV, OV, EV, each at two generations) and two for code signing - were created in a 30 April 2025 key-generation ceremony with private keys that were cryptographically trivial. The keys were nominally P-384 ECC. In reality, each private scalar fit in a single byte. The recovered scalars were:
text14, 56, 66, 115, 155, 156, 230, 237
A single-byte P-384 private key means the private key can be recovered from the public key in milliseconds by brute-forcing 256 candidate scalars, or (if you want to be fancy about it) by Pollard's baby-step giant-step. Any party with the public certificate could compute the private key with a laptop.
All eight intermediates were cross-signed by Sectigo's AAA Certificate Services root in May 2025. From that point on, any TLS or code-signing client that trusts Sectigo's AAA Certificate Services root transitively trusted a set of CAs whose private keys were, in effect, published alongside the certificates.
How It Got There
eMudhra's own report describes the failure as a key-generation script that produced a second set of weaker keys during the HSM ceremony, with naming similar enough to the real keys that the operators used the weak ones for the CSR by mistake. The HSM itself did its job - it generated proper 384-bit scalars. The script wrapper around it zeroed out everything but the low byte.
Several patterns fit the symptom - a Python string handling error that returned key[0] instead of the key, HSM slot numbers used as scalars, a positional-argument bug in the CSR generator. Which specific bug is less important than the structural failure: a script sat between the HSM and the production artifact, and nobody caught that the output of the script was not a valid private key.
What should have caught it:
- A sanity check on the generated key - any check more rigorous than "the file is non-empty" would have failed on a single-digit scalar.
- The signing ceremony witnesses - a witness-signed ceremony is supposed to produce artefacts that are inspected before being released. Nobody looked at the key sizes.
- The downstream audit at Sectigo - cross-signing is supposed to involve diligence on the subordinate CA's key material. A cross-sign of a CA whose public key is on a known curve but whose private scalar is single-byte should have been flagged by any serious inspection.
Each of these checkpoints existed. None of them fired. That's the real finding.
The Fast Response (Browsers)
The browsers moved within hours of the report landing:
- Chrome pushed a CRLSet update the same evening blocking the eight intermediates. Chrome Root Store v43 included an explicit revocation.
- Mozilla blocked the intermediates via OneCRL the same day and announced the eMudhra root removal shortly after.
- Safari followed through Apple's own revocation list.
For users on current browsers, the window of exploitability closed within one update cycle. The browser trust-store governance machinery - CCADB, root program policies, OneCRL / CRLSet / Apple's blocklist - is actually built for exactly this scenario, and it worked.
The Slow Response (Everything Else)
The browser response is not the end of the story. The browser response is the best case. The problem is that the rest of the TLS and code-signing ecosystem is not a browser.
The eight compromised intermediates issue certificates that chain through Sectigo's AAA Certificate Services, which is included in essentially every TLS trust store on earth:
- Non-browser TLS clients -
curl,wget,python requests, Node.js, Go, Java, Rust (reqwest), git (clone over HTTPS), package managers (npm, pip, cargo, go modules). None of these check Certificate Transparency by default. Most don't check CRL / OCSP by default. curl | shstyle installers - rustup, nvm, Homebrew installers, everywget https://... | shout there. If the install URL is served through a forged certificate for a known-good domain, the chain validates.- CI/CD pipelines - CI runners that hold cloud API keys, signing credentials, and deployment permissions. These typically make server-to-server HTTPS calls with Go or Python clients that validate the chain but check neither CT nor revocation.
- Server-to-server API calls - payments, webhooks, OAuth token endpoints, database proxies. Same story.
- IoT and OT firmware - devices shipped from 2018-2024 with Sectigo in the trust store and no over-the-air trust-store update path. Many are rated for a decade of field life. The eMudhra root removal doesn't help if the firmware doesn't fetch an update.
- Code signing - the two code-signing intermediates mean that any Windows binary (or in some setups, macOS or Java) signed with a forged cert from the compromised intermediates could appear legitimate on any system that doesn't check revocation. Windows code-signing does soft-fail on revocation checks by default.
The researcher confirmed this hypothesis directly - they issued forged certificates for api.stripe.com and others, and curl, Python, and Node accepted them on test systems without revocation checks enabled.
The Trust-Store Timebomb
The item that keeps PKI practitioners awake is the IoT / OT angle. A device shipped in 2019 with Sectigo AAA Certificate Services pinned in firmware is, as of this writing, trusting eight compromised CAs. The device has no way to know. The device will not get a trust-store update. The device will remain in the field for another decade.
For any attacker who recovered the eMudhra private keys (and anyone could, the moment the certificates landed in Certificate Transparency logs - which is how the researcher found them), the window of exploitability against those devices is "until the device dies."
This is the structural issue the browser-governance response doesn't address and can't address. Browsers can un-trust an intermediate in an evening. The embedded ecosystem cannot.
What To Actually Do About It (Operator Edition)
If you run production systems, here is the concrete punch list:
- Inventory your trust stores. You almost certainly do not have a current inventory of which trust anchors are present on your production hosts, which anchors are pinned by your network appliances, which anchors are compiled into your Go binaries'
x509roots. Build the inventory. For TigerTrust customers, this is what the agent trust-store enumeration surfaces - every anchor on every host, cross-referenced against known-good lists. - Pin issuer identity on critical paths. For any server-to-server call that terminates against a specific hostname, use certificate pinning or certificate-authority pinning (SPKI pinning).
curl --pinnedpubkey, Go'scrypto/tls.Config.VerifyPeerCertificate, Node'scheckServerIdentity. This moves the trust decision from "any CA on the roots list" to "this specific key or CA". - Enable OCSP stapling and hard-fail revocation. For TLS endpoints you control, serve OCSP responses with the handshake. For clients, configure them to hard-fail on revocation check failure, not soft-fail. The default soft-fail posture is why the eMudhra response matters so much less on non-browser clients than on browsers.
- Short-lived certs are a backstop. The eMudhra incident is not solved by short lifetimes alone - a 30-day cert from a compromised CA still works for 30 days. But a 30-day cert does bound the blast radius of any future similar incident. If you're already thinking about the 47-day transition, the eMudhra incident is one more reason to accelerate.
- Install the trust-store update path. For devices and appliances you ship, build the over-the-air trust-store update path now. Don't wait for the next incident. If you can't update the trust store, your device is a long-term liability regardless of its code quality.
- Validate the chain, not the leaf. For critical workloads, log every chain your relying parties see. If a chain to a critical hostname suddenly includes an intermediate you've never seen before, that's a signal. Chain-anomaly detection catches the type of attack this incident enabled - forging certs for well-known hostnames through a new intermediate.
What To Do About It (CA Edition)
If you run a CA - public, private, or internal:
- Script output is not a key. A key-generation ceremony that produces its output through a script has to validate the script's output against the HSM's own expected shape. If the HSM says it generated a P-384 scalar, the artefact that gets handed to the CSR step has to be verified as a scalar on P-384 with its full bit length.
- Ceremony witnesses have to look at the output. The witness isn't there to confirm that the operator pressed Enter. The witness is there to confirm that the output of the process matches what the process is supposed to produce. That means somebody has to actually check that the key has >1 byte of entropy before releasing the CSR.
- Cross-signing CAs owe their relying parties diligence. Sectigo is now in the awkward position of having cross-signed CAs that compromised the parent root's reputation. The specific technical check - "is the subordinate's public key on the correct curve, and does the subordinate's claimed private key have enough entropy to not be trivially recoverable" - is cheap. Cross-signing audits should include it.
Why This Matters For TigerTrust
TigerTrust customers are affected in two separate ways by this incident:
On the issuance side, if any of your systems trust Sectigo's AAA Certificate Services as a trust anchor, you were - for roughly six months between April 2025 and the first blocks going out - vulnerable to any adversary with eMudhra's leaked keys forging certificates for your domains. The agent's trust-store enumeration surfaces whether that root is present on each host. Enumeration plus workspace-wide fingerprint matching gives you the inventory you can audit against the incident timeline.
On the governance side, the incident validates the shadow-issuance pattern we built for CA migrations. The architectural idea - never trust a single CA's output without a parallel-run proof - is the same architecture we recommend for anyone running a private CA. The eMudhra ceremony would have been caught in Phase 2 of a shadow run; the single-byte keys would have failed the first parity check.
Related Reading
- Scott Helme: Eight Bits of Trust
- The non-browser trust store blind spot - the deep dive on the ecosystem below the browser
- The multi-CA governance model after eMudhra - what this incident means for root programs
- Shadow issuance pattern - the architecture that would have caught this at ceremony time