<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Elizabeth Omokhaye</title>
    <description>The latest articles on DEV Community by Elizabeth Omokhaye (@khaye).</description>
    <link>https://dev.to/khaye</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3129576%2F1fcc49f1-8bb5-4f88-b344-9e922f13c390.png</url>
      <title>DEV Community: Elizabeth Omokhaye</title>
      <link>https://dev.to/khaye</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/khaye"/>
    <language>en</language>
    <item>
      <title>In TEE, Attestation Is Always Enough… Right?</title>
      <dc:creator>Elizabeth Omokhaye</dc:creator>
      <pubDate>Tue, 27 Oct 2026 16:38:15 +0000</pubDate>
      <link>https://dev.to/khaye/in-tee-attestation-is-always-enough-right-3ea0</link>
      <guid>https://dev.to/khaye/in-tee-attestation-is-always-enough-right-3ea0</guid>
      <description>&lt;p&gt;Start with the secret&lt;/p&gt;

&lt;p&gt;For this investigation, I want a small model: a confidential blockchain runtime needs a key to decrypt its state. A key manager holds that key and decides whether to release it. The machine hosting the runtime is untrusted in this model. Operating the machine should not automatically give its operator the plaintext key.&lt;/p&gt;

&lt;p&gt;There is a real example of this distinction in Oasis’s key manager documentation. Its access policy identifies which enclaves may query private keys and which may replicate a master secret. Accepted policies require a configured threshold of signatures. That gives me a useful starting point: specify which identity may receive which material, rather than simply asking whether a machine has a TEE. This is an example of a documented design, not an allegation of a flaw in Oasis.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.oasis.io/core/consensus/services/keymanager/" rel="noopener noreferrer"&gt;https://docs.oasis.io/core/consensus/services/keymanager/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would write the rule for my test system as follows: only the approved workload, through its authenticated recipient endpoint, may obtain the test secret. The host may relay the messages, but must not become the recipient by changing a field in them.&lt;/p&gt;

&lt;p&gt;That rule is more useful to investigate than the broad question “Are TEEs secure?” It names an asset, an intended recipient and an attacker who should remain outside the boundary.&lt;/p&gt;

&lt;p&gt;A verifier that collected evidence without enforcing it&lt;/p&gt;

&lt;p&gt;Bluethroat’s report on CVE-2026-22696 describes a gap in Phala’s dcap-qvl quote verifier. QE means Quoting Enclave. The affected verification path collected QE Identity material, including its signature and issuer certificate chain, but did not perform the corresponding authentication and policy checks.&lt;/p&gt;

&lt;p&gt;The report’s representative scenario uses a platform with an outdated QE. Other cryptographic checks can still succeed, while the missing QE policy check fails to reject a QE version that the policy should exclude. The useful detail here is that a successful subset of checks did not establish the complete acceptance condition.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://bluethroatlabs.com/reveries/cve-2026-22696-fetching-collateral-is-not-enough" rel="noopener noreferrer"&gt;https://bluethroatlabs.com/reveries/cve-2026-22696-fetching-collateral-is-not-enough&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The maintainer advisory confirms missing QE Identity signature verification and constraints on the QE Report. It records a fix in version 0.3.9, including checks involving the QE signer, product identity and security version. Package ranges differ: for example, Rust dcap-qvl was affected below 0.3.9, while @phala/dcap-qvl lists versions through 0.3.0. The older Node and web package names have migration guidance.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/Phala-Network/dcap-qvl/security/advisories/GHSA-796p-j2gh-9m2q" rel="noopener noreferrer"&gt;https://github.com/Phala-Network/dcap-qvl/security/advisories/GHSA-796p-j2gh-9m2q&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I would be careful about the claim I take from this case. The advisory describes broader quote-forgery consequences than the report’s representative outdated-QE scenario. I am not presenting an independently verified exploit chain for either. Nor does this case demonstrate that a host substituted a recipient key and stole a secret. That is the separate hypothesis below.&lt;/p&gt;

&lt;p&gt;For a review, I would turn the lesson into a practical question: where does each piece of evidence become a condition that can actually reject the request? Seeing a field fetched, parsed or stored would not answer that question. I would trace it to the decision it is supposed to constrain.&lt;/p&gt;

&lt;p&gt;What happens after verification&lt;/p&gt;

&lt;p&gt;The current dcap-qvl documentation distinguishes verified claims from downstream appraisal. Its API can return claims for an application’s policy engine; it also offers policy validation. This makes it important to inspect the caller’s decision, rather than treat every successful library return as permission to release a particular secret.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://github.com/Phala-Network/dcap-qvl#detailed-claims-and-optional-policy-validation" rel="noopener noreferrer"&gt;https://github.com/Phala-Network/dcap-qvl#detailed-claims-and-optional-policy-validation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Key binding gives the investigation another concrete question. Oasis’s runtime-host protocol describes an attestation report that binds a Runtime Attestation Key to TEE identity, together with replay protection. That illustrates the kind of relationship I need to identify. It does not establish that this particular key is the recipient encryption key in my proposed test. I would have to trace the actual protocol from its attested identity to its authenticated channel or recipient key.&lt;/p&gt;

&lt;p&gt;Source: &lt;a href="https://docs.oasis.io/core/runtime/runtime-host-protocol/#remote-attestation" rel="noopener noreferrer"&gt;https://docs.oasis.io/core/runtime/runtime-host-protocol/#remote-attestation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My proposed failure mode is a mismatch between those stages: the service accepts genuine evidence for an approved workload, but uses a separately supplied, unauthenticated public key to encrypt the response. If the host controls that second key, the service could release the secret to the host while logging a successful attestation check.&lt;/p&gt;

&lt;p&gt;This is a hypothesis about a possible implementation mistake. I have not established that it exists in Phala, Oasis or another deployed system.&lt;/p&gt;

&lt;p&gt;The attacker and the boundary&lt;/p&gt;

&lt;p&gt;For the experiment, I would give the attacker control over the untrusted host and the messages it relays. The attacker could start or stop the test workload, observe public messages, delay them and change fields that the protocol leaves outside authenticated protection. The attacker could generate a separate key pair whose private key remains outside the TEE.&lt;/p&gt;

&lt;p&gt;I would not give the attacker the key manager’s secret, its policy-signing authority or the workload’s protected private key. I would also exclude a hardware break or a vulnerability that already allows reading the enclave’s memory. Otherwise, the experiment could “succeed” by assuming away the boundary it is supposed to test.&lt;/p&gt;

&lt;p&gt;The approved workload matters too. If its interface willingly accepts any host-supplied public key and attests it, that would not establish that the matching private key lives inside the TEE. I would check how the key is generated, who controls it and what the approved code promises before interpreting a successful binding check.&lt;/p&gt;

&lt;p&gt;The outcome I am looking for is a new capability: the host decrypts a secret that host control alone was not supposed to provide.&lt;/p&gt;

&lt;p&gt;How I would test it&lt;/p&gt;

&lt;p&gt;I would begin in an isolated environment using dummy secrets. Before testing a real implementation, I would record its version, release policy, expected workload identity and the documented binding between attestation and the response recipient.&lt;/p&gt;

&lt;p&gt;The baseline would use an approved workload with a key generated and retained within its protected environment. The key manager would validate the evidence, apply its release policy and return the dummy secret over the protocol’s intended protected path. I would confirm that the intended workload receives it and that the host cannot decrypt the response with its separate private key. If this baseline fails, there is no useful negative test yet.&lt;/p&gt;

&lt;p&gt;Next, I would change one relationship at a time:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Recipient substitution: retain the legitimate evidence but replace a separately supplied recipient public key with the host’s key. The expected result is rejection, or delivery that remains decrypt able only by the authenticated workload.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Session substitution: attempt to reuse evidence from one exchange in a different exchange. The expected result depends on the protocol’s freshness and channel-binding rules. Reusing a quote is not automatically a vulnerability if the protocol securely authenticates a fresh channel another way.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unauthorized identity: obtain valid evidence for a workload excluded by the test release policy. Cryptographic validity should not override that policy.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Missing binding: omit or alter the field that is supposed to connect the accepted identity to the recipient. I would check whether the service fails closed or silently falls back to untrusted request data.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For every case I would preserve the input, verification result, policy decision, selected recipient and decryption outcome. An HTTP success response would not prove secret disclosure. The decisive evidence would be the host recovering the dummy plaintext using a key it controls, despite the documented policy excluding that recipient.&lt;/p&gt;

&lt;p&gt;I would also test the harness itself with an intentionally incorrect mock service that ignores recipient binding. That would show whether the test detects the deliberately introduced mistake. A mock result would remain a demonstration of the failure mode, not evidence that a production service has it. Without access to suitable TEE hardware, I would label all mocked attestation results accordingly.&lt;/p&gt;

&lt;p&gt;What I would be able to conclude&lt;/p&gt;

&lt;p&gt;A successful attack in this model would need a complete explanation: what the host controlled, which authenticated relationship was missing, why the release policy should have prevented the result, and how the host obtained the plaintext. I would then repeat the case after fixing the missing check and confirm that the legitimate baseline still works.&lt;/p&gt;

&lt;p&gt;If the substitution is rejected, that is useful evidence about the tested path and version. It would not establish that the whole system is secure. Conversely, finding an unverified input would be a lead, not a finished finding, until I could show that it changes the release decision or recipient.&lt;/p&gt;

&lt;p&gt;I have not reproduced the published vulnerability or run the proposed experiment. That is the direction I want to take with TEE research: start with a precise promise, learn from a documented failure, and design an experiment that can prove me wrong. For this article, the published verification gap is established by its sources. The recipient-binding issue remains a proposed investigation.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>blockchain</category>
      <category>security</category>
      <category>web3</category>
    </item>
  </channel>
</rss>
