<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://dlp.rip/feed.xml" rel="self" type="application/atom+xml" /><link href="https://dlp.rip/" rel="alternate" type="text/html" /><updated>2026-01-04T19:11:14+00:00</updated><id>https://dlp.rip/feed.xml</id><title type="html">Chris Fenner’s Personal Blog</title><subtitle>Almost worth the price of admission.</subtitle><author><name>Chris Fenner</name></author><entry><title type="html">Decorative Cryptography</title><link href="https://dlp.rip/decorative-cryptography" rel="alternate" type="text/html" title="Decorative Cryptography" /><published>2026-01-03T00:00:00+00:00</published><updated>2026-01-03T00:00:00+00:00</updated><id>https://dlp.rip/decorative-cryptography</id><content type="html" xml:base="https://dlp.rip/decorative-cryptography"><![CDATA[<p>All encryption is end-to-end, if you’re not picky about the ends.</p>

<pre><code class="language-kconfig">config TCG_TPM2_HMAC
    bool "Use HMAC and encrypted transactions on the TPM bus"
    default n
    select CRYPTO_ECDH
    select CRYPTO_LIB_AESCFB
    select CRYPTO_LIB_SHA256
    select CRYPTO_LIB_UTILS
    help
      Setting this causes us to deploy a scheme which uses request
      and response HMACs in addition to encryption for
      communicating with the TPM to prevent or detect bus snooping
      and interposer attacks (see tpm-security.rst).  Saying Y
      here adds some encryption overhead to all kernel to TPM
      transactions.
</code></pre>

<p>Last year, I came agross a Linux kernel feature called <code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code>. It
claims to detect or prevent active and passive interposer attackers. That’s <a href="/tpm-genie">one
of my sleeper agent activation phrases</a>, so I dug in.</p>

<!--more-->

<p><code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> lives primarily in
<a href="https://github.com/torvalds/linux/blob/master/drivers/char/tpm/tpm2-sessions.c">drivers/char/tpm/sessions.c</a>
and is discussed at further length in
<a href="https://github.com/torvalds/linux/blob/master/Documentation/security/tpm/tpm-security.rst">Documentation/security/tpm/tpm-security.rst</a>.</p>

<p>It all sounds really great. We should care about interposer adversaries. It’s
great to use the TPM features that were invented to help us with these problems.
Let’s draw a little picture of what’s being attempted here.</p>

<p><img src="/images/2026-01-03-tpm2-hmac-threat-model.drawio.svg" alt="TCG_TPM2_HMAC's Threat Model" /></p>

<p>In this threat model, there is an adversary who can access the untrusted bus
on which all the TPM traffic is sent during the boot. This can be done using
<a href="https://trmm.net/tpm-sniffing/">hardware hacking</a> or by hijacking another
device that controls the TPM bus (e.g., a BMC).</p>

<p><code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> is a kernel feature, and the kernel boots after the platform
firmware and the boot loader, so it can’t do anything about interposer
adversaries tampering with firmware and boot loader measurements. Let’s assume
for now that the firmware and boot loader are just implicitly trusted to have
booted “correct” code and successfully made honest measurements of all the boot
stages up to and including the kernel. We also implicitly trust the TPM to
behave correctly, here. <a href="/attesting-tpm-firmware">Or if you have a newer TPM, don’t!</a></p>

<p>Someone familiar with
<a href="https://en.wikipedia.org/wiki/STRIDE_model">the STRIDE model</a> can easily
observe the following threats just on the big red wire in our picture above:</p>

<table>
  <thead>
    <tr>
      <th>Attack</th>
      <th>Example</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><strong>Spoofing</strong></td>
      <td>Attacker pretends to be the TPM or the CPU to the other device</td>
    </tr>
    <tr>
      <td><strong>Tampering</strong></td>
      <td>Attacker drops or modifies measurements sent to the TPM</td>
    </tr>
    <tr>
      <td>Repudiation</td>
      <td>Not obviously applicable in this case</td>
    </tr>
    <tr>
      <td><strong>Information Disclosure</strong></td>
      <td>Attacker obtains unsealed secrets (e.g., disk encryption keys)</td>
    </tr>
    <tr>
      <td><strong>Denial of Service</strong></td>
      <td>Attacker drops measurements sent to the TPM</td>
    </tr>
    <tr>
      <td>Escalation of Privilege</td>
      <td>Not obviously applicable in this case</td>
    </tr>
  </tbody>
</table>

<p>The attacker may or may not necessarily get anything out of manipulating the TPM
traffic itself (unless they are some kind of <a href="/">degenerate</a> that
just likes to talk to TPMs for fun). But folks who are familiar with TPM-based
measured boot and attestation should be able to immediately see the value to the
attacker of “modifying measurements” or “obtaining unsealed secrets”.</p>

<p>Let’s take a second to distinguish the two types of attackers here:</p>

<ul>
  <li><strong>Passive Interposers</strong> aka snoopers can only read from the bus but not modify
the data.</li>
  <li><strong>Active Interposers</strong> can read and write to the bus.</li>
</ul>

<p>The very best thing a <strong>passive</strong> interposer can do here is <strong>Information</strong>
<strong>Disclosure</strong>: read data from the bus. Since measurements should typically not
be secret, the (legitimate) measurements sent to the TPM are not very
interesting. Unsealed secrets (that were sealed to the measurements in the TPM)
might very much be! That’s why
<a href="https://github.com/torvalds/linux/blob/master/security/keys/trusted-keys/trusted_tpm2.c">security/keys/trusted-keys/trusted_tpm2.c</a>
<a href="https://github.com/torvalds/linux/blob/aacb0a6d604ac9953b261963efe25ae7521b7c58/security/keys/trusted-keys/trusted_tpm2.c#L506">uses an <code class="language-plaintext highlighter-rouge">encrypt</code> session</a>
using the helper
<a href="https://github.com/torvalds/linux/blob/aacb0a6d604ac9953b261963efe25ae7521b7c58/drivers/char/tpm/tpm2-sessions.c#L364"><code class="language-plaintext highlighter-rouge">tpm_buf_append_hmac_session</code></a>
which is unfortunately a little bit entangled with the <code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> feature
(but that’s how software development goes). All that really needs to be done
here for this case is to use an <code class="language-plaintext highlighter-rouge">encrypt</code> session key established using the EK
as discussed widely by many others but also <a href="/tpm-genie">myself</a>.</p>

<p>The remainder of this blog post discusses the <strong>active</strong> interposer case.</p>

<p>An active interposer generally wants to do one of two things in this scenario:</p>

<ol>
  <li>(<strong>Tampering</strong>, <strong>Denial of Service</strong>) Tamper with TPM measurements made by
the kernel, to falsely attest or unseal as the “intended” code or state, from
“unintended” code or state.</li>
  <li>(<strong>Spoofing</strong>, <strong>Information Disclosure</strong>) Interpose the TPM connection and
defeat the encrypt session solution for unsealing secrets.</li>
</ol>

<p>The <code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> feature will
<a href="https://github.com/torvalds/linux/blob/aacb0a6d604ac9953b261963efe25ae7521b7c58/drivers/char/tpm/tpm2-sessions.c#L982">establish an auth session</a>
salted (key-encapsulated) to the EK every time the kernel
<a href="https://github.com/torvalds/linux/blob/aacb0a6d604ac9953b261963efe25ae7521b7c58/drivers/char/tpm/tpm2-cmd.c#L192">extends a PCR</a>
or
<a href="https://github.com/torvalds/linux/blob/aacb0a6d604ac9953b261963efe25ae7521b7c58/drivers/char/tpm/tpm2-cmd.c#L273">gets randomness</a>.
You might say to yourself, “self, that’s a lot of overhead (asymmetric crypto
in the TPM) for common, fast operations (PCR extensions, randomness generation)”
and
<a href="https://lore.kernel.org/linux-integrity/b8a7b3566e6014ba102ab98e10ede0d574d8930e.camel@huaweicloud.com/">you’d be right</a>.
Wow, this feature is expensive! Good thing it’s solving a real problem, right?</p>

<p><img src="/images/2026-01-03-anakin-padme.jpg" alt="Anakin says no" /></p>

<p>Every time a session is needed (e.g., every time the kernel needs to extend a
PCR), the <code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> feature key-encapsulates a new session key with
something called the “Null Primary Key” which is a P256 ECDH key derived from
the Null hierarchy (which means it changes on every boot). It uses this session
key to protect the TPM command by encrypting the inputs and outputs and
adding an HMAC to detect tampering. Great.</p>

<p>One problem: how does the kernel know what the Null Primary Key should be? Read
<a href="https://lore.kernel.org/linux-integrity/CAMigqh0x+yK25f8J_Yrn9v93969zQxbpevivWWZ6-NLFy4pDHQ@mail.gmail.com/T/#m835cf391e6ff8b3f85ff72e064798f9439b5770a">this thread</a>
to not find out.</p>

<p>The kernel takes the Null Primary Key at face value and stashes the Name (hash)
of it at <code class="language-plaintext highlighter-rouge">/sys/class/tpm/tpm0/null_name</code> and <strong>trusts that userspace will</strong>
<strong>attest the key later using the EK.</strong></p>

<p>This inverts the chain of trust for measured boot: the kernel is responsible for
measuring userspace, so that “bad” or “malicious” or “unintended” userspace
cannot impersonate “good” or “well-behaved” or “intended” userspace.</p>

<p>This means that all the active-interposer attacker has to do to defeat
<code class="language-plaintext highlighter-rouge">TCG_TPM2_HMAC</code> is:</p>

<ol>
  <li>Replace or hijack the userspace component responsible for checking the
Null Primary Key. Call this “Component X”.</li>
  <li>Interpose HMAC session establishment by creating a fake Null Primary Key
themselves (e.g., in software) and pretend to be the TPM responding to
requests.</li>
  <li>Intercept <code class="language-plaintext highlighter-rouge">TPM2_PCR_Extend</code> commands, replacing the measurements as desired
(e.g., replace “hash of malicious Component X” with “hash of good Component
X”).</li>
  <li>Malicious component X ignores the “wrong” Null Primary Key name at
<code class="language-plaintext highlighter-rouge">/sys/class/tpm/tpm0/null_name</code>.</li>
</ol>

<p>You can solve this problem by threat model gerrymandering: simply declare that
the active interposer adversary is not able to tamper with userspace, which is
stored on physical media less than 12 inches away from the TPM in most cases.
Note that full disk encryption using the TPM cannot save you here, because
if the booting system can fetch the key, so can the physical adversary. If you
still think you have a tamper-proof userspace at this point, ask yourself why
you need the kernel to measure it anymore.</p>

<p>Adding remote attestation also does not help here, because while a remote system
can spot-attest a “Null Primary Key”, it has no way of knowing which key the
kernel used when making its measurements.</p>

<p><code class="language-plaintext highlighter-rouge">TPM2_TCG_HMAC</code> was disabled by default again
<a href="https://lore.kernel.org/linux-integrity/20250825203223.629515-1-jarkko@kernel.org/">in August 2025</a>
<a href="https://lore.kernel.org/all/aOibAOKu_lEsSlC8@kernel.org/">starting with version 6.18</a>.</p>

<p>What lessons can we learn from all this?</p>

<ol>
  <li>
    <p><strong>Applied cryptography cannot solve a security problem. It can only convert</strong>
<strong>a security problem into a key-management problem.</strong></p>

    <p>Corollary: If you aren’t actually solving the key-management problem, your
cryptography is strictly decorative. This is not only not helpful, it is
actively harmful, because it gives users a false sense of security, leading
them to skip other precautions they would have otherwise taken.</p>
  </li>
  <li>
    <p><strong>Chains of trust are directional. Do not invert them.</strong></p>

    <p>Corollary:</p>

    <p><img src="/images/2026-01-03-jayne.jpg" alt="Jayne" /></p>

    <p style="text-align:center;"><em>
You know what the chain of trust is? It's the chain I go get and beat you
 with 'til ya understand who's trustin' who here.
 </em></p>
  </li>
  <li>
    <p><strong>Unexplainable security features are just marketing materials.</strong></p>

    <p>Corollary: While attestation protocols can be quite byzantine, they should
always boil down to 1 or more of “X checks Y against Z” and it should always
be possible to explain why X, Y, and Z are each trusted. The explanations
may lead to more X, Y, Z tuples, and this is fine, but don’t give up if your
questions aren’t being answered.</p>

    <p>Corollary 2: When someone comes along with detailed questions about something
you’re responsible for, don’t take it personally. Instead, build trust by
engaging in a good-faith discussion. You’ll either be right, and your answers
appreciated, or you’ll learn about a gap in your system you can improve.</p>
  </li>
</ol>

<p>Active physical interposer adversaries are a very real part of legitimate threat
models. You need an integrated root-of-trust in your CPU in order to solve
these. Check out <a href="https://chipsalliance.github.io/caliptra-web/">Caliptra</a>,
which provides
<a href="https://trustedcomputinggroup.org/work-groups/dice-architectures/">TCG DICE</a>
APIs from within the SoC itself as an integrated root-of-trust. This can be
used on its own, or in conjunction with a discrete TPM.</p>

<p><em>Opinions expressed here are my own and do not represent the official positions
of any employer(s) of mine, past or present</em></p>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[All encryption is end-to-end, if you’re not picky about the ends. config TCG_TPM2_HMAC bool "Use HMAC and encrypted transactions on the TPM bus" default n select CRYPTO_ECDH select CRYPTO_LIB_AESCFB select CRYPTO_LIB_SHA256 select CRYPTO_LIB_UTILS help Setting this causes us to deploy a scheme which uses request and response HMACs in addition to encryption for communicating with the TPM to prevent or detect bus snooping and interposer attacks (see tpm-security.rst). Saying Y here adds some encryption overhead to all kernel to TPM transactions. Last year, I came agross a Linux kernel feature called TCG_TPM2_HMAC. It claims to detect or prevent active and passive interposer attackers. That’s one of my sleeper agent activation phrases, so I dug in.]]></summary></entry><entry><title type="html">Attesting to the TPM’s Firmware</title><link href="https://dlp.rip/attesting-tpm-firmware" rel="alternate" type="text/html" title="Attesting to the TPM’s Firmware" /><published>2024-03-18T00:00:00+00:00</published><updated>2024-03-18T00:00:00+00:00</updated><id>https://dlp.rip/attestable-tpm</id><content type="html" xml:base="https://dlp.rip/attesting-tpm-firmware"><![CDATA[<p><img src="/images/2024-03-18-attestable-tpm.drawio.svg" alt="a buggy and a fixed TPM" /></p>

<p><a href="https://en.wikipedia.org/wiki/Murphy%27s_law">Murphy’s Law</a> says: Anything that can go wrong will go wrong.
Unfortunately, TPMs fall into the category of “anything.”</p>

<!--more-->

<p>You can usually tell embedded security people apart from not-embedded security people, because the not-embedded
security people will say things like “this is secure because the hardware does it for us” and the embedded
security people will adopt this vacant, dead expression in their eyes when the not-embedded security people
say things like that. Don’t get me wrong: the isolation of “doing it in hardware” is <strong>great</strong>, but it doesn’t
change the fact that a person had to implement it in the hardware, and people occasionally make mistakes
(just like they do when they implement things in software).
Hardware security vendors work <em>very very hard</em> to avoid these mistakes, but “don’t ever make any mistakes”
is not a very good strategy for the same reason that people buy fire alarms even though they typically don’t
plan on burning down their houses.</p>

<p>Attestation is the continual process of fixing mistakes wherever they are found, and then checking your work.
This is the superpower of a complex system with a hardware root-of-trust (such as a TPM)! Measure whatever you booted
into the root-of-trust (e.g., TPM PCRs) before you boot it, and then get the root-of-trust to provide proof
of those measurements (e.g., TPM PCR quotes). When you roll out an update to part of your system, the updated
software’s measurements will be evident in the proof that you got from the root-of-trust.</p>

<p><img src="/images/2024-03-18-measurement-chain.drawio.svg" alt="TCB and the boot sequence" /></p>

<p>What can’t be attested? The Trusted Computing Base (TCB) itself. Elements of the TCB (i.e., the Roots of Trust)
must be implicitly trusted because they are not measured (what else would measure them?) This is because of the
definition of the Trusted Computing Base:</p>

<p><strong>Trusted Computing Base (TCB)</strong>: <em>The set of components of a system that must be assumed to be trustworthy because there is no way to check them.</em> <sup id="fnref:TCB" role="doc-noteref"><a href="#fn:TCB" class="footnote" rel="footnote">1</a></sup></p>

<p>There’s about 60K lines of code in the <a href="https://github.com/trustedcomputinggroup/tpm">TPM Reference Implementation</a>.
On top of this, TPM implementations have several subsystems that all contribute to the trustworthiness of a TPM,
for example:</p>

<ul>
  <li>Clock</li>
  <li>NV (non-volatile memory)</li>
  <li>Cryptographic key generation and operations</li>
  <li>Vendor-specific functionality</li>
</ul>

<p>TPM vendors usually get it right, but occasionally, mistakes get made <sup id="fnref:ROCA" role="doc-noteref"><a href="#fn:ROCA" class="footnote" rel="footnote">2</a></sup> <sup id="fnref:TPMfail" role="doc-noteref"><a href="#fn:TPMfail" class="footnote" rel="footnote">3</a></sup> <sup id="fnref:EK" role="doc-noteref"><a href="#fn:EK" class="footnote" rel="footnote">4</a></sup>.
When a bug is identified inside the TCB, it should be fixed <sup id="fnref:9225" role="doc-noteref"><a href="#fn:9225" class="footnote" rel="footnote">5</a></sup>. Since the TCB has to be
assumed to be trustworthy, you have to ask it nicely to update itself and hope it’s not trying
to trick you. When your TCB is a Root of Trust inside a lot of remote machines (e.g., a datacenter),
it’s hard to have this hope. One attacker on one of the machines might intercept your request,
respond with “OK” (perhaps asserting the identity of the root-of-trust, using secrets it
exflitrated out of it), and then… not update the Root of Trust. As represented in the drawing
at the top of this post, both a buggy and fixed TPM have a valid EK cert. How do we check?</p>

<p>So, we decided to shrink the TCB of the TPM down to just its boot loader:</p>

<p><img src="/images/2024-03-18-tpm-tcb.drawio.svg" alt="TPM TCB" /></p>

<p>The job of the TPM’s boot loader is to validate, measure, and start up the TPM’s application firmware.
The job of the TPM’s application firmware is to do all the things you ask your TPM to do after that.</p>

<p>Note that there is no requirement that a TPM be organized in this particular way. It might have a big
monolithic firmware, or several small firmware bits; the boot loader may be one stage or several. The
key idea here is that the TCB of the TPM should be as small as possible, and be the minimal set of TPM
implementation parts that allow verifying and launching the rest of the TPM.</p>

<p>The latest version of the TPM 2.0 specification, <a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/">1.83</a>, introduces a feature called “Firmware-Limited Objects” that allows us to
shrink the TCB of the TPM in this way.</p>

<p>A TPM implementing Firmware-Limited Objects offers some new hierarchies for use with <code class="language-plaintext highlighter-rouge">TPM2_CreatePrimary</code>.
If you create an EK using <code class="language-plaintext highlighter-rouge">TPM_RH_ENDORSEMENT</code> you will get the same EK regardless of TPM firmware
version. This is why updating your TPM doesn’t destroy your EK cert. However, now (on TPMs that implement
Firmware-Limited Objects!) you can create a key based on the new <code class="language-plaintext highlighter-rouge">TPM_RH_FW_ENDORSEMENT</code>, which will <em>change</em>
if the TPM’s firmware changes (but otherwise stay the same if you don’t change the TPM’s firmware).
If the TPM provides a Firmware Endorsement Key certificate, it can
include the firmware version in the cert, and the whole (cert + key) will change between different
firmware versions. With the Firmware EK cert, you can attest the TPM after you update it by simply
challenging the EK associated with the firmware version you rolled out.
If this all sounds kind of familiar, it’s because it’s just
<a href="https://trustedcomputinggroup.org/work-groups/dice-architectures/">DICE</a> (although the TPM spec
does not require the TPM to use DICE <em>per se</em> to implement the firmware-limited hierarchy feature).</p>

<p>If you’re not familiar with DICE, that means the TPM will do something like this:</p>

<p><img src="/images/2024-03-18-tpm-dice.drawio.svg" alt="TPM TCB" /></p>

<p>The TPM’s boot loader measures the to-be-launched TPM application firmware (e.g., by hash),
then mixes that hash with a secret that it keeps (<em>BL Secret</em>) that the application firmware
never gets access to. The resulting derived secret is called the <em>FW Secret</em>, and it is mixed
into Primary Keys generated in <code class="language-plaintext highlighter-rouge">TPM_RH_FW_*</code> hierarchies. This gives us the following nice properties:</p>

<ul>
  <li>Each firmware version on a given TPM gets a fresh <em>FW Secret</em> that no other firmware on that TPM (or any other TPM) has</li>
  <li>Each firmware’s <em>FM Secret</em> is stable if the firmware is not changed</li>
</ul>

<p>This shrinks the TPM’s TCB to effectively just the TPM’s boot loader and the hardware itself: the rest of
the TPM’s firmware is measured and can be attested by using firmware-limited keys. Therefore, on
such a TPM, we can recover (remotely and at scale) from ~any conceivable bug in the TPM’s application firmware.</p>

<p>What if you want to seal data to the TPM’s firmware, and unseal your data later (potentially from
an even newer firmware)? Check out the <code class="language-plaintext highlighter-rouge">TPM_RH_SVN_*</code> hierarchies in 1.83.</p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:TCB" role="doc-endnote">
      <p>There are several conflicting definitions of “Trusted Computing Base” in the literature. The other ones tend to predate the invention of hardware Roots of Trust and are not very useful in a discussion of modern systems. I am not taking feedback on this correct opinion at this time. <a href="#fnref:TCB" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:ROCA" role="doc-endnote">
      <p><a href="https://en.wikipedia.org/wiki/ROCA_vulnerability">https://en.wikipedia.org/wiki/ROCA_vulnerability</a> <a href="#fnref:ROCA" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:TPMfail" role="doc-endnote">
      <p><a href="https://tpm.fail">https://tpm.fail</a> <a href="#fnref:TPMfail" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:EK" role="doc-endnote">
      <p><a href="https://seclists.org/fulldisclosure/2018/Jan/12">https://seclists.org/fulldisclosure/2018/Jan/12</a> <a href="#fnref:EK" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:9225" role="doc-endnote">
      <p><a href="https://datatracker.ietf.org/doc/html/rfc9225">https://datatracker.ietf.org/doc/html/rfc9225</a> <a href="#fnref:9225" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[Murphy’s Law says: Anything that can go wrong will go wrong. Unfortunately, TPMs fall into the category of “anything.”]]></summary></entry><entry><title type="html">BitLocker is Designed to Have This Problem</title><link href="https://dlp.rip/tpm-genie" rel="alternate" type="text/html" title="BitLocker is Designed to Have This Problem" /><published>2024-02-07T00:00:00+00:00</published><updated>2024-02-07T00:00:00+00:00</updated><id>https://dlp.rip/tpm-genie</id><content type="html" xml:base="https://dlp.rip/tpm-genie"><![CDATA[<p>The purpose of a system is what it does <sup id="fnref:posiwid" role="doc-noteref"><a href="#fn:posiwid" class="footnote" rel="footnote">1</a></sup>. The purpose of BitLocker is
to generate articles about interposer attacks.</p>

<div class="mermaid" id="fig:interpose">
sequenceDiagram
    participant host as Host system
    actor interposer as interposer
    participant tpm as TPM

    loop Boot
        host-&gt;&gt;tpm: Measure some software into PCRs
    end
    host-&gt;&gt;tpm: Load a sealed blob with PCR policy P
    host-&gt;&gt;tpm: Satisfy P with PolicyPCR in policy session S1
    note right of tpm: Assert that PCRs are as required by P
    rect rgb(255, 127, 127)
        host-&gt;&gt;tpm: Unseal the key using S1
    end
    note left of host: Decrypt disk and boot
    note right of interposer: Decrypt disk and<br />post on HackerNews
</div>

<!--more-->

<p><em>Disclaimer: I work on TPM at
<a href="https://trustedcomputinggroup.org/">Trusted Computing Group</a>. As always, the
views expressed in this blog are my own.</em></p>

<p>Every year or so, we see another version <sup id="fnref:genie" role="doc-noteref"><a href="#fn:genie" class="footnote" rel="footnote">2</a></sup> <sup id="fnref:trammell" role="doc-noteref"><a href="#fn:trammell" class="footnote" rel="footnote">3</a></sup> <sup id="fnref:riverloop" role="doc-noteref"><a href="#fn:riverloop" class="footnote" rel="footnote">4</a></sup>
<sup id="fnref:dolos" role="doc-noteref"><a href="#fn:dolos" class="footnote" rel="footnote">5</a></sup> <sup id="fnref:stacksmashing" role="doc-noteref"><a href="#fn:stacksmashing" class="footnote" rel="footnote">6</a></sup> of a paper called “I Hacked
BitLocker in 25 Seconds With This Soldering Iron and the Power of Friendship”.</p>

<p>And every year, I assume a conversation like this occurs in Redmond:</p>

<blockquote>
  <p>“Should we fix BitLocker to deal with this issue?”</p>
</blockquote>

<blockquote>
  <p>“No, we (picked our threat model 20 years ago|are too afraid of the code|sacked
the people responsible for that so the execs could get bigger bonuses last year).”</p>
</blockquote>

<p>Invariably, the telephone game that is tech journalism and social media pollutes
the conversation, and people end up asking “why does the TPM suck so much?”
(Answer: It’s not the TPM that sucks in this situation).</p>

<p>Security is about solving threat models, not extra credit. People (e.g., I) have
asked the BitLocker team to update their threat model in the past. They haven’t so far.
They probably won’t this year, or next time this topic hits my feed. So I am going to
assume that BitLocker will never change. But here is how a full-disk encryption
system like BitLocker could use the TPM properly. If Microsoft wants to hire an
intern to finally fix BitLocker’s threat model some summer, that would be cool too.</p>

<h1 id="the-problem">The Problem</h1>

<p>See the <a href="#fig:interpose">diagram at the top of the post</a>.</p>

<p>The naive implementation of a system that uses the TPM to unseal a disk
encryption key causes the disk
encryption key to appear in-the-clear on the TPM bus (typically,
<a href="https://en.wikipedia.org/wiki/Low_Pin_Count">LPC</a> or
<a href="https://en.wikipedia.org/wiki/Serial_Peripheral_Interface">SPI</a>). It’s not
hard to read the 1s and 0s on these wires and convert them back into data.
And nobody assumed that it would be. The TPM may as well be shouting the
BitLocker key back to the CPU.</p>

<p>So, all these papers about breaking BitLocker are really about parsing LPC or
SPI framed TPM commands.
There isn’t, like, cryptography or anything happening here.</p>

<h1 id="the-solution">The Solution</h1>

<p>So, how can a full-disk encryption product protect itself from sophisticated
attackers with access to a Raspberry Pi?</p>

<p>In terms of applied crypto toolboxes and the threat model, we would say that we
want the host and the TPM to perform some sort of
<a href="https://en.wikipedia.org/wiki/Key_exchange">key establishment</a> and then use
that key to protect the secrets that have to traverse the bus.</p>

<p>Since this particular threat model includes a passive interposer who can read
the bus but not modify/drop commands/responses, any type of key establishment
would help here. Let’s say that we use
<a href="https://en.wikipedia.org/wiki/Key_encapsulation_mechanism">Key Encapsulation</a>
as our key-agreement mechanism.</p>

<p>TPM supports TPM-specific RSA and ECC KEMs,
designed to allow use of a restricted decryption key for this use-case,
though without using the term “KEM” as that wasn’t popular until NIST’s
PQC competition despite being apparently coined in <a href="https://eprint.iacr.org/2001/108.pdf">2001</a>.</p>

<div class="mermaid">
sequenceDiagram
    participant host as Host system
    actor interposer as interposer
    participant tpm as TPM

    loop Boot
        host-&gt;&gt;tpm: Measure some software into PCRs
    end
    host-&gt;&gt;tpm: Load a sealed blob with PCR policy P
    host-&gt;&gt;tpm: Satisfy P with PolicyPCR in policy session S1
    note right of tpm: Assert that PCRs are as required by P
    rect rgb(127, 255, 255)
    critical Key exchange
        host-&gt;&gt;tpm: Generate an asymmetric keypair K and get Kpub
        host-&gt;&gt;tpm: Encapsulate a session key SK for S2 to the TPM Kpub
    end
    end
        rect rgb(127, 255, 127)
        host-&gt;&gt;tpm: Unseal the key using S1 <br /> using S2 for response parameter encryption
        host-&gt;&gt;host: Decrypt the unsealed key using SK
    end
    note left of host: Decrypt disk and boot
</div>

<h1 id="the-work">The Work</h1>

<ol>
  <li>Create a <code class="language-plaintext highlighter-rouge">restricted</code> ECDH <code class="language-plaintext highlighter-rouge">decrypt</code> key in the TPM during boot (do not use RSA unless you aren’t in a hurry to boot).</li>
  <li>Perform the encapsulation to create an additional encrypt session for the unseal command.</li>
  <li>Decrypt the encrypted response parameter using the key you encapsulated in (2).</li>
</ol>

<h1 id="the-future">The Future</h1>

<p>Interested in <strong>active</strong> interposers? Now you’re thinking with interposers. You
need some way for the host and the TPM to mutually authenticate.
Check out what my colleagues and I are working on: <a href="https://www.osfc.io/2022/talks/protecting-tpm-commands-from-active-interposers/">https://www.osfc.io/2022/talks/protecting-tpm-commands-from-active-interposers/</a></p>

<div class="footnotes" role="doc-endnotes">
  <ol>
    <li id="fn:posiwid" role="doc-endnote">
      <p><a href="https://en.wikipedia.org/wiki/The_purpose_of_a_system_is_what_it_does">https://en.wikipedia.org/wiki/The_purpose_of_a_system_is_what_it_does</a> <a href="#fnref:posiwid" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:genie" role="doc-endnote">
      <p><a href="https://research.nccgroup.com/2018/03/09/tpm-genie-interposer-attacks-against-the-trusted-platform-module-serial-bus/">https://research.nccgroup.com/2018/03/09/tpm-genie-interposer-attacks-against-the-trusted-platform-module-serial-bus/</a> <a href="#fnref:genie" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:trammell" role="doc-endnote">
      <p><a href="https://conference.hitb.org/hitbsecconf2019ams/materials/D1T1%20-%20Toctou%20Attacks%20Against%20Secure%20Boot%20-%20Trammell%20Hudson%20&amp;%20Peter%20Bosch.pdf">https://conference.hitb.org/hitbsecconf2019ams/materials/D1T1%20-%20Toctou%20Attacks%20Against%20Secure%20Boot%20-%20Trammell%20Hudson%20&amp;%20Peter%20Bosch.pdf</a> <a href="#fnref:trammell" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:riverloop" role="doc-endnote">
      <p><a href="https://riverloopsecurity.com/blog/2021/01/ieee-paine-tpm-lpc-bus-implant/">https://riverloopsecurity.com/blog/2021/01/ieee-paine-tpm-lpc-bus-implant/</a> <a href="#fnref:riverloop" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:dolos" role="doc-endnote">
      <p><a href="https://dolosgroup.io/blog/2021/7/9/from-stolen-laptop-to-inside-the-company-network">https://dolosgroup.io/blog/2021/7/9/from-stolen-laptop-to-inside-the-company-network</a> <a href="#fnref:dolos" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
    <li id="fn:stacksmashing" role="doc-endnote">
      <p><a href="https://www.tomshardware.com/pc-components/cpus/youtuber-breaks-bitlocker-encryption-in-less-than-43-seconds-with-sub-dollar10-raspberry-pi-pico">https://www.tomshardware.com/pc-components/cpus/youtuber-breaks-bitlocker-encryption-in-less-than-43-seconds-with-sub-dollar10-raspberry-pi-pico</a> <a href="#fnref:stacksmashing" class="reversefootnote" role="doc-backlink">&#8617;</a></p>
    </li>
  </ol>
</div>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[The purpose of a system is what it does 1. The purpose of BitLocker is to generate articles about interposer attacks. sequenceDiagram participant host as Host system actor interposer as interposer participant tpm as TPM loop Boot host-&gt;&gt;tpm: Measure some software into PCRs end host-&gt;&gt;tpm: Load a sealed blob with PCR policy P host-&gt;&gt;tpm: Satisfy P with PolicyPCR in policy session S1 note right of tpm: Assert that PCRs are as required by P rect rgb(255, 127, 127) host-&gt;&gt;tpm: Unseal the key using S1 end note left of host: Decrypt disk and boot note right of interposer: Decrypt disk andpost on HackerNews https://en.wikipedia.org/wiki/The_purpose_of_a_system_is_what_it_does &#8617;]]></summary></entry><entry><title type="html">The Brick Test</title><link href="https://dlp.rip/brick" rel="alternate" type="text/html" title="The Brick Test" /><published>2023-10-01T00:00:00+00:00</published><updated>2023-10-01T00:00:00+00:00</updated><id>https://dlp.rip/brick</id><content type="html" xml:base="https://dlp.rip/brick"><![CDATA[<p><img src="/images/2023-10-01-brick-fire.png" alt="burning-brick" /></p>

<p>I’d like to propose a simple test for any definition of security success: <strong>the Brick Test</strong>.</p>

<!--more-->

<p>As Security Team people, we tend to evaluate our own work in terms of prevention. Under a certain threat model:
the secrets are prevented from being exfiltrated; the messages are prevented against being forged; the company
property is prevented against being misused.</p>

<p>If we’re being good engineers about it, we usually try to agree on a definition of success before we start:
otherwise it’s not clear when we’re done or if we’ve done anything at all. Knowing what success looks like can
also help us avoid starting unrealistic projects.</p>

<p>What should be considered in the definition of success? Certainly all the things we want to prevent (and the
threat models under which they should be prevented), as discussed above. But what else?</p>

<p>The Brick Test asks just one question:</p>

<p><strong>could a standard brick (i.e., a bit of masonry, any color, heavy enough to break glass when propelled) satisfy your definition of success?</strong></p>

<p>If success is “the user’s disk can’t be decrypted by an attacker,” then a brick could achieve success more more
permanently and completely than a whole team of very smart engineers. Bricks are also considered post-quantum.</p>

<p>If a brick can achieve success, it’s probably cheaper than you are. In this case, re-think your definition of
success! (Or, shut down the project, job’s done!)</p>

<p><em>Corollary: any system that uses machine learning to satisfy criteria that fail the Brick Test is called “BrickGPT.”</em></p>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[Defining success carefully]]></summary></entry><entry><title type="html">TPM Carte Blanche-resistant Boot Attestation</title><link href="https://dlp.rip/tcb-attestation" rel="alternate" type="text/html" title="TPM Carte Blanche-resistant Boot Attestation" /><published>2021-10-25T00:00:00+00:00</published><updated>2021-10-25T00:00:00+00:00</updated><id>https://dlp.rip/tcb-attestation</id><content type="html" xml:base="https://dlp.rip/tcb-attestation"><![CDATA[<p><img src="/images/2021-10-25-tcb-attestation.svg" alt="tcb-demo" /></p>

<!--more-->

<p>As I mentioned <a href="/tpm-carte-blanche">last week</a>, your TCB is important and
without a good one, your capabilities are quite limited when it comes to
attestation.</p>

<p>So, all right, we now live in a world where we know bugs like
<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-42299">TPM Carte Blanche</a>
exist, and we can never go back to the world where it doesn’t. (Actually, we’ve
been living in that world since
<a href="https://en.wikipedia.org/wiki/Surface_Pro_3">2014</a>.) So what do we do now?</p>

<p>The best thing to do is probably to find a hardware
<a href="https://www.ieee802.org/1/files/public/docs2004/af-congdon-tcg-overview-1104.pdf">Root of Trust for Measurement</a>.
Your CPU vendor may have some ideas, but a non-exhaustive list probably doesn’t
leave out
<a href="https://www.ieee802.org/1/files/public/docs2004/af-congdon-tcg-overview-1104.pdf">Boot Guard</a>
or <a href="https://www.amd.com/en/technologies/pro-security">Platform Secure Boot</a>.</p>

<p>What about the millions of devices out there in the world today that don’t have
a hardware RTM? Well, it turns out TPM 2.0 has some uncommonly-used features
that can come in handy here.</p>

<p>TPM 2.0 has a feature called audit sessions, which you can read about in Part
1 of <a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/">the TPM 2.0 specification</a>.
Audit sessions serve two purposes:</p>

<ul>
  <li>An audit session with a session key (set up by “salting” or “binding” the
session) causes the TPM to use that key to calculate an HMAC over its response.</li>
  <li>An audit session digest can be explicitly attested by an Attestation Key for
consumption by remote verifiers, using a command called
<code class="language-plaintext highlighter-rouge">TPM2_GetSessionAuditDigest</code>.</li>
</ul>

<p>When you set up an audit session, it gets initialized with an “empty digest”
(a buffer of all <code class="language-plaintext highlighter-rouge">0x00</code> bytes the size of the session hash algorithm like a
PCR). Each time you send a command in the audit session, the session is
extended:</p>

\[commandParameterHash = hash(\hspace{0.5em}commandCode\hspace{0.5em}\| \hspace{0.5em}names\hspace{0.5em} \| \hspace{0.5em}commandParams\hspace{0.5em})\]

<p>where \(commandCode\) is the command code constant associated with the command,
\(names\) is the concatenation of all authorized TPM Names (TPM object
unique identifiers; hash of public area or primary seed handle value) used in
the command, and \(commandParams\) is the concatenation of all the command
parameters.</p>

\[responseParamHash = hash(\hspace{0.5em}responseCode\hspace{0.5em} \| \hspace{0.5em}commandCode\hspace{0.5em} \| \hspace{0.5em}responseParams\hspace{0.5em})\]

<p>where \(responseCode\) is a <code class="language-plaintext highlighter-rouge">TPM_RC</code> value and \(responseParams\) is the
concatenation of all the response parameters.</p>

\[digest_{new} = hash(\hspace{0.5em}digest_{old}\hspace{0.5em} \| \hspace{0.5em}commandParameterHash\hspace{0.5em} \| \hspace{0.5em}responseParameterHash\hspace{0.5em})\]

<p>These formulas can be combined into higher-layer logic that lets a verifier
plug in expected command/response invocations, like in
<a href="https://github.com/chrisfenner/tcb-attestation/blob/ebec77fcfa8531a256f67bd77e2cc5447e07112d/attestation_test.go#L242-L262">this example</a>:</p>

<div class="language-go highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">audit</span> <span class="o">:=</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">NewAudit</span><span class="p">(</span><span class="n">tpm2</span><span class="o">.</span><span class="n">TPMAlgSHA256</span><span class="p">)</span>
<span class="n">getCapCmd</span> <span class="o">:=</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">GetCapabilityCommand</span><span class="p">{</span>
	<span class="n">Capability</span><span class="o">:</span>    <span class="n">tpm2</span><span class="o">.</span><span class="n">TPMCapPCRs</span><span class="p">,</span>
	<span class="n">Property</span><span class="o">:</span>      <span class="m">0</span><span class="p">,</span>
	<span class="n">PropertyCount</span><span class="o">:</span> <span class="m">1</span><span class="p">,</span>
<span class="p">}</span>
<span class="n">getCapRsp</span> <span class="o">:=</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">GetCapabilityResponse</span><span class="p">{</span>
	<span class="n">MoreData</span><span class="o">:</span> <span class="no">false</span><span class="p">,</span>
	<span class="n">CapabilityData</span><span class="o">:</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">TPMSCapabilityData</span><span class="p">{</span>
		<span class="n">Capability</span><span class="o">:</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">TPMCapPCRs</span><span class="p">,</span>
		<span class="n">Data</span><span class="o">:</span> <span class="n">tpm2</span><span class="o">.</span><span class="n">TPMUCapabilities</span><span class="p">{</span>
			<span class="n">AssignedPCR</span><span class="o">:</span> <span class="o">&amp;</span><span class="n">quote</span><span class="o">.</span><span class="n">Attested</span><span class="o">.</span><span class="n">Quote</span><span class="o">.</span><span class="n">PCRSelect</span><span class="p">,</span>
		<span class="p">},</span>
	<span class="p">},</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">err</span> <span class="o">:=</span> <span class="n">audit</span><span class="o">.</span><span class="n">Extend</span><span class="p">(</span><span class="o">&amp;</span><span class="n">getCapCmd</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">getCapRsp</span><span class="p">);</span> <span class="n">err</span> <span class="o">!=</span> <span class="no">nil</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">err</span>
<span class="p">}</span>
<span class="k">if</span> <span class="o">!</span><span class="n">bytes</span><span class="o">.</span><span class="n">Equal</span><span class="p">(</span><span class="n">auditAttest</span><span class="o">.</span><span class="n">Attested</span><span class="o">.</span><span class="n">SessionAudit</span><span class="o">.</span><span class="n">SessionDigest</span><span class="o">.</span><span class="n">Buffer</span><span class="p">,</span> <span class="n">audit</span><span class="o">.</span><span class="n">Digest</span><span class="p">())</span> <span class="p">{</span>
	<span class="k">return</span> <span class="n">fmt</span><span class="o">.</span><span class="n">Errorf</span><span class="p">(</span><span class="s">"invalid audit digest"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>

<p>In the above example, a test verifier checks to make sure that the audited
session proves that the caller called <code class="language-plaintext highlighter-rouge">TPM2_GetCapability(TPM_CAP_PCRS)</code> and
got back the same set of PCR banks they quoted with <code class="language-plaintext highlighter-rouge">TPM2_Quote</code> (see
<a href="https://github.com/chrisfenner/tcb-attestation/blob/main/attestation_test.go#L212-L225">this</a>
part of the same test).</p>

<p>This allows the verifier to establish the following web of trust:</p>

<ol>
  <li>The AK is trusted because the caller provided
<a href="https://github.com/google/security-research/blob/master/pocs/bios/tpm-carte-blanche/writeup.md#appendix-b-reverse-engineering-aik-service">an AK certificate</a>.</li>
  <li>The two things signed by the AK, the PCR quote and session audit digest,
are trusted because the AK is trusted in (1)</li>
  <li>The <code class="language-plaintext highlighter-rouge">TPML_PCR_SELECTION</code> quoted in the PCR quote is trusted because the
signed audit log digest trusted in (2) is the digest of the well-known
<code class="language-plaintext highlighter-rouge">TPM2_GetCapability</code> command with the TPM returning the same PCR selection in
response.</li>
  <li>The preimage PCR values are trusted because the quote is trusted in (2)
and we know from (3) that the quote covers all active PCR banks.</li>
  <li>The boot log is trusted because when replayed it results in the preimage
PCR values trusted in (4).</li>
</ol>

<p>Attesting a PCR quote, <strong>along with a reliable signal that there are no other
active PCR banks on the system</strong>, helps defend against BIOS bugs where some of
the PCR banks are left uncapped, by allowing the verifier to either mandate
that every event in the boot log is measured into every PCR bank, or (more
simply) that the device is configured correctly and only using a single PCR
bank (e.g., SHA256). Because there’s not actually a good reason to have
some of the software measuring into the SHA1 bank and other software
measuring into the SHA256 bank.</p>

<p>This still leaves open other types of BIOS bugs, for instance a bug where the
SHA384 PCR bank is just never extended even if it is the only active bank.
(BIOS writers: please just use <code class="language-plaintext highlighter-rouge">TPM2_PCR_Event</code> to make the TPM do all the
hashing and automagically populate every PCR.)</p>

<p>For a more complete example of this type of PCR attestation, please see
<a href="https://github.com/chrisfenner/tcb-attestation">https://github.com/chrisfenner/tcb-attestation</a>.</p>

<p>For more information, see last week’s <a href="/tpm-carte-blanche">blog post</a> about TPM
Carte Blanche.</p>

<p><em>Opinions expressed here are my own and do not represent the official positions
of any employer(s) of mine, past or present</em></p>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[Some tips for verifiers]]></summary></entry><entry><title type="html">TPM Carte Blanche</title><link href="https://dlp.rip/tpm-carte-blanche" rel="alternate" type="text/html" title="TPM Carte Blanche" /><published>2021-10-18T00:00:00+00:00</published><updated>2021-10-18T00:00:00+00:00</updated><id>https://dlp.rip/tpm-carte-blanche</id><content type="html" xml:base="https://dlp.rip/tpm-carte-blanche"><![CDATA[<p><img src="/images/2021-10-18-tcb.webp" alt="tcb-demo" /></p>

<!--more-->

<p>I like to tinker with the
<a href="https://trustedcomputinggroup.org/resource/tpm-library-specification/">TPM</a>
in my spare time. It’s like a great big box of security legos, or like a
dryer, punk-er form of Minecraft. It’s pretty fun.</p>

<p>In 2017 I had the privilege of working on mitigations for an issue called
<a href="https://en.wikipedia.org/wiki/ROCA_vulnerability">ROCA</a>. From then on, I’ve
been fascinated by the idea of the
<a href="https://en.wikipedia.org/wiki/Trusted_computing_base">Trusted Computing Base</a>.</p>

<p>I believe that <a href="https://en.wikipedia.org/wiki/Murphy%27s_law">Murphy’s Law</a>
applies equally to code as it does to which way buttered toast will fall, or
whether two intersecting clues in the New York Times crossword will be unusual
names of minor celebrities from the 1970’s. The meaning of the TCB isn’t so
much “your system is safe because of this smart stuff in this box” as it is
“your system is utterly booched <del>if</del> when we find any important mistakes in this
box”.</p>

<p>Also, the amount of mistakes we know about in any given box is a monotonically
increasing function of time. Nobody says “good news, we’ve discovered some
unexpectedly correct behavior in your kernel.”</p>

<p>I recently came into the possession of a Surface Pro 3, which is a machine that
I happen to know shipped with TPMs affected by ROCA. I thought “ah, this is my chance
to apply my superficial understanding of finite-field arithmetic to learn some
more about this bug and how it was discovered.” So, I installed Linux on it,
<code class="language-plaintext highlighter-rouge">ssh</code>ed in, and installed some TPM tools. My go-to “hello world” TPM tool is
<a href="https://github.com/google/go-tpm-tools"><code class="language-plaintext highlighter-rouge">gotpm</code></a> and reading the
<a href="https://docs.microsoft.com/en-us/windows/security/information-protection/tpm/switch-pcr-banks-on-tpm-2-0-devices">PCRs</a>
is a pretty basic TPM activity that lets you know you’re talking to a TPM.</p>

<p>So, when I was greeted with these PCRs, I knew that obviously I had made a
mistake, and I was talking to some simulated, shim TPM or something:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: 0000000000000000000000000000000000000000000000000000000000000000
1: 0000000000000000000000000000000000000000000000000000000000000000
2: 0000000000000000000000000000000000000000000000000000000000000000
3: 0000000000000000000000000000000000000000000000000000000000000000
4: 0000000000000000000000000000000000000000000000000000000000000000
5: 0000000000000000000000000000000000000000000000000000000000000000
6: 0000000000000000000000000000000000000000000000000000000000000000
7: 0000000000000000000000000000000000000000000000000000000000000000
8: 0000000000000000000000000000000000000000000000000000000000000000
9: 0000000000000000000000000000000000000000000000000000000000000000
10: 52dafc83858586083a5b09d80f4c75e77180691ee717d30bc07194713d5884b3
11: 0000000000000000000000000000000000000000000000000000000000000000
12: 0000000000000000000000000000000000000000000000000000000000000000
13: 0000000000000000000000000000000000000000000000000000000000000000
14: 0000000000000000000000000000000000000000000000000000000000000000
15: 0000000000000000000000000000000000000000000000000000000000000000
16: 0000000000000000000000000000000000000000000000000000000000000000
17: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
18: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
19: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
20: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
21: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
22: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
23: 0000000000000000000000000000000000000000000000000000000000000000
</code></pre></div></div>

<p>Except I wasn’t. This was my real TPM. For a sense of which of these rows of
nonsense are supposed to not all be 0’s or f’s, see the SHA1 bank:</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>0: 3dcaea25dc86554d94b94aa5bc8f735a49212af8
1: b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
2: b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
3: b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
4: 27b2c869333dbe59c520294ffb652964da78b7ce
5: fe1b29141dcded019fe3423df304b5676c6c58d6
6: b2a83b0ebf2f8374299a5b2bdfc31ea955ad7236
7: 03e7b21f363721d4a04a550602c0742291f735b4
8: c7e3ff980c58cd67bc554519847b585de6b9bd33
9: 6167364dca8c424a2f5b1a9b3df5f121ddcfab4b
10: e13d0ffa292ef1e530005679978a0c5aae8967d3
11: 0000000000000000000000000000000000000000
12: 0000000000000000000000000000000000000000
13: 0000000000000000000000000000000000000000
14: 0000000000000000000000000000000000000000
15: 0000000000000000000000000000000000000000
16: 0000000000000000000000000000000000000000
17: ffffffffffffffffffffffffffffffffffffffff
18: ffffffffffffffffffffffffffffffffffffffff
19: ffffffffffffffffffffffffffffffffffffffff
20: ffffffffffffffffffffffffffffffffffffffff
21: ffffffffffffffffffffffffffffffffffffffff
22: ffffffffffffffffffffffffffffffffffffffff
23: 0000000000000000000000000000000000000000
</code></pre></div></div>

<p>This is pretty bad, because a bank of empty PCRs means an <del>attacker</del> researcher
can just boot up a custom OS that doesn’t make any measurements, and use
<a href="https://github.com/google/security-research/tree/master/pocs/bios/tpm-carte-blanche/cmd/dhatool">simple tools</a>
to extend whatever they want into those PCRs and attest them honestly (from the
TPM’s point of view).</p>

<p>So, what prevents this happening normally, on all of the computers all of the
time? Well, theoretically your <a href="https://en.wikipedia.org/wiki/BIOS">BIOS</a> has
a well-behaved, immutable initial boot block that always makes proper
measurements into the TPM, and whenever it fails to make proper measurements
into the TPM it creates a small wormhole and sucks itself and your
<a href="https://docs.microsoft.com/en-us/windows/security/information-protection/bitlocker/bitlocker-overview">BitLocker</a>
key into the <a href="https://en.wikipedia.org/wiki/Negative_Zone">Negative Zone</a> or,
like, North Dakota, or something.</p>

<p>It turns out the code in your initial boot block is more or less like code you
might find elsewhere in your computer, and you should not assume it’s better
just because it’s more important.</p>

<p>I have three takeaways from working on this project:</p>

<ol>
  <li>Building a <a href="https://firmwaresecurity.com/tag/measured-boot/">measured</a>
and/or
<a href="https://superuser.com/questions/1360485/what-is-the-difference-between-secure-boot-and-verified-boot/1361267">verified aka Secure</a>
boot attestation system without considering the TCB is like building an ice
sculpture on top of a dumpster full of matches. Like, chock full, to the brim
of matches. And it’s in Oklahoma in July. Your plans are neat and also doomed.</li>
  <li>People who build security frameworks should prefer simplicity over elegance
and flexibility. It’s easier not to notice one of the PCR banks hasn’t been
<a href="https://trustedcomputinggroup.org/wp-content/uploads/PC-ClientSpecific_Platform_Profile_for_TPM_2p0_Systems_v21.pdf">capped</a>
by the firmware when there are N banks of them (one per hash algorithm) and all
the software running on the system can interact with whichever bank suits its
delicate, not-crypto-agile preferences. If there were only ever one bank of PCR
active at a time, it would be harder to miss a bug like this. (Note I didn’t
say it would be impossible.)</li>
  <li>The best place for TCB is in the immutable
<a href="https://en.wikipedia.org/wiki/Read-only_memory">ROM</a> of something that’s very
hard to probe, swap, or tamper. Code in ROM should measure/verify “mutable”
code (and by “mutable” I really mean “your threat model shouldn’t assume your
adversary can’t afford
<a href="https://www.dediprog.com/category/spi-flash-solution">one of these</a>”). This
step should be as simple as possible, with respect for the fact that if it’s
broken you may need to throw away hardware. TCG
<a href="https://trustedcomputinggroup.org/resource/dice-attestation-architecture/">DICE</a>
is an example of up-and-coming new hotness in this area.</li>
</ol>

<p>Microsoft’s <a href="https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-42299">advisory</a>
was published on 2021-10-18.</p>

<p>For more information, see the full writeup at
<a href="https://github.com/google/security-research/blob/master/pocs/bios/tpm-carte-blanche/writeup.md">google/security-research</a>.</p>

<p><em>Opinions expressed here are my own and do not represent the official positions
of any employer(s) of mine, past or present</em></p>]]></content><author><name>Chris Fenner</name></author><summary type="html"><![CDATA[The true TCB of self-measured BIOS]]></summary></entry></feed>