DEV Community

Cover image for Post-Quantum Crypto Meets a Non-Extractable TPM Key
Alex
Alex

Posted on AI-assisted

Post-Quantum Crypto Meets a Non-Extractable TPM Key

Post-Quantum Crypto Meets a Non-Extractable TPM Key

An engineering story about combining two useful properties without pretending either one solves the whole problem.

I ran into an interesting design problem while working on device-bound encryption.

I wanted two things at once:

  • a key-establishment path designed to withstand future quantum-capable attackers;
  • a device identity whose private P-256 key stays inside the machine's TPM.

Each goal has a familiar tool. Getting both into one key-establishment flow is where the details start to matter.

The part the TPM already did well

On Windows, the Microsoft Platform Crypto Provider can use a TPM for cryptographic operations. With a suitable key configuration, an application can ask the provider to perform an operation without exporting the private key material into the application.

For an ECDH P-256 key, the rough idea is:

                 Windows application
                         |
                  request ECDH
                         |
                         v
                 Platform Crypto Provider
                         |
                         v
                 TPM-held P-256 key
                         |
                         v
                  shared secret bytes
Enter fullscreen mode Exit fullscreen mode

The application works with a key handle and an operation result. It does not need to copy the private scalar out of the TPM.

That is useful for device binding. Copying the application's files to another machine does not, by itself, copy the TPM-held private key too.

But P-256 is classical elliptic-curve cryptography. A sufficiently capable quantum computer would change the assumptions behind it. I wanted a post-quantum component in the design as well.

ML-KEM brought a different strength

NIST standardized ML-KEM in FIPS 203. ML-KEM-768 lets one party encapsulate a shared secret to a public key; the other party decapsulates it with the corresponding private key.

Sender                                      Recipient
------                                      ---------
ML-KEM public key                           ML-KEM private key
       |                                           |
 encapsulate                                  decapsulate
       |                                           |
       +-- ciphertext ----------------------------+
       +-- shared secret --------------------------+
Enter fullscreen mode Exit fullscreen mode

That gives the design a post-quantum component. It does not make the ML-KEM private key hardware-bound: in this design, that private component is still managed by software and can be copied if the machine or process exposes it.

So the two mechanisms had complementary properties:

Component Intended contribution Where the private component lives
ML-KEM-768 Post-quantum key establishment Software-managed key material
P-256 ECDH Device-bound classical contribution TPM-backed key handle and operation

I wanted both contributions to matter to the final shared secret.

Why two independent recovery slots are not a hybrid

A tempting design is to wrap the same content key twice: once for ML-KEM and once for P-256, then let either path recover it.

                         +-- ML-KEM path --+
Encrypted content key --|                 |--> recovered key
                         +-- P-256 path ---+
Enter fullscreen mode Exit fullscreen mode

That is two alternative ways in. If either path is enough on its own, the overall result can be limited by whichever path is easier to defeat. It does not require an attacker to break both components.

The design I wanted instead has both components feed one hybrid key-establishment construction:

ML-KEM decapsulation ---- shared secret A --+
                                             +--> specified hybrid KDF --> final secret
TPM ECDH operation ------- shared secret B --+
Enter fullscreen mode Exit fullscreen mode

This is the point where I did not want to invent a combiner from intuition. A line such as hash(secret_a || secret_b) looks simple, but a secure construction also has to define input ordering, labels, transcript binding, failure behavior, and the security model.

The concrete MLKEM768-P256 construction in the CFRG hybrid-KEM draft specifies how these pieces fit together. It uses ML-KEM-768 and P-256 with a defined combiner and domain-separation label. The draft's current wire sizes are 1,249 bytes for the encapsulation key and 1,153 bytes for the ciphertext.

Those are larger than a plain P-256 exchange. For a file-encryption envelope, the extra bytes are usually less important than getting the composition right.

Keeping the P-256 operation inside the TPM

The implementation challenge was to preserve the TPM boundary while following the hybrid construction.

Conceptually, the recipient side looks like this:

// Simplified pseudocode, not a drop-in implementation.
let pq_secret = mlkem768_decapsulate(
    &mlkem_private_key,
    &mlkem_ciphertext,
)?;

let p256_secret = platform_provider.ecdh(
    tpm_key_handle,
    &ephemeral_p256_public_key,
)?;

let final_secret = specified_hybrid_kem_combine(
    pq_secret,
    p256_secret,
    &encapsulation_context,
)?;
Enter fullscreen mode Exit fullscreen mode

The exact inputs and encoding must come from the selected specification and implementation. The architectural boundary is the useful part to see: software handles the ML-KEM operation; the platform provider asks the TPM to do ECDH; the hybrid construction consumes both results.

There is no step that exports the TPM's P-256 private scalar. The TPM returns the result of an operation, not the key itself.

The keys need independent origins

A hybrid design can quietly lose its point if one private component is derived from the other.

For example, deriving an ML-KEM key from the P-256 secret might look convenient:

P-256 secret --> KDF --> ML-KEM private key
Enter fullscreen mode Exit fullscreen mode

But then a failure that exposes the classical secret could also expose the post-quantum key derived from it. Two algorithms would not mean two independent failure domains.

The key material should be generated independently, with the right randomness and key-generation procedure for each component:

independent random input --> ML-KEM key pair
TPM key generation       --> P-256 key pair
Enter fullscreen mode Exit fullscreen mode

The exact lifecycle, backup, and recovery rules still need to be designed for the product. Independence at key generation is one necessary piece, not a complete lifecycle policy.

What “hybrid” does and does not promise

It is easy to summarize a hybrid as “if one algorithm breaks, we're safe.” That is too broad without assumptions.

The intended benefit is that recovering the final secret should require defeating the specified hybrid construction, rather than merely finding either of two independent unwrap paths. The exact claim depends on the construction, correct implementation, key independence, and the attacker's access to both components over time.

For example, if an attacker copies the software ML-KEM private key today and P-256 becomes breakable later, the two events can combine.

A TPM boundary raises the bar for copying the classical private key, but it does not erase every possible compromise path or make stored data invulnerable forever.

And “TPM-backed” does not mean ML-KEM runs inside the TPM. In this design it does not. The software-held ML-KEM key remains a software secret; the hybrid makes the TPM-held operation another required component of the specified key-establishment flow.

Testing the boundary, not just the math

Cryptographic test vectors and a real TPM exercise answer different questions.

  • Known-answer tests can check that an implementation matches expected algorithm outputs.
  • A Windows integration test can check that the configured provider and key handle actually perform the P-256 operation through the expected platform path on that machine.

The second check matters. A build that references a TPM API is not proof that the operation reached a hardware-backed key. In one recorded live kem 5 check, the hybrid approval flow ran against a real TPM and passed 1/1 on a single machine. That is useful evidence that this path reached hardware in that setup.

It is not hardware certification. Vendor EK attestation and multi-machine hardware behavior remain unverified, and one successful run does not establish that every TPM, firmware, provider configuration, or Windows release behaves identically. The claim should stay scoped to what was actually tested.

The result: two failure domains, one specified construction

The useful mental model is not “post-quantum TPM.” It is two independently managed components brought together by a defined hybrid KEM:

software-held ML-KEM component ----+
                                    +--> specified hybrid secret --> envelope key
TPM-held P-256 operation ----------+
Enter fullscreen mode Exit fullscreen mode

ML-KEM contributes the post-quantum side. The TPM-held key contributes a device-bound operation. Neither label should be stretched into a claim that the other component has moved into that boundary.

The lesson for me was less about picking a fashionable algorithm and more about preserving the properties we actually wanted: independent key material, a non-exportable device key, and a combiner designed and specified as a whole.

When two primitives are meant to protect against different failures, the composition is the security design. The diagram is the easy part.

References


Tags: #security #windows #rust #systemsprogramming

This is an engineering narrative, not a claim of certification or a substitute for a review of a concrete implementation.

Top comments (0)