What we found in TeeChat’s seal-sync protocol — and how we closed it

On August 30, 2026, an internal security review found a critical flaw in the implementation of the seal-sync protocol, the administrative protocol TeeChat uses to move a serving TLS key between blue/green trusted environments.

We found this issue before enabling payments and beginning to promote the product.

This post is the closure announcement. We contained the reachable ports, found no evidence of a malicious key export or user-data leak, deployed the fixed version, replaced the assumed-compromised serving keys, and revoked the associated public certificates.

What the seal-sync protocol does

All of TeeChat’s internal and external TLS connections use TLS 1.3, for the strongest practical transport guarantee. Every private key is generated inside a confidential container (a key ceremony) and sealed to disk so that only that same container can open it. During a service upgrade, we use the seal-sync protocol to encrypt and transfer the private key between attested service components. The private key never leaves the confidential container in plaintext.

The flaw

The intended design called for attested TLS 1.3. The implementation did not fully satisfy that contract:

An attacker would have needed to reach a seal-sync listener and pass or bypass these checks. If successful, the attacker could receive the serving TLS private key.

If a key were stolen, what an attacker could and could not see

Even if the certificate private key were stolen, an attacker could not decrypt previously recorded TeeChat traffic. The affected services require TLS 1.3, whose ephemeral key exchange provides forward secrecy.

A more serious possible outcome would be an attacker impersonating the identity and establishing a connection. The attacker would still need control over routing, DNS, a proxy, an upstream host, or a client endpoint. A successfully impersonated OpenAPI endpoint could then see new requests and credentials presented to that endpoint.

We found no evidence that this happened.

How we found it

The finding came from TeeChat’s internal security review, not from a customer report or an external disclosure.

We compared the written protocol contract with the actual Rust implementations, deployment templates and running network surfaces. Read-only identity requests confirmed that production OpenAPI and gateway seal-sync listeners were reachable publicly. Git history then showed that an empty QEMU host address had created all-interface forwards. The first affected OpenAPI configuration entered the repository on July 22; gateway followed on July 26; SGX lab listeners followed on July 29.

The public-bind source paths were corrected and host firewall containment was applied on August 30. These dates bound the possible exposure period.

What the retained logs show

The retained SGX lab logs contain:

The successful records correlate with our blue/green lab migrations and are classified as legitimate with moderate confidence.

Current and immediately previous retained production OpenAPI and gateway logs contain zero successful export records. That retention does not cover the entire possible exposure period.

The old audit format also omitted reliable timestamps, source addresses and a cryptographically authenticated requester key.

Conclusion

Combined with the additional controls a successful attack would still need — routing, DNS, a proxy, an upstream host, or a client endpoint — our conclusion is: no confirmed attack or leak occurred, and the security of user data was not affected. Even so, we are publishing this announcement to stay transparent on security that matters. This will also be the standard we follow going forward.

Immediate containment

We:

  1. blocked non-loopback ingress to every identified seal-sync port;
  2. changed future QEMU and SGX launches to bind seal-sync on loopback;
  3. externally checked the OpenAPI, gateway and SGX lab port matrix from an independent host;
  4. kept ceremony exporters parked outside witnessed migration windows;
  5. added regression tests that reject wildcard seal-sync forwards.

These controls closed the public network path. Containment is not the same as correcting the protocol, so we then deployed the fixed version and replaced the serving keys.

Permanent fix

The fixed version now:

Replacing the assumed-compromised keys

We treated the existing serving keys as assumed compromised even though we found no confirmed export.

We minted new identities in measured ceremony environments, published the new SPKIs, migrated OpenAPI and gateway through the fixed version, retired the old SPKIs, and verified the resulting production and lab environments (external port checks, attestation checks, and product regression).

A stolen private key can still be used with a not-yet-expired public certificate. We therefore treated the associated certificates as assumed compromised as well and revoked them at the issuing certificate authority, so relying parties that check revocation will reject the old leaves.

Revision log

  1. Link the public seal-sync protocol and state that the private key never leaves the confidential container in plaintext.
  2. Note that associated certificates were assumed compromised and revoked at the CA.

← All posts