<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Parag Mali - tag: cryptography</title><description>Posts tagged cryptography.</description><link>https://paragmali.com/</link><language>en-US</language><lastBuildDate>Thu, 18 Jun 2026 02:11:07 GMT</lastBuildDate><atom:link href="https://paragmali.com/tags/cryptography/rss.xml" rel="self" type="application/rss+xml"/><item><title>Rotating Every Cipher: SChannel and the Twenty-Year Algorithm-Agility Story of Windows TLS</title><link>https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm/</link><guid isPermaLink="true">https://paragmali.com/blog/rotating-every-cipher-schannel-and-the-twenty-year-algorithm/</guid><description>How one Windows DLL rotated every TLS primitive from RC4 to ML-KEM without breaking IIS, RDP, SQL Server, or .NET SslStream -- and why Vista&apos;s 2007 CNG was the inflection point.</description><pubDate>Wed, 03 Jun 2026 00:00:00 GMT</pubDate><content:encoded>
Windows speaks TLS through **SChannel**, the SSPI provider in `schannel.dll` [@ms-learn-schannel-ssp]. Across roughly twenty years SChannel has rotated every cryptographic primitive in its default cipher list -- from RSA key transport and RC4 to ECDHE, AES-GCM, and ML-KEM -- without breaking IIS, RDP, SQL Server, LDAPS, WinHTTP, or .NET `SslStream`. That was only possible because Microsoft, in Vista&apos;s 2007 **CNG** (Cryptography API: Next Generation), made algorithm agility a first-class architectural property [@ms-learn-cng-portal]: BCrypt for primitive dispatch, NCrypt for key custodians, SymCrypt as the unified FIPS-validated backend [@symcrypt-github]. This article walks the substrate from CryptoAPI 1.0 through CNG and SymCrypt, the five cipher-suite generations the substrate carried, the 2014 MS14-066 / WinShock RCE (which was *not* Heartbleed) [@ms14-066], the certificate-validation pipeline, and the in-flight post-quantum hybrid TLS 1.3 rollout (`X25519MLKEM768`, FIPS 203) [@fips-203][@ms-learn-cng-mlkem-examples].
&lt;h2&gt;1. Two PowerShell Outputs, Twelve Years Apart&lt;/h2&gt;
&lt;p&gt;Run &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; on a freshly installed Windows Server 2025 and the output is unrecognisable to a 2012 administrator [@ms-learn-get-tlsciphersuite]. RC4 is gone. 3DES is gone. The list is led by &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt; and &lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt; -- TLS 1.3 cipher suites that did not exist when &lt;code&gt;schannel.dll&lt;/code&gt; was first written. Yet IIS, SQL Server, RDP via CredSSP, LDAPS, WinHTTP, and every .NET &lt;code&gt;SslStream&lt;/code&gt; consumer on the planet still compiles against the same Win32 SSPI surface they did in 2007 [@ms-learn-schannel-ssp]. How does one DLL rotate every cryptographic primitive in its lineup without breaking the world above it?&lt;/p&gt;
&lt;p&gt;That question is this article&apos;s organising prompt. The answer, held back deliberately until Section 4, is &lt;strong&gt;algorithm agility&lt;/strong&gt; -- the architectural property Microsoft made first-class when it shipped Cryptography API: Next Generation alongside Windows Vista in early 2007 [@ms-learn-cng-portal].&lt;/p&gt;

The Win32 abstraction that lets an application acquire credentials, build a security context, and exchange authentication tokens without knowing which protocol (Kerberos, NTLM, Negotiate, or **Schannel**) is doing the work underneath. SChannel is the SSP that implements SSL, TLS, and DTLS on Windows; its module is `schannel.dll` and its public surface is `AcquireCredentialsHandle` / `InitializeSecurityContext` / `AcceptSecurityContext` [@ms-learn-schannel-ssp].
&lt;h3&gt;Which Windows endpoints SChannel actually owns&lt;/h3&gt;
&lt;p&gt;SChannel is not the only TLS stack that runs on Windows, but the Windows TLS endpoints Microsoft itself owns all run through it. SChannel is the SSP behind:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;IIS&lt;/strong&gt; TLS termination for HTTP/1.1 and HTTP/2 (HTTP/3 over QUIC terminates in &lt;code&gt;msquic.dll&lt;/code&gt;, which uses SChannel for the TLS 1.3 handshake key derivation and then performs the per-packet AEAD outside &lt;code&gt;schannel.dll&lt;/code&gt; per RFC 9001 §5 [@msquic-tls-md][@rfc-9001]).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RDP&lt;/strong&gt; Network Level Authentication via CredSSP -- the CredSSP SSP wraps SChannel to deliver the TLS-protected credential prompt before the RDP session opens.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LDAPS&lt;/strong&gt; for Active Directory client and server bindings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;RPC over HTTPS&lt;/strong&gt; as used by Outlook Anywhere and historical Exchange topologies.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SQL Server&lt;/strong&gt; TDS-over-TLS encryption on Windows.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WinHTTP&lt;/strong&gt; and &lt;strong&gt;WinINet&lt;/strong&gt; -- the Win32 HTTP clients behind &lt;code&gt;BITS&lt;/code&gt;, &lt;code&gt;WebClient&lt;/code&gt;, and many enterprise agents.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;.NET &lt;code&gt;SslStream&lt;/code&gt;&lt;/strong&gt; when running on Windows. On Linux .NET delegates to OpenSSL; on macOS it uses Apple&apos;s Network framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The endpoints SChannel does &lt;em&gt;not&lt;/em&gt; own on a typical Windows box are equally important to name. &lt;strong&gt;Chromium and (via Chromium) Microsoft Edge&lt;/strong&gt; ship BoringSSL -- legacy EdgeHTML used Windows native crypto, but it has been end-of-life since Edge&apos;s January 15, 2020 Chromium-based re-launch. &lt;strong&gt;Firefox&lt;/strong&gt; ships NSS. &lt;strong&gt;Containerised .NET workloads on Linux&lt;/strong&gt; ship with OpenSSL. &lt;strong&gt;SQL Server on Linux&lt;/strong&gt; uses OpenSSL too [@boringssl-readme][@dotnet-cross-platform-crypto]. The Windows TLS story is genuinely a Windows-platform story, not a &quot;what speaks TLS on a Windows machine&quot; story.On Linux, .NET&apos;s &lt;code&gt;SslStream&lt;/code&gt; does not use SChannel at all -- it delegates to OpenSSL [@dotnet-cross-platform-crypto]. The Win32 SChannel story really is a Windows-platform story, not a story about everything TLS-shaped that happens on a Windows machine.MsQuic uses SChannel only for the TLS 1.3 handshake key derivation -- the per-packet AEAD that protects QUIC payloads runs &lt;em&gt;outside&lt;/em&gt; &lt;code&gt;schannel.dll&lt;/code&gt;, in MsQuic itself, per RFC 9001 §5 packet protection [@msquic-tls-md][@rfc-9001]. The MsQuic project documents the TLS abstraction layer (&lt;code&gt;CxPlatTlsProcessData&lt;/code&gt;) and notes explicitly that &quot;the TLS record layer is not included&quot; and that &quot;TLS exposes the encryption key material to QUIC to secure its own packets&quot; [@msquic-tls-md].&lt;/p&gt;
&lt;h3&gt;The artifact comparison&lt;/h3&gt;
&lt;p&gt;The cleanest way to see the substrate&apos;s twenty-year track record is to compare what &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; returns on two Windows generations [@ms-learn-get-tlsciphersuite][@ms-learn-cipher-suites-schannel]. The TLS 1.3 cipher suites listed on the Windows Server 2022 / 2025 page (&lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;, &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt;, &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt;) [@ms-learn-tls-cipher-suites-server-2022] simply are not on the Windows 7 / Server 2008 R2 page [@ms-learn-tls-cipher-suites-windows-7]; conversely, the Windows 7 page enumerates &lt;code&gt;TLS_RSA_WITH_RC4_128_SHA&lt;/code&gt;, &lt;code&gt;TLS_RSA_WITH_3DES_EDE_CBC_SHA&lt;/code&gt;, and &lt;code&gt;TLS_RSA_WITH_AES_128_CBC_SHA&lt;/code&gt; as enabled by default -- suites that newer Windows builds have either removed or moved off-by-default [@ms-learn-tls-registry-settings].&lt;/p&gt;
&lt;p&gt;{`
// Approximation of the SChannel cipher-suite roster on two Windows generations.
const server2012R2 = [
  &apos;TLS_RSA_WITH_RC4_128_SHA&apos;,
  &apos;TLS_RSA_WITH_3DES_EDE_CBC_SHA&apos;,
  &apos;TLS_RSA_WITH_AES_128_CBC_SHA&apos;,
  &apos;TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA&apos;,
  &apos;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256&apos;,
];&lt;/p&gt;
&lt;p&gt;const server2025 = [
  &apos;TLS_AES_256_GCM_SHA384&apos;,
  &apos;TLS_AES_128_GCM_SHA256&apos;,
  &apos;TLS_CHACHA20_POLY1305_SHA256&apos;,
  &apos;TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384&apos;,
  &apos;TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256&apos;,
];&lt;/p&gt;
&lt;p&gt;const rotatedOut = server2012R2.filter(s =&amp;gt; !server2025.includes(s));
const rotatedIn  = server2025.filter(s =&amp;gt; !server2012R2.includes(s));&lt;/p&gt;
&lt;p&gt;console.log(&apos;Rotated out (2012R2 default -&amp;gt; 2025 absent):&apos;);
rotatedOut.forEach(s =&amp;gt; console.log(&apos;  - &apos; + s));
console.log(&apos;Rotated in (2025 default -&amp;gt; 2012R2 unavailable):&apos;);
rotatedIn.forEach(s =&amp;gt; console.log(&apos;  + &apos; + s));
`}&lt;/p&gt;
&lt;p&gt;The whole journey, on one timeline:&lt;/p&gt;

gantt
    dateFormat YYYY
    axisFormat %Y
    title SChannel substrate eras and primitive rotations
    section Substrate
    CryptoAPI 1.0 (CSPs)        :crit, capi, 1996, 2007
    CNG (BCrypt + NCrypt)       :active, cng, 2007, 2026
    SymCrypt unified engine     :sym, 2017, 2026
    section Protocol versions
    SSL 2.0 / 3.0 / TLS 1.0     :p10, 1996, 2014
    TLS 1.1 / 1.2               :p12, 2008, 2026
    TLS 1.3 default-on          :p13, 2021, 2026
    section Cipher generations
    ECDHE plus AES-GCM debut    :g1, 2009, 2026
    RC4 deprecation             :g2, 2013, 2016
    3DES retirement             :g3, 2019, 2026
    SHA-1 sunset                :g4, 2016, 2022
    TLS 1.0 / 1.1 off-default   :g5, 2020, 2025
    X25519MLKEM768 hybrid       :g6, 2025, 2026
&lt;p&gt;CNG did not exist for the first eleven years of SChannel&apos;s life. To see why CNG had to be invented, the next section walks the rigidity that almost broke the Windows TLS stack before AES could even be standardised.&lt;/p&gt;
&lt;h2&gt;2. Before CNG: PCT 1.0, SSL, and the Tyranny of &lt;code&gt;ALG_ID&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;In October 1995 a five-author byline from Microsoft Corporation -- Josh Benaloh, Butler Lampson, Daniel Simon, Terence Spies, and Bennet Yee -- posted &lt;code&gt;draft-benaloh-pct-00&lt;/code&gt; to the IETF [@draft-benaloh-pct-00]. The draft introduced &lt;strong&gt;Private Communication Technology version 1&lt;/strong&gt;, a protocol whose abstract reads: &quot;this protocol corrects or improves on several weaknesses of SSL.&quot; The byline matters. Butler Lampson had received the Turing Award in 1992. Microsoft was not toying with PCT; it intended to win the secure-transport standardisation race.Butler Lampson&apos;s appearance on the PCT 1.0 draft byline (alongside Benaloh, Simon, Spies, Yee) is not incidental. Lampson won the Turing Award in 1992. Microsoft was serious about PCT 1.0 as a protocol, not merely an implementation.&lt;/p&gt;
&lt;p&gt;PCT lost. By the time &lt;code&gt;schannel.dll&lt;/code&gt; shipped in Windows NT 4.0 (commonly placed in 1996 per contemporary release histories), the new SChannel SSP had to negotiate three incompatible handshakes on the same wire: SSL 2.0, SSL 3.0, and PCT 1.0 [@ms-learn-schannel-ssp]. By Vista, PCT was gone; SSL 2.0 was on its way to formal IETF prohibition [@rfc-6176]; SSL 3.0 had a few years left before POODLE would kill it off in 2014 [@poodle-pdf]. The protocol-level story is well-trodden. The substrate underneath -- the &lt;em&gt;engine&lt;/em&gt; SChannel called into to compute each primitive -- is what made the next decade much harder than it had to be.&lt;/p&gt;
&lt;h3&gt;CryptoAPI 1.0 and the CSP cage&lt;/h3&gt;

A loadable DLL that implements a fixed catalog of cryptographic operations under **CryptoAPI 1.0**. Each CSP advertises a *provider type* (e.g. `PROV_RSA_FULL`, `PROV_RSA_SCHANNEL`) and exposes its primitives through opaque `ALG_ID` constants such as `CALG_RC4`, `CALG_3DES`, and `CALG_SHA1`. Adding a new primitive meant shipping a new CSP DLL, registering it under `HKLM\Software\Microsoft\Cryptography\Defaults\Provider`, and threading a fresh BLOB type through every consumer that called `CryptAcquireContext`.
&lt;p&gt;The CryptoAPI 1.0 model had a single fatal property: the primitive &lt;em&gt;was&lt;/em&gt; the API. To compute SHA-256, code had to ask CAPI for an &lt;code&gt;ALG_ID&lt;/code&gt; whose numeric value was &lt;code&gt;CALG_SHA_256&lt;/code&gt; -- and that constant only existed once Microsoft shipped a CSP that defined it, in the same OS release that introduced the algorithm [@ms-learn-alg-id]. Elliptic-curve cryptography never arrived in CAPI in any usable form; the &lt;code&gt;ALG_ID + key BLOB&lt;/code&gt; shape simply could not express the named curves, parameter sets, point-compression flags, or per-curve coordinate sizes that ECC required.&lt;/p&gt;
&lt;p&gt;So in the early 2000s SChannel&apos;s cipher-suite list was less a menu of cryptography and more a snapshot of what CSPs had shipped. FIPS 197 (the AES standard) was published in November 2001. Windows XP shipped without AES in its default SChannel cipher list and only got it broadly via Service Pack 3 and Server 2003. &lt;strong&gt;The four-year AES gap was not Microsoft dragging its feet -- it was the thickness of a CSP-rev cycle.&lt;/strong&gt; RC4 dominance, 3DES persistence, 1024-bit RSA inertia, no ECC: these were the substrate&apos;s fingerprints, not the vendor&apos;s preferences.&lt;/p&gt;

flowchart LR
    A[Application -- IIS / IE / RPC] --&amp;gt; B[SChannel SSP]
    B --&amp;gt; C[CryptoAPI 1.0 / CryptAcquireContext]
    C --&amp;gt; D[&quot;RSA SChannel CSP -- ALG_ID lookup&quot;]
    C --&amp;gt; E[&quot;Base / Enhanced CSP -- ALG_ID lookup&quot;]
    C --&amp;gt; F[Smart Card CSP]
    D -. &quot;Adding ECC requires a new CSP, new ALG_ID, new BLOB type, new IANA codepoint&quot; .-&amp;gt; G((Friction))
    E -. &quot;Adding SHA-256 requires CSP rev + OS release&quot; .-&amp;gt; G
&lt;h3&gt;The PCT failure as a &lt;em&gt;positive&lt;/em&gt; lesson&lt;/h3&gt;
&lt;p&gt;PCT&apos;s loss is, in retrospect, the strongest early case for algorithm agility. SChannel had shipped PCT-the-protocol in 1996; by 2007 PCT was a footnote and SChannel was speaking TLS 1.0, TLS 1.1, SSL 3.0, and (with the right service pack) early TLS 1.2 drafts. The application surface above SChannel did not flinch. Microsoft had bet on PCT, lost, rotated to TLS, and shipped the rotation through the &lt;em&gt;protocol&lt;/em&gt; abstraction that the SSP boundary provided.&lt;/p&gt;
&lt;p&gt;What the SSP boundary did &lt;em&gt;not&lt;/em&gt; shield was the primitive layer. Algorithm rotation had to happen one CSP rev at a time. By the mid-2000s Microsoft&apos;s engineering leadership had a clear diagnosis: the protocol abstraction worked; the primitive abstraction did not. The next CSP rev would not save them, because there were not enough CSP revs in the future to keep up with what cryptography was about to do -- ECC was already standardised, AEAD constructions were being designed, and the post-quantum research had been live for a decade.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The early-2000s lag in AES adoption, the persistence of RC4 and 3DES, and the absence of ECC in Windows were not vendor laziness. CryptoAPI 1.0&apos;s &lt;code&gt;ALG_ID&lt;/code&gt; + provider-type model was &lt;em&gt;structurally incapable&lt;/em&gt; of representing ECC&apos;s named curves and parameter sets. The right question was never &quot;why is Microsoft slow?&quot; -- it was &quot;what would a Windows cryptographic substrate that was &lt;em&gt;not&lt;/em&gt; slow look like?&quot; The Vista CNG redesign is what that question&apos;s answer looks like.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;RC4 dominance, 3DES persistence, 1024-bit RSA inertia, no ECC -- these were not laziness, they were the substrate. A fix to TLS 1.0 was easy; a fix to &lt;em&gt;the way Windows let an application reach a primitive&lt;/em&gt; was a rewrite.&lt;/p&gt;
&lt;h2&gt;3. Configuration Agility Without Substrate Agility: XP and Server 2003&lt;/h2&gt;
&lt;p&gt;Consider a single registry path: &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Client\DisabledByDefault&lt;/code&gt;. That value was introduced for SChannel&apos;s TLS 1.0 support in the XP / Server 2003 era. It is &lt;em&gt;exactly&lt;/em&gt; the same registry sub-tree path an operator uses in 2026 to disable TLS 1.0 itself [@ms-learn-tls-registry-settings]. The configuration surface from 1999 has outlasted three generations of TLS.&lt;/p&gt;
&lt;p&gt;This is not an accident. By the time TLS 1.0 landed in Windows (RFC 2246, Tim Dierks and Christopher Allen at Certicom, January 1999 [@rfc-2246]) and TLS 1.1 followed (RFC 4346, Dierks and Eric Rescorla, April 2006 [@rfc-4346]), SChannel had developed an emerging design pattern: every protocol version became a sub-key, every cipher suite became a registry-driven enable/disable, and the &lt;code&gt;SSL Cipher Suite Order&lt;/code&gt; Group Policy gave administrators a single rope to pull when an algorithm fell from grace.&lt;/p&gt;
&lt;p&gt;That model has aged well. Microsoft&apos;s current &lt;code&gt;tls-registry-settings&lt;/code&gt; page is essentially the same structural document it would have been twenty years ago, with new sub-keys for each new protocol version (SSL 2.0, SSL 3.0, TLS 1.0, TLS 1.1, TLS 1.2, TLS 1.3, DTLS 1.0, DTLS 1.2) and new values for the policy levers Microsoft has added along the way [@ms-learn-tls-registry-settings].The same &lt;code&gt;SCHANNEL\Protocols\&amp;lt;ver&amp;gt;\&amp;lt;role&amp;gt;\Enabled&lt;/code&gt; pattern handles SSL 2, SSL 3, TLS 1.0, TLS 1.1, TLS 1.2, TLS 1.3, DTLS 1.0, and DTLS 1.2. A single sub-key per protocol; new versions slot in without reorganising the hive.&lt;/p&gt;
&lt;h3&gt;The four sub-keys an XP / Server 2003 box exposed&lt;/h3&gt;
&lt;p&gt;The shape of the &lt;code&gt;SCHANNEL\&lt;/code&gt; hive on a representative Server 2003 R2 box, reconstructed from Microsoft Knowledge Base article KB245030 (&quot;How to restrict the use of certain cryptographic algorithms and protocols in Schannel.dll&quot;) and the modern Microsoft Learn &lt;code&gt;tls-registry-settings&lt;/code&gt; page that preserves the same structural document [@ms-learn-tls-registry-settings], is shown below.Microsoft Knowledge Base article KB245030 (&quot;How to restrict the use of certain cryptographic algorithms and protocols in Schannel.dll&quot;) is the *origin document* for the four-sub-key SCHANNEL\ registry pattern this section dumps. The original &lt;code&gt;support.microsoft.com&lt;/code&gt; URL now returns HTTP 404; the same content lives at Microsoft Learn&apos;s &lt;code&gt;tls-registry-settings&lt;/code&gt; page [@ms-learn-tls-registry-settings]. The four sub-keys (&lt;code&gt;Protocols&lt;/code&gt;, &lt;code&gt;Ciphers&lt;/code&gt;, &lt;code&gt;Hashes&lt;/code&gt;, &lt;code&gt;KeyExchangeAlgorithms&lt;/code&gt;) have been stable since Windows 2000. The DWORD convention is itself the agility affordance: &lt;code&gt;0xffffffff&lt;/code&gt; means &quot;enabled,&quot; &lt;code&gt;0&lt;/code&gt; means &quot;disabled,&quot; and the &lt;code&gt;Server&lt;/code&gt; versus &lt;code&gt;Client&lt;/code&gt; role split lets an admin disable SSL 2.0 &lt;em&gt;server&lt;/em&gt;-side without breaking outbound HTTPS &lt;em&gt;client&lt;/em&gt;-side during the transition.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\
  Protocols\
    SSL 2.0\Server\Enabled = 0xffffffff   ; ON by default on 2003 R2
    SSL 3.0\Server\Enabled = 0xffffffff
    TLS 1.0\Server\Enabled = 0xffffffff
    ; TLS 1.1 / 1.2 sub-keys absent -- those protocols do not exist on 2003 R2
  Ciphers\
    RC4 128/128\Enabled        = 0xffffffff
    RC4 56/128\Enabled         = 0xffffffff   ; export-grade, still present
    RC4 40/128\Enabled         = 0xffffffff   ; export-grade
    Triple DES 168\Enabled     = 0xffffffff
    DES 56/56\Enabled          = 0xffffffff
    RC2 40/128\Enabled         = 0xffffffff   ; export-grade
    NULL\Enabled               = 0
  Hashes\
    MD5\Enabled                = 0xffffffff
    SHA\Enabled                = 0xffffffff
  KeyExchangeAlgorithms\
    Diffie-Hellman\Enabled     = 0xffffffff
    PKCS\Enabled               = 0xffffffff   ; RSA key transport
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Notice what is &lt;em&gt;not&lt;/em&gt; there. No &lt;code&gt;TLS 1.1&lt;/code&gt;, no &lt;code&gt;TLS 1.2&lt;/code&gt;, no AES sub-key (AES &lt;code&gt;ALG_ID&lt;/code&gt; constants arrived in &lt;code&gt;rsaenh.dll&lt;/code&gt; via XP SP3 and Server 2003 SP2 but SChannel had to learn the suite-name strings separately). No ECC primitive &lt;em&gt;at all&lt;/em&gt; -- CryptoAPI 1.0 could not express named curve parameters in the &lt;code&gt;ALG_ID + key BLOB&lt;/code&gt; shape, so no amount of registry editing could unlock an ECDHE cipher suite on a 2003-era box. The four-sub-key layout (&lt;code&gt;Protocols&lt;/code&gt;, &lt;code&gt;Ciphers&lt;/code&gt;, &lt;code&gt;Hashes&lt;/code&gt;, &lt;code&gt;KeyExchangeAlgorithms&lt;/code&gt;) is the &lt;em&gt;configuration surface&lt;/em&gt;; what the surface can offer is bounded by the &lt;em&gt;substrate&lt;/em&gt; underneath it.&lt;/p&gt;
&lt;h3&gt;The CSP layer underneath: &lt;code&gt;PROV_RSA_SCHANNEL&lt;/code&gt; and &lt;code&gt;CALG_TLS1PRF&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;On the dispatch side of that same XP / 2003 box, SChannel relied on two CryptoAPI 1.0 Cryptographic Service Providers in particular. The Microsoft Learn &quot;Cryptographic Provider Types&quot; page enumerates the provider types Microsoft shipped [@ms-learn-cryptographic-provider-types]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PROV_RSA_SCHANNEL&lt;/code&gt;&lt;/strong&gt; (provider type &lt;code&gt;12&lt;/code&gt;) -- the SChannel-private CSP. It carried the TLS-specific primitives: the &lt;code&gt;CALG_TLS1PRF&lt;/code&gt; pseudorandom function (algorithm identifier &lt;code&gt;0x0000800a&lt;/code&gt;), the &lt;code&gt;CALG_SCHANNEL_MASTER_HASH&lt;/code&gt; and &lt;code&gt;CALG_SCHANNEL_MAC_KEY&lt;/code&gt; and &lt;code&gt;CALG_SCHANNEL_ENC_KEY&lt;/code&gt; key-derivation handles, and (because the substrate had to negotiate three handshake protocols) the &lt;code&gt;CALG_SSL2_MASTER&lt;/code&gt; and &lt;code&gt;CALG_PCT1_MASTER&lt;/code&gt; constants documented on the Microsoft Learn &lt;code&gt;ALG_ID&lt;/code&gt; page [@ms-learn-alg-id].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PROV_RSA_FULL&lt;/code&gt; / &lt;code&gt;PROV_RSA_AES&lt;/code&gt; (&lt;code&gt;rsaenh.dll&lt;/code&gt;)&lt;/strong&gt; -- the general-purpose enhanced CSP, which carried the bulk symmetric primitives the cipher list named (&lt;code&gt;CALG_RC4&lt;/code&gt;, &lt;code&gt;CALG_DES&lt;/code&gt;, &lt;code&gt;CALG_3DES&lt;/code&gt;, eventually &lt;code&gt;CALG_AES_128&lt;/code&gt;, &lt;code&gt;CALG_AES_256&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both CSPs were loaded by &lt;code&gt;CryptAcquireContext&lt;/code&gt; against the &lt;code&gt;HKLM\SOFTWARE\Microsoft\Cryptography\Defaults\Provider&lt;/code&gt; registry hierarchy. Neither was extensible without an &lt;code&gt;rsaenh.dll&lt;/code&gt; (or analogous) revision and a CSP-rev ship cycle. The registry hive let an operator turn primitives off; it could not let an operator turn a &lt;em&gt;new&lt;/em&gt; primitive on, because the CSP catalog itself was the menu. Adding ECC to that menu was not a configuration problem -- it required a different substrate.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;SSL Cipher Suite Order&lt;/code&gt; GPO -- and why it is a Vista-era artifact, not a 2003-era one&lt;/h3&gt;
&lt;p&gt;Cipher-suite &lt;em&gt;ordering&lt;/em&gt; (as opposed to &lt;em&gt;enablement&lt;/em&gt;) was not exposed as an administrative tunable until Windows Vista and Server 2008 added the &lt;code&gt;Computer Configuration &amp;gt; Administrative Templates &amp;gt; Network &amp;gt; SSL Configuration Settings &amp;gt; SSL Cipher Suite Order&lt;/code&gt; Group Policy. The current Microsoft Learn &quot;Manage Transport Layer Security (TLS)&quot; page documents the format verbatim: &quot;a strict comma delimited format. Each cipher suite string ends with a comma to the right side of it... the list of cipher suites is limited to 1,023 characters.&quot; [@ms-learn-manage-tls] A representative XP-era ordering string -- if the GPO had existed for the operator to set -- would have read something like &lt;code&gt;TLS_RSA_WITH_RC4_128_SHA,TLS_RSA_WITH_3DES_EDE_CBC_SHA,TLS_RSA_WITH_DES_CBC_SHA,...&lt;/code&gt;, walking the actual Server 2003 default lineup that the CSP catalog could deliver. The fact that this lever did not exist on 2003 -- the operator was limited to flipping &lt;code&gt;Ciphers\&amp;lt;name&amp;gt;\Enabled&lt;/code&gt; DWORDs in the per-cipher sub-tree -- is itself evidence of how the &lt;em&gt;operator-facing&lt;/em&gt; SChannel surface matured one Windows release at a time.&lt;/p&gt;
&lt;h3&gt;No enumeration tool on Server 2003&lt;/h3&gt;
&lt;p&gt;There is no &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; cmdlet on Windows Server 2003. Windows PowerShell itself only shipped (as KB968930) in 2009, and the &lt;code&gt;TLS&lt;/code&gt; PowerShell module first appeared in Windows 8 and Server 2012 [@ms-learn-get-tlsciphersuite]. On a 2003-era box the empirical answer to &quot;what does this server actually negotiate?&quot; was either a &lt;code&gt;reg query &quot;HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.0\Server&quot; /v Enabled&lt;/code&gt; against the registry sub-tree above, or -- for what the &lt;em&gt;client&lt;/em&gt; actually picked -- an outbound Internet Explorer 6 trace, or -- for what the &lt;em&gt;server&lt;/em&gt; actually accepted -- a TCP-connect dump against port 443 with a TLS scanner of the era (typically &lt;code&gt;openssl s_client -connect host:443 -cipher ALL&lt;/code&gt; running on a separately-administered Linux box). The operator-visible inventory tool an admin reaches for in 2026 is itself a CNG-era artifact.&lt;/p&gt;
&lt;h3&gt;The agility split: configuration vs. substrate&lt;/h3&gt;
&lt;p&gt;Here is the structural problem that XP-era SChannel revealed. The configuration surface was getting more agile -- an operator could turn cipher suites on and off, prefer one over another, disable an entire protocol version -- but the &lt;em&gt;engine&lt;/em&gt; underneath was not. New primitives still required a CSP rev. New named curves were unrepresentable. SHA-256 in TLS handshake signatures was a several-year project.&lt;/p&gt;
&lt;p&gt;A useful metaphor: configuration agility without substrate agility is a treadmill. You can disable bad cipher suites at will. You cannot &lt;em&gt;add&lt;/em&gt; a new family of primitives without rebuilding the engine. By the mid-2000s Microsoft had two options. Patch CAPI in place forever -- absorb every new algorithm as a new &lt;code&gt;ALG_ID&lt;/code&gt; constant, a new CSP DLL, a new BLOB type, a new round of partner re-certification. Or ship a successor.&lt;/p&gt;

Every Microsoft technology that needed cryptography was caught in the same trap as SChannel. IPsec, EFS, BitLocker&apos;s predecessors, S/MIME in Outlook, smart-card login, Authenticode code-signing verification -- all dispatched through CryptoAPI 1.0 CSPs. The agility problem was not localised to TLS; it was the *Windows cryptography* problem. The successor Microsoft built would therefore have to be the substrate for *all* of these consumers, not just for SChannel. That is exactly what CNG ended up being.
&lt;p&gt;They chose the second. The next section is the eureka moment the rest of the article hangs on.&lt;/p&gt;
&lt;h2&gt;4. CNG: Where Vista Made Algorithm Agility First-Class (January 2007)&lt;/h2&gt;
&lt;p&gt;Vista is where the article&apos;s clock starts. In January 2007 Microsoft did not patch CryptoAPI 1.0; it shipped a parallel substrate alongside it: &lt;strong&gt;Cryptography API: Next Generation&lt;/strong&gt;. The Microsoft Learn portal still describes it in one sentence that doubles as the article&apos;s thesis: &quot;CNG is the long-term replacement for the CryptoAPI. CNG is designed to be extensible at many levels and cryptography agnostic in behavior.&quot; [@ms-learn-cng-portal]&lt;/p&gt;

CNG is the long-term replacement for the CryptoAPI. CNG is designed to be extensible at many levels and cryptography agnostic in behavior. -- Microsoft Learn, *Cryptography API: Next Generation* portal [@ms-learn-cng-portal]
&lt;p&gt;The two splits that look prosaic in the documentation -- BCrypt for primitives, NCrypt for key custodians -- are, in fact, the single architectural decision that makes the rest of this article&apos;s twenty-year story possible.&lt;/p&gt;

The post-Vista replacement for CryptoAPI 1.0. CNG splits cryptography into two API surfaces. **BCrypt** (`bcrypt.dll`) handles primitive operations (hashes, ciphers, key-agreement, signing) and addresses algorithms by string identifier through `BCryptOpenAlgorithmProvider`. **NCrypt** (`ncrypt.dll`) handles key storage and custody through pluggable Key Storage Providers (KSPs). CNG is the substrate Microsoft built so that every later cryptographic primitive could be added as a provider update rather than an API rewrite [@ms-learn-cng-portal].
&lt;h3&gt;BCrypt: algorithms become strings&lt;/h3&gt;
&lt;p&gt;The shape of the BCrypt API is the eureka moment.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;BCRYPT_ALG_HANDLE hAlg;
NTSTATUS status = BCryptOpenAlgorithmProvider(
    &amp;amp;hAlg,
    BCRYPT_AES_ALGORITHM,       // string identifier
    NULL,                        // default provider
    0);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;BCRYPT_AES_ALGORITHM&lt;/code&gt; is the literal string &lt;code&gt;&quot;AES&quot;&lt;/code&gt;. The handle returned by &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt; does not encode which DLL implements AES; it encodes the &lt;em&gt;contract&lt;/em&gt; that the resulting handle satisfies (block cipher, configurable mode, configurable key length). The same shape later admits &lt;code&gt;BCRYPT_ECDH_P256_ALGORITHM&lt;/code&gt;, &lt;code&gt;BCRYPT_SHA384_ALGORITHM&lt;/code&gt;, &lt;code&gt;BCRYPT_CHACHA20_POLY1305_ALG_HANDLE&lt;/code&gt;, and -- in 2024-2026 -- &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; with parameter-set selectors such as &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_768&lt;/code&gt; [@ms-learn-cng-mlkem-examples].&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; resolves through the exact same hash-table lookup as &lt;code&gt;BCRYPT_AES_ALGORITHM&lt;/code&gt; did in 2007. The substrate did not need an architectural change to absorb a brand-new algorithm family seventeen years later -- the dispatch was already built for it.&lt;/p&gt;
&lt;h3&gt;NCrypt: key custodians become pluggable&lt;/h3&gt;

**BCrypt** is the CNG API for *primitives* -- arithmetic that takes plaintext and a key and returns ciphertext (or hash, or signature). **NCrypt** is the CNG API for *key custodians* -- objects that own a private key and expose only signing, decryption, and key-derivation operations. The split lets a TLS server hold a private key whose material it never sees: SChannel calls `NCryptSignHash` against an `NCRYPT_KEY_HANDLE`, and the handle&apos;s owning KSP (software, smart card, or TPM) performs the operation in its own trust boundary.

A CNG-loadable module that owns the lifecycle and operations of a private key. Microsoft ships three out of the box: the **Microsoft Software KSP** (keys at rest in the user or machine profile, protected by DPAPI), the **Microsoft Smart Card KSP** (keys on a PIV / CCID device), and the **Microsoft Platform Crypto Provider** (keys non-exportable from the TPM 2.0). Third parties ship KSPs for HSMs and cloud KMS systems. SChannel sees only the `NCRYPT_KEY_HANDLE`; the custodian is opaque to the SSP.
&lt;h3&gt;How SChannel uses CNG&lt;/h3&gt;
&lt;p&gt;After Vista, SChannel&apos;s internals look very different. The cipher-suite registry resolves to BCrypt algorithm identifiers rather than &lt;code&gt;ALG_ID&lt;/code&gt; constants. The credentials handle that an IIS worker process receives from &lt;code&gt;AcquireCredentialsHandle&lt;/code&gt; holds an &lt;code&gt;NCRYPT_KEY_HANDLE&lt;/code&gt; for the server certificate&apos;s private key; signing operations during the handshake (CertificateVerify) dispatch through &lt;code&gt;NCryptSignHash&lt;/code&gt; to whichever KSP owns the key.&lt;/p&gt;

flowchart TD
    A[&quot;IIS / SQL Server / SslStream / WinHTTP&quot;] --&amp;gt; B[SChannel SSP -- schannel.dll]
    B --&amp;gt; C[&quot;BCrypt -- bcrypt.dll&quot;]
    B --&amp;gt; D[&quot;NCrypt -- ncrypt.dll&quot;]
    C --&amp;gt; E[&quot;SymCrypt primitive engine&quot;]
    C --&amp;gt; F[&quot;Third-party BCrypt providers&quot;]
    D --&amp;gt; G[Microsoft Software KSP]
    D --&amp;gt; H[Smart Card KSP]
    D --&amp;gt; I[&quot;Microsoft Platform Crypto Provider -- TPM 2.0&quot;]
    D --&amp;gt; J[&quot;HSM / cloud KMS KSPs&quot;]
    E --&amp;gt; K[(Algorithm dispatch by string identifier)]
    G --&amp;gt; L[(Key operations by opaque handle)]
&lt;h3&gt;The agility property, stated forward&lt;/h3&gt;
&lt;p&gt;From 2007 onward, &lt;strong&gt;adding a primitive to Windows TLS is a CNG-provider-update problem, not an SChannel-rewrite problem&lt;/strong&gt;. The application surface stays put. IIS does not get rebuilt. &lt;code&gt;SslStream&lt;/code&gt; does not change. The cipher suite negotiated on the wire is whatever the SChannel cipher-suite registry currently exposes; the cipher-suite registry resolves to whatever BCrypt providers are loaded.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Algorithm agility is not a property of TLS-the-protocol. The cipher-suite codepoint is the minor half of the work; the major half is having a substrate that resolves a new algorithm identifier without rebuilding every consumer. CNG&apos;s BCrypt dispatch is what that substrate looks like in Windows. The protocol&apos;s cipher-suite registry is &lt;em&gt;enumerated&lt;/em&gt;; the substrate&apos;s algorithm registry is &lt;em&gt;open&lt;/em&gt;. That asymmetry is the entire game.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;SymCrypt as the parallel track&lt;/h3&gt;

Microsoft&apos;s unified, FIPS 140-validated cryptographic primitive engine. Niels Ferguson began the project in **late 2006** with the first sources committed in February 2007 [@symcrypt-github] -- nearly a decade before Heartbleed. SymCrypt became the primary library for symmetric algorithms starting with Windows 8 and the primary library for all algorithms across Windows since the Windows 10 1703 release in March 2017. Microsoft open-sourced SymCrypt under the MIT license in July 2019 [@symcrypt-github]. Its release-by-release primitive timeline lives in the public CHANGELOG [@symcrypt-changelog].
&lt;p&gt;This timing matters because the original framing many readers carry around -- &quot;Microsoft rewrote its crypto engine after Heartbleed&quot; -- is historically wrong on every axis. SymCrypt predates Heartbleed by seven years [@symcrypt-github]. Heartbleed was an OpenSSL heartbeat-extension bug and did not affect SChannel because SChannel does not implement that code path [@nvd-cve-2014-0160]. The article&apos;s Section 6 treats this conflation in detail. For now, the honest framing is: SymCrypt was the long, quiet maturation of CNG&apos;s primitive layer over a decade, designed by a working Microsoft cryptographer for a substrate already built to accept it.Niels Ferguson&apos;s publicly visible work -- including his co-authorship of &lt;em&gt;Cryptography Engineering&lt;/em&gt; with Bruce Schneier and Tadayoshi Kohno [@schneier-cryptography-engineering] -- is the closest the public has to a primitive-design rationale for what eventually became SymCrypt.&lt;/p&gt;
&lt;p&gt;CNG was Microsoft betting that Windows could keep its Win32 API contract stable while every cryptographic primitive underneath it rotated. The next four sections are the receipts on that bet -- five complete cipher-suite rotations, a parsing-path RCE that almost broke trust in the substrate, and a present-day post-quantum pivot that is the cleanest agility receipt of all.&lt;/p&gt;
&lt;h2&gt;5. Five Generations of Cipher-Suite Rotation, 2009 to 2025&lt;/h2&gt;
&lt;p&gt;Between Windows 7 in 2009 [@ms-learn-tls-cipher-suites-windows-7] and the rolling Windows Server 2022 / 2025 default lists [@ms-learn-tls-cipher-suites-server-2022], Microsoft rotated every primitive in SChannel&apos;s default cipher list at least five times. Not once did IIS, SQL Server, or &lt;code&gt;SslStream&lt;/code&gt; get a source-code change because of it. Those five rotations are the agility receipts.&lt;/p&gt;
&lt;p&gt;The rough shape of the rotations:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Window&lt;/th&gt;
&lt;th&gt;New primitive(s)&lt;/th&gt;
&lt;th&gt;Old primitive(s) retired&lt;/th&gt;
&lt;th&gt;Disablement mechanism&lt;/th&gt;
&lt;th&gt;Cryptographic indictment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;G1&lt;/td&gt;
&lt;td&gt;Win7 / 2008 R2, Oct 2009&lt;/td&gt;
&lt;td&gt;ECDHE key exchange + AES-GCM AEAD&lt;/td&gt;
&lt;td&gt;Static RSA key transport + AES-CBC + HMAC&lt;/td&gt;
&lt;td&gt;New cipher-suite registrations [@ms-learn-tls-cipher-suites-windows-7]&lt;/td&gt;
&lt;td&gt;Lucky13 (2013), BEAST (2011) [@beast-pdf]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G2&lt;/td&gt;
&lt;td&gt;KB2868725, Nov 12 2013 -&amp;gt; off-default 2016&lt;/td&gt;
&lt;td&gt;(none added)&lt;/td&gt;
&lt;td&gt;RC4 stream cipher&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SCH_USE_STRONG_CRYPTO&lt;/code&gt; registry value [@ms-advisory-2868725]&lt;/td&gt;
&lt;td&gt;RC4 NOMORE (75-hour cookie recovery) [@usenix-rc4nomore]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G3&lt;/td&gt;
&lt;td&gt;Win10 v1903, May 2019 Update&lt;/td&gt;
&lt;td&gt;(none added)&lt;/td&gt;
&lt;td&gt;3DES (64-bit block)&lt;/td&gt;
&lt;td&gt;Cipher-suite default-off in cipher list [@ms-learn-cipher-suites-schannel]&lt;/td&gt;
&lt;td&gt;SWEET32 (785 GB / less than 48 h) [@sweet32-info]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G4&lt;/td&gt;
&lt;td&gt;2016-2022&lt;/td&gt;
&lt;td&gt;SHA-256 / SHA-384 handshake signatures&lt;/td&gt;
&lt;td&gt;SHA-1 handshake signatures and SHA-1 trust-store roots&lt;/td&gt;
&lt;td&gt;Microsoft Trusted Root Program distrust events; chain-engine policy&lt;/td&gt;
&lt;td&gt;SHAttered (Feb 2017) [@iacr-eprint-shattered]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;G5&lt;/td&gt;
&lt;td&gt;2020-2025&lt;/td&gt;
&lt;td&gt;TLS 1.3 (default-on Win11 / Server 2022)&lt;/td&gt;
&lt;td&gt;TLS 1.0 and TLS 1.1&lt;/td&gt;
&lt;td&gt;&lt;code&gt;SCHANNEL\Protocols\TLS 1.0\&amp;lt;role&amp;gt;\DisabledByDefault&lt;/code&gt; [@ms-learn-tls-registry-settings]&lt;/td&gt;
&lt;td&gt;Decade of attack research (BEAST, POODLE, FREAK, Logjam) [@weakdh-logjam]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Each row below adds the engineering detail that the table compresses.&lt;/p&gt;
&lt;h3&gt;G1 -- ECDHE plus AES-GCM (Windows 7 / Server 2008 R2, October 2009)&lt;/h3&gt;

A key-agreement protocol where both parties generate a fresh elliptic-curve key pair for every handshake and exchange public points; the shared secret is never derived from the long-term server certificate&apos;s private key. ECDHE provides **forward secrecy**: compromising the server&apos;s RSA or ECDSA private key tomorrow does not let an adversary decrypt connections recorded today. ECDHE cipher suites first appear in SChannel on Windows 7 / Server 2008 R2 in 2009 [@ms-learn-tls-cipher-suites-windows-7].

A construction that encrypts plaintext and produces an authentication tag in a single operation, with neither output usable in isolation. AEAD ends an entire class of &quot;mac-then-encrypt vs. encrypt-then-mac&quot; padding-oracle bugs (Lucky13, POODLE-style attacks on CBC) by removing the separable padding step entirely. The AEAD framework is introduced in RFC 5246 §6.2.3.3 [@rfc-5246]; AES-GCM is the canonical instantiation on Windows.
&lt;p&gt;The Windows 7 cipher-suite roster enumerates ECDHE-based suites like &lt;code&gt;TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256&lt;/code&gt; and the AES-GCM AEAD suites &lt;code&gt;TLS_RSA_WITH_AES_256_GCM_SHA384&lt;/code&gt; and &lt;code&gt;TLS_RSA_WITH_AES_128_GCM_SHA256&lt;/code&gt; [@ms-learn-tls-cipher-suites-windows-7]. These suites simply did not exist in the SChannel cipher-suite list of XP / Server 2003. Microsoft was able to add them because the CNG substrate, two years old by Windows 7&apos;s RTM, dispatched algorithms by string; the cipher-suite registry just gained new rows that resolved to &lt;code&gt;BCRYPT_ECDH_P256_ALGORITHM&lt;/code&gt; and &lt;code&gt;BCRYPT_AES_ALGORITHM&lt;/code&gt; with the GCM chaining mode set.&lt;/p&gt;
&lt;h3&gt;G2 -- RC4 deprecation (KB2868725, November 12 2013, default-off 2016)&lt;/h3&gt;
&lt;p&gt;In November 2013 Microsoft published Security Advisory 2868725, &quot;Update for Disabling RC4,&quot; which introduced the &lt;code&gt;SCH_USE_STRONG_CRYPTO&lt;/code&gt; flag in the &lt;code&gt;SCHANNEL_CRED&lt;/code&gt; structure and the matching registry mechanism [@ms-advisory-2868725]. The press around the advisory was driven by the BEAST attack (2011) -- whose practical mitigation had been to &lt;em&gt;prefer&lt;/em&gt; RC4 over CBC suites to dodge the CBC implicit-IV bug -- and by mounting attacks against RC4 itself by AlFardan et al. and others.&lt;/p&gt;
&lt;p&gt;The full cryptographic indictment landed at USENIX Security in August 2015: Mathy Vanhoef and Frank Piessens published &quot;All Your Biases Belong to Us: Breaking RC4 in WPA-TKIP and TLS,&quot; demonstrating a 75-hour HTTPS cookie recovery against RC4-secured TLS [@usenix-rc4nomore]. Six months earlier, Andrei Popov of Microsoft Corp. had authored RFC 7465, &quot;Prohibiting RC4 Cipher Suites&quot; [@rfc-7465]. Edge and IE 11 disabled RC4 by default in late 2016; SChannel&apos;s RC4 suites moved to off-by-default on the same trajectory [@ms-learn-cipher-suites-schannel].RFC 7465 (&quot;Prohibiting RC4 Cipher Suites in TLS&quot;) was authored by A. Popov of Microsoft Corp. [@rfc-7465] -- the same engineer whose name is on the current Microsoft Learn SChannel SSP overview page [@ms-learn-schannel-ssp]. Microsoft&apos;s anti-RC4 push was Microsoft-led at the IETF, not just internally.&lt;/p&gt;
&lt;h3&gt;G3 -- 3DES retirement (Windows 10 v1903, May 2019 Update)&lt;/h3&gt;
&lt;p&gt;The cryptographic indictment for 3DES is a textbook example of the &lt;strong&gt;block-cipher birthday bound&lt;/strong&gt;. A 64-bit block cipher reaches a 50% probability of an internal collision after roughly $2^{32}$ encrypted blocks under a single key -- around 32 GB. The SWEET32 paper (Bhargavan and Leurent, ACM CCS 2016) translated that bound into a practical TLS cookie-recovery attack: with 785 GB of induced HTTP traffic over a long-lived 3DES-encrypted connection, an adversary could recover an HTTPS cookie in less than two days [@sweet32-info].&lt;/p&gt;
&lt;p&gt;Microsoft moved 3DES cipher suites to off-by-default starting with &lt;strong&gt;Windows 10 version 1903 (May 2019 Update)&lt;/strong&gt;. The exact pivot is visible in the per-OS Microsoft Learn cipher-suite tables: &lt;code&gt;TLS_RSA_WITH_3DES_EDE_CBC_SHA&lt;/code&gt; appears in the default-enabled list for Windows 10 v1709 and was removed from the default-enabled list for v1903 onward [@ms-learn-cipher-suites-schannel][@ms-learn-tls-cipher-suites-server-2022]. The registry-toggle mechanism is the same &lt;code&gt;SCHANNEL\Ciphers\&amp;lt;algorithm&amp;gt;\Enabled&lt;/code&gt; shape that has been in place since the XP era [@ms-learn-tls-registry-settings]. Crucially, no application changed -- IIS, SQL Server, and &lt;code&gt;SslStream&lt;/code&gt; simply stopped negotiating 3DES because the cipher list no longer offered it.&lt;/p&gt;
&lt;h3&gt;G4 -- SHA-1 sunset (2016 to 2022)&lt;/h3&gt;
&lt;p&gt;SHA-1 deprecation in SChannel was not a single registry flip; it was a coordinated rotation across the &lt;strong&gt;certificate trust pipeline&lt;/strong&gt; (covered in Section 7), the &lt;strong&gt;handshake signature suite&lt;/strong&gt;, and the &lt;strong&gt;trust-store membership&lt;/strong&gt; of root CAs that issued SHA-1 leaves. Two cryptographic indictments did the load-bearing work. The first was &lt;em&gt;protocol-level&lt;/em&gt;: SLOTH (Bhargavan and Leurent, NDSS 2016 [@mitls-sloth][@iacr-eprint-sloth]) showed that an attacker able to compute MD5 or SHA-1 transcript-hash collisions could impersonate one party to the other inside TLS 1.2&apos;s client-authenticated handshake by forging matching &lt;code&gt;CertificateVerify&lt;/code&gt; signatures -- a direct attack on authentication, not on the primitive&apos;s collision resistance in the abstract. The second was &lt;em&gt;primitive-level&lt;/em&gt;: SHAttered (Stevens, Bursztein, Karpman, Albertini, Markov; February 2017 [@iacr-eprint-shattered]) supplied the concrete colliding PDF pair that closed the public debate about SHA-1&apos;s safety margin, published as IACR ePrint 2017/190. Two indictments together -- protocol-level via SLOTH, primitive-level via SHAttered -- is the empirically-correct framing for the SHA-1 retirement timeline.&lt;/p&gt;
&lt;p&gt;For SChannel specifically the rotation was: SHA-256 / SHA-384 handshake signatures for new connections; chain-engine policy stops accepting SHA-1 leaves for &lt;code&gt;id-kp-serverAuth&lt;/code&gt;; and the Microsoft Trusted Root Program distrust events that retired SHA-1 code-signing and TLS certificates from &lt;code&gt;authrootstl.cab&lt;/code&gt; over 2016 to 2022. Section 7 walks the trust pipeline in detail; for the agility argument what matters is that none of these required an &lt;code&gt;SslStream&lt;/code&gt; change.&lt;/p&gt;
&lt;h3&gt;G5 -- TLS 1.0 / 1.1 disablement (2020 to 2025)&lt;/h3&gt;
&lt;p&gt;Microsoft&apos;s rollout used the registry pattern from Section 3. Per-application disablement first -- IE 11, Edge legacy, .NET via &lt;code&gt;ServicePointManager.SecurityProtocol&lt;/code&gt;, individual server roles -- then OS-level defaults in 2024 and 2025 [@ms-learn-tls-registry-settings]. The TLS 1.0 / 1.1 lifecycle is the article&apos;s clearest data point on the difference between &quot;the substrate can rotate&quot; and &quot;the world will move&quot; (Section 11 returns to this).&lt;/p&gt;
&lt;p&gt;The TLS 1.3 side of G5 -- the &lt;em&gt;positive&lt;/em&gt; half of the protocol-version rotation -- shipped default-on in Windows Server 2022 (GA August 2021) and Windows 11 (GA October 5, 2021). Windows 10 and Server 2019 SChannel remain TLS 1.2 only [@ms-learn-tls-cipher-suites-server-2022]. The three TLS 1.3 AEAD suites (&lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;, &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt;, &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt;) [@rfc-8446] became the default lineup -- another row of new entries in the cipher-suite registry, with the BCrypt providers behind them already shipping.&lt;/p&gt;
&lt;p&gt;Five rotations, zero application source changes. But one episode from the same era &lt;em&gt;did&lt;/em&gt; break the calm -- a parsing-path remote code execution in SChannel itself, published on Patch Tuesday in November 2014, that the press still routinely confuses with Heartbleed [@ms14-066]. The agility substrate did not protect Windows from that one.&lt;/p&gt;
&lt;h2&gt;6. MS14-066 / WinShock: What Happened, What It Was Not&lt;/h2&gt;
&lt;p&gt;If you searched &quot;SChannel 2014 vulnerability&quot; in late 2014 you got two stories blended together: Heartbleed (April) and the November SChannel RCE everyone called WinShock. They are not the same story. They are not the same vulnerability. They are not even the same vendor. The blending is the single most-misremembered SChannel event, and this article exists in part to set the record straight.&lt;/p&gt;
&lt;h3&gt;What MS14-066 actually was&lt;/h3&gt;
&lt;p&gt;On Patch Tuesday, November 11, 2014, Microsoft published &lt;strong&gt;Security Bulletin MS14-066&lt;/strong&gt; -- &quot;Vulnerability in Schannel Could Allow Remote Code Execution (2992611)&quot; [@ms14-066]. The vulnerability identifier was CVE-2014-6321. The bulletin&apos;s first sentence reads, verbatim:&lt;/p&gt;

This security update resolves a privately reported vulnerability in the Microsoft Secure Channel (Schannel) security package in Windows. The vulnerability could allow remote code execution if an attacker sends specially crafted packets to a Windows server. -- Microsoft Security Bulletin MS14-066, November 11, 2014 [@ms14-066]
&lt;p&gt;The technical character of the bug was a pre-authentication remote code execution in SChannel&apos;s TLS message-parsing path. The NVD record summarises it as &quot;Schannel in Microsoft Windows Server 2003 SP2, Windows Vista SP2, Windows Server 2008 SP2 and R2 SP1, Windows 7 SP1, Windows 8, Windows 8.1, Windows Server 2012 Gold and R2, and Windows RT Gold and 8.1 allows remote attackers to execute arbitrary code via crafted packets&quot; [@nvd-cve-2014-6321]. US-CERT issued Alert TA14-318A confirming the severity and noting the wide platform coverage [@uscert-ta14-318a]; CERT/CC published vulnerability note VU#505120 with the same substance [@certcc-vu505120]. &lt;strong&gt;The bulletin was disclosed under coordinated vulnerability disclosure on the standard Patch Tuesday cadence; IBM X-Force researcher Robert Freeman is publicly credited as the discoverer.&lt;/strong&gt; The &quot;privately reported&quot; phrasing in MS14-066 [@ms14-066] is Microsoft&apos;s standard nomenclature for coordinated-disclosure intake, not a claim that the discovery was internal to Microsoft.&lt;/p&gt;
&lt;h3&gt;What MS14-066 was not&lt;/h3&gt;
&lt;p&gt;It was not Heartbleed. &lt;strong&gt;Heartbleed (CVE-2014-0160), disclosed April 7, 2014, was a flaw in OpenSSL&apos;s TLS Heartbeat extension code path&lt;/strong&gt; [@nvd-cve-2014-0160]. The bug let an attacker over-read OpenSSL process memory by sending a Heartbeat request whose declared payload length exceeded the actual payload. SChannel does not implement the OpenSSL Heartbeat extension; that code simply did not exist in &lt;code&gt;schannel.dll&lt;/code&gt;. Microsoft&apos;s MSRC publicly noted in April 2014 that Microsoft Services were not affected by the Heartbleed vulnerability -- the substance held because the affected codebase was OpenSSL&apos;s, not Microsoft&apos;s [@nvd-cve-2014-0160].The original April 2014 MSRC blog post stating SChannel was unaffected by Heartbleed has migrated and renders only its page chrome today. The substance is independently anchored by the NVD record for CVE-2014-0160, which explicitly scopes the vulnerability to OpenSSL 1.0.1 through 1.0.1f.&lt;/p&gt;
&lt;p&gt;It was not &quot;silently patched,&quot; at least not at the headline level. CVE-2014-6321 had a public Patch Tuesday bulletin, contemporary Krebs and NVD coverage, US-CERT and CERT/CC alerts, and proof-of-concept walkthroughs from BeyondTrust and Security Sift within months [@certcc-vu505120][@uscert-ta14-318a]. The &quot;silently patched&quot; framing in the press is the residue of a &lt;em&gt;real but narrower&lt;/em&gt; fact: the same KB shipped additional Schannel hardening fixes that were not separately bulletined.The &quot;silently patched&quot; framing of MS14-066 is itself the residue of a real fact -- the November 11, 2014 KB included Schannel hardening fixes that were not separately bulletined. The headline CVE itself was very much public, and the discovery is publicly credited to IBM X-Force researcher Robert Freeman under coordinated vulnerability disclosure. This article does not assign specific CVE IDs to the &lt;em&gt;bundled&lt;/em&gt; hardening extras, in line with the project&apos;s premise-audit discipline.&lt;/p&gt;
&lt;h3&gt;What it occasioned&lt;/h3&gt;
&lt;p&gt;Three lasting effects of MS14-066 are worth naming.&lt;/p&gt;
&lt;p&gt;First, &lt;strong&gt;the cipher-suite expansion in the same KB&lt;/strong&gt;. The patch bundled new TLS 1.2 cipher suites (the ECDHE-RSA suites that Windows 7 and Server 2008 R2 had partially supported, broadened across the entire then-supported family). Some operators were caught off guard by the new lineup; the registry-toggle pattern from Section 3 was what got them out of the bind.&lt;/p&gt;
&lt;p&gt;Second, &lt;strong&gt;a measurable uptick in external SChannel fuzzing&lt;/strong&gt;. After 2014, the public TLS-stack-testing community treated SChannel as a first-class target, not as a closed-source black box no one could meaningfully probe. The most visible artifact is Hubert Kario&apos;s TLS-Fuzzer at Red Hat -- a test suite that, in the project&apos;s own framing, &quot;doesn&apos;t check only that the system under test didn&apos;t crash, it checks that it returned correct error messages&quot; [@tlsfuzzer-github]. Section 11 returns to TLS-Fuzzer as the closest public substitute for a behavioural specification of SChannel.&lt;/p&gt;
&lt;p&gt;Third, the lesson the substrate could not absorb: &lt;strong&gt;algorithm-agility does not extend to the parsing path&lt;/strong&gt;. The wire-format state machine has to be correct because no provider model can fix a bug in &lt;code&gt;schannel.dll&lt;/code&gt; itself. CNG could rotate primitives without rewriting SChannel; CNG could not rotate SChannel&apos;s TLS message parser. That asymmetry is structural and remains true today.&lt;/p&gt;
&lt;h3&gt;What it was not, part two: not the trigger for SymCrypt&lt;/h3&gt;
&lt;p&gt;Some narratives connect MS14-066 to a &quot;SChannel rewrite&quot; or a &quot;FIPS rewrite&quot; project that followed. The dates do not support either framing. SymCrypt was started by Niels Ferguson in &lt;strong&gt;late 2006&lt;/strong&gt;, with the first sources committed in February 2007 [@symcrypt-github] -- seven years before Heartbleed and eight years before MS14-066. SymCrypt became the primary library for symmetric algorithms with Windows 8 (October 2012, before MS14-066) and the primary library for all algorithms across Windows starting with the Windows 10 1703 release in March 2017. Open-sourcing followed under the MIT license in July 2019 [@symcrypt-github]. The honest story is that SymCrypt was the maturation of CNG&apos;s primitive layer over a decade; it had no causal relationship to either 2014 disclosure.&lt;/p&gt;

This article refuses to assert any causal link between Heartbleed and SymCrypt because the timeline does not support it. SymCrypt began in late 2006; Heartbleed was disclosed in April 2014. SymCrypt&apos;s role as the Windows-wide primary crypto library lands with Windows 10 1703 in March 2017 [@symcrypt-github]. Conflations of this kind are how the security-pop-press version of history overwrites the engineering version. The agility argument is stronger, not weaker, when the actual causal chains are preserved.
&lt;p&gt;MS14-066 taught Microsoft that the substrate&apos;s algorithm-agility property does not extend to the parsing path -- the wire-format state machine has to be correct because no provider model can fix a bug in &lt;code&gt;schannel.dll&lt;/code&gt; itself. The next section turns to the &lt;em&gt;other&lt;/em&gt; load-bearing path: not the bytes on the wire, but the certificate the server presents to authenticate.&lt;/p&gt;
&lt;h2&gt;7. The Certificate-Validation Pipeline: &lt;code&gt;CertGetCertificateChain&lt;/code&gt;, OCSP, and the Microsoft Trusted Root Program&lt;/h2&gt;
&lt;p&gt;The other half of any TLS handshake is &lt;strong&gt;trust&lt;/strong&gt;. Bytes can be encrypted with the strongest AEAD in the SymCrypt CHANGELOG and the handshake can use a quantum-resistant key exchange -- and the whole exchange still means nothing if the certificate the server presents traces back to an attacker-controlled CA. On Windows, that whole question routes through one API: &lt;code&gt;CertGetCertificateChain&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;The chain engine&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;CertGetCertificateChain&lt;/code&gt; walks from leaf to trusted root using Authority Key Identifier / Subject matching, fetches any missing intermediates via the certificate&apos;s Authority Information Access (AIA) &lt;code&gt;caIssuers&lt;/code&gt; URL, and resolves against the local Microsoft Trusted Root Store. The store itself is kept current through the &lt;code&gt;crypt32.dll&lt;/code&gt; auto-update mechanism, which downloads a signed &lt;code&gt;authrootstl.cab&lt;/code&gt; periodically and updates the trust list in place.&lt;/p&gt;
&lt;p&gt;Per-certificate checks follow the X.509 PKI profile (RFC 5280, May 2008) [@rfc-5280]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Signature verification&lt;/strong&gt; -- each cert is signed by the next-up cert&apos;s private key.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validity&lt;/strong&gt; -- &lt;code&gt;notBefore&lt;/code&gt; / &lt;code&gt;notAfter&lt;/code&gt; within the current time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Key Usage and Extended Key Usage&lt;/strong&gt; -- the leaf must include &lt;code&gt;id-kp-serverAuth&lt;/code&gt; (&lt;code&gt;1.3.6.1.5.5.7.3.1&lt;/code&gt;) for a TLS server presentation, and the chain&apos;s intermediates must permit &lt;code&gt;serverAuth&lt;/code&gt; in their EKU constraints if they declare any.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Basic Constraints&lt;/strong&gt; -- non-leaf certs must have &lt;code&gt;cA=TRUE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name Constraints&lt;/strong&gt; -- per RFC 5280 §4.2.1.10, intermediates may declare &lt;code&gt;permittedSubtrees&lt;/code&gt; and &lt;code&gt;excludedSubtrees&lt;/code&gt; over DNS names, IP ranges, and other name forms; the chain engine enforces these against the leaf&apos;s SAN.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Revocation&lt;/strong&gt; -- per-cert, against the chosen revocation source.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;CertVerifyCertificateChainPolicy&lt;/code&gt; then layers protocol-specific overlays on top of that purely structural validation. The most important for TLS is &lt;code&gt;CERT_CHAIN_POLICY_SSL&lt;/code&gt;, which adds the SNI / SAN hostname match and TLS-specific server-auth constraints.&lt;/p&gt;

flowchart TD
    A[&quot;Leaf certificate from TLS handshake&quot;] --&amp;gt; B[&quot;Chain engine -- CertGetCertificateChain&quot;]
    B --&amp;gt; C{&quot;Path build via AKI / SKI matching, AIA caIssuers fetch&quot;}
    C --&amp;gt; D[&quot;Per-cert structural checks (RFC 5280)&quot;]
    D --&amp;gt; E{&quot;Revocation source&quot;}
    E --&amp;gt; F[CRL distribution point]
    E --&amp;gt; G[OCSP responder]
    E --&amp;gt; H[OCSP stapled response]
    D --&amp;gt; I[&quot;CertVerifyCertificateChainPolicy&quot;]
    I --&amp;gt; J{&quot;CERT_CHAIN_POLICY_SSL -- SNI / SAN match, serverAuth EKU&quot;}
    J --&amp;gt; K[Chain valid for TLS server]
    F --&amp;gt; I
    G --&amp;gt; I
    H --&amp;gt; I
&lt;h3&gt;Revocation: CRL, OCSP, OCSP stapling&lt;/h3&gt;

The **Online Certificate Status Protocol** (RFC 6960) lets a client ask the issuing CA&apos;s OCSP responder whether a specific certificate is revoked, by serial number [@rfc-6960]. Plain OCSP is a separate request to the CA on every connection, which leaks visited hostnames to the CA and adds latency. **OCSP stapling** lets the server fetch a fresh signed OCSP response on a schedule and &quot;staple&quot; it into the TLS handshake via the `status_request` extension -- the client gets the same revocation proof without the side channel. SChannel consumes stapled OCSP responses through the `status_request` extension (RFC 6066 §8 for TLS 1.2, RFC 8446 §4.4.2.1 for TLS 1.3 [@rfc-8446]) and feeds the result into the chain engine.
&lt;p&gt;A practical SChannel deployment combines CRL fetching, OCSP, and OCSP stapling: stapling preferred when present, OCSP fallback when not, CRL as the long-tail safety net. IIS&apos;s stapling support is on by default in modern releases; turning it off is the wrong default for any internet-facing endpoint.&lt;/p&gt;
&lt;h3&gt;The Microsoft Trusted Root Program and the CCADB&lt;/h3&gt;
&lt;p&gt;SChannel&apos;s trust posture inherits the Microsoft Trusted Root Program&apos;s membership decisions. Microsoft does not run the trust program in isolation. It participates in the &lt;strong&gt;Common CA Database (CCADB)&lt;/strong&gt; alongside Mozilla, Google, and Apple, sharing root inclusion / removal / audit data across the major root stores [@ccadb-resources]. The CCADB Resources page lists the public extractions (Microsoft&apos;s TLS roots, Mozilla&apos;s TLS roots, code-signing roots, S/MIME roots) and the program-specific report URLs.&lt;/p&gt;
&lt;p&gt;The governance flow is documented end-to-end on the Microsoft Trusted Root Program program-requirements page [@ms-trusted-root-program-requirements]. Membership requires annual WebTrust or ETSI EN 319 411 audits, full CCADB disclosure of the PKI hierarchy, and adherence to the technical requirements (minimum key sizes, signature-algorithm policy, extension constraints, name-form profiles). Distrust decisions can be triggered by (a) CCADB-coordinated cross-vendor consensus where Microsoft acts alongside Mozilla, Apple, and Google; (b) unilateral Microsoft action when the program judges a CA below the bar; or (c) audit-failure findings that fail to remediate inside an agreed window.&lt;/p&gt;
&lt;p&gt;Propagation to Windows clients goes through two signed trust lists distributed via the Automatic Root Update mechanism: &lt;strong&gt;&lt;code&gt;authrootstl.cab&lt;/code&gt;&lt;/strong&gt; carries the currently-trusted roots together with per-EKU enablement bits, and &lt;strong&gt;&lt;code&gt;disallowedcertstl.cab&lt;/code&gt;&lt;/strong&gt; is the explicit untrust list. Both are fetched by &lt;code&gt;crypt32.dll&lt;/code&gt; from &lt;code&gt;http://ctldl.windowsupdate.com/...&lt;/code&gt; on a periodic schedule and consumed by the chain engine on its next chain build. The SChannel SSP itself does not maintain a separate trust list; it inherits whatever &lt;code&gt;CertGetCertificateChain&lt;/code&gt; resolves against the auto-updated stores.&lt;/p&gt;

flowchart TB
    A[CA submits audit, CCADB disclosure, technical compliance] --&amp;gt; B[Microsoft Trusted Root Program review]
    B --&amp;gt; C{&quot;Decision -- include, distrust, NotBefore-date schedule&quot;}
    C --&amp;gt; D[CCADB cross-vendor coordination -- Mozilla, Apple, Google]
    C --&amp;gt; E[authrootstl.cab updates]
    C --&amp;gt; F[disallowedcertstl.cab updates]
    E --&amp;gt; G[ctldl.windowsupdate.com distribution]
    F --&amp;gt; G
    G --&amp;gt; H[crypt32.dll Automatic Root Update on client]
    H --&amp;gt; I[CertGetCertificateChain consults updated stores]
    I --&amp;gt; J[SChannel SSP handshake trust decision]
&lt;h3&gt;Two worked examples: DigiNotar (2011) and Symantec (2018)&lt;/h3&gt;
&lt;p&gt;The MTRP governance flow looks abstract until two real distrust events make it concrete.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DigiNotar -- August / September 2011 -- panic-mode revocation.&lt;/strong&gt; Microsoft Security Advisory 2607712 (&quot;Fraudulent Digital Certificates Could Allow Spoofing&quot;) was published on August 29, 2011 and updated through September 19 to version 5.0 [@ms-advisory-2607712-diginotar]. The Dutch CA DigiNotar&apos;s signing infrastructure had been breached by an attacker who issued fraudulent certificates for &lt;code&gt;*.google.com&lt;/code&gt; and other high-value names. Microsoft, Mozilla, Apple, and Google removed DigiNotar&apos;s roots from their trust stores within days. The Microsoft-side propagation pushed the DigiNotar Root CA out of &lt;code&gt;authrootstl.cab&lt;/code&gt; and added the relevant entries to &lt;code&gt;disallowedcertstl.cab&lt;/code&gt;; clients on the Automatic Root Update pipeline picked up the change within the next refresh cycle. SChannel&apos;s chain engine then refused to validate any leaf signed under the DigiNotar hierarchy -- not because SChannel changed, but because the trust store it consults changed underneath it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Symantec deprecation -- October 2018 -- planned per-NotBefore-date schedule.&lt;/strong&gt; The Symantec distrust is the cleanest published example of how a CCADB-coordinated &lt;em&gt;planned&lt;/em&gt; deprecation differs from a &lt;em&gt;panic-mode&lt;/em&gt; revocation. Microsoft&apos;s October 4, 2018 Security Blog post documents the four-vendor (Microsoft, Mozilla, Apple, Google) coordinated schedule, keyed on the certificate&apos;s &lt;code&gt;NotBefore&lt;/code&gt; date rather than on the root itself: per the per-root table in the blog post, the relevant cut-overs were September 30, 2018; January 31, 2019; and January 1, 2020 [@ms-blog-symantec-distrust]. Certificates &lt;em&gt;issued before&lt;/em&gt; the per-root NotBefore date stayed trusted to their natural expiration; certificates &lt;em&gt;issued after&lt;/em&gt; were rejected. The mechanism on the SChannel side is unchanged from DigiNotar -- the chain engine reads the updated trust posture from &lt;code&gt;authrootstl.cab&lt;/code&gt; / &lt;code&gt;disallowedcertstl.cab&lt;/code&gt; and applies it on the next chain build -- but the &lt;em&gt;operational character&lt;/em&gt; is completely different: a years-long planned phase-out instead of a week-long emergency cleanup.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A panic-mode distrust (DigiNotar) removes a root outright and propagates over days. A planned distrust (Symantec) uses NotBefore dates to grandfather pre-existing certificates while rejecting new ones, propagates over months to years, and gives the broader industry time to migrate. Both flow through the same &lt;code&gt;authrootstl.cab&lt;/code&gt; / &lt;code&gt;disallowedcertstl.cab&lt;/code&gt; plumbing. The governance subtlety lives in &lt;em&gt;which kind&lt;/em&gt; of distrust the program issues for a given CA&apos;s circumstances.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Enterprise observability: CAPI2/Operational event IDs&lt;/h3&gt;
&lt;p&gt;The governance flow ends at the operator&apos;s host -- but only if the operator can see it land. The Microsoft Learn troubleshooting article on the May 24, 2022 removal of the U.S. Federal Common Policy CA &quot;G1&quot; root carries the canonical observability recipe [@ms-learn-fcpca-removal]. On any Windows host you can enable the per-event tracing channel with &lt;code&gt;wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true&lt;/code&gt; and then watch the Event Viewer under &lt;code&gt;Applications and Services Logs &amp;gt; Microsoft &amp;gt; Windows &amp;gt; CAPI2 &amp;gt; Operational&lt;/code&gt; for the chain-engine events that the FCPCA-removal article enumerates verbatim [@ms-learn-fcpca-removal]: &lt;strong&gt;Event ID 90&lt;/strong&gt; logs every certificate consulted during chain building, &lt;strong&gt;Event ID 11&lt;/strong&gt; records chain-build failures, &lt;strong&gt;Event ID 30&lt;/strong&gt; records SSL or NTAuth policy-layer failures, &lt;strong&gt;Events 40-43&lt;/strong&gt; show stored CRLs and AIA paths, and &lt;strong&gt;Events 50-53&lt;/strong&gt; show network CRL accesses. The same article documents the empirical post-distrust propagation window in plain language: &quot;Applications and operations that depend on the &apos;G1&apos; root certificate will fail one to seven days after they receive the root certificate update.&quot; That one-to-seven-day window is the realistic latency budget between an MTRP distrust event landing in &lt;code&gt;authrootstl.cab&lt;/code&gt; and a given Windows host actually applying it -- a fingerprint operators can validate per host, not just per the rollout calendar.&lt;/p&gt;
&lt;p&gt;The PowerShell complement is brief and worth keeping in the muscle memory: &lt;code&gt;Get-ChildItem Cert:\LocalMachine\AuthRoot&lt;/code&gt; enumerates the currently-trusted roots; &lt;code&gt;Get-ChildItem Cert:\LocalMachine\Disallowed&lt;/code&gt; enumerates the disallowed store; both reflect whatever the last &lt;code&gt;crypt32.dll&lt;/code&gt; Automatic Root Update cycle left in place.&lt;/p&gt;
&lt;h3&gt;A cautionary tale: CVE-2020-0601, &quot;Curveball&quot;&lt;/h3&gt;
&lt;p&gt;In January 2020 the NSA disclosed a chain-engine spoofing vulnerability in &lt;code&gt;crypt32.dll&lt;/code&gt;&apos;s ECC certificate validation [@nvd-cve-2020-0601][@nsa-curveball-alternative]. The bug let an attacker craft a fraudulent ECC certificate that the Windows chain engine would treat as having been signed by a trusted root, by failing to fully verify the curve parameters against the cached trusted root&apos;s curve. Curveball is strictly a &lt;code&gt;crypt32.dll&lt;/code&gt; bug, not a SChannel SSP bug -- but it shaped the SChannel posture in two ways. First, it demonstrated that the chain engine and the SSP are &lt;em&gt;equally&lt;/em&gt; load-bearing for &quot;is this TLS connection trustworthy?&quot; Second, it was the most prominent example of the NSA disclosing a Windows vulnerability via the regular MSRC channel rather than hoarding it. Microsoft&apos;s January 2020 Patch Tuesday cycle addressed CVE-2020-0601 ahead of any public proof-of-concept.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The agility property the rest of this article celebrates is a property of CNG and SChannel. The trust pipeline -- &lt;code&gt;CertGetCertificateChain&lt;/code&gt;, &lt;code&gt;CertVerifyCertificateChainPolicy&lt;/code&gt;, the trust-store update mechanism in &lt;code&gt;crypt32.dll&lt;/code&gt; -- is a parallel concern. A perfectly executed TLS 1.3 handshake against a trusted-looking certificate that is actually fraudulent is still a compromise. Curveball is the canonical reminder that audit posture for SChannel-served endpoints has to cover both halves.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Certificate validation is the other axis on which SChannel has had to evolve. With the substrate (Sections 4 through 6) and the trust pipeline (this section) both stabilised, the article now turns to what &quot;modern SChannel&quot; actually looks like in the field -- TLS 1.3 on by default, TPM-backed server keys available for compliance scenarios, and the LSA-protection moat that makes session-key extraction harder than it used to be.&lt;/p&gt;
&lt;h2&gt;8. Modern SChannel: TLS 1.3, CredSSP for RDP, TPM-Backed Keys, and the LSASS Moat&lt;/h2&gt;
&lt;p&gt;By mid-2026 a default Windows 11 or Server 2022 / 2025 box is doing things its 2019 equivalent could not. TLS 1.3 is on. CredSSP wraps the RDP credential-delegation path inside a SChannel-protected TLS tunnel [@ms-cssp-landing]. The TPM is available as a key custodian. LSASS is a Protected Process; on most newer Windows 11 builds, Credential Guard is on by default. These are not four independent stories; they are four layers of the same defence-in-depth posture for the modern SChannel-served TLS endpoint.&lt;/p&gt;
&lt;h3&gt;TLS 1.3 in SChannel&lt;/h3&gt;
&lt;p&gt;RFC 8446 (Eric Rescorla, Mozilla, August 2018) [@rfc-8446] is the protocol generation that SChannel finally ships default-on in Windows Server 2022 (GA August 2021) and Windows 11 (GA October 5, 2021). Windows 10 and Windows Server 2019 SChannel remain TLS 1.2 only -- a fact worth naming because it is the most common cause of confusion in mixed-version Windows fleets [@ms-learn-tls-cipher-suites-server-2022].&lt;/p&gt;
&lt;p&gt;What changed at the wire-format level matters less for SChannel than how cleanly the changes mapped through CNG. TLS 1.3 shrank the cipher-suite menu to three AEAD suites: &lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;, &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt;, and &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt; [@rfc-8446]. The key-share namespace separated from the cipher-suite namespace -- &lt;code&gt;supported_groups&lt;/code&gt; (X25519, secp256r1, secp384r1, and now &lt;code&gt;X25519MLKEM768&lt;/code&gt;) is an independent extension from &lt;code&gt;cipher_suites&lt;/code&gt;. The handshake collapsed to one round trip.The 0-RTT (early data) feature of TLS 1.3 trades a round trip for replay-resistance complexity. SChannel&apos;s posture on 0-RTT is conservative: clients can request it, servers default to off unless explicitly opted in, and the documentation flags the replay-protection trade-offs.&lt;/p&gt;
&lt;p&gt;The downgrade-resistance sentinel in &lt;code&gt;ServerHello.random&lt;/code&gt; (RFC 8446 §4.1.3) is worth a beat. A TLS 1.3 server that, for whatever reason, is negotiated down to TLS 1.2 or below by middlebox interference fills the last eight bytes of its &lt;code&gt;ServerHello.random&lt;/code&gt; with one of two well-known sentinels (&lt;code&gt;44 4F 57 4E 47 52 44 01&lt;/code&gt; for &quot;downgraded from 1.3 to 1.2&quot;; &lt;code&gt;44 4F 57 4E 47 52 44 00&lt;/code&gt; for &quot;downgraded from 1.3 to 1.1 or earlier&quot;). A genuinely TLS 1.3-capable client checks for the sentinel after the handshake and aborts on mismatch. This puts the active-downgrade-attack envelope inside TLS 1.3 at a much narrower place than it was in TLS 1.2.&lt;/p&gt;

sequenceDiagram
    participant App as Application
    participant SC as SChannel SSP
    participant CNG as BCrypt / NCrypt
    participant Peer as Remote endpoint&lt;pre&gt;&lt;code&gt;App-&amp;gt;&amp;gt;SC: AcquireCredentialsHandle (server cert, key handle)
App-&amp;gt;&amp;gt;SC: InitializeSecurityContext (first call)
SC-&amp;gt;&amp;gt;CNG: BCrypt ECDH or MLKEM key share
SC-&amp;gt;&amp;gt;Peer: ClientHello (cipher_suites, supported_groups, key_share)
Peer--&amp;gt;&amp;gt;SC: ServerHello, EncryptedExtensions, Certificate, CertVerify, Finished
SC-&amp;gt;&amp;gt;CNG: NCryptSignHash or NCrypt key derive
SC-&amp;gt;&amp;gt;App: SECBUFFER tokens, then SEC_E_OK
App-&amp;gt;&amp;gt;SC: EncryptMessage and DecryptMessage on every record
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;CredSSP and the Remote Desktop NLA path&lt;/h3&gt;

The SSPI provider in `credssp.dll` that securely delegates Windows credentials from a client to a target server inside a TLS-protected tunnel. CredSSP is the SSP that backs Remote Desktop Network Level Authentication (RDP NLA): it wraps a SChannel TLS handshake, tunnels an SPNEGO / Kerberos / NTLM authentication inside that tunnel, performs a channel-binding hash exchange, and finally transmits the user&apos;s credential material to the destination encrypted under the SSPI session key [@ms-cssp-landing][@ms-cssp-glossary].
&lt;p&gt;The Microsoft Open Specifications page for the &lt;strong&gt;Credential Security Support Provider Protocol&lt;/strong&gt; ([MS-CSSP], version 21.0, April 2024) [@ms-cssp-landing] defines the protocol that backs Remote Desktop NLA. CredSSP is not a TLS protocol of its own; it is an SSP that &lt;em&gt;uses&lt;/em&gt; SChannel as its transport. The relationship is structural -- CredSSP is one of the most consequential &lt;em&gt;consumers&lt;/em&gt; of SChannel inside Windows, and almost every RDP session opened against a modern Windows host runs the CredSSP-over-SChannel sequence before the RDP video stream even starts.&lt;/p&gt;
&lt;p&gt;The five-step CredSSP-over-TLS sequence per the open-spec &quot;Processing Events and Sequencing Rules&quot; page [@ms-cssp-sequencing]:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;TLS handshake&lt;/strong&gt;. The CredSSP client and CredSSP server complete the SChannel TLS handshake; only the server presents a certificate, so the TLS-layer client is anonymous. After this step, all subsequent CredSSP messages are encrypted by the TLS channel. The MS-CSSP spec is explicit that &quot;the CredSSP Protocol does not extend the TLS wire protocol&quot; and that &quot;TLS session resumption is not supported&quot; [@ms-cssp-sequencing].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SPNEGO / Kerberos / NTLM tunnelled inside TLS&lt;/strong&gt;. Authentication tokens are carried in the &lt;code&gt;negoTokens&lt;/code&gt; field of the protocol&apos;s &lt;code&gt;TSRequest&lt;/code&gt; ASN.1 structure. The negotiation is performed by the SSPI Negotiate provider, which usually selects Kerberos when the client is domain-joined and falls back to NTLM otherwise.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Public-key (channel-binding) hash exchange&lt;/strong&gt;. This is the post-CVE-2018-0886 mechanism. The client computes a SHA-256 hash over a fixed magic string concatenated with a nonce and the server&apos;s &lt;code&gt;SubjectPublicKey&lt;/code&gt;, encrypts that hash under the SSPI session key established in step 2, and sends it in the &lt;code&gt;pubKeyAuth&lt;/code&gt; field of &lt;code&gt;TSRequest&lt;/code&gt;. The earlier (v2 / v3 / v4) &quot;encrypt the public key + 1&quot; scheme that was broken by CVE-2018-0886 has been replaced by this channel-binding hash for protocol versions 5 and 6 of CredSSP.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Server-side hash response with the server magic&lt;/strong&gt;. The server computes its own version of the hash (using a different fixed magic string for the server-to-client direction), encrypts it under the session key, and returns it in its own &lt;code&gt;pubKeyAuth&lt;/code&gt;. Both sides have now proven they hold the same session key bound to the same server public key, which closes a class of man-in-the-middle attacks against the inner authentication.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Encrypted credential transfer in &lt;code&gt;authInfo&lt;/code&gt;&lt;/strong&gt;. The credentials themselves -- a &lt;code&gt;TSPasswordCreds&lt;/code&gt;, &lt;code&gt;TSSmartCardCreds&lt;/code&gt;, or &lt;code&gt;TSRemoteGuardCreds&lt;/code&gt; structure depending on the chosen logon style -- are encrypted under the SSPI session key and transmitted in the &lt;code&gt;authInfo&lt;/code&gt; field. The destination decrypts them inside &lt;code&gt;lsass.exe&lt;/code&gt; (a PPL-protected process when RunAsPPL is enabled, see below), and the operating system then uses them to log the user on.&lt;/li&gt;
&lt;/ol&gt;

sequenceDiagram
    participant Client as RDP client
    participant Server as RDP server
    Note over Client,Server: Step 1 -- SChannel TLS handshake, server cert only, client anonymous at TLS layer
    Client-&amp;gt;&amp;gt;Server: ClientHello
    Server--&amp;gt;&amp;gt;Client: ServerHello, Certificate, ServerHelloDone (TLS 1.2) or one-RTT TLS 1.3 equivalent
    Client-&amp;gt;&amp;gt;Server: Finished -- TLS tunnel up
    Note over Client,Server: Step 2 -- SPNEGO Kerberos or NTLM tokens inside TSRequest.negoTokens, all inside TLS
    Client-&amp;gt;&amp;gt;Server: TSRequest with negoTokens (Kerberos AP-REQ or NTLM Type 1)
    Server--&amp;gt;&amp;gt;Client: TSRequest with negoTokens (Kerberos AP-REP or NTLM Type 2 then 3)
    Note over Client,Server: Step 3 -- channel-binding hash, client side (replaces broken pre-CVE-2018-0886 scheme)
    Client-&amp;gt;&amp;gt;Server: TSRequest.pubKeyAuth -- E(sessionKey, SHA256(client-magic, nonce, server SubjectPublicKey))
    Note over Client,Server: Step 4 -- server-side hash response with server-magic
    Server--&amp;gt;&amp;gt;Client: TSRequest.pubKeyAuth -- E(sessionKey, SHA256(server-magic, nonce, server SubjectPublicKey))
    Note over Client,Server: Step 5 -- encrypted credentials in TSRequest.authInfo
    Client-&amp;gt;&amp;gt;Server: TSRequest.authInfo -- E(sessionKey, TSPasswordCreds or TSSmartCardCreds or TSRemoteGuardCreds)
    Note over Server: lsass.exe decrypts, logs the user on
&lt;p&gt;The NLA threat-model framing per the archived Server 2008 R2 TechNet content is worth quoting because it captures what NLA actually buys [@ms-archive-nla]. NLA forces user authentication &lt;em&gt;before&lt;/em&gt; RDP session resources are allocated: &quot;It requires fewer remote computer resources initially. The remote computer uses a limited number of resources before authenticating the user, rather than starting a full remote desktop connection as in previous versions. It can help provide better security by reducing the risk of denial-of-service attacks.&quot; The two concrete payoffs are pre-auth DoS resistance and pre-auth RDP-codepath RCE mitigation. &lt;strong&gt;BlueKeep (CVE-2019-0708)&lt;/strong&gt; and &lt;strong&gt;DejaBlue (CVE-2019-1181 / 1182)&lt;/strong&gt; would each have been substantially harder to exploit on NLA-enabled hosts because the vulnerable RDP code paths sit &lt;em&gt;behind&lt;/em&gt; the NLA gate. NLA has been on by default for RDP Session Hosts since Windows Server 2012 R2.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A naive TLS-only deployment authenticates the &lt;em&gt;server&lt;/em&gt; to the &lt;em&gt;client&lt;/em&gt; via the server certificate, and authenticates the &lt;em&gt;user&lt;/em&gt; in plaintext above TLS. CredSSP adds a second layer: the user&apos;s authentication runs inside the TLS tunnel via SPNEGO / Kerberos / NTLM, and the user&apos;s credentials -- if delegated at all -- are encrypted under a session key that is channel-bound to the server&apos;s public key. With Remote Credential Guard (&lt;code&gt;TSRemoteGuardCreds&lt;/code&gt;), the destination&apos;s plaintext-credential exposure can be reduced to zero -- the destination receives only a service ticket usable for the session, not a reusable password hash.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;TPM-backed server keys via the Microsoft Platform Crypto Provider&lt;/h3&gt;
&lt;p&gt;The Microsoft Platform Crypto Provider (PCP) is a KSP that stores private keys non-exportable inside TPM 2.0. For an IIS or &lt;code&gt;SslStream&lt;/code&gt; server, switching to a PCP-backed certificate means the certificate&apos;s private key never resides in software memory; CertificateVerify signing during the handshake dispatches through &lt;code&gt;NCryptSignHash&lt;/code&gt; to PCP to &lt;code&gt;TPM2_Sign&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Two caveats need stating plainly. First, PCP-backed key operations are slower than software-backed key operations -- TPM 2.0 ECDSA / RSA signing latency is in the tens of milliseconds, which is a hard cap on handshake throughput. A high-volume edge IIS workload cannot meet its handshake-rate SLA with TPM-backed keys. Second, &lt;strong&gt;production prevalence of PCP-backed server keys remains low outside specific compliance scenarios&lt;/strong&gt;. The capability is shipping; the typical pattern is software-backed keys at the edge and TPM-backed keys for long-lived service-identity certificates where the latency does not dominate.TPM 2.0 signing latency is the ceiling for TPM-backed TLS handshake throughput. A high-volume IIS edge cannot meet handshake-rate SLAs with TPM-backed keys; that is why the typical pattern is software-backed keys at the edge and TPM-backed keys for service identity at lower call rates.&lt;/p&gt;
&lt;h3&gt;LSA Protection (RunAsPPL) and Credential Guard&lt;/h3&gt;

A Windows process-protection lattice where a &quot;protected&quot; process can be opened for certain rights only by callers whose protection level is greater than or equal to the target&apos;s. When LSASS runs as a PPL (via `HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL`), a non-PPL caller&apos;s `OpenProcess(LSASS, PROCESS_VM_READ, ...)` returns `ERROR_ACCESS_DENIED`. PPL is a same-privilege gate: it operates entirely inside Virtual Trust Level 0 (VTL0), the normal kernel/user world [@ms-learn-lsa-protection][@itm4n-runasppl].
&lt;p&gt;LSASS holds the cleartext session keys SChannel derives for each active TLS connection. Historically Mimikatz&apos;s &lt;code&gt;sekurlsa::schannel&lt;/code&gt; command read those keys directly out of LSASS memory after a debug-privilege &lt;code&gt;OpenProcess&lt;/code&gt;. Once RunAsPPL is enforced, the read fails: a non-PPL Mimikatz cannot open LSASS for memory read [@ms-learn-lsa-protection].&lt;/p&gt;
&lt;p&gt;Clément Labro&apos;s RunAsPPL analysis (&lt;code&gt;itm4n&lt;/code&gt;) is the canonical practitioner&apos;s text on the gotchas [@itm4n-runasppl]. The single most important framing point Labro makes is the disambiguation between PPL and Credential Guard:&lt;/p&gt;

When it comes to protecting against credentials theft on Windows, enabling LSA Protection (a.k.a. RunAsPPL) on LSASS may be considered as the very first recommendation to implement... Credential Guard and LSA Protection are actually complementary. -- Clément Labro, *Do You Really Know About LSA Protection (RunAsPPL)?* [@itm4n-runasppl]
&lt;p&gt;The disambiguation matters because the two mechanisms operate at different layers. &lt;strong&gt;PPL is a same-privilege gate inside VTL0.&lt;/strong&gt; &lt;strong&gt;Credential Guard moves credential material into the LSAIso trustlet at VTL1&lt;/strong&gt;, behind the VBS / Hyper-V boundary -- a cross-privilege isolation that PPL cannot provide [@ms-learn-credential-guard]. The misconception that Credential Guard alone defeats &lt;code&gt;mimikatz sekurlsa::schannel&lt;/code&gt; is one of the most common operator errors in this space. They stack. They are not substitutes.&lt;/p&gt;

flowchart TB
    subgraph VTL0[&quot;VTL0 -- Normal World&quot;]
        subgraph User[&quot;User mode&quot;]
            App[&quot;Mimikatz / arbitrary code -- non-PPL&quot;]
        end
        subgraph Kern[&quot;Kernel mode (NT kernel)&quot;]
            LSASS[&quot;LSASS -- PPL when RunAsPPL=1&quot;]
            SCh[schannel.dll loaded in LSASS]
        end
    end
    subgraph VTL1[&quot;VTL1 -- Isolated User Mode (VBS)&quot;]
        LSAIso[&quot;LSAIso trustlet -- Credential Guard&quot;]
    end
    App -. &quot;OpenProcess(LSASS, VM_READ) -- denied when PPL on&quot; .-&amp;gt; LSASS
    LSASS -. &quot;RPC to LSAIso for credential ops&quot; .-&amp;gt; LSAIso
    SCh --&amp;gt; LSASS
&lt;p&gt;The last open question on RunAsPPL is whether the protection itself is bypassable. The honest answer is &quot;less so than it used to be.&quot; Labro&apos;s follow-up &quot;The End of PPLdump&quot; walks through how a 2021-era SymLink + KnownDlls trick that defeated PPL was patched, and how the post-patch PPL invariant holds for current Windows servicing branches [@itm4n-ppldump]. Combined with HVCI and VBS-on-by-default in newer Windows 11 builds, the modern SChannel session key is genuinely harder to lift than it was in 2019.&lt;/p&gt;

Operators frequently set `HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL = 1` and stop there. Microsoft&apos;s Configure-Added-LSA-Protection doc walks through the additional values (`RunAsPPLBoot` for the boot-level enforcement, the corresponding UEFI variable for tamper resistance) that complete the posture [@ms-learn-lsa-protection]. The minimum recommended configuration is not a single value in a single hive; reading the official doc end to end is faster than rediscovering this from a bug report.
&lt;p&gt;Modern SChannel is the substrate plus the trust pipeline plus the CredSSP RDP wrapper plus the LSASS moat. The one piece still in flight as of mid-2026 is the cryptographic primitive nobody had in 2009 -- the post-quantum hybrid key exchange.&lt;/p&gt;
&lt;h2&gt;9. The Post-Quantum Pivot: ML-KEM, SymCrypt, and Hybrid TLS 1.3&lt;/h2&gt;
&lt;p&gt;On August 13, 2024, NIST published &lt;strong&gt;FIPS 203&lt;/strong&gt; -- the standard for ML-KEM, the first quantum-resistant key-encapsulation mechanism the United States government endorses for production use [@fips-203]. The standard defines three parameter sets (ML-KEM-512, ML-KEM-768, ML-KEM-1024) with security grounded in the Module Learning With Errors problem. The SymCrypt CHANGELOG entry for v103.5.0 reads, verbatim: &quot;Add ML-KEM per final FIPS 203&quot; [@symcrypt-changelog]. That single line is what the receipts on Microsoft&apos;s twenty-year algorithm-agility bet look like in the present tense.&lt;/p&gt;

A **KEM** is a public-key construction that, given a recipient&apos;s public key, produces a (ciphertext, shared-secret) pair such that the recipient can recover the shared secret from the ciphertext using its private key. ML-KEM is the NIST-standardised KEM derived from the CRYSTALS-Kyber proposal; ML-KEM-768 generates a 1184-byte public key and a 1088-byte ciphertext and produces a 32-byte shared secret. FIPS 203 [@fips-203] is the final standard; SymCrypt v103.5.0 is the first SymCrypt release shipping ML-KEM per that standard [@symcrypt-changelog].
&lt;h3&gt;What is shipping&lt;/h3&gt;
&lt;p&gt;The PQC primitives Microsoft has rolled into SymCrypt are publicly tracked in the project CHANGELOG [@symcrypt-changelog]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;v103.5.0&lt;/strong&gt; -- ML-KEM (FIPS 203) [@fips-203].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v103.6.0&lt;/strong&gt; -- LMS (NIST SP 800-208 stateful hash-based signature) and AES-KW(P).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v103.7.0&lt;/strong&gt; -- ML-DSA (FIPS 204) [@fips-204].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v103.11.0&lt;/strong&gt; -- Composite ML-KEM (hybrid ML-KEM with a classical KEM).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v103.12.0&lt;/strong&gt; -- Composite ML-DSA (hybrid ML-DSA with a classical signature scheme).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;v103.12.1&lt;/strong&gt; -- AVX-512 AES-GCM (up to ~35% throughput improvement).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CNG exposes the matching &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; with parameter-set selectors -- &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_768&lt;/code&gt;, &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_1024&lt;/code&gt;, and so on [@ms-learn-cng-mlkem-examples]. The Microsoft Learn page for the CNG ML-KEM API surface carries an explicit &quot;prerelease product / Windows Insider Preview&quot; banner. The article therefore frames SChannel&apos;s PQC support as &lt;strong&gt;preview / Insider-channel as of mid-2026&lt;/strong&gt;, with broader GA rollout in flight; the Microsoft Tech Community PQC announcement (December 2024) is the narrative anchor and the Insider-Preview banner on the API doc is the technical hedge [@ms-tech-community-pqc][@ms-tech-community-pqc-companion].&lt;/p&gt;
&lt;p&gt;The hash-based and stateless-hash-based signature side of PQC (SLH-DSA, FIPS 205 [@fips-205]) is shipping in SymCrypt and CNG along the same trajectory. Section 11 returns to why the &lt;em&gt;signature&lt;/em&gt;-side PQC transition is harder than the &lt;em&gt;KEM&lt;/em&gt;-side transition.&lt;/p&gt;
&lt;h3&gt;Hybrid TLS 1.3 key exchange: &lt;code&gt;X25519MLKEM768&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;The IETF-converged named group for the most-deployed hybrid is &lt;code&gt;X25519MLKEM768&lt;/code&gt;, defined in &lt;code&gt;draft-ietf-tls-ecdhe-mlkem&lt;/code&gt; (Kris Kwiatkowski / PQShield, Panos Kampanakis / AWS, Bas Westerbaan / Cloudflare, Douglas Stebila / University of Waterloo; currently -05 as of May 26, 2026) [@draft-ietf-tls-ecdhe-mlkem]. The draft also defines &lt;code&gt;SecP256r1MLKEM768&lt;/code&gt; and &lt;code&gt;SecP384r1MLKEM1024&lt;/code&gt; for deployments that prefer NIST curves over X25519.&lt;/p&gt;
&lt;p&gt;The handshake mechanics are clean. The client sends &lt;code&gt;mlkem_pk || x25519_pk&lt;/code&gt; (1184 + 32 = 1216 bytes) in its &lt;code&gt;key_share&lt;/code&gt;; the server responds with &lt;code&gt;mlkem_ct || x25519_pk&lt;/code&gt; (1088 + 32 = 1120 bytes); both sides compute &lt;code&gt;shared_secret = mlkem_ss || x25519_ss&lt;/code&gt; (32 + 32 = 64 bytes) and feed that into TLS 1.3&apos;s HKDF-Extract as &lt;code&gt;IKM&lt;/code&gt;.&lt;/p&gt;

sequenceDiagram
    participant Client
    participant Server&lt;pre&gt;&lt;code&gt;Note over Client: Generate X25519 keypair and ML-KEM-768 keypair
Client-&amp;gt;&amp;gt;Server: ClientHello with key_share (mlkem_pk concatenated with x25519_pk -- 1216 B)
Note over Server: Generate X25519 keypair, ML-KEM encapsulate to client mlkem_pk
Server-&amp;gt;&amp;gt;Client: ServerHello with key_share (mlkem_ct concatenated with x25519_pk -- 1120 B)
Note over Client: X25519 DH, ML-KEM decapsulate
Note over Client,Server: shared_secret -- mlkem_ss concatenated with x25519_ss (64 B)
Note over Client,Server: HKDF-Extract over shared_secret continues TLS 1.3 key schedule
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The construction is &lt;strong&gt;defence-in-depth against either a classical-only break or a quantum-only break&lt;/strong&gt;: an adversary must defeat &lt;em&gt;both&lt;/em&gt; X25519 and ML-KEM-768 to recover the session key, and the hybrid analysis (Bindel et al., PQCrypto 2019) shows the construction is at least as secure as the stronger of the two components. The minor cost is the inflated &lt;code&gt;ClientHello&lt;/code&gt; and &lt;code&gt;ServerHello&lt;/code&gt; (about 1.2 KB extra) and a couple of milliseconds of ML-KEM operations.&lt;/p&gt;
&lt;p&gt;{`
// Pseudocode for the X25519MLKEM768 shared-secret concatenation.
// In real SChannel: BCryptSecretAgreement(BCRYPT_ECDH_P256_ALGORITHM_HANDLE / X25519, ...)
//                 + BCryptDecapsulate(BCRYPT_MLKEM_ALG_HANDLE, parameter_set = 768, ...)&lt;/p&gt;
&lt;p&gt;function x25519_dh(privA, pubB)          { return new Uint8Array(32).fill(0xAA); }  // 32 B
function mlkem768_decaps(privA, ct)      { return new Uint8Array(32).fill(0xBB); }  // 32 B&lt;/p&gt;
&lt;p&gt;const x25519_ss = x25519_dh(&apos;clientX25519Priv&apos;, &apos;serverX25519Pub&apos;);
const mlkem_ss  = mlkem768_decaps(&apos;clientMLKEMPriv&apos;, &apos;serverMLKEMCt&apos;);&lt;/p&gt;
&lt;p&gt;const hybrid_secret = new Uint8Array(64);
hybrid_secret.set(mlkem_ss, 0);
hybrid_secret.set(x25519_ss, 32);&lt;/p&gt;
&lt;p&gt;console.log(&apos;IKM length for HKDF-Extract:&apos;, hybrid_secret.length, &apos;bytes&apos;);
console.log(&apos;First byte: 0x&apos; + hybrid_secret[0].toString(16),
            &apos;(from ML-KEM half, defends against quantum break)&apos;);
console.log(&apos;Byte 32: 0x&apos; + hybrid_secret[32].toString(16),
            &apos;(from X25519 half, defends against classical break)&apos;);
`}&lt;/p&gt;
&lt;h3&gt;The agility payoff&lt;/h3&gt;
&lt;p&gt;This rotation is the cleanest demonstration of Section 4&apos;s thesis. Adding &lt;code&gt;X25519MLKEM768&lt;/code&gt; to SChannel required: (a) a SymCrypt primitive (v103.5.0+ for ML-KEM, with X25519 long present per RFC 7748 [@rfc-7748]); (b) a new BCrypt provider registration (&lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; and the hybrid named-group plumbing); (c) a new SChannel named-group entry. No IIS source change. No SQL Server source change. No &lt;code&gt;SslStream&lt;/code&gt; source change. Eighteen years after Vista shipped CNG, the substrate is producing receipts for a brand-new algorithm family.&lt;/p&gt;
&lt;p&gt;The deployment side is moving faster than most ten-year forecasts in cryptography ever predicted. Cloudflare&apos;s measurements (March 2024) put PQC-secured TLS 1.3 connections at &quot;nearly two percent&quot; of inbound, with the team forecasting double-digit percentages by end of 2024 [@cloudflare-pq-2024]. Cloudflare&apos;s origin-side PQC rollout has been live since September 2023 [@cloudflare-pq-origins]. Chrome / BoringSSL, Edge (via BoringSSL), and Firefox / NSS ship &lt;code&gt;X25519MLKEM768&lt;/code&gt; client-side. OpenSSL 3.5 ships ML-KEM. Server-side SChannel adoption is rolling through the Insider channel and the official Tech Community posts as of mid-2026 [@ms-learn-cng-mlkem-examples][@ms-tech-community-pqc-companion].Cloudflare&apos;s measurements (March 2024) put PQC-secured TLS 1.3 connections at &quot;nearly two percent&quot; of their inbound; by the end of 2024 they expected double-digit percentages [@cloudflare-pq-2024]. The transition is moving faster than most ten-year forecasts in cryptography ever predicted.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The hybrid PQC handshake is cheap in absolute terms but the cost is not uniform across deployment shapes. On a typical Server 2022 IIS edge with software-backed RSA-2048 plus AES-NI, sustained handshake rates run in the &lt;strong&gt;thousands per second per core&lt;/strong&gt;; the X25519MLKEM768 hybrid adds roughly &lt;strong&gt;5-10 ms of handshake latency&lt;/strong&gt;, which is in the noise relative to the per-handshake cost of an RSA-2048 signature. On a TPM-key-bound edge the picture inverts: the Microsoft Platform Crypto Provider is serialised by TPM 2.0 &lt;code&gt;TPM2_Sign&lt;/code&gt; latency (tens of milliseconds per signature), so sustained handshake rates sit in the &lt;strong&gt;tens to roughly one hundred handshakes per second per host&lt;/strong&gt;, and the same ~5-10 ms hybrid delta becomes a non-trivial fraction of the per-handshake budget. AES-NI bulk throughput on AES-256-GCM is roughly &lt;strong&gt;5-10 Gbps per core&lt;/strong&gt; (the AVX-512 AES-GCM landing in SymCrypt v103.12.1 shifts that ceiling further [@symcrypt-changelog]) so the post-handshake data path is not the bottleneck. Operator decision support: if you are software-key-bound, the hybrid PQC delta is noise. If you are TPM-key-bound, your handshake rate is already in the tens, and the hybrid delta is meaningful enough to budget for.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; As of article publication (mid-2026), SChannel&apos;s &lt;code&gt;X25519MLKEM768&lt;/code&gt; support is preview / Insider-channel; the CNG ML-KEM page carries the explicit Windows Insider Preview banner [@ms-learn-cng-mlkem-examples]. Track the SymCrypt CHANGELOG for primitive landings [@symcrypt-changelog] and the Microsoft Tech Community PQC posts for OS-channel GA announcements [@ms-tech-community-pqc]. Do not assert GA dates that have not landed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the PQC rotation is the agility payoff, the next question is the obvious one: how does SChannel&apos;s answer compare to the other TLS stacks shipping on the same calendar? OpenSSL, BoringSSL, NSS, and Apple&apos;s Network framework have all had to solve the same algorithm-agility problem -- and they have all made different trade-offs.&lt;/p&gt;
&lt;h2&gt;10. Competing Approaches: How Other TLS Stacks Solve Algorithm Agility&lt;/h2&gt;
&lt;p&gt;Algorithm agility is not a property of TLS-the-protocol. It is a property of the &lt;em&gt;substrate&lt;/em&gt; underneath the protocol. Every major TLS implementation has had to answer the same question -- &quot;how do we add a new primitive without breaking our consumers?&quot; -- and the answers are surprisingly different.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Stack&lt;/th&gt;
&lt;th&gt;Substrate model&lt;/th&gt;
&lt;th&gt;Stability commitment&lt;/th&gt;
&lt;th&gt;PQC integration as of mid-2026&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;SChannel / CNG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;BCrypt providers + NCrypt KSPs; Win32 API-stable [@ms-learn-cng-portal]&lt;/td&gt;
&lt;td&gt;Strong: Win32 SSPI surface frozen&lt;/td&gt;
&lt;td&gt;ML-KEM in SymCrypt v103.5.0 [@symcrypt-changelog]; &lt;code&gt;X25519MLKEM768&lt;/code&gt; Insider Preview [@ms-learn-cng-mlkem-examples]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenSSL 3.x&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;OSSL_PROVIDER&lt;/code&gt; modules via &lt;code&gt;OSSL_DISPATCH&lt;/code&gt; arrays [@openssl-provider7-3.0]&lt;/td&gt;
&lt;td&gt;Strong-by-major-version&lt;/td&gt;
&lt;td&gt;OQS-Provider for early PQC; ML-KEM in OpenSSL 3.5&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BoringSSL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Single source tree; &quot;rolling release&quot;; no provider model [@boringssl-readme]&lt;/td&gt;
&lt;td&gt;Explicitly none (&quot;no guarantees of API or ABI stability&quot;)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;X25519MLKEM768&lt;/code&gt; shipping; consumer vendoring required&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NSS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OASIS PKCS #11 v3.1 modules via &lt;code&gt;CK_FUNCTION_LIST&lt;/code&gt; [@nss-3.111-release-notes][@oasis-pkcs11-v3.1]&lt;/td&gt;
&lt;td&gt;Strong (Firefox compatibility)&lt;/td&gt;
&lt;td&gt;ML-KEM via PKCS #11 v3.1 &lt;code&gt;C_Encapsulate&lt;/code&gt; / &lt;code&gt;C_Decapsulate&lt;/code&gt;; &lt;code&gt;X25519MLKEM768&lt;/code&gt; in Firefox 132&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Apple Network framework / Secure Transport&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Framework-version pinning per OS release [@apple-network-framework][@apple-secure-transport]&lt;/td&gt;
&lt;td&gt;Strong per OS version&lt;/td&gt;
&lt;td&gt;Hybrid KEM shipping in newer Network framework releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;.NET &lt;code&gt;SslStream&lt;/code&gt; cross-platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Delegates to host OS stack [@dotnet-cross-platform-crypto]&lt;/td&gt;
&lt;td&gt;Strong per .NET version&lt;/td&gt;
&lt;td&gt;Inherits underlying stack&apos;s PQC support&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;OpenSSL 3.x: &lt;code&gt;OSSL_PROVIDER&lt;/code&gt;, explicit contexts, three in-tree providers&lt;/h3&gt;
&lt;p&gt;OpenSSL 3.0 replaced the older &lt;code&gt;ENGINE&lt;/code&gt; model with the &lt;strong&gt;&lt;code&gt;OSSL_PROVIDER&lt;/code&gt;&lt;/strong&gt; system, described in the &lt;code&gt;provider(7)&lt;/code&gt; manpage as &quot;a unit of code that provides one or more implementations for various operations for diverse algorithms&quot; [@openssl-provider7-3.0]. A provider exposes its operations through an &lt;code&gt;OSSL_DISPATCH&lt;/code&gt; array of &lt;code&gt;{function-id, function-pointer}&lt;/code&gt; pairs. The loader&apos;s entry point is a single exported function with this exact signature [@openssl-provider7-3.0]:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-c&quot;&gt;int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,
                       const OSSL_DISPATCH *in,
                       const OSSL_DISPATCH **out,
                       void **provctx);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;in&lt;/code&gt; array gives the provider the callbacks the OpenSSL library is willing to provide to the provider (logging, error reporting, library-context queries, parameter accessors) [@openssl-provider-base7-3.0]; the &lt;code&gt;out&lt;/code&gt; array is filled in by the provider with the operations it implements. The &lt;code&gt;provctx&lt;/code&gt; is the provider&apos;s own per-instance state.&lt;/p&gt;
&lt;p&gt;OpenSSL 3.0 ships three in-tree providers: &lt;strong&gt;default&lt;/strong&gt; (the modern algorithm set), &lt;strong&gt;legacy&lt;/strong&gt; (RC4, MD4, IDEA, and other backward-compatibility primitives), and &lt;strong&gt;FIPS&lt;/strong&gt; (the FIPS 140-3-validated subset). Out-of-tree, the OQS-Provider plugs PQC primitives into OpenSSL without recompiling the OpenSSL build itself. The substantive contrast with CNG: OpenSSL makes the provider context an &lt;em&gt;explicit&lt;/em&gt; parameter via &lt;code&gt;OSSL_LIB_CTX *&lt;/code&gt;, which means multiple isolated provider sets can coexist inside one process (a FIPS-validated workload and a legacy workload in the same binary). CNG keeps provider dispatch global per-process. Both models are functionally agile; OpenSSL&apos;s is more &lt;em&gt;compositional&lt;/em&gt; at runtime, while CNG&apos;s is more &lt;em&gt;governed&lt;/em&gt; through the Windows servicing branch.&lt;/p&gt;
&lt;h3&gt;BoringSSL&apos;s anti-agility position&lt;/h3&gt;
&lt;p&gt;BoringSSL is Google&apos;s TLS stack used by Chromium and (via Chromium) Microsoft Edge. The project README says, verbatim:&lt;/p&gt;

Although BoringSSL is an open source project, it is not intended for general use, as OpenSSL is. We don&apos;t recommend that third parties depend upon it. Doing so is likely to be frustrating because there are no guarantees of API or ABI stability. -- BoringSSL README [@boringssl-readme]
&lt;p&gt;BoringSSL achieves agility by &lt;em&gt;refusing&lt;/em&gt; to absorb it as a public API. Consumers vendor BoringSSL into their own build tree and accept the lift of tracking head. Chromium does this; Edge inherits it; cURL ships configurations that link against BoringSSL when the consumer asks for it. The model is the inverse of CNG&apos;s: maximum velocity for the maintainer, maximum churn for the consumer. For a vendor whose chief constraint is API stability for the Win32 / .NET universe, BoringSSL&apos;s model is structurally incompatible. For a vendor whose chief constraint is shipping the modern internet&apos;s TLS posture into a browser monthly, BoringSSL&apos;s model is the right answer.&lt;/p&gt;
&lt;h3&gt;NSS and PKCS #11&lt;/h3&gt;
&lt;p&gt;Mozilla&apos;s NSS predates almost every other stack here and uses the &lt;strong&gt;OASIS PKCS #11&lt;/strong&gt; (Cryptoki) module standard as its agility hinge [@oasis-pkcs11-v3.1]. A PKCS #11 module exposes a single entry point, &lt;code&gt;C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)&lt;/code&gt;, which returns a table of roughly seventy function pointers. Those functions are organised around a three-level hierarchy: a &lt;em&gt;slot&lt;/em&gt; is a place where a &lt;em&gt;token&lt;/em&gt; sits; a &lt;em&gt;token&lt;/em&gt; holds &lt;em&gt;objects&lt;/em&gt; (keys, certificates, data); cryptographic operations are invoked against an object referenced by &lt;code&gt;CK_OBJECT_HANDLE&lt;/code&gt; and parametrised by a &lt;code&gt;CK_MECHANISM&lt;/code&gt; (e.g. &lt;code&gt;CKM_AES_GCM&lt;/code&gt;, &lt;code&gt;CKM_ECDH1_DERIVE&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;NSS itself ships two PKCS #11 modules out of the box: the &lt;strong&gt;NSS softoken&lt;/strong&gt; (&lt;code&gt;softokn3&lt;/code&gt;, in-process software primitives) and the &lt;strong&gt;NSS FIPS softoken&lt;/strong&gt; (the FIPS-validated variant). Hardware PKCS #11 modules for HSMs and smart cards load through the same &lt;code&gt;SECMOD_LoadUserModule&lt;/code&gt; API. PQC arrived in NSS via PKCS #11 v3.1&apos;s KEM operations: &lt;code&gt;C_Encapsulate&lt;/code&gt; and &lt;code&gt;C_Decapsulate&lt;/code&gt; are standardised verbs that ML-KEM-768 implementations can expose without needing the historic &lt;code&gt;CKM_VENDOR_DEFINED&lt;/code&gt; mechanism-ID reservation pattern. NSS 3.111 (released April 28, 2025) is the release marker for full PKCS #11 v3.1 adoption in NSS [@nss-3.111-release-notes]; Firefox shipped &lt;code&gt;X25519MLKEM768&lt;/code&gt; client-side in Firefox 132 (October 2024). The high-order contrast with CNG: PKCS #11 is a &lt;em&gt;cross-vendor industry standard&lt;/em&gt;, so the same NSS / Firefox runtime can talk to a Mozilla softoken, an HSM module, and a smart-card module through a single interface; CNG is single-vendor (Microsoft) but exposes a fully Microsoft-curated provider universe through a stable Win32 API.&lt;/p&gt;
&lt;h3&gt;Apple Network framework and CryptoKit&lt;/h3&gt;
&lt;p&gt;Apple&apos;s TLS-stack history splits into the deprecated &lt;strong&gt;Secure Transport&lt;/strong&gt; API [@apple-secure-transport] and the modern &lt;strong&gt;Network framework&lt;/strong&gt; introduced in macOS 10.14 and iOS 12 [@apple-network-framework]. Secure Transport&apos;s design was C-API and typed-enum: &lt;code&gt;SSLProtocol&lt;/code&gt; selected the TLS version; &lt;code&gt;SSLCipherSuite&lt;/code&gt; integers were the IANA cipher-suite codepoints; the developer worked with &lt;code&gt;SSLContextRef&lt;/code&gt; handles much as a Windows developer works with &lt;code&gt;CtxtHandle&lt;/code&gt;. The agility model was &lt;em&gt;named-enum-per-OS-release&lt;/em&gt;: every TLS version and cipher was a compile-time constant, and the SDK version the application was built against determined what was selectable.&lt;/p&gt;
&lt;p&gt;Network framework moved the API to a Swift-first surface (&lt;code&gt;NWProtocolTLS.Options&lt;/code&gt;, &lt;code&gt;sec_protocol_options_set_min_tls_protocol_version&lt;/code&gt;) and started Apple&apos;s deprecation glide for Secure Transport. On top of the network-layer primitives, &lt;strong&gt;CryptoKit&lt;/strong&gt; (iOS 13 / macOS 10.15) provides the Swift-idiomatic high-level crypto API for symmetric AEAD, ECDH, ECDSA, and (via subsequent OS releases) the post-quantum primitives [@apple-cryptokit]. The cadence is bound to Apple&apos;s annual OS release: a new algorithm becomes available when the OS that ships it becomes available, and applications that need it bump their minimum-deployment target.&lt;/p&gt;
&lt;p&gt;The structural contrast with CNG: Apple&apos;s model gives the platform vendor very tight control over what is selectable and a clean deprecation path (you simply drop a constant from a future SDK), but the cost is that &lt;em&gt;the application&apos;s algorithm options track the OS version the application is built for&lt;/em&gt;. CNG decouples those -- a Windows 11 application built against a 2015 Win32 SDK still sees new BCrypt algorithm strings as the OS ships them, because the dispatch is by string at runtime.&lt;/p&gt;
&lt;h3&gt;.NET &lt;code&gt;SslStream&lt;/code&gt; -- one API, three host backends&lt;/h3&gt;
&lt;p&gt;.NET&apos;s &lt;code&gt;System.Net.Security.SslStream&lt;/code&gt; is &lt;em&gt;identical&lt;/em&gt; on every host. The implementation, however, delegates to the host operating system&apos;s TLS stack. On Windows it calls into SChannel through SSPI; on Linux it calls into OpenSSL via &lt;code&gt;System.Security.Cryptography.Native.OpenSsl&lt;/code&gt;; on macOS it calls into Apple&apos;s Network framework via &lt;code&gt;System.Security.Cryptography.Native.Apple&lt;/code&gt; [@dotnet-cross-platform-crypto]. There is no &quot;pick a backend&quot; knob in &lt;code&gt;SslStream&lt;/code&gt;; the runtime picks whichever backend the host OS provides.&lt;/p&gt;
&lt;p&gt;The agility consequence for PQC is direct. A &lt;code&gt;.NET 10&lt;/code&gt; application running on a Windows Insider build whose SChannel has &lt;code&gt;X25519MLKEM768&lt;/code&gt; enabled by default will negotiate hybrid PQC automatically. The same application running on macOS gets classical X25519 until Apple ships hybrid in Network framework. The same application running on Linux against OpenSSL 3.5 gets ML-KEM via OpenSSL&apos;s in-tree implementation. &lt;strong&gt;The application source code never changes; the wire-level cryptography is whatever the host&apos;s TLS stack negotiates.&lt;/strong&gt; This is the agility property in cross-platform clothing -- and it works because each host&apos;s substrate is itself agile.&lt;/p&gt;
&lt;h3&gt;Five substrates, five answers&lt;/h3&gt;
&lt;p&gt;The cross-stack comparison surfaces the meta-point. Five substrates: CNG, OpenSSL &lt;code&gt;OSSL_PROVIDER&lt;/code&gt;, BoringSSL&apos;s vendored tree, PKCS #11, Apple&apos;s named-enum SDK. Five answers, all functional, all optimising for different deployment models. SChannel / CNG is the most &lt;em&gt;registry-driven and single-vendor-extensible&lt;/em&gt;; OpenSSL is the most &lt;em&gt;context-explicit&lt;/em&gt;; PKCS #11 is the most &lt;em&gt;cross-vendor-standardised&lt;/em&gt;; BoringSSL is the most &lt;em&gt;aggressive-by-refusing-stability&lt;/em&gt;; Apple is the most &lt;em&gt;named-enum-SDK-bound&lt;/em&gt;. None of these is &quot;the right&quot; answer -- each is the answer that fits its vendor&apos;s deployment shape. The agility property is &lt;em&gt;of the substrate&lt;/em&gt;, and the right substrate depends on what you ship and to whom.&lt;/p&gt;
&lt;p&gt;Agility is the &lt;em&gt;capacity&lt;/em&gt; for rotation. Whether the rotation actually happens is a separate problem -- one that the empirical evidence of TLS 1.0 / 1.1&apos;s 25-year tail tells a sobering story about.&lt;/p&gt;
&lt;h2&gt;11. Limits and Open Problems&lt;/h2&gt;
&lt;p&gt;Algorithm agility is necessary. It is not sufficient. TLS 1.0 was published in 1999 [@rfc-2246]; default-off in stable Windows did not arrive until 2024-2025 [@ms-learn-tls-registry-settings]. Twenty-five years. The substrate could have rotated TLS 1.0 out a decade earlier; the world would not move.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Agility lets the substrate add a new primitive and lets operators disable an old one. It cannot force operators of TLS-1.2-only or TLS-1.0-only endpoints to upgrade. The substrate solved the rotation problem; the world is the bottleneck. Trust-store distrust events, OS-level deprecation defaults, browser warnings, and eventual code-path removal are the levers that close the gap -- but each operates on the scale of years, not weeks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This re-organises the reader&apos;s understanding of why Microsoft&apos;s posture on PQC is &quot;ship and hedge&quot; rather than &quot;ship and declare victory.&quot; The substrate is ahead of the protocol; the protocol is ahead of the deployments; the deployments are years behind.&lt;/p&gt;
&lt;h3&gt;The downgrade-attack envelope&lt;/h3&gt;
&lt;p&gt;RFC 7568 (June 2015) formally prohibits SSL 3.0 [@rfc-7568]; RFC 6176 (March 2011) formally prohibits SSL 2.0 [@rfc-6176]. The TLS Fallback SCSV cipher suite (RFC 7507) bounded downgrade attacks within TLS 1.0 / 1.1 / 1.2. TLS 1.3&apos;s &lt;code&gt;ServerHello.random&lt;/code&gt; downgrade-resistance sentinel (RFC 8446 §4.1.3 [@rfc-8446]) closes the downgrade attack surface &lt;em&gt;within&lt;/em&gt; TLS 1.3. The remaining downgrade exposure lives at the boundary with TLS-1.2-only counterparties -- a boundary that shrinks every year but does not yet close.&lt;/p&gt;
&lt;h3&gt;Signature-side PQC and the chain-size problem&lt;/h3&gt;
&lt;p&gt;The PQC hybrid KEM transition is the easy half of the post-quantum migration. The signature side is harder. ML-DSA-65 produces ~3.3 KB signatures with ~2 KB public keys [@fips-204]; SLH-DSA at 128-bit security produces signatures in the 7 to 17 KB range [@fips-205]; Falcon (FN-DSA in the NIST nomenclature) produces ~1 KB signatures but is harder to implement correctly because of its floating-point Gaussian sampling.&lt;/p&gt;
&lt;p&gt;Why this matters for SChannel: TLS server certificate chains are sent in the &lt;code&gt;Certificate&lt;/code&gt; handshake message. A chain that fits inside TCP&apos;s initial congestion window (typically 10 segments, or about 14.6 KB) ships in one round trip; a chain that overflows the IW takes another RTT. Adding a 3.3 KB ML-DSA signature plus a 2 KB ML-DSA public key to every cert in a chain rapidly blows past 14.6 KB for a typical leaf-intermediate-root structure. The community working hypothesis is that the hybrid-signature transition in TLS will lag the hybrid-KEM transition by years; SymCrypt&apos;s Composite ML-DSA support (v103.12.0) [@symcrypt-changelog] is the substrate-side preparation for that transition, but the IETF TLS WG signature-side drafts are still in flight.&lt;/p&gt;
&lt;h3&gt;Composite-identifier namespace sprawl in CNG&lt;/h3&gt;
&lt;p&gt;Every hybrid construction adds at least one new CNG algorithm identifier. &lt;code&gt;X25519MLKEM768&lt;/code&gt;, &lt;code&gt;SecP256r1MLKEM768&lt;/code&gt;, &lt;code&gt;SecP384r1MLKEM1024&lt;/code&gt; already exist. Composite ML-DSA + ECDSA is in flight. If pure ML-KEM-1024 and pure SLH-DSA are eventually default-on, the algorithm namespace doubles per hybrid family. The substrate is &lt;em&gt;capable&lt;/em&gt; of absorbing the sprawl; whether the cipher-suite registry remains legible to administrators is a separate user-interface problem.&lt;/p&gt;
&lt;h3&gt;The opaque-engine bargain&lt;/h3&gt;
&lt;p&gt;SymCrypt is open since July 2019 and externally auditable [@symcrypt-github]. The SChannel SSP binary itself remains closed-source. External behavioural verification -- Hubert Kario&apos;s &lt;code&gt;tlsfuzzer&lt;/code&gt; -- is the closest the public has to a formal specification of &lt;code&gt;schannel.dll&lt;/code&gt;&apos;s wire-level behaviour [@tlsfuzzer-github]. The project&apos;s framing is precise: it &quot;doesn&apos;t check only that the system under test didn&apos;t crash, it checks that it returned correct error messages&quot; [@tlsfuzzer-github]. That is the closest practitioners get to a behavioural spec without source.&lt;/p&gt;
&lt;p&gt;The asymmetry has a name in the article&apos;s argument: open-source substrate, closed-source SSP. The agility receipts of Sections 5 and 9 are auditable at the primitive layer. The parsing-path correctness of Section 6 is not -- the coordinated-disclosure intake of MS14-066 (with IBM X-Force researcher Robert Freeman credited as the discoverer per the bulletin&apos;s acknowledgments [@ms14-066]) is the kind of receipt the binary-only delivery model can produce. Modern external fuzzing has narrowed the gap but does not close it.&lt;/p&gt;
&lt;h3&gt;Legacy protocol &lt;em&gt;removal&lt;/em&gt; versus disablement&lt;/h3&gt;
&lt;p&gt;Disablement by default is universal in Windows 11 / Server 2022+. &lt;em&gt;Removing the negotiation code paths&lt;/em&gt; is a separate, slower trajectory. SSL 3.0&apos;s code paths are largely gone from current SChannel; TLS 1.0 / 1.1 code paths remain reachable behind registry flags because some long-tail enterprise scenarios still require them. The protocol surface of SChannel is wider than its default-enabled surface; an audit posture must account for the difference.&lt;/p&gt;
&lt;h3&gt;Dead ends and the diseases they failed to cure&lt;/h3&gt;
&lt;p&gt;The five agility receipts of Section 5 are the &lt;em&gt;primitive-rotation&lt;/em&gt; story. But not every TLS failure is a primitive failure, and the substrate could not save Windows from the four most-instructive engineering dead ends the IETF eventually had to legislate out of the protocol itself. &lt;strong&gt;CRIME / TLS-level DEFLATE compression&lt;/strong&gt; (Rizzo and Duong, ekoparty 2012) was a compression-then-encryption side-channel that no primitive substitution could fix; TLS 1.3 removed compression from the protocol entirely (RFC 8446 §4.1.2 [@rfc-8446]) and SChannel never shipped TLS-level compression in the first place. &lt;strong&gt;Insecure renegotiation (CVE-2009-3555)&lt;/strong&gt; -- Ray and Dispensa, November 2009 -- let an MITM splice attacker-prefix application data into a victim&apos;s authenticated session because the mid-session re-handshake had no transcript binding to the prior session; RFC 5746 (Eric Rescorla et al., February 2010 [@rfc-5746]) added the &lt;code&gt;renegotiation_info&lt;/code&gt; extension, Microsoft shipped the fix in SChannel via KB977377 / KB980436 in early 2010, and TLS 1.3 removed renegotiation outright in favour of the narrowly-scoped &lt;code&gt;KeyUpdate&lt;/code&gt; message and post-handshake &lt;code&gt;CertificateRequest&lt;/code&gt;. &lt;strong&gt;Anonymous Diffie-Hellman cipher suites&lt;/strong&gt; (TLS 1.0 through 1.2 specified &lt;code&gt;TLS_DH_anon_*&lt;/code&gt; and &lt;code&gt;TLS_ECDH_anon_*&lt;/code&gt; with no server certificate at all -- forward secrecy without authentication) were off-by-default in every major stack including SChannel and removed from the TLS 1.3 cipher-suite namespace entirely. &lt;strong&gt;Export-grade RSA / FREAK&lt;/strong&gt; (Beurdouche, Bhargavan and colleagues, IEEE S&amp;amp;P 2015 [@smacktls]) used roughly $100 of EC2 compute to break 512-bit RSA in hours and force-downgrade non-export-aware servers; Microsoft pruned the export-RSA suites from SChannel&apos;s default set via MS15-031 / KB3046049 in March 2015 [@smacktls].&lt;/p&gt;
&lt;p&gt;These four dead ends share a structural lesson: each is a &lt;em&gt;different axis of failure&lt;/em&gt;. CRIME was a side-channel no algorithm could fix. Insecure renegotiation was a feature whose protocol design admitted MITM splicing. Anonymous DH was a configuration the protocol should never have exposed. FREAK was an obsolete primitive whose continued availability invited downgrade. All four sit &lt;em&gt;above&lt;/em&gt; the substrate -- none is a primitive-design defect like MD5 or DES-56. The thesis the article advances -- that the substrate changed because it &lt;em&gt;had to support&lt;/em&gt; the changes but did not &lt;em&gt;invent&lt;/em&gt; them -- is illustrated negatively by these four: the &lt;em&gt;protocol&lt;/em&gt; axis had to do the work, often by removal rather than refinement. The agility receipt of Section 5 G5 (TLS 1.0 / 1.1 disablement) is, in this light, just the most visible item in a longer ledger.&lt;/p&gt;
&lt;p&gt;If the theoretical limits are humbling, the practical day-to-day -- &quot;what should I actually do with my SChannel-served TLS endpoints this Monday morning?&quot; -- has a much cleaner set of answers.&lt;/p&gt;
&lt;h2&gt;12. Practical Guide: Nine Things to Do on a Windows-Served TLS Endpoint&lt;/h2&gt;
&lt;p&gt;A working operator&apos;s reference distilled to the essentials -- the nine things that, if you do nothing else this quarter, materially improve the security posture of a Windows-served TLS endpoint.&lt;/p&gt;
&lt;h3&gt;1. Inventory&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;# Cipher suites enabled on this host, in negotiation order
Get-TlsCipherSuite | Select-Object -Property Name, Cipher, CipherLength, KeyType, Exchange

# ECC named groups (TLS 1.3 key shares; X25519, secp256r1, secp384r1; PQC hybrids on newer builds)
Get-TlsEccCurve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pair this with a registry walk of &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\&lt;/code&gt; -- &lt;code&gt;Protocols\&amp;lt;ver&amp;gt;\&amp;lt;role&amp;gt;\Enabled&lt;/code&gt; and &lt;code&gt;DisabledByDefault&lt;/code&gt; for protocol versions, &lt;code&gt;Ciphers\&amp;lt;algorithm&amp;gt;\Enabled&lt;/code&gt; for primitive disables, and &lt;code&gt;Hashes\&amp;lt;algorithm&amp;gt;\Enabled&lt;/code&gt; for handshake-hash disables [@ms-learn-tls-registry-settings].&lt;/p&gt;
&lt;h3&gt;2. Disable the legacy protocol versions&lt;/h3&gt;
&lt;p&gt;Set &lt;code&gt;SCHANNEL\Protocols\SSL 3.0\&amp;lt;role&amp;gt;\Enabled = 0&lt;/code&gt; and &lt;code&gt;DisabledByDefault = 1&lt;/code&gt; for both &lt;code&gt;Client&lt;/code&gt; and &lt;code&gt;Server&lt;/code&gt; sub-keys. Repeat for TLS 1.0 and TLS 1.1. The asymmetry between &lt;code&gt;Client&lt;/code&gt; and &lt;code&gt;Server&lt;/code&gt; hives bites: an outbound &lt;code&gt;WinHTTP&lt;/code&gt; call from your IIS worker is governed by the &lt;code&gt;Client&lt;/code&gt; sub-key even though the server itself is gated by &lt;code&gt;Server&lt;/code&gt; [@ms-learn-tls-registry-settings].&lt;/p&gt;
&lt;h3&gt;3. Disable RC4 and 3DES at the cipher level&lt;/h3&gt;
&lt;p&gt;RC4: KB2868725 [@ms-advisory-2868725] introduced the mechanism. Set &lt;code&gt;Ciphers\RC4 40/128\Enabled = 0&lt;/code&gt;, &lt;code&gt;Ciphers\RC4 56/128\Enabled = 0&lt;/code&gt;, &lt;code&gt;Ciphers\RC4 64/128\Enabled = 0&lt;/code&gt;, &lt;code&gt;Ciphers\RC4 128/128\Enabled = 0&lt;/code&gt;. 3DES: &lt;code&gt;Ciphers\Triple DES 168\Enabled = 0&lt;/code&gt;. Then verify with &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; that no &lt;code&gt;*RC4*&lt;/code&gt; or &lt;code&gt;*3DES*&lt;/code&gt; suites are still listed.&lt;/p&gt;
&lt;h3&gt;4. Cipher-suite ordering for TLS 1.2&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;SSL Cipher Suite Order&lt;/code&gt; GPO is the lever. Put &lt;code&gt;ECDHE&lt;/code&gt; + &lt;code&gt;AES-GCM&lt;/code&gt; suites at the top; keep CHACHA20-POLY1305 as a fallback for clients without AES-NI; pull legacy AES-CBC suites to the bottom. The Microsoft Learn &quot;Manage TLS&quot; page walks through the GPO interaction [@ms-learn-manage-tls].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Setting an explicit cipher-suite order via the older &lt;code&gt;SSL Cipher Suite Order&lt;/code&gt; GPO can accidentally exclude TLS 1.3 cipher suites if the list does not enumerate them. The TLS 1.3 suites (&lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;, &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt;, &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt;) must appear in the configured list, otherwise TLS 1.3 effectively gets disabled on the host. Verify with &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; after applying any GPO change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;5. Enable OCSP stapling on IIS, and enable CAPI2/Operational logging for distrust observability&lt;/h3&gt;
&lt;p&gt;OCSP stapling is on by default in modern IIS. Verify that your front door is sending stapled responses (via &lt;code&gt;openssl s_client -status -connect host:443 &amp;lt; /dev/null | grep -i ocsp&lt;/code&gt; from a test client). If your CA does not support OCSP for the issued cert, the stapling fails silently and you lose the revocation channel; pick a CA that does.&lt;/p&gt;
&lt;p&gt;For trust-store observability, enable the per-host CAPI2/Operational tracing channel with &lt;code&gt;wevtutil sl Microsoft-Windows-CAPI2/Operational /e:true&lt;/code&gt; and watch for the chain-engine events the Microsoft Learn FCPCA-removal article enumerates: Event ID 11 (chain-build failures), Event ID 30 (SSL or NTAuth policy failures), Event ID 90 (every certificate consulted during chain build) [@ms-learn-fcpca-removal]. The FCPCA article also documents the empirical &quot;one to seven days&quot; propagation latency between an MTRP distrust landing in &lt;code&gt;authrootstl.cab&lt;/code&gt; and a given client actually applying it -- the same window applies to any future CCADB-coordinated removal (cross-reference Section 7).&lt;/p&gt;
&lt;h3&gt;6. Enforce RunAsPPL &lt;strong&gt;and&lt;/strong&gt; Credential Guard&lt;/h3&gt;
&lt;p&gt;These are &lt;em&gt;complementary&lt;/em&gt;, not alternatives [@itm4n-runasppl]. Set &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Lsa\RunAsPPL = 1&lt;/code&gt; and reboot; verify LSASS comes back as a Protected Process with &lt;code&gt;Get-Process lsass | Select Name, Protect*&lt;/code&gt; [@ms-learn-lsa-protection]. Then enable Credential Guard via Group Policy or MDM; on most newer Windows 11 builds it is on by default [@ms-learn-credential-guard]. Auditing-only mode (&lt;code&gt;AuditLevel&lt;/code&gt;) is the right step before enforcement to identify any legacy LSA plug-ins that fail to load as PPL.&lt;/p&gt;
&lt;h3&gt;7. Lock down CredSSP / RDP NLA on Remote Desktop Session Hosts&lt;/h3&gt;
&lt;p&gt;Confirm Network Level Authentication is enabled on any RDP Session Host (it has been default-on since Windows Server 2012 R2) [@ms-archive-nla]. Confirm the host is running CredSSP version 5 or higher, so the channel-binding hash mechanism that replaced the broken pre-CVE-2018-0886 &quot;encrypt the public key + 1&quot; scheme is in force [@ms-cssp-sequencing]. For any administrative jump-host scenario where the destination&apos;s plaintext-credential exposure must be zero, use &lt;strong&gt;Remote Credential Guard&lt;/strong&gt; (&lt;code&gt;TSRemoteGuardCreds&lt;/code&gt;) -- the destination receives only a service ticket usable for the session, not a reusable password or hash. Pair NLA enforcement with the certificate-validation knobs in item 5: the SChannel server certificate the CredSSP TLS handshake validates is the same one a TLS-only audit covers, so the trust pipeline reuse is exact.&lt;/p&gt;
&lt;h3&gt;8. FIPS-mode toggle: what &lt;code&gt;FipsAlgorithmPolicy = 1&lt;/code&gt; actually means in 2026&lt;/h3&gt;
&lt;p&gt;The Local Security Policy setting &quot;&lt;strong&gt;System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing&lt;/strong&gt;&quot; (registry: &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Lsa\FipsAlgorithmPolicy\Enabled = 1&lt;/code&gt;) is the operator-side policy lever that pins SChannel, EFS, BitLocker, and RDP encryption to the FIPS 140-validated subset of CNG&apos;s catalog [@ms-learn-fips-policy]. The &quot;what it disables&quot; question has changed since the legacy &quot;TLS_RSA_WITH_3DES_EDE_CBC_SHA only&quot; framing on the policy reference page itself [@ms-learn-fips-policy]. The modern Microsoft Learn &quot;TLS Cipher Suites in Windows 11&quot; page is explicit that &quot;FIPS-compliance has become more complex with the addition of elliptic curves making the FIPS mode enabled column in previous versions of this table misleading,&quot; and points readers to NIST SP 800-52 Rev. 2 section 3.3.1 for the authoritative FIPS-approved TLS 1.2 / 1.3 cipher-suite list [@ms-learn-tls-cipher-suites-windows-11][@nist-sp-800-52r2].&lt;/p&gt;
&lt;p&gt;In practice on a Windows 11 / Server 2022 box with &lt;code&gt;FipsAlgorithmPolicy = 1&lt;/code&gt;: SChannel will negotiate TLS 1.3&apos;s &lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt; and &lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt; (the third TLS 1.3 mandatory suite, &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt;, is &lt;strong&gt;not&lt;/strong&gt; FIPS-approved because ChaCha20-Poly1305 is not on the FIPS algorithm list); for TLS 1.2 it will negotiate the ECDHE-with-AES-GCM and ECDHE-with-AES-CBC-SHA2 variants over the NIST curves P-256, P-384, and P-521 only; the X25519 named group is &lt;strong&gt;not&lt;/strong&gt; FIPS-approved as of the May 2026 Windows servicing snapshot; and the X25519MLKEM768 hybrid in Insider channels is &lt;strong&gt;not&lt;/strong&gt; FIPS-approved either, because of the X25519 component.&lt;/p&gt;
&lt;p&gt;Two-sided framing: &lt;strong&gt;SymCrypt&apos;s FIPS 140-3 validation is the &lt;em&gt;engine-side receipt&lt;/em&gt;; &lt;code&gt;FipsAlgorithmPolicy = 1&lt;/code&gt; is the &lt;em&gt;consumer-side policy lever&lt;/em&gt; that pins consumers to the validated subset.&lt;/strong&gt; Both are required for the system to be &quot;operating in FIPS mode&quot; in the CMVP sense [@ms-learn-fips-140-validation]. At the BCrypt layer, FIPS enforcement is &lt;em&gt;opt-in&lt;/em&gt; via a CNG flag that callers pass to &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt;; SChannel honours the system policy directly, but legacy applications loading deprecated CryptoAPI 1.0 CSPs (&lt;code&gt;PROV_RSA_FULL&lt;/code&gt;, &lt;code&gt;rsaenh.dll&lt;/code&gt;, etc.) bypass the toggle entirely [@ms-learn-cryptographic-provider-types].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Enabling &lt;code&gt;FipsAlgorithmPolicy = 1&lt;/code&gt; is prospective only. It affects &lt;em&gt;future&lt;/em&gt; BCrypt opens, &lt;em&gt;future&lt;/em&gt; SChannel handshakes, and &lt;em&gt;future&lt;/em&gt; EFS encryptions. It does &lt;strong&gt;not&lt;/strong&gt; re-derive existing TLS session keys, does &lt;strong&gt;not&lt;/strong&gt; re-encrypt existing EFS-protected files, and may break RDP between a FIPS-on Server 2022 host and a not-FIPS-configured Windows 10 1809 client because the two ends can no longer agree on a common cipher suite. Plan rollout carefully and verify mixed-version paths before flipping the bit fleet-wide.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;9. Pilot the PQC hybrid where you can&lt;/h3&gt;
&lt;p&gt;Where Windows builds support &lt;code&gt;X25519MLKEM768&lt;/code&gt; -- presently Insider Preview channels per the CNG ML-KEM page&apos;s banner [@ms-learn-cng-mlkem-examples] -- pilot the hybrid against an internal client. Validate via Wireshark (looking for the &lt;code&gt;X25519MLKEM768&lt;/code&gt; named-group selector in &lt;code&gt;ClientHello&lt;/code&gt; / &lt;code&gt;ServerHello&lt;/code&gt; &lt;code&gt;key_share&lt;/code&gt; extensions) and a &lt;code&gt;curl&lt;/code&gt; build with ML-KEM support. Measure connection-establishment latency; for a typical handshake the additional ~5-10 ms is in the noise (see the §9 PQC handshake budget Callout for the TPM-bound exception).&lt;/p&gt;

The `X25519MLKEM768` named group has IANA codepoint `0x11ec`. A Wireshark display filter of `tls.handshake.extensions_key_share_group == 0x11ec` flags handshakes that negotiated the hybrid. Combined with `tls.handshake.version == 0x0304`, you can quickly spot whether a peer actually used the PQC hybrid or fell back to plain X25519.
&lt;h3&gt;Common pitfalls&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Client&lt;/code&gt; vs &lt;code&gt;Server&lt;/code&gt; asymmetry.&lt;/strong&gt; Two sub-keys, two hives, four registry edits per protocol version. Tooling like &lt;code&gt;IISCrypto&lt;/code&gt; automates the matrix; doing it by hand is the most common source of &quot;we thought we disabled TLS 1.0 but our outbound WinHTTP still negotiates it&quot; tickets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SCH_USE_STRONG_CRYPTO&lt;/code&gt;&lt;/strong&gt; -- the SCHANNEL_CRED flag is per-call, not per-machine. .NET sets it by default on modern targets but historically didn&apos;t on .NET Framework 4.5.x. If you maintain old .NET Framework workloads, audit them.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;SSLKEYLOGFILE&lt;/code&gt;&lt;/strong&gt; -- SChannel does not export keys to &lt;code&gt;SSLKEYLOGFILE&lt;/code&gt;. Wireshark cannot decrypt SChannel-served TLS traffic without separate key extraction (etw-based, or a TLS-terminating proxy). Plan your packet-capture strategy accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the practical guide is the &quot;what to do,&quot; the FAQ that follows is the &quot;what to stop believing.&quot;&lt;/p&gt;
&lt;h2&gt;13. Frequently Asked Questions&lt;/h2&gt;

No. They were different bugs, different stacks, different vendors. **Heartbleed (CVE-2014-0160), April 7, 2014, was a flaw in OpenSSL&apos;s TLS Heartbeat extension code path** [@nvd-cve-2014-0160]. **MS14-066 / CVE-2014-6321 (&quot;WinShock&quot;), November 11, 2014, was a pre-authentication remote code execution in SChannel&apos;s TLS message-parsing path** [@ms14-066][@nvd-cve-2014-6321], disclosed under coordinated vulnerability disclosure and credited by IBM X-Force to researcher Robert Freeman. SChannel does not implement OpenSSL&apos;s Heartbeat code and was not affected by Heartbleed; Microsoft confirmed this publicly in April 2014. The two events have been blended in many secondary accounts since.

No. CVE-2014-6321 had a public Patch Tuesday bulletin (MS14-066, November 11, 2014) [@ms14-066], a US-CERT alert (TA14-318A, November 18, 2014) [@uscert-ta14-318a], and a CERT/CC vulnerability note (VU#505120) [@certcc-vu505120]. The &quot;silently patched&quot; framing in some accounts refers to the *additional* SChannel hardening fixes Microsoft bundled into the same KB without separate bulletins, not to the headline CVE itself. This article does not assign specific CVE IDs to those bundled extras.

No. **ZeroLogon affected the Netlogon Remote Protocol (MS-NRPC), implemented in `netlogon.dll`**. The &quot;Netlogon secure channel&quot; and the &quot;SChannel SSP&quot; (`schannel.dll`, the TLS provider this article is about) share a name root but are different protocols, different DLLs, different code paths, and different bug classes. Confusing the two is one of the most common Windows-security naming traps.

Yes -- they are complementary, not alternatives [@itm4n-runasppl]. **PPL is a same-privilege gate inside Virtual Trust Level 0 (VTL0)**: it stops a non-PPL process from opening LSASS for memory read [@ms-learn-lsa-protection]. **Credential Guard moves credential material into the `LSAIso` trustlet at VTL1**, behind the VBS / Hyper-V boundary [@ms-learn-credential-guard]. They protect against different threats and stack rather than substitute.

No. The Microsoft Learn page for the CNG ML-KEM API carries an explicit &quot;prerelease product / Windows Insider Preview&quot; banner as of mid-2026 [@ms-learn-cng-mlkem-examples]. The primitive ships in SymCrypt v103.5.0 and later [@symcrypt-changelog]; the CNG and SChannel surfaces are rolling through the Insider channel. Track the Microsoft Tech Community PQC posts for OS-channel GA announcements [@ms-tech-community-pqc][@ms-tech-community-pqc-companion].

On Windows, yes. On Linux .NET delegates `SslStream` to OpenSSL; on macOS it uses Apple&apos;s Network framework. PQC support follows the underlying stack, so the same .NET binary&apos;s TLS posture differs by host OS in mid-2026.

Because the *agility property* the article is about is anchored to CNG, which shipped in Vista in January 2007 [@ms-learn-cng-portal] -- about nineteen years to mid-2026. Pre-CNG SChannel was not algorithm-agile in any meaningful sense: primitives were baked into CryptoAPI 1.0 CSP DLLs, ECC could not be expressed in the `ALG_ID + key BLOB` model at all, and adding a new algorithm required a CSP rev plus an OS release. CNG is when &quot;rotate every cipher&quot; stopped being a slogan and started being a property the substrate could deliver. The thirty-year framing would be arithmetically accurate but argumentatively wrong.
&lt;p&gt;The Microsoft TLS stack has spent twenty years proving that one architectural decision -- decouple algorithms from DLLs, addressed by string identifier through a stable provider model -- can carry a vendor through every primitive rotation cryptography throws at it. The receipts now include a post-quantum hybrid key exchange that runs through the same dispatch path Vista shipped in 2007. The next test, the signature-side PQC transition, is already in flight inside SymCrypt. Whatever the world chooses to do with those primitives, the substrate is ready.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;schannel-twenty-year-algorithm-agility&quot; keyTerms={[
  { term: &quot;SChannel SSP&quot;, definition: &quot;The Microsoft Security Support Provider (schannel.dll) implementing SSL, TLS, and DTLS on Windows.&quot; },
  { term: &quot;CNG (Cryptography API: Next Generation)&quot;, definition: &quot;Vista-era substrate replacing CryptoAPI 1.0; splits into BCrypt (primitives) and NCrypt (key custodians).&quot; },
  { term: &quot;BCrypt&quot;, definition: &quot;CNG API for primitive cryptographic operations addressed by string algorithm identifier.&quot; },
  { term: &quot;NCrypt&quot;, definition: &quot;CNG API for key custodians via pluggable Key Storage Providers (software, smart card, TPM, HSM).&quot; },
  { term: &quot;KSP (Key Storage Provider)&quot;, definition: &quot;Pluggable NCrypt module that owns a private key&apos;s lifecycle and operations.&quot; },
  { term: &quot;SymCrypt&quot;, definition: &quot;Microsoft&apos;s unified FIPS-validated primitive engine started by Niels Ferguson in late 2006; open-sourced under MIT in July 2019.&quot; },
  { term: &quot;AEAD&quot;, definition: &quot;Authenticated Encryption with Associated Data; single-pass encrypt-and-authenticate construction that eliminates mac-then-encrypt padding-oracle bugs.&quot; },
  { term: &quot;ECDHE&quot;, definition: &quot;Ephemeral Elliptic-Curve Diffie-Hellman; provides forward secrecy by deriving each session key from a fresh elliptic-curve key pair.&quot; },
  { term: &quot;ML-KEM&quot;, definition: &quot;FIPS 203 module-lattice-based Key Encapsulation Mechanism; the NIST-standardised PQC KEM that X25519MLKEM768 wraps.&quot; },
  { term: &quot;PPL (Protected Process Light)&quot;, definition: &quot;Same-privilege process-protection gate inside VTL0; blocks non-PPL callers from opening LSASS for memory read when RunAsPPL is enabled.&quot; },
  { term: &quot;CertGetCertificateChain&quot;, definition: &quot;The Windows chain-building API that walks from leaf to trusted root with revocation, name-constraint, and EKU enforcement.&quot; },
  { term: &quot;MS14-066 / WinShock&quot;, definition: &quot;Pre-authentication SChannel parsing-path RCE (CVE-2014-6321), patched November 11, 2014; not Heartbleed.&quot; }
]} questions={[
  { q: &quot;Name the structural property of CryptoAPI 1.0 that prevented ECC from being expressed.&quot;, a: &quot;The ALG_ID + key BLOB model could not represent named curves, parameter sets, or point compression -- ECC&apos;s identity is per-curve, not per-algorithm-type.&quot; },
  { q: &quot;What two API splits did CNG introduce, and what does each abstract?&quot;, a: &quot;BCrypt abstracts primitives (algorithms by string identifier); NCrypt abstracts key custodians (via Key Storage Providers).&quot; },
  { q: &quot;Why is the &apos;twenty-year algorithm-agility&apos; frame anchored to 2007 rather than 1996?&quot;, a: &quot;CNG (2007) is when algorithm agility became a first-class substrate property. Pre-CNG SChannel was protocol-agile (it could rotate SSL/TLS versions) but not primitive-agile -- adding a new primitive required a CSP DLL rev.&quot; },
  { q: &quot;What is the SChannel-side disambiguation between MS14-066 and Heartbleed?&quot;, a: &quot;MS14-066 was a pre-auth RCE in SChannel&apos;s TLS parsing path (Microsoft, November 2014). Heartbleed was an OpenSSL Heartbeat over-read (April 2014). SChannel does not implement the OpenSSL Heartbeat code path.&quot; },
  { q: &quot;What concrete handshake-time data does the X25519MLKEM768 named group concatenate?&quot;, a: &quot;ClientHello key_share is mlkem_pk || x25519_pk (1216 B); ServerHello key_share is mlkem_ct || x25519_pk (1120 B); both sides derive shared_secret = mlkem_ss || x25519_ss (64 B) and feed it to TLS 1.3&apos;s HKDF-Extract.&quot; },
  { q: &quot;Why are PPL (RunAsPPL) and Credential Guard described as complementary rather than substitutes?&quot;, a: &quot;PPL is a same-privilege gate inside VTL0 (it blocks OpenProcess(LSASS, VM_READ) from non-PPL callers). Credential Guard moves credential material into the LSAIso trustlet at VTL1 behind the VBS / Hyper-V boundary. They protect against different attack classes.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows-security</category><category>tls</category><category>cryptography</category><category>post-quantum</category><category>schannel</category><category>cng</category><category>algorithm-agility</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>The Connection That Refused to Downgrade: Twenty-Five Years of SMB Cryptography, Finally Default-On</title><link>https://paragmali.com/blog/the-connection-that-refused-to-downgrade-twenty-five-years-o/</link><guid isPermaLink="true">https://paragmali.com/blog/the-connection-that-refused-to-downgrade-twenty-five-years-o/</guid><description>How SMB 3.1.1 pre-authentication integrity, AES-256-GCM, and SMB-over-QUIC closed a 25-year attack tradition, and which attacks still survive in 2026.</description><pubDate>Tue, 26 May 2026 00:00:00 GMT</pubDate><content:encoded>
**SMB 3.1.1 closed the 25-year-old negotiate-downgrade attack in 2015, but the defaults did not catch up until October 2024.** Pre-authentication integrity hashes every byte of NEGOTIATE and SESSION_SETUP into the SP 800-108 KDF salt, so any tampering produces divergent session keys rather than detectable mismatches. Windows 11 24H2 and Server 2025 finally make SMB signing required by default; encryption is now mandate-able but remains opt-in. The attacks that still work (PetitPotam) route around SMB entirely.
&lt;h2&gt;1. Three Failures in a Coffee Shop&lt;/h2&gt;
&lt;p&gt;A laptop opens its first SMB connection to a corporate file share over the wifi of a coffee shop. An attacker on the same network, holding the same family of relay tooling that Sir Dystic first demonstrated on &lt;strong&gt;March 31, 2001 at @lanta.con in Atlanta&lt;/strong&gt; [@smbrelay-cdc], watches three packets go by: a NEGOTIATE Request, a NEGOTIATE Response, and an authenticated SESSION_SETUP that finishes with a signed READ.&lt;/p&gt;
&lt;p&gt;She tries to relay the credentials to a neighbouring server. She tries to strip the signing-required bit from the NEGOTIATE Response. She tries to inject ciphertext she captured from a previous session of the same user.&lt;/p&gt;
&lt;p&gt;All three attempts fail. None of them touch the contents of the file.&lt;/p&gt;
&lt;p&gt;That outcome is the 2026 default behaviour of a Windows 11 24H2 client talking to a Windows Server 2025 file server [@pyle-techcom-4226591][@smb-security-hardening]. And it is the punch line of a story that took twenty-five years to land.&lt;/p&gt;

This topic was discussed on March 31, 2001 at @lanta.con in Atlanta, Georgia. -- Sir Dystic, cDc disclosure page for SMBRelay [@smbrelay-cdc]
&lt;p&gt;Sir Dystic was the cDc handle of the researcher who shipped the first practical SMB relay tool.&quot;Sir Dystic&quot; is a cult of the Dead Cow pseudonym. The cDc primary disclosure page names only the handle; a legal name attributed in secondary press is not load-bearing for this article, and the primary citation stays with the pseudonym [@smbrelay-cdc]. His attack relayed a client&apos;s &lt;a href=&quot;https://paragmali.com/blog/ntlmless-the-death-of-ntlm-in-windows/&quot; rel=&quot;noopener&quot;&gt;NTLM&lt;/a&gt; challenge response, captured on the wire, into a fresh SMB session against another host where that user already had credentials.&lt;/p&gt;
&lt;p&gt;The technique generalised, and &quot;SMB relay&quot; became a generic term for an attack family that has, over the intervening years, eaten domain controllers, file shares, and Active Directory Certificate Services enrollment endpoints.&lt;/p&gt;
&lt;p&gt;This article walks the twenty-five-year arc of cryptographic responses to that attack family: how each retrofit before 2015 left a load-bearing weakness in the NEGOTIATE handshake; how SMB 3.1.1 closed that weakness with a mechanism that is usually misremembered; how SMB-over-QUIC arrived in 2021 as a parallel transport; how the October 2024 default-on flip in Windows 11 24H2 finally made signing mandatory on every share, not just SYSVOL and NETLOGON [@smb-security-hardening]; and what attacks the SMB team designed against still work in 2026, by routing around SMB entirely.&lt;/p&gt;
&lt;p&gt;There will be three aha moments. The first arrives at the end of section 3: every defence before 2015 was applied after-the-fact to a message an attacker already controlled. The second arrives in section 4: pre-authentication integrity is not a signed negotiate, it is a hash chain that becomes a key-derivation salt. The third arrives in section 6: SMB is now cryptographically sufficient at the wire layer, and the attacks that still matter coerce SMB and relay the authentication somewhere else.&lt;/p&gt;
&lt;p&gt;To see why the attacker&apos;s toolkit failed in 2026, and why the same toolkit worked everywhere from 2001 to 2023, we have to start in 1983.&lt;/p&gt;
&lt;h2&gt;2. The Cleartext Era and the Birth of the Relay Class&lt;/h2&gt;
&lt;p&gt;Barry Feigenbaum designed SMB at IBM Boca Raton in 1983 [@ms-cifs-landing] as a way to make a PC&apos;s open-file table look like it lived on a server. It ran on a single LAN segment, behind a desk, in a room where the only adversary was someone who walked in the door. There was no notion of an attacker on the wire because there was no wire to attack: the protocol predated TCP/IP&apos;s commercial reach by a decade.&lt;/p&gt;
&lt;p&gt;Microsoft adopted the design for LAN Manager in 1987 and carried it into Windows NT in 1993 [@ms-cifs-landing]. Paul Leach&apos;s 1996-1997 IETF draft, eventually known as &lt;code&gt;draft-leach-cifs-v1-spec-02&lt;/code&gt; [@leach-cifs-draft], rebranded the protocol as the &lt;strong&gt;Common Internet File System&lt;/strong&gt; and tried to standardise the dialect at IETF.&lt;/p&gt;
&lt;p&gt;The standardisation effort stalled, but the brand stuck for a decade. The normative SMB1 / CIFS reference today is the Microsoft &lt;code&gt;[MS-CIFS]&lt;/code&gt; open specification [@ms-cifs-landing], which still records the SMB1 dialect strings &lt;code&gt;PC NETWORK PROGRAM 1.0&lt;/code&gt;, &lt;code&gt;MICROSOFT NETWORKS 1.03&lt;/code&gt;, &lt;code&gt;LANMAN1.0&lt;/code&gt;, and &lt;code&gt;NT LM 0.12&lt;/code&gt; that the protocol negotiates at hello.&lt;/p&gt;

Paul Leach (Microsoft) circulated a CIFS Internet Draft at the IETF in 1996-1997 [@leach-cifs-draft], hoping to take SMB through the standards-track process. Microsoft kept revising the spec internally; the IETF draft never advanced to RFC. The &quot;CIFS&quot; name persisted as marketing for SMB1 well after Microsoft had moved on to SMB 2.0 in 2006, which is why product documentation continued to refer to &quot;CIFS shares&quot; through the 2010s even though the wire protocol had changed twice. Today the only meaningful use of the word is as a label for the SMB1 wire dialect family, normatively documented in `[MS-CIFS]` [@ms-cifs-landing].
&lt;p&gt;In parallel, Andrew Tridgell at the Australian National University started reverse-engineering the SMB wire protocol so he could mount a DEC Pathworks server from his Linux box. His first tentative code appeared in early 1992 [@samba-10years]; that codebase grew into Samba, which is the canonical open-source SMB server in 2026 and ships every algorithm matrix the Microsoft client expects to negotiate.&lt;/p&gt;
&lt;p&gt;Microsoft added SMB1 to the Windows Server 2012 R2 deprecation list in &lt;strong&gt;June 2013&lt;/strong&gt;, in advance of the Server 2012 R2 October 2013 release, per Jose Barreto&apos;s 2015 TechNet post (preserved via the Wayback Machine) [@barreto-archive]. &quot;Deprecation&quot; meant the feature was marked for potential removal in subsequent releases, not yet removed. The actual default-off step came with Windows 10 1709 in the Fall Creators Update and Windows Server 2019 [@smb-interception-defense].&lt;/p&gt;

A family of attacks that captures a client&apos;s authentication exchange on the wire and replays it, in real time, into a fresh authenticated session against a different target where the same user already has access. The defining property is that the attacker never learns the plaintext credential; she only needs to ferry the cryptographic responses between two sockets. Originally demonstrated by Sir Dystic for SMB on March 31, 2001 [@smbrelay-cdc], the class generalised to NTLM-over-HTTP, NTLM-over-LDAP, NTLM-over-DCOM, and eventually to cross-protocol variants like PetitPotam that coerce SMB authentication and relay it to ADCS Web Enrollment over HTTPS.
&lt;h3&gt;The 2001 disclosure&lt;/h3&gt;
&lt;p&gt;Sir Dystic&apos;s SMBRelay was a small Windows binary that ran a fake SMB server, accepted a connection from a victim, captured the NTLM challenge-response, and immediately forwarded that response to a real SMB server where the victim had access. The technique worked because SMB1 signing was opt-in by default; on a typical Windows network in 2001, signing was negotiated for connections to SYSVOL and NETLOGON on a domain controller and almost nowhere else. Microsoft&apos;s response at the time was that signing existed and could be turned on. Technically correct; politically untenable.&lt;/p&gt;
&lt;p&gt;The &quot;almost nowhere else&quot; matters. SMB1 did support signing, and the algorithm was a truncated MD5 of the packet concatenated with the session key, mediated by a sequence number, per &lt;code&gt;[MS-CIFS]&lt;/code&gt; §3.1.4.1 [@ms-cifs-landing]. It was not an HMAC, and it was not a strong MAC by 2001 cryptographic standards. But that was not the load-bearing weakness. The load-bearing weakness was that the &lt;strong&gt;signing-required bit travelled in the NEGOTIATE Response itself&lt;/strong&gt;, before any session key existed to protect it. An attacker who could rewrite NEGOTIATE could strip the bit; signing was then mutually disabled and the session continued unsigned.&lt;/p&gt;
&lt;p&gt;The cDc primary page dates the disclosure to &lt;em&gt;March 31, 2001&lt;/em&gt;. The Wikipedia article on SMBRelay says March 21, 2001, and a chain of secondary press has repeated the March 21 date. The primary disclosure page is the dispositive source; the article uses March 31 [@smbrelay-cdc].&lt;/p&gt;
&lt;h3&gt;The 2008 patch&lt;/h3&gt;
&lt;p&gt;Seven years and eight months later, on &lt;strong&gt;November 11, 2008&lt;/strong&gt;, Microsoft shipped MS08-068, which addressed CVE-2008-4037, the same-host SMB credential reflection vulnerability [@ms08-068][@nvd-cve-2008-4037]. The fix changed how SMB authentication replies were validated so that an attacker could no longer reflect a client&apos;s credentials back to the &lt;em&gt;same host&lt;/em&gt; that initiated the authentication.&lt;/p&gt;
&lt;p&gt;That closed one specific variant of Sir Dystic&apos;s attack. It did not close the cross-host case, where the attacker forwards credentials to a different SMB server that the victim has access to. That case was the architectural shape that future relay tooling like Impacket&apos;s &lt;code&gt;smbrelayx&lt;/code&gt; exploited for years.&lt;/p&gt;
&lt;p&gt;What MS08-068 demonstrated, more than anything else, was the cost of fixing a protocol with a single-issue patch when the load-bearing weakness was structural. The 2001 disclosure named the class. The 2008 patch closed one variant. The next defence was not another bulletin, and it was not going to be another patch. It was a complete redesign of the SMB header model and a new dialect family.&lt;/p&gt;

gantt
    title SMB cryptographic generations
    dateFormat YYYY
    axisFormat %Y
    section SMB1
    Cleartext on the LAN          :1983, 1996
    MD5 signing (opt-in)          :1997, 2006
    section SMB 2.x
    HMAC-SHA-256 signing          :2006, 2012
    section SMB 3.x
    AES-CMAC and AES-128-CCM      :2012, 2013
    Secure Negotiate FSCTL        :2013, 2015
    section SMB 3.1.1
    Pre-auth integrity, AES-128-GCM :2015, 2021
    AES-256 ciphers, AES-GMAC, SMB-over-QUIC :2021, 2024
    Signing required by default   :2024, 2026
&lt;h2&gt;3. Five Retrofits Before the Real Fix&lt;/h2&gt;
&lt;p&gt;Between 2006 and 2013, Microsoft tried four different cryptographic primitives and one post-hoc validation message. None of them closed the negotiate-downgrade primitive that SMBRelay opened.&lt;/p&gt;
&lt;h3&gt;SMB 2.0 (November 8, 2006) [@ms-smb2-versioning]&lt;/h3&gt;
&lt;p&gt;Windows Vista RTM shipped a redesigned SMB header. The protocol still carried a sequence number, but the per-packet signature now came from HMAC-SHA-256 keyed by the session key derived from authentication, not from a truncated MD5 [@ms-smb2-versioning]. The new MAC closed all the trivial attacks on the signing algorithm itself.&lt;/p&gt;
&lt;p&gt;But signing was still opt-in: a typical Windows network ran it only between domain controllers and member servers, not between general clients and file shares. And the NEGOTIATE Response still travelled unsigned, so the signing-required bit was still strippable by an in-path attacker.&lt;/p&gt;

A keyed message authentication code, defined in RFC 2104 with SHA-256 as the underlying hash, that produces a 256-bit tag binding a message and a key. HMAC&apos;s security reduces to the pseudorandomness of the underlying hash compression function; SHA-256 with HMAC has no known practical forgery for any key. SMB 2.0 and later use HMAC-SHA-256 to sign each SMB header when AES-CMAC and AES-GMAC are not negotiated [@ms-smb2-versioning].
&lt;h3&gt;SMB 2.1 (October 22, 2009) [@ms-smb2-versioning]&lt;/h3&gt;
&lt;p&gt;Windows 7 and Server 2008 R2 shipped SMB 2.1, dialect &lt;code&gt;0x0210&lt;/code&gt; [@ms-smb2-versioning]. The dialect introduced opportunistic locks, branch caching, and large MTUs. The cryptographic primitives did not change: HMAC-SHA-256 signing, still opt-in.&lt;/p&gt;
&lt;h3&gt;SMB 3.0 (October 26, 2012) [@ms-smb2-versioning]&lt;/h3&gt;
&lt;p&gt;Windows 8 and Server 2012 shipped the first cryptographically modern SMB dialect, &lt;code&gt;0x0300&lt;/code&gt; [@ms-smb2-versioning]. It introduced three things at once:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;AES-CMAC signing&lt;/strong&gt;, the NIST SP 800-38B [@nist-sp-800-38b] block-cipher MAC, swapping the SHA-256 compression function for an AES-128 cipher operation that runs at hardware speed on chips with AES-NI. The standard identifier in the spec is &quot;AES-CMAC&quot;. Cryptographers will sometimes recognise the same construction as OMAC1 from Iwata and Kurosawa, 2003.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AES-128-CCM encryption&lt;/strong&gt;, the two-pass AEAD construction from NIST SP 800-38C [@nist-sp-800-38c], which produces an authenticated ciphertext that an attacker cannot tamper with without invalidating the tag.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A proper SP 800-108 key hierarchy.&lt;/strong&gt; Every per-session signing key, encryption key, and decryption key derives from a session-level secret via the counter-mode HMAC-SHA-256 KDF in NIST SP 800-108 [@nist-sp-800-108r1].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This was a real cryptographic upgrade. But it did not close the negotiate-downgrade primitive. An attacker on the wire could still rewrite the NEGOTIATE Response to remove the &lt;code&gt;SMB2_GLOBAL_CAP_ENCRYPTION&lt;/code&gt; capability bit and the &lt;code&gt;SMB2_NEGOTIATE_SIGNING_REQUIRED&lt;/code&gt; bit; the client and server would then agree on a session that was neither signed nor encrypted, and the cryptographic primitives the attacker was trying to defeat were never invoked at all.&lt;/p&gt;

A block-cipher message authentication code standardised in NIST SP 800-38B in 2005 [@nist-sp-800-38b]. CMAC iterates AES in CBC mode over the message and applies a final subkey to the last block. The construction tolerates messages of any length, including the empty message, and has a security bound that matches the generic upper bound for any deterministic MAC built from a pseudorandom permutation. SMB 3.0 through 3.1.1 use AES-CMAC as the default signing algorithm; AES-GMAC, introduced as a negotiable alternative in 2021, replaces it on hardware where Galois-field multiplication is faster than AES-CBC.

Authenticated Encryption with Associated Data. A class of symmetric encryption scheme that simultaneously provides confidentiality of the message body and integrity of both the body and a separate &quot;associated data&quot; header. AES-CCM (SP 800-38C) [@nist-sp-800-38c] and AES-GCM (SP 800-38D) [@nist-sp-800-38d] are the two AEADs SMB 3.x uses. AEADs replace the older &quot;encrypt-then-MAC&quot; or &quot;MAC-then-encrypt&quot; compositions, which historically were a rich source of cryptographic mistakes.
&lt;p&gt;NIST identifies AES-CMAC by the cipher and mode strings; the construction is mathematically equivalent to Tetsu Iwata and Kaoru Kurosawa&apos;s OMAC1 from 2003. SP 800-38B notes the lineage, attributing the design to Iwata-Kurosawa with subsequent refinements by Black-Rogaway [@nist-sp-800-38b].&lt;/p&gt;
&lt;h3&gt;SMB 3.0.2 Secure Negotiate (October 18, 2013) [@ms-smb2-versioning]&lt;/h3&gt;
&lt;p&gt;Windows 8.1 and Server 2012 R2 shipped dialect &lt;code&gt;0x0302&lt;/code&gt; with a post-hoc validation message: &lt;code&gt;FSCTL_VALIDATE_NEGOTIATE_INFO&lt;/code&gt;. The idea was that after authentication, the client would send the server its view of what NEGOTIATE looked like, signed by the freshly-derived session key. The server compared the client&apos;s view against its own, and if they disagreed, the connection terminated. This was the first protocol-level attempt to detect negotiate-downgrade in SMB. It is also, retrospectively, the most instructive failure.&lt;/p&gt;
&lt;p&gt;Secure Negotiate had five structural limits, and each of them mattered:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;It was post-hoc, not preventive.&lt;/strong&gt; The session key was already derived. The encryption capability was already mutually decided. If an attacker had stripped encryption, you were going to &lt;em&gt;detect&lt;/em&gt; that you were in a degraded session and tear it down. You were not going to prevent the degradation in the first place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The FSCTL was skippable.&lt;/strong&gt; A pre-3.0 client did not send &lt;code&gt;FSCTL_VALIDATE_NEGOTIATE_INFO&lt;/code&gt;. An attacker who downgraded a 3.0.2 client to 2.0 simply got a session that never asked the question.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It validated capability bits, not every byte of NEGOTIATE.&lt;/strong&gt; The FSCTL message contained a fixed structure with specific fields. Tampering that did not touch those specific fields could survive validation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It inherited the dialect-monopoly extensibility flaw.&lt;/strong&gt; Adding a new capability meant adding a new bit to the capability bitmap, which the Secure Negotiate FSCTL would also have to learn about. The structure was not future-proof.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;It was trust-on-second-use, not first-use.&lt;/strong&gt; The signed FSCTL validated &lt;em&gt;that this session&apos;s NEGOTIATE was not tampered with&lt;/em&gt; only after authentication had completed. The first message of authentication itself, where the attacker could rewrite signing requirements, was still vulnerable.&lt;/li&gt;
&lt;/ol&gt;

An attack on a protocol with an in-band cryptographic capability negotiation in which an in-path attacker tampers with the negotiation messages so that the two endpoints agree on a weaker algorithm, or no algorithm at all. SMB-relay-class attacks before 2015 universally relied on negotiate-downgrade: the attacker stripped the `SMB2_NEGOTIATE_SIGNING_REQUIRED` bit from the NEGOTIATE Response, and the resulting session was unsigned and forwardable. The structural defence against negotiate-downgrade is to bind every byte of the negotiation transcript into the key-derivation function, which is what TLS 1.2 Finished and SMB 3.1.1 pre-authentication integrity both do, by different mechanics.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Every SMB defence before 2015 was a tag applied after-the-fact to a message an attacker already controlled. Signing was opt-in. The signing-required bit was strippable during unsigned NEGOTIATE. Secure Negotiate detected tampering only after the attacker had already chosen what to tamper with. The shape of the fix is forced once you state the flaw this way: the negotiation has to bind into the key-derivation function itself, so that tampering does not produce a detectable mismatch -- it produces session keys that simply do not work.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Five generations, one column&lt;/h3&gt;
&lt;p&gt;The shape of the failure is easiest to see in a table.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Generation&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Signing&lt;/th&gt;
&lt;th&gt;Encryption&lt;/th&gt;
&lt;th&gt;NEGOTIATE protection&lt;/th&gt;
&lt;th&gt;Why it failed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;SMB1 cleartext&lt;/td&gt;
&lt;td&gt;1983-1996&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;No cryptography at all&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB1 + MD5&lt;/td&gt;
&lt;td&gt;1997-2006&lt;/td&gt;
&lt;td&gt;Opt-in MD5 truncated to 8 bytes&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Signing-required bit strippable in unsigned NEGOTIATE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB 2.0 / 2.1&lt;/td&gt;
&lt;td&gt;2006-2012&lt;/td&gt;
&lt;td&gt;HMAC-SHA-256&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Same NEGOTIATE-strip primitive, stronger MAC&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB 3.0&lt;/td&gt;
&lt;td&gt;2012-2013&lt;/td&gt;
&lt;td&gt;AES-CMAC&lt;/td&gt;
&lt;td&gt;AES-128-CCM&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Strip the &lt;code&gt;SMB2_GLOBAL_CAP_ENCRYPTION&lt;/code&gt; bit and CCM never runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB 3.0.2&lt;/td&gt;
&lt;td&gt;2013-2015&lt;/td&gt;
&lt;td&gt;AES-CMAC&lt;/td&gt;
&lt;td&gt;AES-128-CCM&lt;/td&gt;
&lt;td&gt;Post-hoc FSCTL&lt;/td&gt;
&lt;td&gt;Detection, not prevention; skippable by older clients&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Read the &lt;strong&gt;NEGOTIATE protection&lt;/strong&gt; column top to bottom. &lt;em&gt;None / None / None / None / Post-hoc FSCTL.&lt;/em&gt; Twenty-five years of cryptographic work without a single defence that bound the negotiation transcript to the eventual session key. Then, in 2015, buried in the unremarkable &lt;code&gt;.1&lt;/code&gt; of a &lt;code&gt;.1&lt;/code&gt; dialect bump, the SMB team finally fixed the load-bearing weakness.&lt;/p&gt;

sequenceDiagram
    participant C as Client
    participant M as In-path attacker
    participant S as Server
    C-&amp;gt;&amp;gt;M: NEGOTIATE Request, signing required, ciphers AES-128-CCM
    M-&amp;gt;&amp;gt;S: NEGOTIATE Request, signing required, ciphers AES-128-CCM
    S-&amp;gt;&amp;gt;M: NEGOTIATE Response, signing required, encryption capability set
    M-&amp;gt;&amp;gt;C: NEGOTIATE Response with signing-required bit cleared, encryption capability removed
    Note over C,S: Client and server now agree on an unsigned, unencrypted session
    C-&amp;gt;&amp;gt;M: SESSION_SETUP Request, no signature
    M-&amp;gt;&amp;gt;S: Relayed SESSION_SETUP Request
    Note over M,S: Attacker controls the cleartext channel
&lt;h2&gt;4. The Hash Chain Becomes the Salt&lt;/h2&gt;
&lt;p&gt;On &lt;strong&gt;July 29, 2015&lt;/strong&gt;, Windows 10 RTM (build 10240, version 1507) shipped a protocol that no relay tool released since has been able to downgrade [@win10-release-info]. The fix was not what most readers remember. It was much stranger and much cleaner.&lt;/p&gt;
&lt;p&gt;The dialect was &lt;code&gt;0x0311&lt;/code&gt;, &quot;SMB 3.1.1&quot; [@ms-smb2-versioning]. Greg Kramer and Dan Lovinger of the Microsoft SMB team presented the design at the SNIA Storage Developer Conference that September [@kramer-lovinger-sdc2015]. The deck contains the single most-quoted sentence in modern SMB cryptography:&lt;/p&gt;

Preauthentication Integrity ... Provides end-to-end, dialect agnostic protection. Session&apos;s secret keys derived from hash of the preauthentication messages. -- Greg Kramer and Dan Lovinger, SNIA SDC 2015 [@kramer-lovinger-sdc2015]
&lt;p&gt;Most secondary references to the SDC 2015 deck cite &quot;Greg Kramer&quot; only. The title slide lists two authors: Greg Kramer, Principal Software Engineer, and Dan Lovinger, Principal Software Engineer, both at Microsoft. The 2015 design has two names on it [@kramer-lovinger-sdc2015].&lt;/p&gt;
&lt;p&gt;The misremembered framing of SMB 3.1.1 pre-authentication integrity is that &quot;negotiate contexts are signed by the eventual session key.&quot; That is not what happens. The actual mechanism is more elegant and structurally stronger.&lt;/p&gt;
&lt;h3&gt;The four updates of the hash chain&lt;/h3&gt;
&lt;p&gt;SMB 3.1.1 defines a per-connection variable called &lt;code&gt;PreauthIntegrityHashValue&lt;/code&gt;, initialised to 64 zero bytes [@ms-smb2-pdf]. Across the four messages of the pre-authentication phase, both client and server update it with the same rule:&lt;/p&gt;
&lt;p&gt;$$H_i = \mathrm{SHA{-}512}(H_{i-1} \mathbin{|} M_i)$$&lt;/p&gt;
&lt;p&gt;The four messages are NEGOTIATE Request, NEGOTIATE Response, SESSION_SETUP Request, and SESSION_SETUP Response. After all four are processed, the running hash &lt;code&gt;H_4&lt;/code&gt; contains a cryptographic commitment to every byte that crossed the wire during pre-authentication.&lt;/p&gt;
&lt;p&gt;This commitment has no value if it stays a local variable. The structural move is in the next step. SMB 3.1.1 uses the NIST SP 800-108 counter-mode KDF [@nist-sp-800-108r1] to derive every per-session key from the authentication-protocol session secret (the NTLM &lt;code&gt;ExportedSessionKey&lt;/code&gt; or the &lt;a href=&quot;https://paragmali.com/blog/kerberos-in-windows-the-other-half-of-ntlmless/&quot; rel=&quot;noopener&quot;&gt;Kerberos&lt;/a&gt; service-ticket session key). The KDF takes two arguments alongside the secret: a &lt;code&gt;Label&lt;/code&gt; identifying the role of the derived key, and a &lt;code&gt;Context&lt;/code&gt; argument.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The &lt;code&gt;Context&lt;/code&gt; argument is &lt;code&gt;PreauthIntegrityHashValue&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is the whole trick. The hash chain enters the key-derivation function as salt. It does not protect the negotiation by tagging it; it protects the negotiation by being part of the input to the function that produces the session keys.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Pre-authentication integrity is not a signed negotiate. It is a SHA-512 hash chain over the four pre-authentication messages whose final value becomes the &lt;code&gt;Context&lt;/code&gt; argument to the SP 800-108 KDF that derives every per-session key. Tampering does not get &lt;em&gt;detected&lt;/em&gt;. Tampering causes the two sides to compute different keys, and the first signed message after SESSION_SETUP simply fails to verify on both ends.&lt;/p&gt;
&lt;/blockquote&gt;

The SMB 3.1.1 mechanism that binds every byte of the four pre-authentication messages (NEGOTIATE Request and Response, SESSION_SETUP Request and Response) into the salt input of the SP 800-108 key-derivation function that derives all per-session SMB keys [@kramer-lovinger-sdc2015][@ms-smb2-pdf]. The bind is constructed by iterating SHA-512 over each pre-authentication message in turn. The mechanism is mandatory in dialect 0x0311; there is no policy switch to disable it.

A family of key-derivation functions standardised by NIST in Special Publication 800-108 [@nist-sp-800-108r1], parameterised by a pseudorandom function (HMAC, KMAC, or CMAC) and a mode (counter, feedback, double-pipeline). SMB 3.x uses CTR-HMAC-SHA-256. The function takes a secret key, a `Label`, a `Context`, and a desired output length, and returns the requested number of bits as a deterministic, distinguishable derivation of the key. In SMB 3.1.1, the `Context` argument is the pre-authentication hash chain; the `Label` is one of four constant strings naming the role of the derived key.
&lt;h3&gt;The four key labels&lt;/h3&gt;
&lt;p&gt;The four per-session keys SMB 3.1.1 derives are tagged with constant &lt;code&gt;Label&lt;/code&gt; strings, lifted directly from [MS-SMB2] §3.2.5.3.1 [@ms-smb2-pdf]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&quot;SMBSigningKey&quot;&lt;/code&gt; -- the key used for the per-packet AES-CMAC or AES-GMAC signature.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;SMBAppKey&quot;&lt;/code&gt; -- the application-layer key that some named pipes can use to derive sub-keys.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;SMBC2SCipherKey&quot;&lt;/code&gt; -- the client-to-server AES encryption key, fed to AES-CCM or AES-GCM.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&quot;SMBS2CCipherKey&quot;&lt;/code&gt; -- the server-to-client encryption key.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each label is passed as the SP 800-108 &lt;code&gt;Label&lt;/code&gt; argument; the same &lt;code&gt;PreauthIntegrityHashValue&lt;/code&gt; is passed as the &lt;code&gt;Context&lt;/code&gt; argument. Different labels produce different keys from the same secret and the same salt; that is what makes role separation work without four separate hash chains.&lt;/p&gt;
&lt;p&gt;The AES-256 ciphers in SMB 3.1.1 consume &lt;code&gt;Session.FullSessionKey&lt;/code&gt; rather than the truncated 16-byte &lt;code&gt;Session.SessionKey&lt;/code&gt; that AES-128 ciphers use [@ms-smb2-pdf]. This is the structural reason an AES-256 SMB session is cryptographically 256-bit-secure rather than capped at 128. The detail matters for anyone evaluating SMB against CNSA 2.0 requirements: the implementation does the right thing, but only because the specification said so explicitly.&lt;/p&gt;
&lt;h3&gt;Why this is stronger than Secure Negotiate&lt;/h3&gt;
&lt;p&gt;Place SMB 3.0.2 Secure Negotiate and SMB 3.1.1 pre-authentication integrity side by side:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Secure Negotiate (SMB 3.0.2)&lt;/th&gt;
&lt;th&gt;Pre-auth integrity (SMB 3.1.1)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Coverage&lt;/td&gt;
&lt;td&gt;Dialect index + capability bitmap&lt;/td&gt;
&lt;td&gt;Every byte of all four pre-auth messages&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;When&lt;/td&gt;
&lt;td&gt;Post-hoc, after session key derived&lt;/td&gt;
&lt;td&gt;At the moment of key derivation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Failure mode&lt;/td&gt;
&lt;td&gt;Detection: signed FSCTL mismatches&lt;/td&gt;
&lt;td&gt;Implicit: derived keys diverge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Disable switch&lt;/td&gt;
&lt;td&gt;Skipped by pre-3.0 clients&lt;/td&gt;
&lt;td&gt;None; mandatory in 0x0311&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Extensibility&lt;/td&gt;
&lt;td&gt;Hard-coded fields in FSCTL&lt;/td&gt;
&lt;td&gt;Hashes whatever bytes flow&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Secure Negotiate had to know &lt;em&gt;what&lt;/em&gt; it was protecting. Pre-auth integrity does not. The hash chain absorbs whatever bytes the dialect happens to carry: new negotiate contexts in future dialects, new capability bits, new ciphers. The mechanism does not need to be updated to learn about them, because it does not parse them. It hashes them.&lt;/p&gt;
&lt;h3&gt;The per-context salts&lt;/h3&gt;
&lt;p&gt;There is one more subtlety. The &lt;code&gt;SMB2_PREAUTH_INTEGRITY_CAPABILITIES&lt;/code&gt; negotiate context, defined in [MS-SMB2] §2.2.3.1.1, contains a 32-byte &lt;code&gt;Salt&lt;/code&gt; field that the client and server populate with fresh PRNG output for each connection [@ms-smb2-pdf]. The salt is hashed in along with everything else, so two different connections from the same client to the same server end up with different &lt;code&gt;PreauthIntegrityHashValue&lt;/code&gt; values even if every other byte of NEGOTIATE is identical.&lt;/p&gt;
&lt;p&gt;The point is not to add entropy to the session key (which already gets entropy from the authentication exchange) but to defeat any attempt at pre-image or rainbow-table attacks on the hash chain itself. SHA-512 is the only hash function currently negotiated, and its 256-bit collision resistance is the protocol floor for pre-authentication integrity.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Microsoft Learn pages, the docs aggregators, and several widely-cited tertiary references describe SMB 3.1.1 pre-authentication integrity as &quot;signing the negotiate contexts with the session key.&quot; That description is wrong in a way that matters. There is no separate signed message validating the negotiation. The protection is constructed by feeding the hash of every pre-authentication message into the KDF that produces the session key. If you remember it as a signed negotiate, you will not understand why it is structurally stronger than Secure Negotiate. The mechanism is a &lt;em&gt;KDF binding&lt;/em&gt;, not a signature.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The empirical demonstration&lt;/h3&gt;
&lt;p&gt;The fastest way to feel the mechanism is to watch keys diverge in real time. The runnable code below produces two parallel SP 800-108-style derivations: an honest one where client and server hash the same four messages, and a tampered one where an in-path attacker flips a single byte in &lt;code&gt;NEGOTIATE Response&lt;/code&gt;. The two derivations produce identical keys in the honest case and visibly different keys in the tampered case.&lt;/p&gt;
&lt;p&gt;{`
const { createHmac, createHash } = require(&apos;crypto&apos;);&lt;/p&gt;
&lt;p&gt;function sha512(buf) {
  return createHash(&apos;sha512&apos;).update(buf).digest();
}&lt;/p&gt;
&lt;p&gt;function updateChain(H, message) {
  return sha512(Buffer.concat([H, Buffer.from(message)]));
}&lt;/p&gt;
&lt;p&gt;// SP 800-108 CTR-HMAC-SHA-256 in its simplest 32-byte-output form
function kdfSP108(secret, label, context, length) {
  const out = [];
  let i = 1;
  while (out.reduce((n, b) =&amp;gt; n + b.length, 0) &amp;lt; length) {
    const counter = Buffer.alloc(4);
    counter.writeUInt32BE(i++, 0);
    const block = createHmac(&apos;sha256&apos;, secret)
      .update(counter)
      .update(Buffer.from(label, &apos;utf8&apos;))
      .update(Buffer.from([0]))
      .update(context)
      .update(Buffer.from([0, 0, 1, 0]))
      .digest();
    out.push(block);
  }
  return Buffer.concat(out).subarray(0, length);
}&lt;/p&gt;
&lt;p&gt;const NEGOTIATE_REQ  = &apos;NEGOTIATE Request (client, signing required)&apos;;
const NEGOTIATE_RESP = &apos;NEGOTIATE Response (server, signing required)&apos;;
const SESSION_REQ    = &apos;SESSION_SETUP Request (NTLM challenge response)&apos;;
const SESSION_RESP   = &apos;SESSION_SETUP Response (success)&apos;;&lt;/p&gt;
&lt;p&gt;function derive(messages) {
  let H = Buffer.alloc(64, 0);
  for (const m of messages) H = updateChain(H, m);
  const authSecret = Buffer.from(&apos;AUTHENTICATION_PROTOCOL_SHARED_SECRET&apos;);
  return kdfSP108(authSecret, &apos;SMBSigningKey&apos;, H, 16).toString(&apos;hex&apos;);
}&lt;/p&gt;
&lt;p&gt;const honestClient = derive([NEGOTIATE_REQ, NEGOTIATE_RESP, SESSION_REQ, SESSION_RESP]);
const honestServer = derive([NEGOTIATE_REQ, NEGOTIATE_RESP, SESSION_REQ, SESSION_RESP]);
console.log(&apos;honest client:&apos;, honestClient);
console.log(&apos;honest server:&apos;, honestServer);
console.log(&apos;match:&apos;, honestClient === honestServer);&lt;/p&gt;
&lt;p&gt;// Attacker flips one byte in NEGOTIATE Response only on the client side
const tamperedClient = derive([NEGOTIATE_REQ, NEGOTIATE_RESP.replace(&apos;required&apos;, &apos;optional&apos;), SESSION_REQ, SESSION_RESP]);
const honestServer2  = derive([NEGOTIATE_REQ, NEGOTIATE_RESP, SESSION_REQ, SESSION_RESP]);
console.log(&apos;tampered client:&apos;, tamperedClient);
console.log(&apos;honest server:  &apos;, honestServer2);
console.log(&apos;match:&apos;, tamperedClient === honestServer2);
`}&lt;/p&gt;
&lt;p&gt;The honest pair of keys match. The tampered pair do not. The first signed READ that arrives at the server gets verified with a key that does not match the one the client used to sign it, and the server returns &lt;code&gt;STATUS_USER_SESSION_DELETED&lt;/code&gt;. The attacker has not been detected by a validation message; she has been routed around by a key-derivation function.&lt;/p&gt;

sequenceDiagram
    participant C as Client
    participant S as Server
    Note over C,S: H starts as 64 zero bytes on both sides
    C-&amp;gt;&amp;gt;S: NEGOTIATE Request
    Note over C,S: H = SHA-512(H, NEGOTIATE Request)
    S-&amp;gt;&amp;gt;C: NEGOTIATE Response
    Note over C,S: H = SHA-512(H, NEGOTIATE Response)
    C-&amp;gt;&amp;gt;S: SESSION_SETUP Request
    Note over C,S: H = SHA-512(H, SESSION_SETUP Request)
    S-&amp;gt;&amp;gt;C: SESSION_SETUP Response
    Note over C,S: H = SHA-512(H, SESSION_SETUP Response)
    Note over C,S: H enters SP 800-108 KDF as Context, derives SigningKey, CipherKey

flowchart LR
    A[Authentication secret] --&amp;gt; B[SP 800-108 CTR-HMAC-SHA-256 KDF]
    L[Label: SMBSigningKey or SMBC2SCipherKey or SMBS2CCipherKey or SMBAppKey] --&amp;gt; B
    H[Context: PreauthIntegrityHashValue] --&amp;gt; B
    B --&amp;gt; K1[SigningKey]
    B --&amp;gt; K2[Client-to-server CipherKey]
    B --&amp;gt; K3[Server-to-client CipherKey]
    B --&amp;gt; K4[AppKey]

A type-length-value carrier introduced in SMB 3.1.1&apos;s NEGOTIATE Request and Response so that future dialects can add new cryptographic capabilities without changing the parent message structure. Each context has a 2-byte type identifier, a length, and a body. The defined types include `SMB2_PREAUTH_INTEGRITY_CAPABILITIES` (the hash function and salt), `SMB2_ENCRYPTION_CAPABILITIES` (the encryption ciphers offered), and `SMB2_SIGNING_CAPABILITIES` (the signing algorithms offered, added in 2021 to support AES-GMAC). Because all negotiate contexts are part of the pre-authentication message stream, they are all hashed into `PreauthIntegrityHashValue` automatically [@ms-smb2-pdf].
&lt;p&gt;From this moment forward, every byte of NEGOTIATE and SESSION_SETUP is cryptographically bound to the eventual session key. There is no way an in-path attacker can downgrade a 3.1.1 connection without producing two divergent session keys and therefore no signed message after authentication can possibly verify on both sides. The mechanism does have one limit, and it is the one that the next decade of SMB hardening had to deal with: pre-authentication integrity ensures that &lt;em&gt;if&lt;/em&gt; signing or encryption is negotiated, an attacker cannot tamper with the negotiation. It does not require either to be negotiated.&lt;/p&gt;
&lt;h2&gt;5. The 2021 Cipher Refresh and SMB-over-QUIC&lt;/h2&gt;
&lt;p&gt;Six years after pre-authentication integrity shipped, two parallel additions arrived in the same Windows release window. Windows 11 21H2 and Server 2022, both from the second half of 2021, extended the SMB cipher matrix and introduced a new transport that ran SMB inside QUIC instead of TCP.&lt;/p&gt;
&lt;h3&gt;The 2021 cipher refresh&lt;/h3&gt;
&lt;p&gt;The Negotiate Context machinery designed in 2015 absorbed three new values for &lt;code&gt;EncryptionAlgorithmId&lt;/code&gt; without a dialect bump [@ms-smb2-versioning]:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0x0002&lt;/code&gt;: AES-128-GCM (already shipped with SMB 3.1.1 in 2015)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0x0003&lt;/code&gt;: AES-256-CCM (added 2021)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0x0004&lt;/code&gt;: AES-256-GCM (added 2021)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A fourth addition was the &lt;code&gt;SMB2_SIGNING_CAPABILITIES&lt;/code&gt; negotiate context, which let the dialect negotiate &lt;strong&gt;AES-GMAC&lt;/strong&gt; as a signing algorithm alongside AES-CMAC and HMAC-SHA-256 [@ms-smb2-versioning]. AES-GMAC is the authentication-only mode derived from AES-GCM, defined in NIST SP 800-38D [@nist-sp-800-38d].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;code&gt;EncryptionAlgorithmId&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Key&lt;/th&gt;
&lt;th&gt;First SMB version&lt;/th&gt;
&lt;th&gt;CNSA 2.0 compatible&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x0001&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AES-128-CCM&lt;/td&gt;
&lt;td&gt;128 bits&lt;/td&gt;
&lt;td&gt;SMB 3.0 (2012)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Two-pass AEAD; pre-AES-NI baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x0002&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AES-128-GCM&lt;/td&gt;
&lt;td&gt;128 bits&lt;/td&gt;
&lt;td&gt;SMB 3.1.1 (2015)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Single-pass AEAD; faster than CCM on AES-NI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x0003&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AES-256-CCM&lt;/td&gt;
&lt;td&gt;256 bits&lt;/td&gt;
&lt;td&gt;SMB 3.1.1 (2021 cipher refresh)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CNSA-grade; rarely chosen over GCM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;0x0004&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;AES-256-GCM&lt;/td&gt;
&lt;td&gt;256 bits&lt;/td&gt;
&lt;td&gt;SMB 3.1.1 (2021 cipher refresh)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;CNSA-grade and AES-NI / VAES accelerated&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The U.S. National Security Agency&apos;s Commercial National Security Algorithm Suite version 2.0, published in 2022 as the symmetric and asymmetric algorithm baseline for protecting U.S. classified information up to TOP SECRET. The symmetric component requires AES-256 in any AEAD mode for confidentiality and AES-256-GMAC or HMAC-SHA-384 for integrity. The SMB combination that satisfies CNSA 2.0 is AES-256-GCM for encryption and AES-GMAC for signing; AES-128 variants are not CNSA-eligible.
&lt;h3&gt;Why AES-GMAC won the speed argument&lt;/h3&gt;
&lt;p&gt;AES-CMAC runs AES in CBC mode over the message, with one AES block operation per 16 bytes of input. AES-GMAC reuses the GCM mode&apos;s Galois-field multiplier, GHASH, which can be implemented in parallel using a single AES key-schedule and the PCLMULQDQ carry-less multiplication instruction that Intel added with the Westmere generation in 2010 [@iacr-2018-392].PCLMULQDQ multiplies two 64-bit operands as polynomials over GF(2), bypassing the carry chain that makes integer multiplication slow. It is the workhorse of every modern AES-GCM implementation.&lt;/p&gt;
&lt;p&gt;Drucker, Gueron and Krasnov&apos;s 2018 ePrint &lt;em&gt;Making AES Great Again&lt;/em&gt; documents the performance ceiling on contemporary Intel hardware [@iacr-2018-392]. They measured &lt;strong&gt;0.64 cycles per byte for AES-GCM single-buffer&lt;/strong&gt; on Skylake-X with AES-NI and PCLMULQDQ, and projected the theoretical bound at &lt;strong&gt;0.16 cycles per byte&lt;/strong&gt; for the AVX-512 + VPCLMULQDQ multi-buffer variant on Ice Lake.&lt;/p&gt;
&lt;p&gt;The single-buffer ratio against AES-CMAC, which the same paper notes runs at roughly 2 to 3 cycles per byte on equivalent hardware, is about 3 to 5x. The multi-buffer ratio is larger, often 6x or more, because GMAC parallelises across independent 16-byte blocks where CMAC&apos;s CBC chain serialises them.&lt;/p&gt;
&lt;p&gt;Taken together, those numbers mean that on a modern file server saturating a 25 Gbit/s NIC, AES-GMAC signing costs roughly one-third the CPU of AES-CMAC for the same packet rate. That is not a marginal optimisation; it is the difference between needing two cores to sign and needing one.&lt;/p&gt;
&lt;h3&gt;SMB-over-QUIC&lt;/h3&gt;
&lt;p&gt;The second 2021 addition was a new transport. Microsoft demoed SMB-over-QUIC in early 2021 and shipped it for general availability in November 2021 with the Windows Server 2022 Datacenter: Azure Edition SKU [@smb-over-quic]. The transport runs SMB inside a QUIC connection (RFC 9000 [@rfc9000]) whose record layer is encrypted by TLS 1.3 (RFC 8446 [@rfc8446]) and bound to QUIC by RFC 9001 [@rfc9001]. The wire-layer port is UDP/443, the same port HTTPS-over-QUIC uses, which means SMB-over-QUIC traverses most firewalls that already pass web traffic.&lt;/p&gt;

A UDP-based multiplexed and secure transport protocol designed by Google in 2012 and standardised at IETF in 2021 as RFC 9000 [@rfc9000]. QUIC carries multiple independent streams of reliable bytes inside a single connection, uses TLS 1.3 for the cryptographic handshake (RFC 9001 [@rfc9001]), and identifies connections by a connection ID rather than the 5-tuple, which means a QUIC connection survives an IP address change. SMB-over-QUIC uses QUIC&apos;s reliable stream abstraction to carry the SMB packets that would otherwise go over TCP, and inherits QUIC&apos;s TLS 1.3 record-layer encryption.
&lt;h3&gt;The trust-anchor shift&lt;/h3&gt;
&lt;p&gt;SMB-over-TCP/445 trusts only the authentication protocol. If Kerberos or NTLM trusts the principal, the SMB session derives a key from that authentication and proceeds. There is no other trust input.&lt;/p&gt;
&lt;p&gt;SMB-over-QUIC adds &lt;strong&gt;server certificate PKI&lt;/strong&gt; to the trust model. The QUIC handshake authenticates the server with a TLS 1.3 X.509 certificate chain that must validate against the client&apos;s trust store. This is a real change in threat model: a compromised certificate authority in the client&apos;s root store can MITM SMB-over-QUIC the same way it can MITM HTTPS. For some organisations that is a feature, a trust anchor that lives in the certificate authority instead of in Kerberos. For others it is a regression.&lt;/p&gt;

Some descriptions of SMB-over-QUIC suggest the SMB application layer is unencrypted because QUIC already encrypts everything. That is wrong in a way that matters. SMB-over-QUIC still negotiates SMB 3.1.1 pre-authentication integrity and SMB signing; the SMB session key derives independently from the authentication protocol, not from the TLS 1.3 session secret. There are two layers of encryption (TLS 1.3 at the QUIC layer and AES-GCM at the SMB layer) precisely because the two layers have independent trust anchors. If the QUIC TLS 1.3 session is MITM&apos;d by a rogue CA, the SMB-layer signing keys still verify against the authentication protocol&apos;s session key; the inner layer is the structural backstop.
&lt;p&gt;The original SMB-over-QUIC release was restricted to Windows Server 2022 Datacenter: Azure Edition, a SKU available only as an Azure-hosted virtual machine [@smb-over-quic]. That restriction made SMB-over-QUIC effectively unusable for the on-premises file-server population that the protocol most needed. The restriction was lifted with Windows Server 2025, which broadens SMB-over-QUIC hosting to every edition of the server SKU [@ws-2025-whats-new][@smb-over-quic]. On the client side, Windows 11 24H2 became the first SMB-over-QUIC-capable mainstream desktop client [@smb-feature-descriptions].&lt;/p&gt;
&lt;p&gt;Samba 4.15.0 (September 2021) added the SMB3 signing-algorithm-negotiation parameters (&lt;code&gt;client smb3 signing algorithms&lt;/code&gt;, &lt;code&gt;server smb3 signing algorithms&lt;/code&gt;) and the SMB3 encryption-algorithm-negotiation parameters, and fixed AES-256-GCM/CCM server-side support that was previously broken under bug 14764 [@samba-4-15-0]. SMB-over-QUIC support for both &lt;code&gt;smbd&lt;/code&gt; and &lt;code&gt;smbclient&lt;/code&gt; did not arrive in Samba until 4.23.0, in September 2025, closing a four-year interop gap with the November 2021 Microsoft release [@samba-4-23-0].&lt;/p&gt;
&lt;p&gt;The 2021 refresh handed enterprises CNSA-grade ciphers and a no-VPN remote-file-access transport. But signing was still opt-in by default for general SMB shares, and the SMB-relay toolkit that Sir Dystic released in 2001 still worked against any 21H2 client that an administrator had not hardened by hand. The defaults had not caught up.&lt;/p&gt;
&lt;h2&gt;6. The 2024 Defaults: Locks Finally Turned On&lt;/h2&gt;
&lt;p&gt;On &lt;strong&gt;October 1, 2024&lt;/strong&gt;, Windows 11 24H2 shipped to retail [@win11-release-info]. A month later, on November 1, 2024, Windows Server 2025 followed [@ws-2025-whats-new]. The two releases changed seven defaults in one go, and exactly one of those changes mattered most.&lt;/p&gt;
&lt;p&gt;The headline change was structural: SMB signing required by default on both outbound and inbound connections for every share, not just SYSVOL and NETLOGON [@smb-security-hardening]. The Microsoft Learn SMB Security Hardening page states it directly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Starting with Windows 11 24H2 and Windows Server 2025, all outbound and inbound SMB connections are now required to be signed by default. Previously, SMB signing was only required by default for connections to shares named SYSVOL and NETLOGON, and for clients of AD domain controllers. [@smb-security-hardening]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ned Pyle, the Microsoft file server product manager who has shipped SMB releases for the better part of a decade, summarised the bundle in a Tech Community post that doubles as the canonical 2024 reference:&lt;/p&gt;

With the release of Windows Server 2025 and Windows 11 24H2, we have made the most changes to SMB security since the introduction of SMB 2 in Windows Vista. -- Ned Pyle, Microsoft [@pyle-techcom-4226591]
&lt;h3&gt;The bundle, in priority order&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Signing required by default&lt;/strong&gt;, the load-bearing change. The omission-downgrade attack that has worked since 2001 is structurally closed for any 24H2-or-later pair, because the server will refuse an unsigned session [@smb-security-hardening][@pyle-techcom-4226591].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-side encryption mandate&lt;/strong&gt;, available but &lt;strong&gt;not on by default&lt;/strong&gt;. &lt;code&gt;Set-SmbClientConfiguration -RequireEncryption $true&lt;/code&gt; now refuses any outbound SMB session that does not negotiate AES-GCM encryption [@smb-security-hardening]. The most-misreported fact about the 2024 rollout is whether this is on. It is not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Authentication rate limiter.&lt;/strong&gt; A 2-second delay between failed NTLM or local-KDC Kerberos authentication attempts, configurable but on by default. Microsoft&apos;s published arithmetic: an attack that sends 300 guesses per second for 5 minutes (90,000 attempts) now takes 50 hours [@smb-security-hardening].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NTLM blocking for outbound SMB.&lt;/strong&gt; &lt;code&gt;Set-SmbClientConfiguration -BlockNTLM $true&lt;/code&gt; refuses outbound NTLM fallback, forcing Kerberos or failing the session [@smb-security-hardening].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SMB dialect minimum and maximum&lt;/strong&gt;, configurable per-host on both client and server [@smb-security-hardening].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SMB alternative ports&lt;/strong&gt;, so that an administrator can run SMB-over-QUIC on a non-default UDP port without losing audit support [@smb-security-hardening].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Insecure guest authentication blocked by default&lt;/strong&gt;, on every edition of Windows 11, closing a gap that previously only Enterprise and Education had been opted into since Windows 10 1709 [@smb-interception-defense].&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The eighth change, also part of the 2024-2025 cycle but described in the Server 2025 release notes rather than the hardening page, was the &lt;strong&gt;broadening of SMB-over-QUIC hosting&lt;/strong&gt; from the Azure Edition SKU to every edition of Windows Server 2025 [@ws-2025-whats-new][@smb-over-quic]. That broadening is the transport-side enabler for the rest of the bundle, because it makes SMB-over-QUIC available to organisations that do not run their file servers in Azure.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The 2024-2025 rollout made SMB &lt;strong&gt;signing&lt;/strong&gt; required by default. It did not make SMB &lt;strong&gt;encryption&lt;/strong&gt; required by default. A 24H2 client speaking to a Server 2025 file share without an administrator&apos;s opt-in will still send the file&apos;s contents over the wire in cleartext, with only the per-packet AES-CMAC signatures preventing tampering. A passive eavesdropper on the path can still read the file. The fix is one line of PowerShell (&lt;code&gt;Set-SmbClientConfiguration -RequireEncryption $true&lt;/code&gt;) or a per-share &lt;code&gt;Set-SmbShare -EncryptData $true&lt;/code&gt;. Several widely-circulated 2024 press summaries got this backwards; the Microsoft Learn page is the source of truth [@smb-security-hardening].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;The arc, closed&lt;/h3&gt;
&lt;p&gt;The 2001 SMBRelay disclosure to the 2024 default-on flip is twenty-three years and seven months [@smbrelay-cdc][@smb-security-hardening]. That is the arithmetic of the SMB story. The mechanism that closed the omission-downgrade attack family for the typical Windows file share existed in production from July 2015 onwards; the defaults that made the mechanism universal arrived nine years later.&lt;/p&gt;
&lt;p&gt;The 2024 default does not retroactively secure the long tail of legacy hosts: the SMB-relay primitive that Sir Dystic shipped in 2001 still works against any pre-24H2 Windows client that an administrator has not turned signing on for by hand, and against any third-party SMB server that does not yet support signing. Default-on protects the leading edge; the legacy fleet upgrades on its own calendar.&lt;/p&gt;
&lt;h3&gt;The compatibility breakage&lt;/h3&gt;
&lt;p&gt;The default-on signing flip broke a long tail of consumer NAS devices that ship SMB server implementations without signing support. Synology and QNAP and NETGEAR home NAS units running older firmware refused 24H2 client connections starting October 2024, and the recovery path required either a firmware update from the vendor or a per-host policy exception. The DSInternals matrix tracks the exact set of clients and servers that pass the new defaults [@dsinternals-24h2].&lt;/p&gt;

The audit channel for tracking which peers an organisation&apos;s clients fail to sign with is `Applications and Services Logs\Microsoft\Windows\SmbClient\Audit`, populated by the SMB client itself whenever it refuses an unsigned session [@smb-security-hardening]. The transitional pattern administrators were forced to adopt in late 2024 was: (a) leave signing required globally, (b) maintain a per-host exception list for unupgradable NAS devices, (c) put the exception list under change control with a target removal date. A few months of audit-log data is usually enough to enumerate the long tail of peers an organisation actually depends on; the policy-exception approach is structurally sound because the exception is narrow and time-bounded.
&lt;h3&gt;What still works: the cross-protocol relay class&lt;/h3&gt;
&lt;p&gt;Default-on signing closed the &lt;em&gt;single-protocol&lt;/em&gt; SMB relay. It did nothing about the &lt;em&gt;cross-protocol&lt;/em&gt; relay class, which routes around SMB entirely. The exemplar attack is &lt;a href=&quot;https://paragmali.com/blog/ntlmless-the-death-of-ntlm-in-windows/&quot; rel=&quot;noopener&quot;&gt;PetitPotam&lt;/a&gt;, disclosed by Lionel &quot;Topotam&quot; Gilles in July 2021 [@petitpotam-gh][@nvd-cve-2021-36942], with a sibling variant CVE-2022-26925 on the LSARPC interface [@nvd-cve-2022-26925].&lt;/p&gt;
&lt;p&gt;PetitPotam abuses the Encrypting File System Remote Protocol (MS-EFSRPC). The &lt;code&gt;EfsRpcOpenFileRaw&lt;/code&gt; method, exposed via the LSARPC named pipe over SMB, accepts a UNC path that the server resolves by &lt;strong&gt;opening an outbound SMB connection&lt;/strong&gt; to whatever host the path points at, using the calling user&apos;s credentials [@petitpotam-gh].&lt;/p&gt;
&lt;p&gt;An attacker who can call &lt;code&gt;EfsRpcOpenFileRaw&lt;/code&gt; on a server with a UNC path pointing back at the attacker&apos;s host obtains an outbound NTLM authentication from the server, typically a domain controller, since DCs run the EFSRPC service. The attacker then relays that NTLM authentication, not to another SMB server, but to &lt;strong&gt;&lt;a href=&quot;https://paragmali.com/blog/certified-pre-owned-ad-cs-and-active-directorys-second-trust/&quot; rel=&quot;noopener&quot;&gt;Active Directory Certificate Services&lt;/a&gt; Web Enrollment&lt;/strong&gt; at &lt;code&gt;/certsrv/&lt;/code&gt;, which speaks HTTP and historically did not require client-side signing of the authentication.&lt;/p&gt;
&lt;p&gt;Once the relay completes, the attacker has a certificate issued in the name of the coerced principal, which on a domain controller is a domain controller account. From there, the attacker forges Kerberos tickets and the domain is compromised.&lt;/p&gt;
&lt;p&gt;A 2019 result by Marina Simakov and Yaron Zinar at Preempt (now CrowdStrike), CVE-2019-1040, sharpens the cross-protocol concern further [@drop-the-mic][@nvd-cve-2019-1040]. The pair found that an in-path attacker could bypass the NTLM Message Integrity Code by manipulating SPNEGO fields, then strip the &quot;signing required&quot; bit from the relayed NTLM message itself.&lt;/p&gt;
&lt;p&gt;That meant a relay attack could survive against targets that required NTLM signing, because the signing-required negotiation could be tampered with on the NTLM authentication exchange. Microsoft patched CVE-2019-1040 in 2019, but the result underscored the same lesson at the NTLM layer that SMB had learned at the SMB layer: any signing negotiation that is not bound to the eventual key derivation can be stripped.&lt;/p&gt;

A coercion-and-relay attack family disclosed by Lionel Gilles in July 2021 [@petitpotam-gh] in which the attacker invokes MS-EFSRPC `EfsRpcOpenFileRaw` on a target server with a UNC path that the server resolves by opening an outbound SMB connection, leaking the server&apos;s NTLM credentials to the attacker. The leaked credentials are then relayed cross-protocol to AD CS Web Enrollment over HTTPS. The class subsumes CVE-2021-36942 (LSA over the LSARPC pipe) and CVE-2022-26925 (a sibling LSARPC variant). The defence is **not** SMB signing on the target; it is Extended Protection for Authentication on the relay-to endpoint [@kb5005413].

A defence against NTLM relay attacks that binds the underlying transport&apos;s channel (typically a TLS server certificate hash) into the GSS channel-binding token that the SPNEGO authentication exchange carries [@kb5005413]. An attacker who relays an NTLM authentication from a victim&apos;s connection to an attacker-controlled TLS connection finds that the channel bindings disagree, and the relay-to endpoint refuses the authentication. EPA on AD CS `/certsrv/` is the canonical fix for PetitPotam-class attacks [@kb5005413].
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; SMB at the wire layer is now cryptographically sufficient. Modern SMB-related attacks no longer try to break SMB. They coerce SMB into authenticating and then relay the authentication to a protocol that does not require signing. The defence is no longer a single-protocol problem. Extended Protection for Authentication on AD CS, and eventually NTLM removal, are the structural fixes -- nothing at the SMB layer can close the cross-protocol relay class.&lt;/p&gt;
&lt;/blockquote&gt;

sequenceDiagram
    participant A as Attacker
    participant V as Victim DC
    participant ADCS as ADCS Web Enrollment over HTTPS
    A-&amp;gt;&amp;gt;V: LSARPC EfsRpcOpenFileRaw, UNC path to attacker share
    V-&amp;gt;&amp;gt;A: Outbound SMB NEGOTIATE with NTLM credentials of DC machine account
    Note over A,V: SMB signing on V does not help, V is the client
    A-&amp;gt;&amp;gt;ADCS: Relayed NTLM authentication to /certsrv/certfnsh.asp
    ADCS-&amp;gt;&amp;gt;A: Issues certificate for DC machine account
    Note over A,ADCS: Without EPA on /certsrv/ the relay succeeds
&lt;p&gt;The 2024 hardening package is real progress. It is also explicitly bounded. The headline change closes one attack class structurally and leaves the cross-protocol class untouched. To see what is still open, we have to leave SMB for a moment and look at the cryptographic limits the protocol still inherits.&lt;/p&gt;
&lt;h3&gt;A short PowerShell-equivalent for the audit&lt;/h3&gt;
&lt;p&gt;To verify the new defaults on a freshly imaged 24H2 client, run &lt;code&gt;Get-SmbClientConfiguration | Select RequireSecuritySignature&lt;/code&gt; and the server analogue. The runnable snippet below shows the &lt;em&gt;logic&lt;/em&gt; of those commands in JavaScript: readable, not directly executable on Windows.&lt;/p&gt;
&lt;p&gt;{`
const smbClientPolicyOn24H2 = {
  RequireSecuritySignature: true,
  EnableSecuritySignature: true,
  BlockNTLM: false,
  RequireEncryption: false,
  AuditServerDoesNotSupportEncryption: true,
  AuditServerDoesNotSupportSigning: true,
};&lt;/p&gt;
&lt;p&gt;function reportPosture(cfg) {
  if (cfg.RequireSecuritySignature) {
    console.log(&apos;Signing: required (24H2 default).&apos;);
  } else {
    console.log(&apos;Signing: NOT required. Run Set-SmbClientConfiguration -RequireSecuritySignature true.&apos;);
  }
  if (cfg.RequireEncryption) {
    console.log(&apos;Encryption: required.&apos;);
  } else {
    console.log(&apos;Encryption: opt-in only. Consider Set-SmbClientConfiguration -RequireEncryption true.&apos;);
  }
  if (cfg.BlockNTLM) {
    console.log(&apos;NTLM outbound: blocked.&apos;);
  } else {
    console.log(&apos;NTLM outbound: allowed. Consider -BlockNTLM true on hardened hosts.&apos;);
  }
}&lt;/p&gt;
&lt;p&gt;reportPosture(smbClientPolicyOn24H2);
`}&lt;/p&gt;
&lt;h2&gt;7. What SMB 3.1.1 Cannot Do&lt;/h2&gt;
&lt;p&gt;Five things SMB 3.1.1 cannot do, even with every default flipped on and every primitive at full strength.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Pre-authentication integrity is dialect-internal.&lt;/strong&gt; The mechanism binds the negotiation transcript to the session key inside SMB. It cannot stop an in-path attacker from refusing to forward a connection at all. Denial of service is structurally outside the threat model of any signing or encryption primitive; the only defence is path redundancy.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Signing requires both endpoints to support it.&lt;/strong&gt; A 24H2 client refuses to talk unsigned to a pre-24H2 server only if the server is willing to sign. A pre-24H2 server that runs without signing turned on -- the long tail of consumer NAS, embedded appliances, older Linux Samba installs, ESXi datastores -- still answers an unsigned NEGOTIATE and still produces an unsigned session. The omission-downgrade is closed only for 24H2-to-24H2 pairs (or 24H2-to-Samba-4.15+ pairs). The legacy fleet upgrades over years, not weeks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Encryption is not authenticated key exchange.&lt;/strong&gt; The SMB session key derives from the authentication protocol&apos;s session secret -- the NTLM &lt;code&gt;ExportedSessionKey&lt;/code&gt; or the Kerberos service-ticket session key [@ms-smb2-pdf]. SMB inherits whatever weakness the authentication protocol carries. A user with a 30-bit-entropy password who authenticates with NTLM has a session key whose effective entropy is bounded above by the password&apos;s. AES-256-GCM does not save that user; the symmetric ciphers are working as advertised, but the secret they are protecting is too weak to survive an offline attack on the password hash.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; SMB encryption is upper-bounded by the password entropy of the authentication protocol that derived the session key. AES-256 does not improve a weak password. Pre-authentication integrity does not improve a weak password. The cryptographic primitives are doing what they say they do; the limit is one layer higher.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the protocol-level analogue of the older cryptographic adage that you cannot create entropy from key-stretching alone. PBKDF2 with a million iterations slows an attacker down by a factor of a million, but if the password has 30 bits of entropy, the work factor is still 2^30 -- a million iterations on a modern GPU is on the order of an hour for a single targeted password.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. SMB-over-QUIC binds the trust anchor to TLS 1.3 PKI.&lt;/strong&gt; Over TCP/445, SMB trusts only Kerberos or NTLM. Over QUIC, SMB additionally trusts the certificate authorities in the client&apos;s TLS trust store.&lt;/p&gt;
&lt;p&gt;A compromised CA in that store can MITM SMB-over-QUIC the way it MITMs HTTPS. The inner SMB layer&apos;s pre-authentication integrity will catch tampering with the SMB session itself, but the outer QUIC layer is now in the threat model. Whether this is a feature or a regression depends on whether you trust your TLS trust store more than you trust the Kerberos service that issues your tickets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Cross-protocol relay survives single-protocol hardening.&lt;/strong&gt; This is the strongest impossibility result that touches SMB. If protocol A (SMB) coerces an outbound authentication from a victim host, and the attacker relays that authentication cross-protocol to a target running protocol B (HTTPS to AD CS Web Enrollment) that does not require signing, no amount of hardening on protocol A&apos;s session layer prevents the attack.&lt;/p&gt;
&lt;p&gt;The fix has to live in the target&apos;s session layer (EPA on &lt;code&gt;/certsrv/&lt;/code&gt;) or in the authentication protocol itself (NTLM disablement). Closing it at the SMB layer is, in the precise cryptographic sense, impossible.&lt;/p&gt;

The observation that the effective security of an SMB session against an offline attack is bounded above by the entropy of the authentication-protocol shared secret that derived the SMB session key, not by the bit length of the negotiated AES cipher. A 256-bit cipher protecting a session key derived from a 30-bit-entropy password offers, against an offline attacker who can capture the authentication exchange, no more than 30 bits of work-factor security. SMB inherits this property from any authentication protocol whose key-establishment phase is a password-equivalent shared-key derivation.
&lt;p&gt;Of these five, three are deployment posture and two are structural. You can fix #2 by replacing the legacy fleet. You can fix #4 by curating your TLS trust store, removing CAs you do not need to trust, and pinning the AD CS PKI explicitly. You can fix #5 by removing NTLM, which Microsoft is in the middle of doing. Limits #1 (denial of service) and #3 (password-equivalence) are not deployment posture; they are properties of the underlying cryptographic objects, and they will be true of any SMB-shaped protocol that inherits a session key from an authentication exchange.&lt;/p&gt;
&lt;h2&gt;8. Competing Protocols, Parallel Paths&lt;/h2&gt;
&lt;p&gt;SMB is one of four families of file-sharing protocols a modern enterprise might deploy. Each made different cryptographic choices, and each got something right that the others got wrong.&lt;/p&gt;
&lt;h3&gt;NFS v4.2 with &lt;code&gt;sec=krb5p&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;NFS version 4, standardised in RFC 7530 [@rfc7530] and refined in RFC 8881 for the 4.1 minor version [@rfc8881], replaced the optional RPCSEC_GSS security flavour of NFSv3 with a mandatory authentication negotiation step. With &lt;code&gt;sec=krb5p&lt;/code&gt;, NFS authenticates each user with a Kerberos service ticket and encrypts each RPC payload with the Kerberos session key.&lt;/p&gt;
&lt;p&gt;The cipher matrix tracks Kerberos&apos;s: RFC 8009 (October 2016) defined enctype 19 (AES-128-CTS-HMAC-SHA-256-128) and enctype 20 (AES-256-CTS-HMAC-SHA-384-192), and &lt;code&gt;sec=krb5p&lt;/code&gt; on a modern Linux client and server uses enctype 20 by default [@rfc8009]. The cryptographic posture is genuinely comparable to SMB 3.1.1 with AES-256-GCM. The differences are platform (NFS is native to Linux and Unix, SMB is native to Windows) and the way transcript binding is achieved (NFS relies on the underlying GSS context binding rather than a separate hash chain).&lt;/p&gt;
&lt;p&gt;NFS-over-QUIC is in IETF draft. The most recent revision of &lt;code&gt;draft-cel-nfsv4-rpc-over-quicv1&lt;/code&gt;, dated 16 May 2026, is at revision -05 and is not yet on a standards-track timeline [@draft-nfs-rpc-over-quic]. SMB-over-QUIC went generally available in November 2021. NFS is roughly four years behind on this transport.&lt;/p&gt;
&lt;h3&gt;WebDAV over HTTPS&lt;/h3&gt;
&lt;p&gt;WebDAV, defined in RFC 4918 [@rfc4918], is HTTP with the verbs &lt;code&gt;PROPFIND&lt;/code&gt;, &lt;code&gt;PROPPATCH&lt;/code&gt;, &lt;code&gt;MKCOL&lt;/code&gt;, &lt;code&gt;COPY&lt;/code&gt;, &lt;code&gt;MOVE&lt;/code&gt;, &lt;code&gt;LOCK&lt;/code&gt;, and &lt;code&gt;UNLOCK&lt;/code&gt;. Its security model is &quot;use TLS.&quot; The OPTIONS and PROPFIND prologue messages that a WebDAV client exchanges with a server are not transcript-bound at the WebDAV layer. The binding lives entirely in the TLS handshake.&lt;/p&gt;
&lt;p&gt;For internet-facing file access that does not need Active Directory integration, WebDAV is a sound choice, and it remains a common SMB alternative for shipping documents to mobile clients. For AD-integrated scenarios, where SMB&apos;s Kerberos integration is the point, WebDAV does not compete.&lt;/p&gt;
&lt;h3&gt;FTPS and SFTP&lt;/h3&gt;
&lt;p&gt;FTPS (RFC 4217 [@rfc4217]) adds opt-in TLS to FTP via the &lt;code&gt;AUTH TLS&lt;/code&gt; command, which is structurally similar to SMTP&apos;s STARTTLS or LDAP&apos;s StartTLS. The opt-in nature is its weakness; the historical &quot;stripping&quot; attacks against opt-in TLS apply to FTPS just as they applied to opt-in NEGOTIATE bits in SMB.&lt;/p&gt;
&lt;p&gt;SFTP runs over SSH-2 (RFC 4253 [@rfc4253]). Because SSH-2 mandatorily encrypts the transport layer, and because the SSH key exchange (§8 of RFC 4253) produces an exchange hash that is signed by the server&apos;s host key and binds the entire key-exchange transcript, SFTP gets transcript binding for free from its substrate.&lt;/p&gt;
&lt;p&gt;The closest cryptographic posture to SMB 3.1.1&apos;s pre-authentication integrity is, structurally, SFTP-over-SSH-2. Both protocols bind the entire negotiation into the session-key derivation. Both make tampering produce key divergence rather than detectable mismatch. SSH took ten years to get there; SMB took twenty-five.&lt;/p&gt;
&lt;p&gt;The reason SFTP is a popular SMB alternative in regulatory-burdened environments is precisely that the transport is mandatorily encrypted and the transcript is mandatorily bound. A PCI-DSS auditor who is asked whether a file transfer was protected against in-path tampering can point to RFC 4253 §8 and the SSH exchange-hash signature, and the answer is yes by construction.&lt;/p&gt;
&lt;h3&gt;Samba&lt;/h3&gt;
&lt;p&gt;Samba is the canonical open-source SMB server. The project tracks the Microsoft cipher matrix with a lag that has varied between months and years.&lt;/p&gt;
&lt;p&gt;Samba 4.15.0 (September 2021) added the SMB3 signing-algorithm-negotiation parameters and fixed AES-256-GCM/CCM server-side support [@samba-4-15-0]. Samba 4.23.0 (September 2025) added SMB-over-QUIC for both &lt;code&gt;smbd&lt;/code&gt; and &lt;code&gt;smbclient&lt;/code&gt;, closing the four-year interop gap with the Microsoft November 2021 SMB-over-QUIC release [@samba-4-23-0]. The Samba team has consistently been the authoritative third-party implementer of the SMB cipher matrix. The gap between Microsoft and Samba is the practical floor on how quickly the SMB world can absorb a new cipher.&lt;/p&gt;
&lt;h3&gt;SMB Direct&lt;/h3&gt;
&lt;p&gt;SMB Direct is not a competitor; it is a parallel transport. Defined in &lt;code&gt;[MS-SMBD]&lt;/code&gt; and shipped with Windows Server 2012 [@smb-direct], SMB Direct runs SMB over RDMA fabrics (RoCE or iWARP) on the LAN, bypassing the kernel TCP stack for high-throughput, low-latency file workloads.&lt;/p&gt;
&lt;p&gt;Encryption with placement was added in Windows Server 2022 [@smb-direct]. SMB Direct does not run over QUIC, because QUIC&apos;s user-space encryption is incompatible with RDMA&apos;s kernel-bypass placement model. The two transports are deployed for different workloads: SMB Direct on the data centre LAN, SMB-over-QUIC on the wide area.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Transport&lt;/th&gt;
&lt;th&gt;Encryption default&lt;/th&gt;
&lt;th&gt;Signing default&lt;/th&gt;
&lt;th&gt;Authentication&lt;/th&gt;
&lt;th&gt;Transcript binding&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;SMB 3.1.1 over TCP/445&lt;/td&gt;
&lt;td&gt;TCP/445&lt;/td&gt;
&lt;td&gt;Opt-in (Server 2025)&lt;/td&gt;
&lt;td&gt;Required by default (24H2)&lt;/td&gt;
&lt;td&gt;Kerberos or NTLM&lt;/td&gt;
&lt;td&gt;Pre-auth integrity (SHA-512 into SP 800-108 KDF)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SMB-over-QUIC&lt;/td&gt;
&lt;td&gt;UDP/443&lt;/td&gt;
&lt;td&gt;TLS 1.3 (QUIC) + opt-in SMB AES-GCM&lt;/td&gt;
&lt;td&gt;Required by default (24H2 client)&lt;/td&gt;
&lt;td&gt;Kerberos or NTLM + TLS 1.3 cert&lt;/td&gt;
&lt;td&gt;Pre-auth integrity inside QUIC + TLS 1.3 transcript hash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NFSv4.2 &lt;code&gt;sec=krb5p&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP (and QUIC draft)&lt;/td&gt;
&lt;td&gt;AES-256-GCM via RFC 8009&lt;/td&gt;
&lt;td&gt;Per-RPC GSS integrity&lt;/td&gt;
&lt;td&gt;Kerberos&lt;/td&gt;
&lt;td&gt;Kerberos GSS context binding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WebDAV over HTTPS&lt;/td&gt;
&lt;td&gt;TCP/443&lt;/td&gt;
&lt;td&gt;TLS-mandated&lt;/td&gt;
&lt;td&gt;TLS-mandated&lt;/td&gt;
&lt;td&gt;HTTP authentication header&lt;/td&gt;
&lt;td&gt;TLS 1.3 transcript hash only&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SFTP over SSH-2&lt;/td&gt;
&lt;td&gt;TCP/22&lt;/td&gt;
&lt;td&gt;Mandatory&lt;/td&gt;
&lt;td&gt;Mandatory&lt;/td&gt;
&lt;td&gt;SSH user authentication&lt;/td&gt;
&lt;td&gt;Exchange-hash signed by host key&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Across the four families, the convergence in 2026 is unmistakable. All paths lead to &lt;em&gt;authenticated transcript binding plus an AEAD over an established session key.&lt;/em&gt; SMB took twenty-five years to get there. SSH took ten. The two protocols arrived at the same shape from different starting points -- one a file-sharing protocol that picked up TLS-like primitives, the other a remote-shell protocol that always had them.&lt;/p&gt;
&lt;h2&gt;9. Open Problems for 2026 to 2028&lt;/h2&gt;
&lt;p&gt;Five problems the SMB team is working on now, and at least one nobody has a credible answer to.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. When does encryption-required-by-default flip?&lt;/strong&gt; The 24H2 client mandate exists, but it is opt-in. Microsoft has not made a public commitment to a default-flip date for &lt;code&gt;RequireEncryption $true&lt;/code&gt; [@smb-security-hardening].&lt;/p&gt;
&lt;p&gt;The stakes are real: passive eavesdropping on an unencrypted SMB session over TCP/445 still recovers the file contents. The deployment-side argument for not flipping yet is the long tail of pre-24H2 clients that cannot negotiate SMB encryption at all. A unilateral server-side flip locks them out. The deployment-side argument for flipping is that signing-required already locks them out of any signed share, and the marginal incompatibility cost is small. The decision is calendar, not cryptography.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. NTLM removal interplay.&lt;/strong&gt; Microsoft&apos;s three-phase NTLM disablement programme is on a multi-year timeline, with the explicit goal of removing the legacy authentication protocol from Windows entirely. The interaction with SMB is structural: an SMB session over Kerberos derives its keys differently than an SMB session over NTLM, and a long tail of NAS appliances and embedded SMB servers still expect NTLM.&lt;/p&gt;
&lt;p&gt;The transition pattern is: Kerberos by default, NTLM as a configurable fallback, NTLM disabled for outbound, NTLM removed. The PetitPotam class disappears the moment NTLM is gone. The cross-protocol relay requires the relayed authentication to be NTLM, because Kerberos service tickets are bound to a specific service principal and cannot be relayed to a different service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Post-quantum key exchange for SMB-over-TCP/445.&lt;/strong&gt; SMB-over-QUIC inherits TLS 1.3&apos;s &lt;a href=&quot;https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/&quot; rel=&quot;noopener&quot;&gt;hybrid post-quantum KEM groups&lt;/a&gt; (ML-KEM-based [@nist-fips-203], currently being deployed by major browsers and CDNs). SMB-over-TCP/445 has no protocol-level key-exchange step; it inherits whatever the authentication protocol provides.&lt;/p&gt;
&lt;p&gt;Kerberos and NTLM today have no PQC posture. The SMB session key is therefore unprotected against a &quot;harvest now, decrypt later&quot; attacker with a future cryptanalytically relevant quantum computer. The fix is either (a) move long-confidentiality workloads to SMB-over-QUIC to get TLS 1.3&apos;s hybrid KEM, or (b) wait for Kerberos to absorb a hybrid PQC enctype.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Cross-protocol relay survival.&lt;/strong&gt; PetitPotam-style attacks are not closeable at the SMB layer (see Section 7, limit #5). The structural fix is NTLM removal, which is problem #2 above. In the interim, the deployment-side defence is Extended Protection for Authentication on every NTLM-accepting endpoint -- AD CS Web Enrollment is the canonical one, but the same logic applies to LDAP, WSMan, IIS-hosted enterprise applications, and any internal HTTP service that accepts NTLM [@kb5005413].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Formal analysis of the full SMB 3.1.1 handshake composition.&lt;/strong&gt; No published Tamarin or ProVerif model of the full pre-authentication integrity plus SP 800-108 KDF plus Kerberos-or-NTLM composition exists in the academic literature.&lt;/p&gt;
&lt;p&gt;Individual analyses of AES-GCM, AES-CMAC, SP 800-108, and SHA-512 are tight at the primitive level. The composition of these primitives in the SMB 3.1.1 handshake is the place where TLS 1.2 historically broke (Lucky 13, Triple Handshake, the renegotiation attack of 2009, the CBC-MAC issues of POODLE). The absence of a published formal model is the single most-cited research gap in the SMB-protocol-security literature.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Problem #5 (formal analysis) is the only one of the five that is a genuine open research problem. Problems #1, #2, and #4 are deployment-posture decisions waiting on calendar dates and platform migration. Problem #3 will look quaint by 2030, when hybrid PQC is everyone&apos;s default rather than the SMB team&apos;s. Problem #5 is hard in a different way: the composition seam between pre-authentication integrity, the SP 800-108 KDF, and the underlying authentication protocol is exactly the shape that historically produces protocol-composition vulnerabilities, and the SMB community has not yet had its TLS-1.3-formal-analysis moment.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The TLS 1.2 vulnerability history is the standard cautionary tale here. Lucky 13 (2013), POODLE (2014), the Triple Handshake (2014), and the renegotiation attack (2009) all arose at composition seams between primitives that were individually sound. The TLS 1.3 design explicitly tried to remove those seams, and the IETF process required formal models (Tamarin and ProVerif) before the protocol could go to RFC. SMB 3.1.1 has not yet been through that process, and the absence is the strongest argument for funding the work.&lt;/p&gt;
&lt;p&gt;None of these five problems undo the 25-year arc that closed in October 2024. Pre-authentication integrity remains structurally sound; the cipher matrix remains at the CNSA 2.0 ceiling; the defaults remain on. The open problems are &lt;em&gt;what comes next&lt;/em&gt;, not &lt;em&gt;what is broken&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;10. Practical Recipes and Frequently Asked Questions&lt;/h2&gt;
&lt;p&gt;If you administer Windows estates, run a Samba server, or audit network traffic for a living, here is what changes in 2026.&lt;/p&gt;
&lt;h3&gt;A 30-minute audit&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; A new Windows 11 24H2 client and a new Windows Server 2025 file server arrive with signing required by default. To verify that an existing estate matches the new posture, run on each client &lt;code&gt;Get-SmbClientConfiguration | Select RequireSecuritySignature, RequireEncryption, BlockNTLM&lt;/code&gt; and on each server &lt;code&gt;Get-SmbServerConfiguration | Select RequireSecuritySignature, EncryptData, RejectUnencryptedAccess&lt;/code&gt;. Any host where &lt;code&gt;RequireSecuritySignature&lt;/code&gt; is &lt;code&gt;False&lt;/code&gt; is at the pre-24H2 posture. Schedule those for either upgrade or a policy push. Pull &lt;code&gt;Applications and Services Logs\Microsoft\Windows\SmbClient\Audit&lt;/code&gt; to enumerate which third-party peers the estate actually fails to sign with; that audit log is the only practical way to find the long tail of NAS devices and ESXi datastores before they hit a user.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The commands below cover the recipes most administrators reach for first.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Verify 24H2 signing defaults&lt;/strong&gt;: &lt;code&gt;Get-SmbClientConfiguration | Select RequireSecuritySignature&lt;/code&gt;; &lt;code&gt;Get-SmbServerConfiguration | Select RequireSecuritySignature&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mandate encryption on the client (24H2 or later)&lt;/strong&gt;: &lt;code&gt;Set-SmbClientConfiguration -RequireEncryption $true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mandate encryption on a specific share (server)&lt;/strong&gt;: &lt;code&gt;Set-SmbShare -Name MyShare -EncryptData $true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Block outbound NTLM fallback (24H2 or later)&lt;/strong&gt;: &lt;code&gt;Set-SmbClientConfiguration -BlockNTLM $true&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deploy SMB-over-QUIC on Server 2025&lt;/strong&gt;: install the SMB Server role, bind a TLS 1.3-capable certificate via &lt;code&gt;New-SmbServerCertificateMapping&lt;/code&gt;, and optionally configure Client Access Control to restrict which principals can connect [@smb-over-quic].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit which peers refuse to sign&lt;/strong&gt;: enable the SmbClient Audit channel and review entries for hosts where signing was negotiated off.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remove SMB1 entirely&lt;/strong&gt;: &lt;code&gt;Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol&lt;/code&gt; [@smb-detect-disable].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Block TCP/445 at the network edge&lt;/strong&gt;: Microsoft&apos;s authoritative recommendation is to block inbound TCP/445 from the public internet at the corporate firewall and to use SMB-over-QUIC for any external file access [@smb-secure-traffic].&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audit PetitPotam exposure&lt;/strong&gt;: verify Extended Protection for Authentication on AD CS &lt;code&gt;/certsrv/&lt;/code&gt; per KB 5005413 [@kb5005413], and ensure the AD CS Web Enrollment endpoint is HTTPS-only with channel binding enforced.&lt;/li&gt;
&lt;/ul&gt;

On a domain-joined audit workstation that has WinRM open to every member, the following pipeline collects the list of peers that any of those members has talked to over SMB without signing in the last 24 hours: `Invoke-Command -ComputerName (Get-ADComputer -Filter * | Select -Expand DnsHostName) -ScriptBlock { Get-WinEvent -LogName &apos;Microsoft-Windows-SmbClient/Audit&apos; -MaxEvents 1000 | Where-Object { $_.Id -eq 31010 } } | Sort-Object Message -Unique`. Event ID 31010 is the SmbClient audit signal that the server did not support signing. The output is the deduplicated list of peer hostnames you need a remediation plan for.
&lt;h3&gt;Frequently asked questions&lt;/h3&gt;


No. SMB signing provides per-packet integrity (AES-CMAC, AES-GMAC, or HMAC-SHA-256 depending on the negotiated algorithm); SMB encryption wraps each packet in an AEAD (AES-CCM or AES-GCM) for confidentiality and integrity. The two are negotiated independently. For 24H2 / Server 2025 default behaviour, see the FAQ entry on the 24H2 default-on push below.


No. SMB signing on the coerced server protects the SMB session that delivers the credentials, but PetitPotam captures and relays those credentials to a different service (AD CS Web Enrollment over HTTPS) -- the defence has to live on the relay-to endpoint as Extended Protection for Authentication, not at the SMB layer [@kb5005413]. See section 6 for the mechanism and section 7 limit 5 for the impossibility framing.


No. SMB-over-QUIC is an additional transport for SMB, primarily intended for clients outside the corporate perimeter that want SMB access without a VPN. Inside the perimeter, TCP/445 remains the default transport on Windows Server 2025 [@smb-over-quic]. SMB Direct over RDMA remains the high-throughput data-centre option [@smb-direct].


No. Secure Negotiate (SMB 3.0.2, 2013) was a post-hoc FSCTL message; pre-authentication integrity (SMB 3.1.1, 2015) is a SHA-512 hash chain that enters the SP 800-108 KDF as salt, causing tampering to produce divergent keys rather than a detected mismatch [@kramer-lovinger-sdc2015][@ms-smb2-pdf]. See section 4 for the mechanism and the five-property comparison table.


AES-128-GCM has no known practical attack at the cipher-level (the GCM forgery bound from RFC 8446 and SP 800-38D applies regardless of key length [@rfc8446][@nist-sp-800-38d]). AES-256-GCM matters because CNSA 2.0 -- the U.S. NSA&apos;s algorithm suite for protecting information up to TOP SECRET -- requires a 256-bit symmetric key. Organisations that need CNSA compliance, including most U.S. federal agencies and defence contractors, must negotiate `EncryptionAlgorithmId 0x0004` (AES-256-GCM) [@ms-smb2-versioning].


Probably only on legacy Windows 10 Home or Pro pre-22H2 hosts and a few pre-Server 2019 environments. SMB1 has not been installed by default on Windows 10 since the Fall Creators Update (1709) and not on Windows Server since Server 2019 [@smb-interception-defense]. To check, run `Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol`; the `State` field will be `Disabled` on a modern install. To remove a stray install, use `Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol` [@smb-detect-disable].


No, and this is the single most-misreported fact about the 2024-2025 rollout. The default change was for *signing*, not *encryption*. SMB encryption is now mandate-able from one line of PowerShell or one Group Policy setting, but Microsoft did not flip the default-on switch. A 24H2 client speaking to a Server 2025 file share without explicit opt-in still sends file contents in cleartext on the wire, signed but not encrypted [@smb-security-hardening][@pyle-techcom-4226591].

&lt;h3&gt;Study guide&lt;/h3&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;smb-3-1-1-security&quot; keyTerms={[
  { term: &quot;SMB-relay attack class&quot;, definition: &quot;Capture-and-replay attacks on an SMB authentication exchange, relayed in real time into a fresh session against a target where the same user has access; originally demonstrated by Sir Dystic on March 31, 2001.&quot; },
  { term: &quot;Negotiate downgrade&quot;, definition: &quot;Tampering with the SMB NEGOTIATE Response so that the client and server agree on a session without signing or encryption; the load-bearing primitive of every SMB-relay attack before 2015.&quot; },
  { term: &quot;Pre-authentication integrity&quot;, definition: &quot;SMB 3.1.1 mechanism that binds every byte of the four pre-authentication messages into the salt argument of the SP 800-108 KDF that derives the SMB session keys; tampering produces divergent keys rather than a detectable mismatch.&quot; },
  { term: &quot;Secure Negotiate&quot;, definition: &quot;SMB 3.0.2 post-hoc validation FSCTL that compared client and server views of the NEGOTIATE capabilities after authentication; superseded by pre-authentication integrity in SMB 3.1.1.&quot; },
  { term: &quot;SP 800-108 KDF&quot;, definition: &quot;NIST counter-mode HMAC-SHA-256 key-derivation function used by SMB 3.x to derive per-session keys from an authentication-protocol shared secret, the role label, and the pre-authentication hash chain as context.&quot; },
  { term: &quot;AES-CMAC&quot;, definition: &quot;Block-cipher message authentication code from NIST SP 800-38B, the default SMB 3.x signing algorithm before AES-GMAC was added as a negotiable alternative in 2021.&quot; },
  { term: &quot;AES-GMAC&quot;, definition: &quot;The authentication-only mode of AES-GCM from NIST SP 800-38D, faster than AES-CMAC on hardware with PCLMULQDQ and VAES; added as a negotiable SMB signing algorithm in 2021.&quot; },
  { term: &quot;AES-256-GCM&quot;, definition: &quot;256-bit AES in Galois/Counter Mode; the CNSA 2.0-compliant SMB encryption algorithm, encoded as EncryptionAlgorithmId 0x0004 and added to SMB 3.1.1 in 2021.&quot; },
  { term: &quot;SMB-over-QUIC&quot;, definition: &quot;SMB transport over RFC 9000 QUIC and RFC 9001 TLS 1.3 on UDP/443, generally available with Windows Server 2022 Datacenter Azure Edition in November 2021 and broadened to all Server 2025 editions in 2024.&quot; },
  { term: &quot;Extended Protection for Authentication&quot;, definition: &quot;Binding of the TLS channel into the GSS channel-binding token in SPNEGO authentication, defeating cross-protocol NTLM relay by causing the relayed authentication to disagree with the channel binding observed at the relay-to endpoint.&quot; },
  { term: &quot;PetitPotam&quot;, definition: &quot;Coercion-and-relay attack disclosed by Lionel Gilles in July 2021 that uses MS-EFSRPC EfsRpcOpenFileRaw to coerce outbound NTLM authentication from a target server, then relays the authentication cross-protocol to AD CS Web Enrollment over HTTPS.&quot; },
  { term: &quot;CNSA 2.0&quot;, definition: &quot;The U.S. NSA Commercial National Security Algorithm Suite 2.0; requires AES-256 in any AEAD mode for confidentiality and AES-256-GMAC or HMAC-SHA-384 for integrity; the SMB combination is AES-256-GCM plus AES-GMAC.&quot; }
]} /&amp;gt;&lt;/p&gt;
&lt;p&gt;On March 31, 2001, Sir Dystic walked onto a stage in Atlanta and demonstrated an attack that should have lasted a year. It lasted twenty-three.&lt;/p&gt;
&lt;p&gt;The cryptographic primitives that closed it -- AES-CMAC, AES-128-CCM, AES-128-GCM, AES-256-GCM, AES-GMAC, the SP 800-108 KDF, SHA-512 -- arrived between 2006 and 2021. The structural mechanism that bound them into a tamper-resistant SMB session, pre-authentication integrity, arrived in July 2015. The defaults that made all of it universal arrived in October 2024.&lt;/p&gt;
&lt;p&gt;The next twenty-five years of SMB security live in the protocols above it: Kerberos, NTLM removal, post-quantum key exchange. Those will be slower, because every protocol seam between SMB and what comes above it is a place where a future Sir Dystic can find new work.&lt;/p&gt;
</content:encoded><category>smb</category><category>cryptography</category><category>windows-security</category><category>network-protocols</category><category>pre-authentication-integrity</category><category>smb-over-quic</category><category>ntlm-relay</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>CNG Architecture: BCrypt, NCrypt, KSPs, and How Windows Picks Its Algorithms</title><link>https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps/</link><guid isPermaLink="true">https://paragmali.com/blog/cng-architecture-bcrypt-ncrypt-ksps/</guid><description>A guided tour of the Cryptography API: Next Generation -- the two-tier API, the Key Storage Provider model, the FIPS toggle, and how PQC slots in.</description><pubDate>Sat, 16 May 2026 00:00:00 GMT</pubDate><content:encoded>
Since Windows Vista, every piece of cryptography in Windows -- TLS, BitLocker, Authenticode, Windows Hello, DPAPI -- flows through the **Cryptography API: Next Generation (CNG)**. CNG splits the world into two layers. **BCrypt** does primitives: AES, SHA, HMAC, RNG, key derivation. **NCrypt** routes calls to a **Key Storage Provider (KSP)** that owns the long-lived private keys: software, TPM, smart card, or a third-party HSM. Algorithm selection is governed by a registered provider-priority list, the Schannel cipher-suite order, and a single FIPS-mode toggle that flips Windows into its validated subset. Windows 11 24H2 added the first post-quantum primitives (ML-KEM, ML-DSA) to the same surface, with no API break. This article walks through how that machine works, why Microsoft designed it that way, and where it leaks.
&lt;h2&gt;1. From CAPI to CNG: why Microsoft started over&lt;/h2&gt;
&lt;p&gt;In the late 1990s, Microsoft shipped its first general cryptographic API. The original Cryptographic Service Providers (CAPI) model [@learn-microsoft-com-service-providers] arrived in Windows NT 4.0 Service Pack 4 in 1998 and defined a plug-in unit called a Cryptographic Service Provider, or CSP. A CSP was a monolithic DLL: it owned the algorithm implementations, the key storage, and the export-control posture all at once. If you wanted to add hardware-backed RSA on Windows NT, you wrote a CSP. If you wanted to add a new hash function, you also wrote a CSP. The model worked for the algorithms Microsoft had in mind when it designed it.&lt;/p&gt;
&lt;p&gt;Then the algorithms changed.&lt;/p&gt;
&lt;p&gt;AES was standardized in 2001, after CAPI&apos;s design was already frozen. Microsoft retrofitted AES into the original architecture by shipping the Microsoft Enhanced RSA and AES Cryptographic Provider [@learn-microsoft-com-cryptographic-provider] as a separate CSP, sitting alongside the original Microsoft Base Cryptographic Provider. Elliptic-curve cryptography was even more awkward: CAPI&apos;s algorithm identifiers and key-blob formats had no place for ECC curves. Every new algorithm required a new CSP or a new release of an existing one. The plug-in surface was rigid, the FIPS validation story was painful, and the API was relentlessly C-shaped in ways that made auditing hard.Microsoft was not alone. The same era produced Intel&apos;s Common Data Security Architecture (CDSA) [@en-wikipedia-org-os-2] and several short-lived crypto frameworks for OS/2 and other platforms. Most of them disappeared. CAPI&apos;s longevity owed more to Windows market share than to its design.&lt;/p&gt;
&lt;p&gt;By 2005, Microsoft started over. The result was the Cryptography API: Next Generation, or CNG, which shipped with Windows Vista and Windows Server 2008 in January 2007 [@learn-microsoft-com-cng-portal]. CNG was not a refactor. It was a clean second system, designed from a different set of assumptions: algorithms would keep arriving, key storage needed to be a separate concern, FIPS validation had to be a first-class output, and the same API had to work in user mode and kernel mode.&lt;/p&gt;

The Windows cryptographic API introduced in Vista (2007) as the long-term replacement for CAPI. CNG splits cryptography into a primitives layer (`bcrypt.h`, `bcryptprimitives.dll`) and a key-storage layer (`ncrypt.h`, `ncrypt.dll`), each pluggable through registered providers. Used by every modern Windows component that touches cryptography.

The plug-in unit of the legacy CAPI architecture (1998-onward). A CSP bundled algorithms, key storage, and FIPS posture into a single DLL. Largely superseded by CNG providers, but still present on the system for backwards compatibility.
&lt;p&gt;The three design pillars Microsoft committed to in the CNG portal documentation were modularity, cryptographic agility, and FIPS-compliance readiness [@learn-microsoft-com-cng-features]. All three would matter twenty years later when post-quantum cryptography arrived without warning the protocol authors. We will get to that.&lt;/p&gt;

Throughout this article, &quot;BCrypt&quot; refers to Microsoft&apos;s CNG primitives header `bcrypt.h` and its companion DLL `bcryptprimitives.dll`. It is not the Provos-Mazieres password-hashing function of the same name, which is unrelated and uses a different spelling in most academic literature (&quot;bcrypt&quot;). The naming collision is unfortunate but firmly entrenched in Windows.
&lt;h2&gt;2. BCrypt: the symmetric stack and the ephemeral key&lt;/h2&gt;
&lt;p&gt;Open a Visual Studio project, include &lt;code&gt;&amp;lt;bcrypt.h&amp;gt;&lt;/code&gt;, link &lt;code&gt;bcrypt.lib&lt;/code&gt;, and you have access to almost every cryptographic primitive Windows ships. AES in CBC, CFB, ECB, GCM, and CCM modes. SHA-1, SHA-256, SHA-384, SHA-512, the SHA-3 family, and the cSHAKE128 and cSHAKE256 extendable-output functions added in Windows 11 24H2 [@learn-microsoft-com-algorithm-identifiers]. HMAC over any of those hashes. PBKDF2. The NIST SP 800-108 key-derivation construction. The DRBG-based random number generator drawn from NIST SP 800-90 [@csrc-nist-gov-1-final]. Ephemeral asymmetric operations -- RSA encrypt, ECDSA sign, ECDH key agreement -- on key handles that vanish when the process exits.&lt;/p&gt;
&lt;p&gt;The canonical BCrypt opening dance is four calls.&lt;/p&gt;
&lt;p&gt;{`
// Pseudocode mirroring the BCryptOpenAlgorithmProvider flow.
// In real C: NTSTATUS values, BCRYPT_ALG_HANDLE, etc.&lt;/p&gt;
&lt;p&gt;const algId       = &quot;AES&quot;;           // wide string
const impl        = null;            // null -&amp;gt; walk the priority list
const flags       = 0;&lt;/p&gt;
&lt;p&gt;const hAlg        = BCryptOpenAlgorithmProvider(algId, impl, flags);
BCryptSetProperty(hAlg, &quot;ChainingMode&quot;, &quot;ChainingModeGCM&quot;);&lt;/p&gt;
&lt;p&gt;const hKey        = BCryptGenerateSymmetricKey(hAlg, keyBytes);
const ciphertext  = BCryptEncrypt(hKey, plaintext, authInfo);&lt;/p&gt;
&lt;p&gt;BCryptDestroyKey(hKey);
BCryptCloseAlgorithmProvider(hAlg, 0);
`}&lt;/p&gt;
&lt;p&gt;The interesting parameter is &lt;code&gt;impl&lt;/code&gt;. When it is &lt;code&gt;NULL&lt;/code&gt;, &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt; &quot;attempts to open each registered provider, in order of priority, for the algorithm specified by the pszAlgId parameter and returns the handle of the first provider that is successfully opened&quot; [@learn-microsoft-com-bcrypt-bcryptopenalgorithmprovider]. That sentence is the whole story of CNG provider priority in nineteen words.&lt;/p&gt;
&lt;p&gt;Algorithm identifiers are wide strings. &lt;code&gt;L&quot;AES&quot;&lt;/code&gt;, &lt;code&gt;L&quot;SHA256&quot;&lt;/code&gt;, &lt;code&gt;L&quot;RSA&quot;&lt;/code&gt;, &lt;code&gt;L&quot;ML-KEM&quot;&lt;/code&gt;, &lt;code&gt;L&quot;ML-DSA&quot;&lt;/code&gt;, &lt;code&gt;L&quot;CHACHA20_POLY1305&quot;&lt;/code&gt;, &lt;code&gt;L&quot;CSHAKE128&quot;&lt;/code&gt;. Each string is registered in CNG&apos;s configuration store under &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Cryptography\Configuration\Local\&lt;/code&gt;, with a per-algorithm ordered list of providers that claim to implement it. Add a new algorithm and you add a new string. Add a new provider and you append to its priority list. The API surface does not change.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The algorithm-identifier string is the seam where cryptographic agility lives. As long as your protocol can encode &quot;use whatever the spec calls AES-256-GCM,&quot; and as long as a CNG provider answers to that name, you can swap implementations without touching the calling code. Protocols whose wire format hard-codes the algorithm (the old SSL 3.0 cipher list, for example) do not get this benefit no matter what crypto API they call.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Underneath the API is a single implementation library. Microsoft&apos;s SymCrypt [@github-com-microsoft-symcrypt] has been the actual workhorse since Windows 10 version 1703: &quot;SymCrypt is the core cryptographic function library currently used by Windows... Since the 1703 release of Windows 10, SymCrypt has been the primary crypto library for all algorithms in Windows.&quot; SymCrypt is open source. It carries hand-tuned assembly for AES-NI, VAES, SHA-NI, and PCLMULQDQ on x64, plus ARM64 SHA and AES intrinsics. On a modern Xeon, AES-GCM throughput from BCrypt routinely sits in the 4 to 8 GB/s range per core.&lt;/p&gt;
&lt;p&gt;SymCrypt&apos;s open-source release in 2019 was a quiet event for a Microsoft library: the algorithms that protect Windows are reviewable by anyone willing to read C and ARM/x64 assembly.&lt;/p&gt;
&lt;p&gt;BCrypt keys are ephemeral by construction. A &lt;code&gt;BCRYPT_KEY_HANDLE&lt;/code&gt; lives in your process and dies with it. If you want to keep a private key around between processes, between reboots, or between machines, you do not use BCrypt. You use NCrypt.&lt;/p&gt;
&lt;p&gt;That distinction is the first thing developers get wrong when they meet CNG. The second thing they get wrong is forgetting that BCrypt&apos;s GCM API does not allocate nonces for you. The NIST SP 800-38D specification of Galois/Counter Mode [@nvlpubs-nist-gov-nistspecialpublication800-38dpdf] is famously brittle under nonce reuse: a single repeated nonce under the same key destroys both confidentiality (XOR of plaintexts leaks) and authenticity (the GHASH authentication key becomes recoverable). With 96-bit random nonces the birthday bound limits safe usage to roughly $2^{32}$ invocations per key before collision probability becomes meaningful. Counter-based nonces sidestep the birthday bound entirely but require persistent state. CNG does neither for you. That part is your problem.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; First, &lt;strong&gt;GCM nonce reuse&lt;/strong&gt;: &lt;code&gt;BCryptEncrypt&lt;/code&gt; with &lt;code&gt;BCRYPT_CHAIN_MODE_GCM&lt;/code&gt; accepts whatever 12 bytes you hand it. Counter or random, but never twice. Second, &lt;strong&gt;algorithm string drift&lt;/strong&gt;: &lt;code&gt;BCRYPT_SHA256_ALGORITHM&lt;/code&gt; is the macro for &lt;code&gt;L&quot;SHA256&quot;&lt;/code&gt;. &lt;code&gt;L&quot;SHA-256&quot;&lt;/code&gt; returns &lt;code&gt;STATUS_NOT_FOUND&lt;/code&gt;. Third, &lt;strong&gt;kernel-mode pseudo-handles&lt;/strong&gt;: the convenient &lt;code&gt;BCRYPT_AES_ALG_HANDLE&lt;/code&gt; shortcut is user-mode only per the BCryptOpenAlgorithmProvider remarks [@learn-microsoft-com-bcrypt-bcryptopenalgorithmprovider]; kernel drivers must use real handles.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Windows 10 added pseudo-handles -- pre-baked handle constants like &lt;code&gt;BCRYPT_AES_ALG_HANDLE&lt;/code&gt; and &lt;code&gt;BCRYPT_SHA256_ALG_HANDLE&lt;/code&gt; -- that skip the provider lookup for the built-in algorithms. The 24H2 release extended that list to include &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; and the cSHAKE handles. Microsoft now recommends pseudo-handles over &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt; for new code [@learn-microsoft-com-bcrypt-bcryptopenalgorithmprovider] when the algorithm is built in. The motivation is performance: pseudo-handles bypass the per-call provider walk and the configuration-store lookup.&lt;/p&gt;
&lt;p&gt;That covers the primitives. Now we need a place to keep the keys.&lt;/p&gt;
&lt;h2&gt;3. NCrypt: where the long-lived secrets live&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;ncrypt.h&lt;/code&gt; header opens a different door. Every function in the NCrypt API surface [@learn-microsoft-com-api-ncrypt] -- &lt;code&gt;NCryptOpenStorageProvider&lt;/code&gt;, &lt;code&gt;NCryptCreatePersistedKey&lt;/code&gt;, &lt;code&gt;NCryptOpenKey&lt;/code&gt;, &lt;code&gt;NCryptSignHash&lt;/code&gt;, &lt;code&gt;NCryptDecrypt&lt;/code&gt;, &lt;code&gt;NCryptKeyDerivation&lt;/code&gt;, &lt;code&gt;NCryptExportKey&lt;/code&gt;, &lt;code&gt;NCryptProtectSecret&lt;/code&gt; -- begins by routing the call through &lt;code&gt;ncrypt.dll&lt;/code&gt;, which acts as a router rather than an implementation. The router decides which Key Storage Provider handles the operation and forwards the call.&lt;/p&gt;
&lt;p&gt;That routing layer is the architectural distinction Microsoft has insisted on for two decades. Microsoft&apos;s Key Storage and Retrieval documentation [@learn-microsoft-com-and-retrieval] describes it like this: the NCrypt router &quot;conceals details, such as key isolation, from both the application and the storage provider itself.&quot; Translation: the application calls &lt;code&gt;NCryptSignHash&lt;/code&gt; and gets back a signature. It does not know -- and should not need to know -- whether the key lives in &lt;code&gt;%APPDATA%&lt;/code&gt;, inside a TPM chip on the motherboard, on a smart card halfway across the room, or in a network-attached hardware security module in a data center on a different continent.&lt;/p&gt;

A registered plug-in DLL that owns persistent private-key material and exposes it through the NCrypt API. Microsoft ships four built-in KSPs (Software, Platform/TPM, Smart Card, and the CNG-DPAPI provider); third parties ship KSPs for HSM appliances, USB security keys, and cloud key services. Selecting a KSP is a matter of passing the right name string to `NCryptOpenStorageProvider`.
&lt;p&gt;The mechanical flow for creating a persisted key looks like this.&lt;/p&gt;

sequenceDiagram
    participant App as Application
    participant Router as ncrypt.dll (NCrypt router)
    participant KSP as Microsoft Software KSP
    participant LSA as LSA key-isolation process
    participant Disk as %APPDATA%\Microsoft\Crypto\Keys\&lt;pre&gt;&lt;code&gt;App-&amp;gt;&amp;gt;Router: NCryptOpenStorageProvider(&quot;Microsoft Software Key Storage Provider&quot;)
Router--&amp;gt;&amp;gt;App: hProvider
App-&amp;gt;&amp;gt;Router: NCryptCreatePersistedKey(hProvider, &quot;RSA&quot;, &quot;MyKey&quot;, 2048, ...)
Router-&amp;gt;&amp;gt;KSP: dispatch via registered KSP entry points
KSP-&amp;gt;&amp;gt;LSA: LRPC: generate key, return handle
LSA-&amp;gt;&amp;gt;Disk: write DPAPI-wrapped private blob
LSA--&amp;gt;&amp;gt;KSP: ok
KSP--&amp;gt;&amp;gt;Router: hKey
Router--&amp;gt;&amp;gt;App: hKey
App-&amp;gt;&amp;gt;Router: NCryptSignHash(hKey, digest)
Router-&amp;gt;&amp;gt;KSP: forward
KSP-&amp;gt;&amp;gt;LSA: LRPC: sign with isolated key
LSA--&amp;gt;&amp;gt;KSP: signature
KSP--&amp;gt;&amp;gt;Router: signature
Router--&amp;gt;&amp;gt;App: signature
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Two facts about that diagram matter. First, the private key bits never enter the calling process. They are generated inside the LSA process and the calling application only ever receives a handle and the eventual signature. Second, the LRPC hop is real: it costs roughly 30 to 100 microseconds per call on modern hardware. For bulk symmetric encryption you would not want this overhead, which is why CNG&apos;s design pushes you toward BCrypt for symmetric work and reserves NCrypt for the rarer, smaller, and more sensitive operations on long-lived asymmetric keys.The LSA key-isolation process is &lt;code&gt;lsaiso.exe&lt;/code&gt; on systems with Credential Guard enabled, hosted inside the Virtualization-Based Security (VBS) trustlet boundary. On systems without VBS, the role is played by &lt;code&gt;lsass.exe&lt;/code&gt; itself. Either way, key material does not enter the application&apos;s address space.&lt;/p&gt;
&lt;p&gt;NCrypt is also where the asymmetric algorithms live in their persistent form. The Microsoft Software Key Storage Provider claims RSA keys from 512 to 16384 bits in 64-bit increments, DSA, DH, and ECDSA/ECDH on the NIST P-256, P-384, and P-521 curves [@learn-microsoft-com-and-retrieval]. Windows 11 24H2 added ML-KEM at the 512, 768, and 1024 parameter sets and ML-DSA at the 44, 65, and 87 parameter sets to the Software KSP&apos;s repertoire.&lt;/p&gt;
&lt;p&gt;The split between BCrypt and NCrypt is sometimes confusing because there is overlap. You can sign with BCrypt&apos;s &lt;code&gt;BCryptSignHash&lt;/code&gt; if you generated an ephemeral key pair. You can also sign with NCrypt&apos;s &lt;code&gt;NCryptSignHash&lt;/code&gt; if the key is persisted in a KSP. The rule of thumb is: if the key needs to survive the process, use NCrypt; if it does not, use BCrypt. Real-world Windows code skews heavily toward NCrypt for asymmetric operations because almost every interesting asymmetric key has an associated certificate, and certificates outlive processes.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The four Microsoft KSP name strings are &lt;code&gt;MS_KEY_STORAGE_PROVIDER&lt;/code&gt; (Software), &lt;code&gt;MS_PLATFORM_KEY_STORAGE_PROVIDER&lt;/code&gt; (TPM/Pluton), &lt;code&gt;MS_SMART_CARD_KEY_STORAGE_PROVIDER&lt;/code&gt;, and &lt;code&gt;MS_NGC_KEY_STORAGE_PROVIDER&lt;/code&gt; (Next Generation Credentials, used by Windows Hello). Typo any of these and you silently fall through to the Software KSP, which is a recurring source of &quot;why is my key on disk instead of in the TPM&quot; incident reports.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The router lets the application speak one language and have the storage backend vary. That makes the KSP plug-in model the most interesting piece of the architecture, and it deserves its own section.&lt;/p&gt;
&lt;h2&gt;4. The KSP model: one API, many places to keep keys&lt;/h2&gt;
&lt;p&gt;A KSP is a DLL on disk and an entry in the registry. The DLL exports a fixed set of function pointers that mirror NCrypt&apos;s API. The registry entry under &lt;code&gt;HKLM\SOFTWARE\Microsoft\Cryptography\Providers\Microsoft Software Key Storage Provider&lt;/code&gt; (and its siblings) tells &lt;code&gt;ncrypt.dll&lt;/code&gt; which DLL to load when an application asks for a provider by name. That is the whole interface contract. If you can produce a DLL that implements the entry points and you can install a registry entry, you have a CNG KSP.&lt;/p&gt;
&lt;p&gt;The platform comes with four. They sit on a spectrum from &quot;your operating system is the entire trust boundary&quot; to &quot;the keys live on a separate piece of silicon and only signatures come back.&quot;&lt;/p&gt;

flowchart LR
    A[&quot;Microsoft Software KSP -- private keys on disk -- (DPAPI-wrapped)&quot;] --&amp;gt; B[&quot;Microsoft Platform Crypto Provider -- TPM 2.0 or Pluton -- on-CPU silicon&quot;]
    B --&amp;gt; C[&quot;Microsoft Smart Card KSP -- removable hardware token -- (PIV, CAC, Yubikey)&quot;]
    C --&amp;gt; D[&quot;Third-party HSM KSP -- Thales Luna, Entrust nShield, -- YubiHSM 2, AWS CloudHSM&quot;]
    A -.-&amp;gt; A1[&quot;~10^4 RSA-2048 sign/sec -- FIPS 140-2 L1&quot;]
    B -.-&amp;gt; B1[&quot;~1-10 sign/sec -- TPM vendor cert&quot;]
    C -.-&amp;gt; C1[&quot;~1-5 sign/sec -- card vendor cert&quot;]
    D -.-&amp;gt; D1[&quot;~10^2-10^4 sign/sec -- FIPS 140-2/-3 L3 typical&quot;]
&lt;h3&gt;4.1 The Microsoft Software KSP&lt;/h3&gt;
&lt;p&gt;The default. If you pass &lt;code&gt;NULL&lt;/code&gt; for the provider name in &lt;code&gt;NCryptOpenStorageProvider&lt;/code&gt;, you get this one. It stores per-user private keys at &lt;code&gt;%APPDATA%\Microsoft\Crypto\Keys\&lt;/code&gt; and per-machine keys at &lt;code&gt;%ALLUSERSPROFILE%\Application Data\Microsoft\Crypto\SystemKeys\&lt;/code&gt;, with each file-level blob further protected by DPAPI under either the user master key or the LocalSystem (&lt;code&gt;S-1-5-18&lt;/code&gt;) master key. The private-key operations dispatch through LRPC into the LSA key-isolation process so that even with administrator privileges on the machine, naive code-injection into the application&apos;s address space does not yield key bits.&lt;/p&gt;
&lt;p&gt;The Microsoft Software KSP is also the only KSP that runs inside the LSA key-isolation process. Third-party KSPs run in the calling application&apos;s process. That difference matters enormously for the threat model. Microsoft notes this explicitly: third-party KSPs &quot;do not run inside the LSA process&quot; [@learn-microsoft-com-and-retrieval]. If you are a third-party KSP that talks to remote HSM hardware, the isolation comes from the HSM itself, not from any Windows process boundary.&lt;/p&gt;
&lt;h3&gt;4.2 The Microsoft Platform Crypto Provider (TPM and Pluton)&lt;/h3&gt;
&lt;p&gt;The KSP that answers to &lt;code&gt;MS_PLATFORM_KEY_STORAGE_PROVIDER&lt;/code&gt; is the TPM&apos;s face to CNG. When you call &lt;code&gt;NCryptCreatePersistedKey&lt;/code&gt; against it, the TPM 2.0 chip itself [@learn-microsoft-com-tpm-fundamentals] generates the key under the protection of its Storage Root Key. The private bits never leave the chip. The application gets back a handle whose only operations are sign, decrypt, and key derivation -- the private key cannot be exported, and that property is enforced by physics, not by software policy.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The Platform Crypto Provider is the place where CNG stops trusting the operating system and starts trusting a separate piece of silicon. Every TPM-backed key in Windows -- BitLocker&apos;s Volume Master Key wrapping, Windows Hello credentials, AD CS attestation-enrolled machine identities -- enters and exits through this single KSP name.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft Pluton, the security processor that shipped in 2022 on AMD Ryzen 6000, Snapdragon 8cx Gen 3, and Intel Core Ultra Series 2 silicon, is exposed to Windows as a TPM 2.0 device behind the same Platform Crypto Provider name [@learn-microsoft-com-security-processor]. Application code that worked against a discrete TPM works against Pluton with no changes. Pluton&apos;s wins are at the supply-chain layer (no SPI bus to physically tap between the chip and the CPU) and the firmware-update layer (Pluton firmware ships via Windows Update). The Windows-facing API is intentionally identical.&lt;/p&gt;
&lt;h3&gt;4.3 The Microsoft Smart Card KSP&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;MS_SMART_CARD_KEY_STORAGE_PROVIDER&lt;/code&gt; is a single KSP that routes to whichever vendor minidriver claims the inserted card. The minidriver model is Microsoft&apos;s plug-in layer below the KSP layer: smart-card vendors do not write CNG KSPs, they write minidrivers, and Microsoft&apos;s single KSP fans the calls out to them via the APDU protocol. Cards that follow Microsoft&apos;s Generic Identity Device Specification (GIDS) [@learn-microsoft-com-device-specification] work without a vendor minidriver. Cards that do not, including most US federal PIV cards before about 2015, ship vendor-specific minidrivers.&lt;/p&gt;
&lt;p&gt;This is the layer that powers Windows Hello for Business &quot;virtual smart card&quot; credentials, which present a TPM-backed key through the smart-card path because so much enterprise software already knew how to talk to PIV-style cards.&lt;/p&gt;
&lt;h3&gt;4.4 Third-party HSM and security-key KSPs&lt;/h3&gt;
&lt;p&gt;YubiHSM 2, Thales Luna, Entrust nShield, AWS CloudHSM Client for Windows, and various cloud-KMS bridges all ship CNG KSPs. The KSP DLL pretends to be a local provider and proxies operations across whatever transport the device uses -- USB for a YubiHSM, PCIe or TCP for a Luna, HTTPS for a cloud HSM. Latency varies from microseconds for a USB device to a few milliseconds for a network HSM. The application code that calls &lt;code&gt;NCryptSignHash&lt;/code&gt; does not change.&lt;/p&gt;

For an internal Active Directory Certificate Services CA, the KSP choice is the entire trust story. A CA whose root key lives in the Software KSP can have that key extracted by any administrator. A CA whose root lives in a FIPS 140-2 Level 3 HSM KSP requires physical access to the HSM (often with multi-person key ceremonies) to recover the key. The application code in `certutil` is identical in both cases. The audit story is not.
&lt;h2&gt;5. The TPM KSP, attestation, and the hardware boundary&lt;/h2&gt;
&lt;p&gt;A TPM-bound key is a useful key, but a TPM-bound key with an attestation statement is a different kind of asset entirely. The Trusted Platform Module supports a primitive called key attestation: the TPM can sign a statement that says, &quot;this key was generated inside me, I will never let it out, and here is a chain of trust back to my Endorsement Key that proves I am a real TPM made by a real vendor.&quot; A certificate authority that requires this attestation can refuse to issue a certificate for any key that did not come from inside a TPM.&lt;/p&gt;
&lt;p&gt;Active Directory Certificate Services supports exactly this flow as &quot;TPM key attestation&quot; [@learn-microsoft-com-key-attestation]. The flow involves three keys: an Endorsement Key (EK) burned into the TPM at manufacture, an Attestation Identity Key (AIK) derived from the EK and certified by Microsoft or by the enterprise PKI, and the application key being attested. The AIK signs a statement covering the application key&apos;s properties; the CA verifies the AIK certificate chain and the statement, and only then issues a certificate.&lt;/p&gt;

flowchart TD
    EK[&quot;Endorsement Key (EK) -- burned into TPM at manufacture -- vendor cert from Intel/AMD/etc.&quot;]
    AIK[&quot;Attestation Identity Key (AIK) -- generated in TPM, certified by -- Microsoft EK CA or enterprise PKI&quot;]
    APPK[&quot;Application key -- generated in TPM via -- NCryptCreatePersistedKey&quot;]
    STMT[&quot;Attestation statement -- signed by AIK&quot;]
    CA[&quot;Enterprise CA (AD CS) -- verifies AIK chain -- and attestation&quot;]
    CERT[&quot;X.509 certificate -- issued to application key&quot;]&lt;pre&gt;&lt;code&gt;EK --&amp;gt; AIK
AIK --&amp;gt; STMT
APPK --&amp;gt; STMT
STMT --&amp;gt; CA
CA --&amp;gt; CERT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CNG-facing API for this is the property bag on a &lt;code&gt;NCRYPT_KEY_HANDLE&lt;/code&gt;. After creating the key, the application calls &lt;code&gt;NCryptGetProperty&lt;/code&gt; with &lt;code&gt;NCRYPT_KEY_ATTESTATION_PROPERTY&lt;/code&gt; (and friends) to retrieve the attestation blob. The CA receives the blob in the certificate request and validates it against Microsoft&apos;s published EK CA roots. The whole protocol fits inside the standard certificate-enrollment flow.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; A software KSP can promise that a key is non-exportable. A TPM KSP can prove it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Throughput is the price. A typical TPM 2.0 chip performs single-digit RSA-2048 signatures per second. Pluton-based platforms are in the same neighborhood. Any architecture that wants to do a TPM signature on every HTTP request will fall over almost immediately. The TPM is the right home for one signature per session, per boot, or per logon -- not one per packet.Key migration between TPMs is essentially impossible by design. Replace a motherboard, and any keys that were sealed to the old TPM&apos;s Storage Root Key are gone. This is the same property that makes BitLocker safe against motherboard theft (the recovery key, escrowed elsewhere, is the only way back) and the same property that makes TPM-bound device identities a key-management headache during hardware refresh cycles.&lt;/p&gt;
&lt;p&gt;There is a deeper, more philosophical reason to use the TPM that the API does not advertise. Software keys are bounded by the kernel&apos;s process-isolation guarantees. Any kernel-level attacker, any user with &lt;code&gt;SeDebugPrivilege&lt;/code&gt;, or any code injected into &lt;code&gt;lsass.exe&lt;/code&gt; can in principle reach key material. The provably stronger bound -- keys that no OS-level code can ever read -- requires an off-CPU hardware boundary. CNG&apos;s own design notes acknowledge this when they say CNG &quot;is designed to be usable as a component in a FIPS level 2 validated system&quot; [@learn-microsoft-com-cng-features]: software-only isolation maps to FIPS 140-2 Levels 1 and 2; hardware boundaries are required for Level 3 and above.&lt;/p&gt;
&lt;h2&gt;6. FIPS 140 mode, compliance, and the one-bit toggle&lt;/h2&gt;
&lt;p&gt;There is a registry value at &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled&lt;/code&gt;. When it is set to 1 (or when the equivalent Group Policy &quot;System cryptography: Use FIPS compliant algorithms for encryption, hashing, and signing&quot; is enabled), Schannel and CNG callers refuse to use algorithms that fall outside the FIPS-approved set. RC4 disappears. MD5 disappears. SHA-1 disappears for new signatures (though not for legacy verification). TLS suites that rely on any of those are removed from the negotiation list.&lt;/p&gt;
&lt;p&gt;The toggle is a runtime gate, not a code path. The underlying modules -- &lt;code&gt;bcryptprimitives.dll&lt;/code&gt; and &lt;code&gt;cng.sys&lt;/code&gt; [@learn-microsoft-com-140-windows11] -- are the same modules either way. They have been submitted to the Cryptographic Module Validation Program [@csrc-nist-gov-modules-search] and validated against the FIPS 140-2 standard [@csrc-nist-gov-2-final]. The toggle simply tells those modules that the calling environment expects FIPS-mode behavior, and the modules then refuse the non-approved algorithms.&lt;/p&gt;

A US federal certification program (Federal Information Processing Standard 140) that subjects a cryptographic module to laboratory testing and NIST review. Validated modules receive a public CMVP certificate. Federal agencies, FedRAMP/CMMC contractors, and most regulated industries can only use validated modules in approved configurations. FIPS 140-2 and the newer FIPS 140-3 differ mainly in test methodology and the standard&apos;s own ISO/IEC alignment.
&lt;p&gt;Two current Windows 11 certificate numbers are worth memorizing. CMVP certificate #4825 covers &lt;code&gt;bcryptprimitives.dll&lt;/code&gt; [@csrc-nist-gov-certificate-4825]. CMVP certificate #4766 covers &lt;code&gt;cng.sys&lt;/code&gt; [@csrc-nist-gov-certificate-4766], the kernel-mode primitives. Both are FIPS 140-2 Level 1 modules with a sunset date of September 21, 2026 under the CMVP&apos;s transition rules. Microsoft maintains the per-version FIPS validation portal for Windows 11 [@learn-microsoft-com-140-windows11], which lists the active certificates per build and the algorithms each one covers.&lt;/p&gt;
&lt;p&gt;The cadence mismatch is the open story here. Windows ships H1 and H2 feature updates roughly every six months. CMVP validation of a new build&apos;s primitives DLL and kernel module typically takes 12 to 24 months. Federal customers, FedRAMP-bound cloud tenants, and CMMC contractors cannot run a Windows build that does not have an active FIPS certificate covering its cryptographic modules. Microsoft submits 140-3 evidence for newer modules, but as of mid-2026 no public 140-3 certificate is visible on CMVP for the &lt;code&gt;bcryptprimitives.dll&lt;/code&gt; shipping in Windows 11 24H2.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Setting &lt;code&gt;FIPSAlgorithmPolicy\Enabled = 1&lt;/code&gt; is necessary for FIPS compliance, but not sufficient. The validated configuration also requires that Windows be a covered build (with an active certificate), that you avoid third-party crypto libraries that have not been validated, and that algorithm choices stay inside the per-certificate Approved Mode list. A Windows version without an active certificate is not in compliance even with the toggle on.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The toggle also does not change the SymCrypt implementations. AES-GCM is still AES-GCM. What changes is which APIs the caller is allowed to reach. From the application&apos;s point of view, the symptom of FIPS mode is &lt;code&gt;STATUS_NOT_SUPPORTED&lt;/code&gt; on &lt;code&gt;BCryptOpenAlgorithmProvider(L&quot;RC4&quot;, ...)&lt;/code&gt;. From an auditor&apos;s point of view, the symptom is the absence of any disallowed primitive call in the binary.&lt;/p&gt;
&lt;h2&gt;7. The post-quantum slide: ML-KEM, ML-DSA, and the agility test&lt;/h2&gt;
&lt;p&gt;The piece of CNG that earns its &quot;agility&quot; billing is the post-quantum transition.&lt;/p&gt;
&lt;p&gt;NIST opened the Post-Quantum Cryptography standardization process in 2016 and ran four rounds of public evaluation [@csrc-nist-gov-quantum-cryptography] before issuing the first final standards in August 2024. FIPS 203 standardizes ML-KEM (formerly CRYSTALS-Kyber), a module-lattice key encapsulation mechanism [@nvlpubs-nist-gov-fips-nistfips203pdf]. FIPS 204 standardizes ML-DSA (formerly CRYSTALS-Dilithium), a module-lattice digital signature algorithm [@csrc-nist-gov-204-final]. Microsoft Research had been working on lattice cryptography for years [@microsoft-com-quantum-cryptography], and the public CNG implementations followed quickly: Windows 11 24H2 ships ML-KEM and ML-DSA as first-class CNG algorithms.&lt;/p&gt;
&lt;p&gt;Here is the surprising part: the CNG API surface did not change. Adding ML-KEM was a matter of registering new algorithm identifier strings -- &lt;code&gt;BCRYPT_MLKEM_ALGORITHM&lt;/code&gt;, the parameter sets &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_512&lt;/code&gt;, &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_768&lt;/code&gt;, &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_1024&lt;/code&gt; -- in the CNG algorithm-identifier registry [@learn-microsoft-com-algorithm-identifiers]. The opening dance for an ML-KEM key encapsulation looks exactly like the opening dance for an ECDH key agreement, except for the string.&lt;/p&gt;
&lt;p&gt;{`
// Mirrors the BCrypt pattern shown in the Microsoft sample
// &quot;Using ML-KEM with CNG for Key Exchange&quot;&lt;/p&gt;
&lt;p&gt;const hAlg = BCryptOpenAlgorithmProvider(&quot;ML-KEM&quot;, null, 0);&lt;/p&gt;
&lt;p&gt;const hKeyPair = BCryptGenerateKeyPair(hAlg, 0, 0);
BCryptSetProperty(hKeyPair, &quot;ParameterSetName&quot;, &quot;ML-KEM-768&quot;);
BCryptFinalizeKeyPair(hKeyPair, 0);&lt;/p&gt;
&lt;p&gt;const pubBlob   = BCryptExportKey(hKeyPair, &quot;MLKEMPUBLICBLOB&quot;);&lt;/p&gt;
&lt;p&gt;// Sender side: encapsulate to recipient&apos;s public key
const recipPub  = BCryptImportKeyPair(hAlg, &quot;MLKEMPUBLICBLOB&quot;, pubBlob);
const { ciphertext, sharedSecret: ssA } = BCryptEncapsulate(recipPub);&lt;/p&gt;
&lt;p&gt;// Recipient side: decapsulate with the matching private key
const ssB = BCryptDecapsulate(hKeyPair, ciphertext);&lt;/p&gt;
&lt;p&gt;// ssA === ssB
`}&lt;/p&gt;
&lt;p&gt;That code is structurally identical to a 2007-era ECDH session. The string changes, the blob format changes, and the wire-format sizes change considerably. ML-KEM ciphertexts at the 512, 768, and 1024 parameter sets are 768, 1088, and 1568 bytes respectively, with public keys of 800, 1184, and 1568 bytes per FIPS 203 [@csrc-nist-gov-203-final]. ML-DSA signatures at parameter sets 44, 65, and 87 are 2420, 3309, and 4627 bytes per FIPS 204 [@csrc-nist-gov-204-final]. For comparison, an ECDSA P-256 signature is 64 bytes and an X25519 public key is 32 bytes. The PQC blowup is roughly an order of magnitude, and that has knock-on consequences for every protocol that carries certificates or handshakes on the wire.&lt;/p&gt;

The reason ML-KEM matters before any large quantum computer exists is the harvest-now, decrypt-later attack: an adversary recording today&apos;s TLS sessions can decrypt them years from now if the long-lived key-exchange material was only protected by RSA or ECDH. Long-lived secrets transmitted over the wire today -- medical records, source code, government cables -- have a confidentiality lifetime measured in decades. The motivation for hybrid PQ key exchange is that you cannot un-record traffic.
&lt;p&gt;The wire-format problem is why most TLS-PQ deployments use hybrid groups: classical X25519 combined with ML-KEM-768, with the shared secret derived from both. If either component breaks, the other one still holds. The IETF draft &lt;code&gt;draft-kwiatkowski-tls-ecdhe-mlkem&lt;/code&gt; [@learn-microsoft-com-mlkem-examples] defines the &lt;code&gt;X25519MLKEM768&lt;/code&gt; group with IANA codepoint 0x11EC, and Chrome, Cloudflare, and AWS shipped support in production in 2024. OpenJDK JEP 527 [@openjdk-org-jeps-527] tracks the equivalent work for Java&apos;s TLS stack. Schannel in Windows 11 24H2 can negotiate ML-KEM through CNG, but Microsoft has not publicly committed to a default-on hybrid group at the Schannel layer as of mid-2026.&lt;/p&gt;

On a Windows 11 24H2 machine, the following PowerShell snippet asks CNG for its registered algorithms:&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[System.Security.Cryptography.CngAlgorithm]::new(&quot;ML-KEM&quot;)
Get-ChildItem &apos;HKLM:\SYSTEM\CurrentControlSet\Control\Cryptography\Configuration\Local\Default\0010&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first line forces a CngAlgorithm lookup. The second walks the configuration store. If the keys &lt;code&gt;ML-KEM&lt;/code&gt; and &lt;code&gt;ML-DSA&lt;/code&gt; appear, your kernel-mode and user-mode primitives are 24H2-current.
&lt;/p&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;The bigger structural lesson is that two decades of &quot;cryptographic agility&quot; claims actually paid off. The PQC transition required a 24H2 update, not a CNG redesign.&lt;/p&gt;
&lt;h2&gt;8. Where CNG actually shows up: TLS, BitLocker, and friends&lt;/h2&gt;
&lt;p&gt;The argument for an OS-level cryptographic API stands or falls on what runs on top of it. Every modern Windows component that touches cryptography is a CNG consumer.&lt;/p&gt;

The Windows implementation of TLS and DTLS, exposed through the SSPI (Security Support Provider Interface). Schannel handles the TLS protocol state machine, certificate validation, and cipher-suite negotiation, then delegates the actual cryptography to BCrypt and NCrypt. The cipher-suite priority list and protocol-version controls are configured per Windows version, often via Group Policy.
&lt;p&gt;&lt;strong&gt;Schannel&lt;/strong&gt;, the Windows TLS stack, sits directly above CNG. The Schannel cipher-suite list is its own per-version object, documented at the Schannel cipher-suites portal [@learn-microsoft-com-in-schannel]. For TLS 1.2 and earlier, the order is administered via the registry key &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Cryptography\Configuration\Local\SSL\00010002&lt;/code&gt; (the &quot;Functions&quot; value) or the Group Policy &quot;SSL Cipher Suite Order.&quot; For TLS 1.3, the three suites (&lt;code&gt;TLS_AES_256_GCM_SHA384&lt;/code&gt;, &lt;code&gt;TLS_AES_128_GCM_SHA256&lt;/code&gt;, &lt;code&gt;TLS_CHACHA20_POLY1305_SHA256&lt;/code&gt;) are not user-orderable; Schannel hard-codes the priority. TLS 1.0 and TLS 1.1 are off by default in Windows 11 23H2 and later, per Microsoft&apos;s August 2023 deprecation announcement [@techcommunity-microsoft-com-windows-3887947].&lt;/p&gt;

flowchart TD
    App[&quot;Application -- (WinHTTP, HttpClient, browser, ...)&quot;]
    SSPI[&quot;SSPI / CredSSP layer&quot;]
    Schannel[&quot;Schannel -- protocol state machine -- cipher-suite negotiation&quot;]
    BCrypt[&quot;BCrypt -- AES-GCM, SHA-2/3, HKDF, RNG&quot;]
    NCrypt[&quot;NCrypt -- server cert private key sign -- client cert auth&quot;]
    KSP[&quot;KSP (Software / TPM / -- Smart Card / HSM)&quot;]&lt;pre&gt;&lt;code&gt;App --&amp;gt; SSPI
SSPI --&amp;gt; Schannel
Schannel --&amp;gt; BCrypt
Schannel --&amp;gt; NCrypt
NCrypt --&amp;gt; KSP
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;BitLocker&lt;/strong&gt; is the canonical NCrypt-and-TPM consumer. The Full Volume Encryption Key (FVEK) is generated and stored encrypted on disk. The Volume Master Key (VMK) wraps the FVEK and is itself wrapped by one or more &quot;protectors&quot;: the TPM, a recovery password, a startup PIN, a USB startup key. The TPM protector is an NCrypt-style operation against the Platform Crypto Provider, sealed to a set of Platform Configuration Register (PCR) measurements that capture the boot state. If anything in the early boot chain changes, the PCRs do not match, the TPM refuses to unwrap the VMK, and BitLocker falls back to recovery.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Authenticode&lt;/strong&gt;, the signature format on Windows binaries, is a NCrypt-driven workflow at signing time and a BCrypt-driven workflow at verification time. The Windows kernel verifies driver signatures, the Windows loader verifies binary signatures, and &lt;code&gt;WinVerifyTrust&lt;/code&gt; exposes the same machinery to applications. The hash algorithm in modern Authenticode is SHA-256, which means every signed executable on the system has a SHA-256 digest computed by BCrypt at some point during validation.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Credential Guard&lt;/strong&gt; runs the LSA isolated process (&lt;code&gt;lsaiso.exe&lt;/code&gt;) inside the Virtualization-Based Security trustlet boundary on systems with VBS enabled. Credential Guard does not replace CNG; it relocates the Microsoft Software KSP into a stronger isolation boundary. NTLM password hashes and Kerberos TGT session keys live inside that boundary, accessible only through the standard CNG calls dispatched into the trustlet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Windows Hello for Business&lt;/strong&gt; uses the Platform Crypto Provider as the home for the user&apos;s gesture-protected authentication key. The biometric (or PIN) unlocks a key in the TPM; that key signs an attestation that is consumed by Azure AD or AD FS. The biometric never leaves the device.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DPAPI and DPAPI-NG&lt;/strong&gt; are themselves built on CNG, and they deserve their own section because they are the easiest place to see how the layering pays off.&lt;/p&gt;

Schannel, BitLocker, EFS, Authenticode, Credential Guard, Windows Hello, DPAPI-NG, IPsec, SMB encryption, Kerberos PKINIT -- every modern Windows component is a CNG consumer.
&lt;h2&gt;9. DPAPI-NG: a worked example of the NCrypt model&lt;/h2&gt;
&lt;p&gt;The original Data Protection API (DPAPI), shipped with Windows 2000, was a per-user secret-protection mechanism. An application called &lt;code&gt;CryptProtectData&lt;/code&gt;, passed a blob of secret data, and got back an encrypted blob that only the same user on the same machine could later unwrap. The mechanism was anchored in the user&apos;s logon credentials, with a master key per user and a complex backup mechanism for password resets. It worked. It also locked the secret to a single machine, which became a problem the moment users started living on more than one device.&lt;/p&gt;
&lt;p&gt;DPAPI-NG, introduced in Windows 8 and Windows Server 2012, is the cloud-era rebuild. The CNG DPAPI documentation [@learn-microsoft-com-cng-dpapi] describes the three calls: &lt;code&gt;NCryptCreateProtectionDescriptor&lt;/code&gt;, &lt;code&gt;NCryptProtectSecret&lt;/code&gt;, and &lt;code&gt;NCryptUnprotectSecret&lt;/code&gt;. The protection descriptor is a small string that names who can unwrap the data. Examples include &lt;code&gt;SID=S-1-5-21-...&lt;/code&gt; for an Active Directory user or group, &lt;code&gt;LOCAL=user&lt;/code&gt; for the legacy single-user behavior, &lt;code&gt;WEBCREDENTIALS=...&lt;/code&gt; for a credential vault entry, and combinations connected by &lt;code&gt;AND&lt;/code&gt; or &lt;code&gt;OR&lt;/code&gt; operators.&lt;/p&gt;

flowchart LR
    Plain[&quot;plaintext secret&quot;] --&amp;gt; Protect[&quot;NCryptProtectSecret(descriptor, plain)&quot;]
    Desc[&quot;descriptor: -- SID=group GUID -- OR -- LOCAL=user&quot;] --&amp;gt; Protect
    Protect --&amp;gt; Blob[&quot;opaque blob&quot;]
    Blob --&amp;gt; Unprotect[&quot;NCryptUnprotectSecret(blob)&quot;]
    Unprotect -.-&amp;gt;|&quot;resolves descriptor -- via AD DC backup keys&quot;| AD[&quot;Active Directory DC -- (DPAPI backup keys)&quot;]
    Unprotect --&amp;gt; Out[&quot;plaintext secret -- on any authorized machine&quot;]
&lt;p&gt;The architectural win is that DPAPI-NG is just NCrypt with a particular protection-descriptor schema. Any KSP that can serve the key referenced by the descriptor can satisfy the unwrap. In an Active-Directory-joined environment, the AD domain controller&apos;s DPAPI backup keys allow any machine where the user (or any member of the named group) authenticates to recover the secret. The application that called &lt;code&gt;NCryptProtectSecret&lt;/code&gt; does not need to know about backup keys, replication topology, or recovery flows. It calls NCrypt; the router and the relevant KSP do the rest.&lt;/p&gt;
&lt;p&gt;This is the design payoff of the two-tier model. A new key-management capability (cross-machine recovery via AD-stored backup keys) becomes a new descriptor type, not a new API. The Windows team has used the same descriptor extensibility to add web-credential descriptors, container-bound descriptors, and the descriptors that protect Group Managed Service Account passwords. Each one is a private key-management concern; none of them broke the public API.The DPAPI-NG descriptor language is small enough to read in one sitting and powerful enough to express &quot;any member of this AD group, on any machine where that member can authenticate.&quot; That is the cloud-era access-control story that the original DPAPI never had.&lt;/p&gt;
&lt;h2&gt;10. Engineering takeaways: choosing the right tool&lt;/h2&gt;
&lt;p&gt;The decision tree for CNG usage in production code is short.&lt;/p&gt;

flowchart TD
    Q1{&quot;Need persistent -- private key?&quot;}
    Q1 -- No --&amp;gt; B[&quot;BCrypt -- (ephemeral key, pseudo-handle)&quot;]
    Q1 -- Yes --&amp;gt; Q2{&quot;Threat model?&quot;}
    Q2 -- &quot;Machine identity, -- hardware-rooted&quot; --&amp;gt; P[&quot;Microsoft Platform -- Crypto Provider -- (TPM / Pluton)&quot;]
    Q2 -- &quot;User-bound PKI, -- removable hardware&quot; --&amp;gt; S[&quot;Microsoft Smart Card KSP -- (PIV / virtual smart card)&quot;]
    Q2 -- &quot;High signing rate, -- regulated custody&quot; --&amp;gt; H[&quot;Third-party HSM KSP -- (YubiHSM / Luna / nShield)&quot;]
    Q2 -- &quot;Default, -- portable, fast&quot; --&amp;gt; SW[&quot;Microsoft Software KSP&quot;]
&lt;p&gt;For algorithm choice in mid-2026, the defensible defaults look like this. Symmetric encryption: ChaCha20-Poly1305 or AES-256-GCM. Hashing: SHA-256 or SHA-3 family. Signatures: ECDSA P-256 or P-384 today, with ML-DSA-65 in the back pocket for the inevitable hybrid transition. Key encapsulation: X25519 today, with X25519+ML-KEM-768 hybrid as soon as your peers support it. RSA-2048 only for legacy interoperability. RC4, 3DES, and SHA-1 only behind explicit deprecation policy, and only for verification of historical artifacts.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The hardest thing about CNG is not learning the API. It is choosing the right KSP. That single decision -- where the private key actually lives -- determines almost everything about your threat model, your throughput, your compliance posture, and your operational complexity.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A few engineering rules survive in any setting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do not put persistent keys in BCrypt.&lt;/strong&gt; Every BCrypt key handle dies with the process. The architectural separation exists for a reason. If the key needs to survive a reboot, it belongs in NCrypt under a named KSP.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do not assume the Software KSP.&lt;/strong&gt; Code that calls &lt;code&gt;NCryptOpenStorageProvider(NULL)&lt;/code&gt; ends up with whatever the default is. On a server with an HSM KSP configured as the default, this might be what you want; on a developer workstation, it might be the Microsoft Software KSP. Be explicit. Pass the name string. Test the negative case where the KSP you named is not registered.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Audit which KSP your certificates actually use.&lt;/strong&gt; A certificate enrolled with the Platform Crypto Provider behaves identically to a certificate enrolled with the Software KSP from &lt;code&gt;certutil&lt;/code&gt;&apos;s point of view. The difference is invisible until you ask. Use &lt;code&gt;certutil -store -v My&lt;/code&gt; to dump certificate properties, and look for the provider field.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Treat FIPS mode as a deployment fact, not a development toggle.&lt;/strong&gt; Code that works fine on a developer workstation can break in surprising ways on a FIPS-enabled production server. Run your CI on a FIPS-toggled image periodically. Catch the &lt;code&gt;STATUS_NOT_SUPPORTED&lt;/code&gt; returns before customers do.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watch the PQC roadmap.&lt;/strong&gt; The ML-KEM and ML-DSA primitives are in 24H2. Hybrid TLS in Schannel is not on by default at the OS level as of mid-2026 (the most recent Microsoft public posture in the cipher-suite documentation does not yet list a default-on hybrid group), but downstream protocol updates will come. Code that uses the BCrypt and NCrypt patterns shown here picks up the new algorithms with a string change.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The single most useful CNG diagnostic command on a modern Windows system is &lt;code&gt;certutil -csptest&lt;/code&gt;, which enumerates registered providers and the algorithms each one claims to support. Run it before you suspect a configuration drift, not after.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The story of CNG is the story of two architectural bets that paid off. The first bet was that algorithms would keep arriving, so the API should be a registry of strings rather than a hard-coded set of functions. The second bet was that key storage was a separate concern from algorithm implementation, so the same primitives could run against software, TPM, smart cards, and HSMs without changing the application. In 2007 those bets looked over-engineered. In 2026, with ML-KEM shipping behind the same &lt;code&gt;BCryptEncapsulate&lt;/code&gt; call that an ECDH consumer would have used, they look like exactly the right design.&lt;/p&gt;
&lt;h2&gt;Frequently asked questions&lt;/h2&gt;

No. Microsoft&apos;s BCrypt is the `bcrypt.h` primitives header in CNG, providing AES, SHA, HMAC, RNG, and related primitives. The Provos-Mazieres bcrypt is a password-hashing function based on the Blowfish cipher, with no connection to Windows. The naming collision is unfortunate but firmly entrenched. When in doubt, BCrypt with a capital &quot;B&quot; usually means Microsoft&apos;s CNG header; lowercase bcrypt usually means the password-hashing function.

On Windows, yes. .NET&apos;s `System.Security.Cryptography` namespace wraps CNG directly: `RSACng`, `ECDsaCng`, `AesGcm`, `SHA256.HashData()`, `CngKey`. Go, Rust, and Python bindings exist as third-party crates and packages (the Rust `windows` crate exposes both BCrypt and NCrypt, for example). OpenSSL on Windows does not transparently use CNG; you need the `openssl-cng` provider or direct CNG calls if you want the OS-validated primitives to do the work.

Both can do RSA, ECDSA, and (in 24H2) ML-DSA signatures. The difference is lifetime. BCrypt key handles are ephemeral: they live in your process and disappear when it exits. NCrypt keys are persisted in a KSP and survive process exit, reboots, and (for AD-replicated descriptors via DPAPI-NG) the loss of a single machine. Use BCrypt for one-shot ephemeral operations (signing a single message, deriving a session key); use NCrypt for anything with a certificate attached or anything that has to be around tomorrow.

Possibly, depending on what algorithms it calls. Setting `HKLM\SYSTEM\CurrentControlSet\Control\Lsa\FIPSAlgorithmPolicy\Enabled = 1` causes CNG to refuse RC4, MD5, SHA-1 for new signatures, and a handful of other non-approved algorithms. Anything that relied on those returns `STATUS_NOT_SUPPORTED`. The fix is to switch to approved algorithms (AES, SHA-2 family, RSA, ECDSA, ML-KEM, ML-DSA), not to disable the toggle. The toggle is also necessary but not sufficient for FIPS compliance: you also need a Windows build with an active CMVP certificate covering the cryptographic modules.

As of mid-2026, the public Schannel documentation does not list a default-on hybrid group like `X25519MLKEM768`. The ML-KEM primitive is in CNG in 24H2, and Schannel can use it through the standard cipher-suite negotiation, but Microsoft has not publicly committed to enabling a hybrid group out of the box at the OS level. Chrome, Cloudflare, and AWS have already shipped hybrid PQ TLS in production at the application layer. Expect Schannel to follow once IETF standardization stabilizes and CMVP validation of the new modules catches up.

For a certificate in the user or machine store, run `certutil -store -v My` (or `My` replaced with the store name) and look at the &quot;Provider&quot; field of each certificate. `Microsoft Software Key Storage Provider` means the key is on disk under `%APPDATA%` or `%ALLUSERSPROFILE%`. `Microsoft Platform Crypto Provider` means the key lives inside the TPM (or Pluton). `Microsoft Smart Card Key Storage Provider` means the key is on a card. Third-party HSM KSPs will show the vendor&apos;s provider name. For a freshly-created key via `NCryptCreatePersistedKey`, the provider name you passed to `NCryptOpenStorageProvider` is the source of truth.

Because private keys do not live in the calling process. For the Microsoft Software KSP, key material lives in the LSA key-isolation process (`lsaiso.exe` under VBS, `lsass.exe` otherwise), and every operation that touches private bits has to cross that process boundary. The cost is around 30 to 100 microseconds per call. That is acceptable for signing or key derivation (operations that happen a handful of times per session); it would be punishing for bulk symmetric encryption. The architectural answer is to keep bulk crypto in BCrypt and let only the persistent-key operations pay the LRPC cost.
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;cng-architecture-bcrypt-ncrypt-ksps-and-windows-crypto&quot; keyTerms={[
  { term: &quot;CAPI (Cryptographic Application Programming Interface)&quot;, definition: &quot;The original Windows cryptographic API (1998-onward). Plug-in unit was the CSP. Superseded by CNG starting in 2007 but still present for backwards compatibility.&quot; },
  { term: &quot;CNG (Cryptography API: Next Generation)&quot;, definition: &quot;The Windows cryptographic API since Vista (2007). Two-tier split: BCrypt for primitives, NCrypt for key storage. The basis for all modern Windows cryptography.&quot; },
  { term: &quot;CSP (Cryptographic Service Provider)&quot;, definition: &quot;The CAPI-era plug-in unit. Monolithic DLL bundling algorithms, key storage, and FIPS posture.&quot; },
  { term: &quot;KSP (Key Storage Provider)&quot;, definition: &quot;The CNG-era plug-in unit for persistent key storage. Microsoft ships four; third parties ship many more. Selected by name string passed to NCryptOpenStorageProvider.&quot; },
  { term: &quot;Microsoft Software Key Storage Provider&quot;, definition: &quot;The default KSP. Stores DPAPI-wrapped keys on disk and dispatches operations through the LSA key-isolation process via LRPC.&quot; },
  { term: &quot;Microsoft Platform Crypto Provider&quot;, definition: &quot;The TPM-and-Pluton-backed KSP. Keys are generated and used inside the TPM chip; private bits never leave the silicon.&quot; },
  { term: &quot;TPM key attestation&quot;, definition: &quot;A three-key chain (EK -&amp;gt; AIK -&amp;gt; application key) that lets a CA verify a key was generated inside a real TPM. Supported by Active Directory Certificate Services since Windows Server 2012 R2.&quot; },
  { term: &quot;FIPS 140&quot;, definition: &quot;US federal certification program for cryptographic modules. Validated modules receive a public CMVP certificate. Windows 11&apos;s bcryptprimitives.dll holds CMVP certificate #4825, cng.sys holds #4766.&quot; },
  { term: &quot;ML-KEM (FIPS 203)&quot;, definition: &quot;Module-Lattice Key Encapsulation Mechanism. The NIST-standardized post-quantum KEM, formerly known as CRYSTALS-Kyber. Shipped in Windows 11 24H2.&quot; },
  { term: &quot;ML-DSA (FIPS 204)&quot;, definition: &quot;Module-Lattice Digital Signature Algorithm. The NIST-standardized post-quantum signature scheme, formerly known as CRYSTALS-Dilithium. Shipped in Windows 11 24H2.&quot; },
  { term: &quot;DPAPI-NG&quot;, definition: &quot;The CNG-era rebuild of the original Data Protection API. Uses NCrypt protection descriptors to bind protected data to AD principals (users, groups, web credentials) rather than to a single machine.&quot; },
  { term: &quot;SymCrypt&quot;, definition: &quot;Microsoft&apos;s open-source cryptographic implementation library. The actual workhorse behind BCrypt and NCrypt since Windows 10 version 1703 (2017).&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows</category><category>cryptography</category><category>cng</category><category>tpm</category><category>pqc</category><category>fips</category><category>ksp</category><category>security</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Direct Anonymous Attestation: The Zero-Knowledge Proof Already in Every TPM</title><link>https://paragmali.com/blog/direct-anonymous-attestation-the-zero-knowledge-proof-alread/</link><guid isPermaLink="true">https://paragmali.com/blog/direct-anonymous-attestation-the-zero-knowledge-proof-alread/</guid><description>TPM 2.0 names a zero-knowledge group-signature primitive in its spec. A billion chips ship it. Almost nobody verifies it. The story of why DAA won every standardization fight and lost every deployment one.</description><pubDate>Tue, 12 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Direct Anonymous Attestation is the zero-knowledge proof your laptop already has -- and never uses.** Every TPM 2.0 specification since 2014 names a group-signature primitive called `TPM_ALG_ECDAA`, with a normative command pair (`TPM2_Commit`, `TPM2_Sign`) and a mandatory curve (`TPM_ECC_BN_P256`). A TPM with ECDAA enabled can prove &quot;I am a genuine TPM whose endorsement key was certified by a known issuer&quot; without revealing *which* TPM and without an online third party in the verification path. ISO/IEC 20008-2:2013 Mechanism 4 standardizes it. FIDO Alliance bound it to authenticator attestation in 2018. WebAuthn Level 1 registered ECDAA as an attestation type carried inside the `packed` and `tpm` attestation statement formats in March 2019. Three years later, WebAuthn Level 2 removed it entirely. The TCG PC Client Platform TPM Profile made `TPM_ALG_ECDAA` optional in February 2020. Microsoft Azure Attestation, Windows Health Attestation, AWS Nitro, Apple App Attest, and Google Play Integrity all use Privacy-CA-shaped broker flows instead. This article walks the thirty-year cryptographic lineage, the TPM 2.0 normative surface, the FIDO ECDAA failure, and the structural reasons Microsoft chose brokers over math.
&lt;h2&gt;1. A Billion Chips, Zero Verifiers&lt;/h2&gt;
&lt;p&gt;Every TPM 2.0 Library Specification published since 2014 names a zero-knowledge proof of knowledge. The algorithm identifier &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt; (value &lt;code&gt;0x001A&lt;/code&gt;) appears in Part 2 (Structures). The command pair &lt;code&gt;TPM2_Commit&lt;/code&gt; and &lt;code&gt;TPM2_Sign&lt;/code&gt; appears in Part 3 (Commands). The mathematical construction appears in Part 1 Annex C.5. The mandatory curve is &lt;code&gt;TPM_ECC_BN_P256&lt;/code&gt; (&lt;code&gt;0x0010&lt;/code&gt;), a 256-bit Barreto-Naehrig curve picked specifically because it admits the asymmetric pairings the protocol needs [@tpm-library-spec]. A conforming &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM 2.0 chip&lt;/a&gt; with ECDAA enabled can produce a signature that proves the chip is a genuine TPM whose endorsement key was certified by a known issuer -- without revealing &lt;em&gt;which&lt;/em&gt; TPM, and without an online certificate authority sitting in the verification path. The cryptography is called Direct Anonymous Attestation, and the Wikipedia article notes that the construction is &quot;implemented by both EPID 2.0 and the TPM 2.0 standard&quot; [@wiki-daa].&lt;/p&gt;
&lt;p&gt;Almost nobody uses it.&lt;/p&gt;
&lt;p&gt;Microsoft Azure Attestation does not. Its public architecture document describes a certificate authority that ingests endorsement-key certificates and issues per-key JWTs with a special issuance policy [@azure-attestation]. The Windows Health Attestation Service does not. AWS Nitro Enclaves does not [@aws-nitro-attestation]. Apple App Attest does not [@apple-app-attest]. Google Play Integrity does not [@google-play-integrity]. WebAuthn Level 1 registered ECDAA as an attestation type carried inside the &lt;code&gt;packed&lt;/code&gt; and &lt;code&gt;tpm&lt;/code&gt; formats in March 2019; WebAuthn Level 2 in April 2021 removed it entirely [@webauthn-2]. The TCG PC Client Platform TPM Profile, the document that governs which TPM 2.0 algorithms an OEM must support to ship a Windows-class platform, made &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt; and &lt;code&gt;TPM_ALG_ECSCHNORR&lt;/code&gt; optional in v1.04 (February 2020) and has carried that designation through v1.07 RC1 (December 2025) [@tcg-ptp]. &lt;a href=&quot;https://paragmali.com/blog/pluton-a-tpm-on-silicon-microsoft-can-patch/&quot; rel=&quot;noopener&quot;&gt;Microsoft Pluton&lt;/a&gt;&apos;s published surface, which enumerates the algorithms the security processor exposes through its TPM 2.0 personality, does not advertise ECDAA at all [@pluton].&lt;/p&gt;
&lt;p&gt;The most thoroughly standardized hardware-anchored group-signature primitive in the history of platform security sits in firmware on a billion-plus machines and runs on almost none.&lt;/p&gt;
&lt;p&gt;Why?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Direct Anonymous Attestation solves the same problem as a Privacy-CA -- prove the TPM is genuine without disclosing &lt;em&gt;which&lt;/em&gt; TPM -- by moving the trust assumption from operational (the broker promises not to log) to cryptographic (the math forbids the issuer from learning). The interesting question is not whether the cryptography works. It is why an industry that spent thirty years building the math chose, in production, the architecture the math was meant to replace.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This article walks the answer in four moves. Sections 2 through 5 reconstruct the cryptographic lineage: the Privacy-CA architecture DAA was invented against (TPM 1.1, 2003), the group-signature pre-history that made the construction possible (Chaum-van Heyst 1991 through Camenisch-Lysyanskaya 2004), the Brickell-Camenisch-Chen breakthrough at ACM CCS 2004, and the seven-year evolution to the elliptic-curve scheme TPM 2.0 actually ships (Chen-Page-Smart, CARDIS 2010). Sections 6 and 7 walk the normative surfaces: the TPM 2.0 ECDAA command surface and the ISO/IEC 20008-2 / 20009-2 standards. Sections 8 and 9 are case studies in non-deployment: FIDO&apos;s three-year experiment with ECDAA-in-WebAuthn, and Microsoft&apos;s two-decade commitment to broker-mediated attestation. Section 10 names the open problems -- post-quantum DAA, confidential computing, the One-TPM-to-Bind-Them-All fix that has not made it into TCG text. Section 11 closes with a role-keyed practical guide and an FAQ.&lt;/p&gt;

timeline
    title Direct Anonymous Attestation, 1991-2024
    1991 : Chaum-van Heyst (EUROCRYPT)
         : Group signature defined
    1997 : Camenisch-Stadler (CRYPTO)
         : Constant-size signatures
    2000 : ACJT (CRYPTO)
         : Coalition resistance
    2004 : Brickell-Camenisch-Chen (CCS)
         : Boneh-Boyen-Shacham short groupsigs
    2005 : DAA-RSA added to TPM 1.2 rev 94
    2007 : Brickell-Li EPID (WPES)
         : Signature-based revocation
    2008 : Brickell-Chen-Li (TRUST)
         : First pairing DAA
         : CMS asymmetric DAA proposed
    2010 : Chen-Li (IPL)
         : CMS proof flaw
         : Chen-Page-Smart (CARDIS)
         : The scheme TPM 2.0 ships
    2013 : BFGSW (IJIS)
         : User-controlled linkability model
         : ISO/IEC 20008-2 / 20009-2
    2014 : TPM 2.0 Library Spec
         : ECDAA in firmware
    2015 : Smyth-Ryan-Chen
         : Retroactive BCC privacy bug
    2018 : FIDO ECDAA v2.0
    2019 : WebAuthn Level 1
         : ecdaa attestation format
    2020 : TCG PTP v1.04
         : ECDAA made optional
    2021 : WebAuthn Level 2
         : ecdaa format removed
    2024 : CoSNIZK
         : Lattice DAA at 38 kB
&lt;p&gt;To answer the question of why, we have to start where every TPM attestation story does -- with the architecture DAA was invented to replace.&lt;/p&gt;
&lt;h2&gt;2. The Privacy-CA Trap (1999-2003)&lt;/h2&gt;
&lt;p&gt;TPM 1.1, originally published by the Trusted Computing Platform Alliance in 2002 and taken over in April 2003 by the Trusted Computing Group that replaced it [@wiki-tcg], had a privacy story. The story was a broker called the Privacy Certificate Authority. The story had a single load-bearing flaw, and the field spent the next two decades writing papers about it.&lt;/p&gt;
&lt;p&gt;The mechanism, paraphrased from the Wikipedia summary that itself paraphrases the TCG spec, is five steps [@wiki-daa]:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A TPM manufacturer embeds a 2048-bit RSA Endorsement Key (EK) at the time the chip is provisioned, along with a certificate &lt;code&gt;EKCert&lt;/code&gt; signed by the manufacturer [@wiki-tpm].&lt;/li&gt;
&lt;li&gt;The platform generates a fresh Attestation Identity Key (AIK) inside the TPM.&lt;/li&gt;
&lt;li&gt;The platform sends &lt;code&gt;(EKCert, AIKpub, proof-of-binding)&lt;/code&gt; to a Privacy-CA.&lt;/li&gt;
&lt;li&gt;The Privacy-CA validates the EK certificate, confirms the binding proof, and issues &lt;code&gt;Cert(AIKpub)&lt;/code&gt; signed by the CA.&lt;/li&gt;
&lt;li&gt;The platform uses the AIK to sign actual attestations -- platform configuration register quotes, boot logs, key-attestation certificates -- and presents &lt;code&gt;Cert(AIKpub)&lt;/code&gt; to relying parties as proof that the AIK is TPM-resident.&lt;/li&gt;
&lt;/ol&gt;

The Endorsement Key is the long-lived, manufacturer-certified asymmetric key anchored in the TPM at manufacture (in TPM 2.0, derived from a persistent endorsement seed). Its public half is the chip&apos;s long-term cryptographic identity; its certificate, signed by the manufacturer, is the platform&apos;s proof that the chip is a real TPM. The Attestation Identity Key is a short-lived TPM-resident key generated for signing attestation outputs. Because the EK is uniquely identifying, the AIK exists to absorb attestation traffic on the EK&apos;s behalf: the EK certifies the AIK once (or once per Privacy-CA), and the AIK does the signing thereafter [@azure-attestation].

The broker introduced by the TCG in TPM 1.1 to separate the unique-by-design Endorsement Key from the per-attestation Attestation Identity Key. The Privacy-CA verifies the EK certificate, attests that the AIK is bound to a real TPM, and issues a certificate on the AIK that the platform then uses to sign quotes. The privacy property is operational, not cryptographic: the CA promises not to log the linkage between EK and AIK [@wiki-daa].
&lt;p&gt;The architecture has three structural problems, and the Wikipedia summary of the original TPM 1.1 design makes the most uncomfortable one explicit: &quot;privacy requirements may be violated if the privacy CA and verifier collude&quot; [@wiki-daa]. The Privacy-CA &lt;em&gt;can&lt;/em&gt; link AIKs to EKs. It promises not to. That promise is enforceable by audit, by legal contract, by reputation, and by the threat of a regulator finding out. It is not enforceable by mathematics.&lt;/p&gt;
&lt;p&gt;The other two problems are availability and concentration. Wikipedia again, on the TPM 1.1 design: &quot;the privacy CA must take part in every transaction&quot; [@wiki-daa]. Every AIK certification is a synchronous network round-trip to a single CA. The CA is therefore a high-availability target, a high-value attack target, and a high-throughput service obligation for whoever decides to operate one. The FIDO Alliance, fifteen years later, wrote down the operational consequences of that obligation with surprising frankness in its ECDAA Algorithm v2.0 specification [@fido-ecdaa-v2]:&lt;/p&gt;

An alternative approach to &apos;group&apos; keys is the use of individual keys combined with a Privacy-CA [TPMv1-2-Part1]. Translated to FIDO, this approach would require one Privacy-CA interaction for each Uauth key. This means relatively high load and high availability requirements for the Privacy-CA. Additionally the Privacy-CA aggregates sensitive information (i.e. knowing the relying parties the user interacts with). This might make the Privacy-CA an interesting attack target. -- FIDO ECDAA Algorithm v2.0 Implementation Draft, 2018
&lt;p&gt;The FIDO document was written in 2018, but it is operating on a problem that was current in 2003. The Privacy-CA model concentrates the very identifiers it is supposed to anonymize. A regulator with a subpoena, an insider with a database query, or a successful attacker with persistent access can recover the linkage the CA promised to forget. In 2003 the TCG named the missing primitive -- a &lt;em&gt;direct&lt;/em&gt; attestation scheme whose anonymity was guaranteed by math rather than a CA&apos;s promise -- and the cryptographic literature went to work on it.The privacy-advocate criticism of the TPM in the 2003-2005 window came from a small but well-placed group. Ross Anderson at Cambridge had been writing critical surveys of trusted computing since 2002, both in a continuously updated TCPA FAQ [@anderson-tcpa-faq] and in a PODC 2003 paper &quot;Cryptography and Competition Policy -- Issues with Trusted Computing&quot; [@anderson-tcpa-paper]. Seth Schoen and the Electronic Frontier Foundation published a 2003 white paper, &quot;Trusted Computing: Promise and Risk,&quot; on the privacy implications of trusted-computing-class identifiers [@eff-schoen-2003]. European data-protection authorities had begun studying TCPA in the same window [@anderson-tcpa-faq]. The DAA construction was, by 2004, a research community answer to these criticisms more than it was a TCG product requirement.&lt;/p&gt;
&lt;p&gt;The Privacy-CA architecture is still production architecture in 2026. Microsoft Azure Attestation runs a Privacy-CA in everything but name. Its public documentation describes a CA-mediated flow whose five-step shape mirrors the TPM 1.1 Privacy-CA almost line for line: &quot;A certification authority (CA) establishes trust in the TPM either via EKPub or EKCert... The CA issues a certificate with a special issuance policy to denote that the key is now attested as protected by a TPM&quot; [@azure-attestation]. The full verbatim Microsoft Learn quote is reproduced in §9, where it anchors the Windows case study.&lt;/p&gt;
&lt;p&gt;The same pattern repeats across every hyperscaler. AWS Nitro Enclaves produces signed attestation documents, verified against an AWS-operated X.509 certificate chain, that contain enclave measurements (PCRs) and instance/module identifiers [@aws-nitro-attestation]. Apple App Attest issues per-app device identifiers from Apple-operated infrastructure [@apple-app-attest]. Google Play Integrity ships integrity verdicts signed by Google-operated infrastructure [@google-play-integrity]. In 2026 the operational descendants of TPM 1.1&apos;s Privacy-CA broker run the production attestation surface of every consumer-grade cloud platform.&lt;/p&gt;

flowchart TD
    M[&quot;TPM manufacturer&quot;] --&amp;gt;|&quot;signs EK with EKCert&quot;| EK[&quot;EK in TPM&quot;]
    EK --&amp;gt; AIK[&quot;TPM generates AIK&quot;]
    AIK --&amp;gt;|&quot;(EKCert, AIKpub, proof)&quot;| CA[&quot;Privacy-CA&quot;]
    CA --&amp;gt;|&quot;issues Cert(AIKpub)&quot;| Plat[&quot;Platform&quot;]
    Plat --&amp;gt;|&quot;AIK signs quote&quot;| V[&quot;Verifier / Relying Party&quot;]
    CA -.-&amp;gt;|&quot;can link AIK to EK&lt;br /&gt;(promises not to)&quot;| AIK
&lt;p&gt;By 2003 the field had a name for the missing primitive: a direct attestation scheme that delivered the Privacy-CA&apos;s anonymity property cryptographically rather than operationally. What followed was an academic lineage that had been quietly building, for a decade and a half, the primitives that lineage required.&lt;/p&gt;
&lt;h2&gt;3. The Pre-History: Group Signatures Before DAA (1991-2003)&lt;/h2&gt;
&lt;p&gt;Direct Anonymous Attestation was invented in 2004. The primitive it was built from was invented in 1991, in a paper that had nothing to do with TPMs.&lt;/p&gt;
&lt;p&gt;David Chaum and Eugene van Heyst presented &quot;Group Signatures&quot; at EUROCRYPT 1991 [@chaum-vh-1991]. The construction was a curiosity: a digital signature scheme in which any one of &lt;code&gt;n&lt;/code&gt; group members could sign on behalf of the group, the verifier could check that &lt;em&gt;some&lt;/em&gt; member of the group signed, and a designated &lt;em&gt;group manager&lt;/em&gt; could, given a signature, recover the identity of the signer. The use case Chaum and van Heyst had in mind was organizational: a company spokesperson signs press releases on behalf of the company; the CEO can, if necessary, recover which spokesperson signed which release.&lt;/p&gt;

A digital signature scheme in which any one of `n` group members can sign on behalf of the group such that (i) verifiers can confirm &quot;some member of the group signed this message&quot; using a single group public key, (ii) verifiers cannot determine which member signed, and (iii) a designated group manager, holding a trapdoor, can *open* any signature to recover the original signer. Chaum and van Heyst introduced the primitive in 1991; the next decade was about making the construction efficient enough to deploy [@wiki-group].
&lt;p&gt;The 1991 construction had a fatal practical property: signature size was linear in the size of the group. A 10,000-member group meant a 10,000-component signature. For a primitive intended to handle organizational use cases at organizational scale, this was a non-starter. The next decade is a sequence of papers, each adding one property to the previous, each addressing the issue that made the previous unfit for deployment.&lt;/p&gt;
&lt;p&gt;Jan Camenisch and Markus Stadler, at CRYPTO 1997, gave the field its first constant-size group signature -- signature length independent of the number of group members, suitable for groups of arbitrary size [@camenisch-stadler-1997]. Their construction relied on a particular kind of zero-knowledge proof of knowledge of a discrete logarithm whose form would, six years later, become the structural template for DAA&apos;s Sign protocol. The CS97 scheme had its own problems -- the security proof made strong assumptions, and the construction was vulnerable to &quot;framing&quot; attacks where a malicious group manager could forge signatures attributable to other members -- but the size barrier was broken.&lt;/p&gt;
&lt;p&gt;Three years later, at CRYPTO 2000, Giuseppe Ateniese, Jan Camenisch, Marc Joye, and Gene Tsudik introduced what the field now calls the ACJT scheme [@acjt-2000]. The Springer abstract is unusually direct about what ACJT contributed: the paper &quot;introduces a new provably secure group signature... proven secure and coalition-resistant under the strong RSA and the decisional Diffie-Hellman assumptions.&quot; The property that made ACJT important was &lt;em&gt;coalition resistance&lt;/em&gt; -- a formal guarantee that no subset of &lt;code&gt;k&lt;/code&gt; group members, no matter how large, could collude to produce a valid signature that did not open to one of them. ACJT&apos;s security proofs were the first in the group-signature literature to treat coalitions as a first-class threat model.Coalition resistance as a property predated ACJT, but coalition resistance as a &lt;em&gt;formal&lt;/em&gt; property -- something proven against an adversary defined in a complexity-theoretic model -- did not. Camenisch and Michels in 1998, and several authors in between, had given coalition-resistance arguments that depended on heuristic assumptions about the underlying hash function or signature scheme [@camenisch-michels-1998]. ACJT 2000 gave the proof under the strong RSA assumption, which by 2000 was a well-understood number-theoretic conjecture that the cryptographic community treated as a load-bearing security primitive.&lt;/p&gt;
&lt;p&gt;ACJT was the construction the DAA designers built on. The reason is in its protocol structure. The ACJT signer holds a &lt;em&gt;signed credential&lt;/em&gt; on a secret membership value &lt;code&gt;f&lt;/code&gt;. Signing a message means producing a non-interactive zero-knowledge proof of knowledge of &lt;code&gt;(f, signature)&lt;/code&gt; satisfying the group manager&apos;s verification equation, bound to the message. The proof is constant-size; the verifier checks it against the group public key and learns only that &lt;em&gt;some&lt;/em&gt; member signed.&lt;/p&gt;
&lt;p&gt;Jan Camenisch and Anna Lysyanskaya, working in parallel, were building the other primitive DAA would need. Their EUROCRYPT 2001 paper introduced what the field now calls CL credentials -- a digital signature scheme with two unusual properties [@cl-2001]. First, a signer can issue a signature on a &lt;em&gt;committed&lt;/em&gt; value &lt;code&gt;Commit(f)&lt;/code&gt; without seeing &lt;code&gt;f&lt;/code&gt; itself, so the holder of &lt;code&gt;f&lt;/code&gt; ends up with a signature on something the signer never learned. Second, a holder of &lt;code&gt;(f, signature)&lt;/code&gt; can prove possession of that pair in zero knowledge, revealing neither &lt;code&gt;f&lt;/code&gt; nor the signature itself.&lt;/p&gt;

A digital signature scheme with two algorithmic protocols on top of the standard sign-and-verify pair. A *blind issuance* protocol lets a signer issue a signature on a value the signer cannot see (the holder commits to a value `f` and proves the commitment well-formed; the signer signs the commitment without learning `f`). A *proof-of-possession* protocol lets a holder of `(f, signature)` prove &quot;I have a CL signature from this signer on some value&quot; without revealing either the value or the signature. CL signatures are the primitive a DAA Issuer uses to issue the long-lived attestation credential the TPM keeps after the Join protocol [@cl-2001] [@cl-2004].
&lt;p&gt;CL signatures gave the field a clean way to issue a member credential without the issuer ever learning the member&apos;s secret -- exactly the property a TPM needs when receiving a long-lived DAA credential from an issuer who, by design, must remain unable to recognize the TPM later. Camenisch and Lysyanskaya&apos;s CRYPTO 2004 paper extended the construction to bilinear pairings [@cl-2004], a generalization that would matter for the elliptic-curve DAA schemes of the next decade.&lt;/p&gt;

flowchart LR
    A[&quot;Chaum-van Heyst 1991&lt;br /&gt;Primitive defined&lt;br /&gt;Linear-size signatures&quot;] --&amp;gt; B[&quot;Camenisch-Stadler 1997&lt;br /&gt;Constant-size signatures&quot;]
    B --&amp;gt; C[&quot;ACJT 2000&lt;br /&gt;Coalition resistance&lt;br /&gt;Strong RSA + DDH&quot;]
    C --&amp;gt; D[&quot;Brickell-Camenisch-Chen 2004&lt;br /&gt;DAA-RSA&quot;]
    A --&amp;gt; E[&quot;Camenisch-Lysyanskaya 2001&lt;br /&gt;Blind issuance&lt;br /&gt;Proof of possession&quot;]
    E --&amp;gt; D
    E --&amp;gt; F[&quot;Camenisch-Lysyanskaya 2004&lt;br /&gt;CL on bilinear pairings&quot;]
    F --&amp;gt; G[&quot;Chen-Page-Smart 2010&lt;br /&gt;EC-DAA&quot;]
&lt;p&gt;A sibling lineage was building in parallel. Dan Boneh, Xavier Boyen, and Hovav Shacham presented &quot;Short Group Signatures&quot; at CRYPTO 2004 [@bbs-2004]. The BBS scheme used bilinear pairings to compress group signatures to a few hundred bytes -- signatures, in the abstract&apos;s words, &quot;approximately the size of a standard RSA signature with the same security.&quot; BBS gave the W3C Verifiable Credentials community a primitive that descendants like BBS+ would later use for selective-disclosure credentials. BBS itself did not become the TPM construction. The DAA designers, working from ACJT and CL, took a different path.&lt;/p&gt;
&lt;p&gt;By 2003 the primitives existed. The TPM community had the use case. The two communities had not yet met. In 2004, three authors at three different industrial labs made the introduction.&lt;/p&gt;
&lt;h2&gt;4. The Breakthrough: DAA-RSA (Brickell-Camenisch-Chen, CCS 2004)&lt;/h2&gt;
&lt;p&gt;The introduction happened at ACM CCS 2004. Ernie Brickell at Intel, Jan Camenisch at IBM Zurich, and Liqun Chen at HP Labs Bristol published &quot;Direct Anonymous Attestation&quot; [@bcc-2004]. The IACR ePrint abstract makes the structural contribution explicit:&lt;/p&gt;

Direct anonymous attestation can be seen as a group signature without the feature that a signature can be opened, i.e., the anonymity is not revocable. Moreover, DAA allows for pseudonyms, i.e., for each signature a user (in agreement with the recipient of the signature) can decide whether or not the signature should be linkable to another signature. DAA furthermore allows for detection of &apos;known&apos; keys: if the DAA secret keys are extracted from a TPM and published, a verifier can detect that a signature was produced using these secret keys. -- BCC 2004 (IACR ePrint 2004/205)
&lt;p&gt;Two design moves did the work, and naming them clearly is the first step in understanding why DAA solved the Privacy-CA problem.&lt;/p&gt;
&lt;p&gt;The first move is a &lt;em&gt;subtraction&lt;/em&gt;. Every prior group-signature scheme -- Chaum-van Heyst, Camenisch-Stadler, ACJT, BBS -- gave a designated group manager the power to &lt;em&gt;open&lt;/em&gt; a signature and recover its signer. For a TPM attestation primitive, the opening capability is undesirable. An issuer who can open is morally a Privacy-CA: it has the linkage information the architecture is supposed to forget. BCC 2004 removes the opening capability entirely. No party can de-anonymize a signature -- not the issuer, not the verifier, not a coalition of either. The IACR ePrint 2004/205 abstract captures the consequence: DAA &quot;can be seen as a group signature without the feature that a signature can be opened, i.e., the anonymity is not revocable&quot; [@bcc-2004]. Once the credential is issued, the issuer has no cryptographic handle left to break the user&apos;s privacy.&lt;/p&gt;

A zero-knowledge attestation primitive in which a TPM holds a long-lived membership credential (the output of a one-time Join protocol with an Issuer) and can subsequently produce signatures that prove &quot;the signing TPM holds a credential certified by this Issuer&quot; without revealing which TPM signed and without an online third party in the verification path. No party -- not the Issuer, not the Verifier, not a coalition of either -- can de-anonymize a DAA signature. The construction first appeared in Brickell-Camenisch-Chen 2004 [@bcc-2004].
&lt;p&gt;The second move is a &lt;em&gt;substitution&lt;/em&gt;. Where prior schemes traced misbehaving signers by manager-controlled opening, DAA introduces a &lt;em&gt;user-controlled&lt;/em&gt; linkability mechanism through what the BCC paper calls a basename-keyed pseudonym. The signing TPM holds a secret membership value &lt;code&gt;f&lt;/code&gt;. The verifier supplies a &lt;em&gt;basename&lt;/em&gt; &lt;code&gt;bsn&lt;/code&gt; (a string the verifier picks per session, per relying party, or per global epoch). The TPM derives a pseudonym&lt;/p&gt;
&lt;p&gt;$$N_V = \zeta^f \pmod \Gamma, \qquad \zeta = H_\Gamma(\text{bsn})$$&lt;/p&gt;
&lt;p&gt;where &lt;code&gt;H_Γ&lt;/code&gt; hashes the basename into a generator of a multiplicative group &lt;code&gt;Γ&lt;/code&gt;. The pseudonym &lt;code&gt;N_V&lt;/code&gt; has two structural properties. If the same verifier reuses the same &lt;code&gt;bsn&lt;/code&gt; across sessions, signatures from the same TPM produce the same &lt;code&gt;N_V&lt;/code&gt;, so the verifier can link them (and blacklist them if needed). If the verifier randomizes &lt;code&gt;bsn&lt;/code&gt; per session, or sets &lt;code&gt;bsn&lt;/code&gt; to the special value &lt;code&gt;⊥&lt;/code&gt; indicating &quot;no linkability,&quot; signatures from the same TPM produce different &lt;code&gt;N_V&lt;/code&gt; values that are indistinguishable from random.&lt;/p&gt;

A DAA property in which the *verifier* chooses a basename `bsn` per session or per relying party. Signatures from the same TPM under the same basename produce the same pseudonym; signatures under different basenames produce pseudonyms indistinguishable from random. The TPM, not a group manager, controls which signatures are linkable to which others. The Bernhard-Fuchsbauer-Ghadafi-Smart-Warinschi 2013 paper gives the canonical formal model [@bfgsw-2013].
&lt;p&gt;Together the subtraction and the substitution define the DAA contract. The Issuer issues a CL signature on the TPM&apos;s secret &lt;code&gt;f&lt;/code&gt; during a one-time Join. The TPM thereafter holds the credential &lt;code&gt;(f, A, e, v)&lt;/code&gt; -- the secret membership value plus the CL signature components. To sign a message &lt;code&gt;m&lt;/code&gt; against a verifier-supplied basename &lt;code&gt;bsn&lt;/code&gt;, the TPM:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Computes the pseudonym &lt;code&gt;N_V = ζ^f mod Γ&lt;/code&gt; where &lt;code&gt;ζ = H_Γ(bsn)&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Randomizes the CL signature: picks a fresh &lt;code&gt;w&lt;/code&gt;, computes &lt;code&gt;T_1 = A · S^w mod n&lt;/code&gt; and &lt;code&gt;T_2 = g^e · h^w mod n&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Produces a Fiat-Shamir non-interactive zero-knowledge proof of knowledge of &lt;code&gt;(f, A, e, v, w)&lt;/code&gt; satisfying the CL verification equation&lt;/p&gt;
&lt;p&gt;$$A^e \equiv Z / (R^f \cdot S^{v&apos; + v&apos;&apos;}) \pmod n,$$&lt;/p&gt;
&lt;p&gt;binding the proof to the tuple &lt;code&gt;(m, T_1, T_2, N_V)&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A verifier checks the proof against the Issuer&apos;s public key. The verifier learns nothing about &lt;code&gt;f&lt;/code&gt;, nothing about the TPM&apos;s identity, nothing about which CL signature was randomized -- and either gains a linkable pseudonym (if &lt;code&gt;bsn&lt;/code&gt; was reused) or no linkability at all (if &lt;code&gt;bsn&lt;/code&gt; was fresh).&lt;/p&gt;
&lt;p&gt;The architectural picture, set against §2&apos;s Privacy-CA flow, makes the contrast vivid.&lt;/p&gt;

flowchart TD
    I[&quot;Issuer&lt;br /&gt;(holds CL signing key)&quot;]
    T[&quot;TPM&lt;br /&gt;(holds secret f)&quot;]
    V[&quot;Verifier&lt;br /&gt;(holds Issuer pub key)&quot;]
    I -.-&amp;gt;|&quot;one-time Join&lt;br /&gt;CL signature on f&lt;br /&gt;(blind, issuer never sees f)&quot;| T
    T --&amp;gt;|&quot;credential (f, A, e, v)&lt;br /&gt;stored in TPM forever&quot;| T
    T --&amp;gt;|&quot;DAA-Sign(m, bsn)&lt;br /&gt;= randomized credential + NIZK + N_V&quot;| V
    V --&amp;gt;|&quot;Verify against Issuer pub key&lt;br /&gt;(no online interaction)&quot;| V
&lt;p&gt;This is the first aha. The reader entered §3 thinking &quot;anonymity with manager-controlled traceability&quot; was the goal of group signatures. They exit §4 understanding that for TPM attestation the goal is &lt;em&gt;anonymity without any opener&lt;/em&gt; plus &lt;em&gt;user-controlled, per-verifier linkability&lt;/em&gt;. The breakthrough is structurally a subtraction (remove the opener) plus a substitution (per-verifier basename pseudonyms in place of manager-controlled opening). It is not an addition.Eleven years after BCC 2004, Ben Smyth, Mark Ryan, and Liqun Chen ran a formal analysis of the original BCC construction and found a retroactive privacy bug [@smyth-ryan-chen-2015]. The bug allowed certain Issuer-coalition adversaries to link signatures across basenames in ways the original security argument had not anticipated. The bug was fixed in the 2008-2010 redesigns (specifically the BCL 2009 simplified-security-notions paper [@bcl-2009] and the CDL 2016 strong-Diffie-Hellman revisitation). The reader interested in why &quot;we proved this in 2004&quot; is not the same as &quot;this is provably secure in 2026&quot; should read SRC 2015 alongside the original BCC abstract.&lt;/p&gt;
&lt;p&gt;On paper, the BCC 2004 construction solved the Privacy-CA trap. In practice, DAA-RSA was hard to ship. The CL signature in the original scheme used strong RSA moduli at 2048 bits. A single Sign operation took several seconds on the TPM 1.2 hardware of the time. The signature itself was approximately 2.5 kilobytes -- larger than the entire AIK signature output a Privacy-CA-mediated attestation produced. TPM 1.2 shipped DAA-RSA as an optional capability when revision 94 of the spec added it in 2005 [@tpm-library-spec]. Almost no platform integrator turned it on. The cryptography worked. The implementation budget did not.&lt;/p&gt;
&lt;p&gt;The next decade was about making the construction small enough to deploy. The path was anything but straight.&lt;/p&gt;
&lt;h2&gt;5. The Evolution: From RSA-DAA to EC-DAA (2007-2013)&lt;/h2&gt;
&lt;p&gt;Six papers in seven years, two industrial branches, one dead end, one production scheme. Why was the EC-DAA story so much harder than it should have been?&lt;/p&gt;
&lt;p&gt;The honest answer: the entire toolkit of pairing-based cryptography arrived at the same time the TPM industry needed it, and the field discovered in real time that not every choice of pairing was safe. The path from BCC 2004 to the construction the TPM 2.0 spec actually shipped runs through five waypoints, each addressing the problem the previous one created.&lt;/p&gt;
&lt;h3&gt;5.1 Brickell-Li 2007: EPID and signature-based revocation&lt;/h3&gt;
&lt;p&gt;In 2007 Ernie Brickell, now leading Intel&apos;s trusted-computing work, and Jiangtao Li published &quot;Enhanced Privacy ID: A Direct Anonymous Attestation Scheme with Enhanced Revocation Capabilities&quot; at WPES 2007 [@brickell-li-epid-2007]. The journal version appeared at IEEE TDSC in 2012 [@brickell-li-tdsc-2012]. The single feature EPID added was a revocation list called Sig-RL: a list of &lt;em&gt;signatures&lt;/em&gt; the issuer wished to disavow. A verifier, given a signature &lt;code&gt;σ&lt;/code&gt; and a Sig-RL containing entries &lt;code&gt;σ_1, ..., σ_k&lt;/code&gt;, could prove that &lt;code&gt;σ&lt;/code&gt; was not produced by the same TPM as any &lt;code&gt;σ_i&lt;/code&gt; -- without learning the linking information itself.&lt;/p&gt;
&lt;p&gt;EPID became Intel&apos;s production attestation primitive. Wikipedia records the deployment scale: &quot;It has been incorporated in several Intel chipsets since 2008,&quot; and &quot;at RSAC 2016 Intel disclosed that it has shipped over 2.4B EPID keys since 2008&quot; [@wiki-epid]. EPID is what Intel SGX enclaves used to attest, before SGX attestation migrated to the vendor-CA DCAP architecture. EPID is what certain Intel-platform Widevine L1 implementations use to attest content-decryption modules. The Intel EPID SDK (the reference implementation) was eventually marked public-archive on GitHub [@epid-sdk]. The Wikipedia entry notes that the original EPID 2.0 specification was contributed by Intel into ISO/IEC 20008 and 20009 under royalty-free terms [@wiki-epid].&lt;/p&gt;
&lt;p&gt;EPID is not exactly DAA. EPID is a DAA variant with the Sig-RL revocation layer added. The Chen-Page-Smart construction that TPM 2.0 actually ships is closer to BCC 2004 plus an elliptic-curve substrate; EPID 2.0 is closer to BCC 2004 plus EC plus Sig-RL plus Intel&apos;s specific basename and key-management conventions. The two converge at the cryptographic core and diverge at the deployment surface.&lt;/p&gt;
&lt;h3&gt;5.2 Brickell-Chen-Li 2008: the first pairing-based DAA&lt;/h3&gt;
&lt;p&gt;At the TRUST 2008 conference, Ernie Brickell, Liqun Chen, and Jiangtao Li published &quot;A New Direct Anonymous Attestation Scheme from Bilinear Maps&quot; -- the first DAA scheme constructed over bilinear pairings instead of strong RSA [@bcl-2008]. Signature size dropped by an order of magnitude relative to BCC 2004, from roughly 2.5 kilobytes to a few hundred bytes [@bcl-2008]. TPM-side sign time, on hardware that supported elliptic-curve arithmetic, came down from seconds to fractions of a second [@bcl-2008]. The construction used symmetric (Type-1) pairings -- pairings where the two input groups &lt;code&gt;G_1&lt;/code&gt; and &lt;code&gt;G_2&lt;/code&gt; are the same -- which the implementation community would, two or three years later, decide were too inefficient for production TPM hardware.&lt;/p&gt;

A function `e : G_1 × G_2 -&amp;gt; G_T` on three elliptic-curve subgroups satisfying *bilinearity* (for all integers `a, b` and points `P ∈ G_1, Q ∈ G_2`, `e(aP, bQ) = e(P, Q)^(ab)`) and *non-degeneracy*. Type-3 (asymmetric) pairings, in which `G_1 ≠ G_2` and no efficient homomorphism is known between them, are the production pairing for TPM 2.0 ECDAA because they admit faster implementations and tighter security reductions than Type-1 (symmetric) pairings. The Chen-Page-Smart 2010 construction is built on Type-3 pairings over Barreto-Naehrig curves [@cps-2010].
&lt;h3&gt;5.3 Chen-Morrissey-Smart 2008: the asymmetric proposal and its proof flaw&lt;/h3&gt;
&lt;p&gt;Pairing 2008 hosted the next move. Liqun Chen, Paul Morrissey, and Nigel Smart published &quot;Pairings in Trusted Computing&quot; [@cms-pairing-2008], proposing a DAA scheme on asymmetric Type-3 pairings -- the kind that admit Barreto-Naehrig curves and the speed-ups TPM hardware needed. The same authors published a companion ProvSec 2008 paper &quot;On Proofs of Security for DAA Schemes&quot; providing the security argument [@cms-provsec-2008].&lt;/p&gt;
&lt;p&gt;Two years later, in Information Processing Letters, Liqun Chen and Jiangtao Li published &quot;A note on the Chen-Morrissey-Smart Direct Anonymous Attestation scheme&quot; [@chen-li-2010] showing that the CMS asymmetric-pairing construction had a flawed proof. The cryptographic intuition was correct; the proof technique used an assumption that did not hold in the asymmetric-pairing setting the construction relied on.The Chen-Morrissey-Smart episode is, in 2026, one of the most cited proof-flaw stories in pairing-based cryptography precisely because the construction was simple and the flaw was subtle. The mathematical content of the scheme was salvageable. The security argument was not. The lesson the field took away -- a proof in the symmetric-pairing model does not transfer to the asymmetric-pairing model without a separate argument -- has been a load-bearing convention in cryptographic publishing since.&lt;/p&gt;
&lt;h3&gt;5.4 Chen-Page-Smart 2010: the scheme TPM 2.0 actually ships&lt;/h3&gt;
&lt;p&gt;The fix arrived at CARDIS 2010 in Passau in April 2010 [@cardis-book]. Liqun Chen, Dan Page, and Nigel Smart published &quot;On the Design and Implementation of an Efficient DAA Scheme&quot; [@cps-2010] [@cps-2010-eprint], proposing an asymmetric-pairing DAA over Barreto-Naehrig curves with a Sign protocol &lt;em&gt;split&lt;/em&gt; between the TPM and the host. The TPM, in the new design, performed only the cryptographic operations that absolutely required custody of the secret &lt;code&gt;f&lt;/code&gt;: it produced commitment points and computed a Schnorr-style response over those commitments. The host -- a comparatively powerful general-purpose CPU sitting in front of the TPM -- composed the Fiat-Shamir challenge, performed the pairing computations, and assembled the final signature.&lt;/p&gt;
&lt;p&gt;The Chen-Page-Smart construction is the scheme TPM 2.0 actually ships. The Wikipedia DAA article makes the attribution direct, in a sentence that is itself the most-cited single primary-source extract in this article:&lt;/p&gt;

Chen, Page, and Smart proposed a new elliptic curve cryptography scheme using Barreto-Naehrig curves. This scheme is implemented by both EPID 2.0 and the TPM 2.0 standard. -- Wikipedia, *Direct Anonymous Attestation* [@wiki-daa]

A family of pairing-friendly elliptic curves with embedding degree 12, parameterized by an integer `u` to admit Type-3 pairings whose arithmetic is fast enough for resource-constrained devices [@bn-2006]. The curve identifier `TPM_ECC_BN_P256` (`0x0010`) is the specific 256-bit instance the TPM 2.0 Library Specification mandates for ECDAA, picked because of its pairing-friendly structure rather than as a NIST P-256 equivalent.
&lt;p&gt;Six years after CPS 2010, Taechan Kim and Razvan Barbulescu (CRYPTO 2016) published &quot;Extended Tower Number Field Sieve: A New Complexity for the Medium Prime Case,&quot; giving an improved sieve attack against pairing-friendly elliptic curves at the 256-bit BN level. The improvement dropped the practical security of BN-256 from roughly 128 bits to roughly 100 bits [@kim-barbulescu-2016]. The TCG normative text for TPM 2.0 ECDAA did not, as of late 2025, change the mandatory curve in response. This is the kind of cryptographic technical debt that lives quietly in deployed systems for a decade -- specs do not migrate on the same calendar as research moves.&lt;/p&gt;
&lt;h3&gt;5.5 BFGSW 2013 and SRC 2015: the formal closure&lt;/h3&gt;
&lt;p&gt;The cryptographic engineering of EC-DAA was done by 2010. What the field still owed itself was a clean security model: one definition of &quot;secure DAA&quot; that captured the user-controlled-linkability property and the TPM/host split, against which any candidate scheme could be evaluated.&lt;/p&gt;
&lt;p&gt;In 2013 David Bernhard, Georg Fuchsbauer, Essam Ghadafi, Nigel Smart, and Bogdan Warinschi published &quot;Anonymous attestation with user-controlled linkability&quot; in the &lt;em&gt;International Journal of Information Security&lt;/em&gt; [@bfgsw-2013] [@bfgsw-2013-eprint]. The BFGSW paper formalized the user-controlled-linkability property the BCC 2004 abstract had described in prose, introduced a clean separation of &quot;pre-DAA signing&quot; (TPM-side operations) from &quot;DAA signing&quot; (TPM + host composition), and proved the security of a representative construction in the resulting model.&lt;/p&gt;
&lt;p&gt;In 2015, Ben Smyth, Mark Ryan, and Liqun Chen published the retroactive analysis that closed the BCC 2004 privacy bug [@smyth-ryan-chen-2015]. By 2015 the cryptography was, formally, settled.&lt;/p&gt;
&lt;p&gt;In 2016 Jan Camenisch, Manu Drijvers, and Anja Lehmann revisited the construction at TRUST 2016 in &quot;Anonymous Attestation Using the Strong Diffie Hellman Assumption Revisited&quot; [@cdl-2016] [@cdl-2016-eprint], giving a tighter security argument under the q-SDH assumption and providing a fix for a Diffie-Hellman-oracle issue in the TPM 2.0 ECDAA interface that &quot;One TPM to Bind Them All&quot; would document in 2017 [@one-tpm-2017]. The CDL16 scheme is what most modern DAA library code references as the canonical construction.&lt;/p&gt;

flowchart LR
    BCC[&quot;BCC 2004&lt;br /&gt;RSA-DAA&lt;br /&gt;TPM 1.2&quot;] --&amp;gt; BL[&quot;Brickell-Li 2007&lt;br /&gt;EPID + Sig-RL&lt;br /&gt;Intel SGX / Widevine&quot;]
    BCC --&amp;gt; BCL[&quot;BCL 2008&lt;br /&gt;Type-1 pairing DAA&quot;]
    BCL --&amp;gt; CMS[&quot;CMS 2008&lt;br /&gt;Asymmetric pairing&lt;br /&gt;(broken by CL 2010)&quot;]
    BCL --&amp;gt; CPS[&quot;CPS 2010&lt;br /&gt;Type-3 BN-curve DAA&lt;br /&gt;TPM 2.0 ECDAA&quot;]
    CPS --&amp;gt; BFGSW[&quot;BFGSW 2013&lt;br /&gt;Formal user-controlled&lt;br /&gt;linkability model&quot;]
    BFGSW --&amp;gt; CDL[&quot;CDL 2016&lt;br /&gt;q-SDH revisitation&lt;br /&gt;Canonical modern DAA&quot;]
    BCC --&amp;gt; SRC[&quot;SRC 2015&lt;br /&gt;Retroactive BCC&lt;br /&gt;privacy bug&quot;]
&lt;p&gt;By 2013 the cryptography was complete. The standards organizations took the construction and made it official -- in two different specifications, on two parallel tracks.&lt;/p&gt;
&lt;h2&gt;6. The TPM 2.0 ECDAA Surface (2014-Present)&lt;/h2&gt;
&lt;p&gt;If you own a Windows laptop with a TPM 2.0, this section is the part of the chip you have never used. What does the spec actually say?&lt;/p&gt;
&lt;p&gt;The TPM 2.0 Library Specification, the canonical document published by the Trusted Computing Group, is a four-part normative reference [@tpm-library-spec]. Part 1 (Architecture) describes the threat model and the mathematical primitives. Part 2 (Structures) defines the data types every TPM command accepts and returns. Part 3 (Commands) defines the commands themselves. Part 4 (Supporting Routines) gives a reference C implementation. The ECDAA surface lives across all four parts.&lt;/p&gt;

An algorithm identifier defined in TPM 2.0 Library Specification Part 2 and selectable from any `TPMT_SIG_SCHEME` field. A signing key tagged with `TPM_ALG_ECDAA` produces signatures using the Chen-Page-Smart 2010 elliptic-curve DAA construction. The same algorithm identifier appears in any signature-scheme negotiation point in the TPM 2.0 command surface [@tpm-library-spec].

The 256-bit Barreto-Naehrig curve identifier the TPM 2.0 Library Specification mandates for any ECDAA-capable signing key. BN-P256 is *not* NIST P-256: it is a pairing-friendly curve with embedding degree 12 whose group structure admits the Type-3 pairings the DAA verification equation requires. Implementations that confuse the two will produce signatures that verify against the wrong group.

The command pair defined in TPM 2.0 Library Specification Part 3 that implements the Chen-Page-Smart 2010 split-protocol structure. `TPM2_Commit(keyHandle, P1, s2, y2)` returns commitment points `(K, L, E)` plus a `counter`. The host then computes the Fiat-Shamir challenge `c` over the message and the commitment points. `TPM2_Sign(keyHandle, digest, scheme=TPM_ALG_ECDAA, validation)` returns the Schnorr-style response `s = r + c·f mod p`. The host assembles the final signature from the commitment points, the challenge, and the response [@tpm-library-spec].
&lt;p&gt;The protocol split matters. The TPM, in the CPS 2010 construction, holds the secret &lt;code&gt;f&lt;/code&gt; and must perform exactly two cryptographic operations on it: produce a freshly randomized commitment to &lt;code&gt;f&lt;/code&gt; (via &lt;code&gt;TPM2_Commit&lt;/code&gt;), and produce a Schnorr response that proves knowledge of &lt;code&gt;f&lt;/code&gt; modulo the verifier&apos;s challenge (via &lt;code&gt;TPM2_Sign&lt;/code&gt;). Everything else -- the pairing computations, the curve arithmetic in &lt;code&gt;G_T&lt;/code&gt;, the Fiat-Shamir hash, the final signature assembly -- happens on the host CPU. This is the &lt;em&gt;only&lt;/em&gt; reason the construction is practical on a TPM. A monolithic Sign that did pairing arithmetic inside the chip would be unshippable; the split offloads the expensive operations onto silicon that has them for free.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The most common implementer mistake when working with TPM 2.0 ECDAA for the first time is to reuse the NIST P-256 ECDSA code path with the curve identifier swapped. The two curves share a bit length and a hash function and otherwise nothing. BN-P256 has a pairing-friendly group structure with embedding degree 12; NIST P-256 does not admit efficient pairings at all. Signatures produced by ECDSA over NIST P-256 will not verify against an ECDAA verifier expecting BN-P256, and the converse is true. The pairing requirement is what forces the BN curve choice; treat BN-P256 as a separate primitive with a separate code path.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Join protocol -- the one-time exchange between the Issuer and the TPM that produces the long-lived credential -- piggybacks on a TPM 2.0 command pair already present in every Windows attestation flow: &lt;code&gt;TPM2_MakeCredential&lt;/code&gt; and &lt;code&gt;TPM2_ActivateCredential&lt;/code&gt; [@tpm-library-spec]. The Issuer wraps the DAA credential under an encryption key derived from the TPM&apos;s Endorsement Key, ensuring that only the legitimate TPM (the one that holds the EK private key) can decrypt the credential and bind it to its internal &lt;code&gt;f&lt;/code&gt;.The choice of &lt;code&gt;TPM2_ActivateCredential&lt;/code&gt; as the Join anchor is convenient. The same primitive that TPM 2.0 attestation-key certification flows use for AIK-binding gets reused for DAA-credential binding. An OEM that supports &lt;code&gt;TPM2_ActivateCredential&lt;/code&gt; for ordinary AIK enrollment already has 80% of the firmware path the Join protocol needs. The difference is in what the Issuer ships back -- a per-TPM AIK certificate in the AIK case, an Issuer-randomized CL credential in the DAA case.&lt;/p&gt;
&lt;p&gt;Part 1 Annex C.5 contains the informative mathematical description -- the actual ECDAA verification equation, the basename-pseudonym derivation, the proof-of-knowledge template. Part 3 contains the normative command definitions. An implementer who reads only the Part 3 command definitions without reading Annex C.5 will have correct byte-buffer-level semantics and no idea what the protocol is computing; an implementer who reads only Annex C.5 without the normative command definitions will have correct math and the wrong API.&lt;/p&gt;
&lt;p&gt;The implementation surface, gathered into one place:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Artifact&lt;/th&gt;
&lt;th&gt;Identifier / location&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Algorithm selector&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TPM_ALG_ECDAA = 0x001A&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TPM 2.0 Library Specification Part 2 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mandatory curve&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TPM_ECC_BN_P256 = 0x0010&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 2 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;First-round command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TPM2_Commit(keyHandle, P1, s2, y2) -&amp;gt; (K, L, E, counter)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 3 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Second-round command&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TPM2_Sign(keyHandle, digest, scheme=TPM_ALG_ECDAA, validation) -&amp;gt; signature&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 3 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Join anchor&lt;/td&gt;
&lt;td&gt;&lt;code&gt;TPM2_MakeCredential&lt;/code&gt; / &lt;code&gt;TPM2_ActivateCredential&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Part 3 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Math description&lt;/td&gt;
&lt;td&gt;Part 1 Annex C.5 (informative)&lt;/td&gt;
&lt;td&gt;Part 1 [@tpm-library-spec]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Optionality status&lt;/td&gt;
&lt;td&gt;Optional since PTP v1.04 (Feb 2020); carried through v1.07 RC1 (Dec 2025)&lt;/td&gt;
&lt;td&gt;TCG PC Client Platform TPM Profile changelog [@tcg-ptp]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

sequenceDiagram
    participant V as Verifier
    participant H as Host (CPU)
    participant T as TPM
    V-&amp;gt;&amp;gt;H: send basename bsn
    H-&amp;gt;&amp;gt;T: TPM2_Commit(keyHandle, P1, s2, y2)
    T--&amp;gt;&amp;gt;H: (K, L, E, counter)
    H-&amp;gt;&amp;gt;H: compute c = H(K, L, E, message, bsn)
    H-&amp;gt;&amp;gt;T: TPM2_Sign(keyHandle, digest=c, scheme=ECDAA)
    T--&amp;gt;&amp;gt;H: response s = r + c*f mod p
    H-&amp;gt;&amp;gt;H: assemble signature (K, L, E, c, s)
    H-&amp;gt;&amp;gt;V: ECDAA signature
    V-&amp;gt;&amp;gt;V: verify pairing equation
&lt;p&gt;The TCG published the TPM 2.0 Library Specification in 2014. From 2014 through early 2020, the PC Client Platform TPM Profile -- the document that says &quot;to ship a TPM 2.0 in a PC-class device, these algorithms must be present&quot; -- listed &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt; as mandatory-if-the-platform-supports-elliptic-curve-cryptography. In v1.04 (released February 2020) the TCG PTP working group made a quiet but consequential change. The changelog records the line verbatim: &quot;Made TPM_ALG_ECDAA and TPM_ALG_ECSCHNORR optional.&quot; The same designation has carried through v1.06 RC1 (January 2025) and v1.07 RC1 (December 2025) [@tcg-ptp]. After February 2020, an OEM can ship a Windows-class TPM 2.0 platform that does not implement ECDAA at all and remain conformant.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The Trusted Computing Group&apos;s resource pages (&lt;code&gt;trustedcomputinggroup.org/resource/tpm-library-specification/&lt;/code&gt; and &lt;code&gt;trustedcomputinggroup.org/resource/pc-client-platform-tpm-profile-ptp-specification/&lt;/code&gt;) reject non-browser User-Agents at the HTTP layer. This is a long-standing anti-bot policy. Citations in this article to the TPM 2.0 Library Specification and to the PC Client Platform TPM Profile point to the canonical URLs but are flagged in the verified-source registry as UNVERIFIED_FETCH; the verbatim changelog text was extracted under primary-source rules during the Stage 0a focus-premise audit and is the audit-of-record for the optionality claim. The downstream accuracy and fact-check stages of this pipeline carry the same caveat forward.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The Pluton question is the second hedge. Microsoft Pluton is the security processor Microsoft has been shipping in successive Windows-class platforms since AMD&apos;s Ryzen 6000 in 2022, in AMD Ryzen 7040 (Phoenix) in 2023, in Qualcomm Snapdragon X Elite in 2024, and in Intel Core Ultra 200V (Lunar Lake) in 2024 and successive Intel Core Ultra generations. Pluton exposes a TPM 2.0 personality. The Microsoft Learn documentation page enumerates the cryptographic algorithms the processor exposes and the platform-security primitives it implements [@pluton].&lt;/p&gt;
&lt;p&gt;The page contains zero occurrences of &lt;code&gt;ECDAA&lt;/code&gt; or &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt;. The honest framing here is &lt;em&gt;not&lt;/em&gt; &quot;Pluton does not implement ECDAA&quot; -- the documentation neither confirms nor denies it -- but &quot;Pluton&apos;s published surface does not advertise ECDAA.&quot; That is the hedged statement this article carries from its opening to its FAQ.&lt;/p&gt;
&lt;p&gt;The runnable demonstration below is &lt;em&gt;educational&lt;/em&gt; -- Microsoft ships no &lt;code&gt;BCryptDirectAnonymousAttestation&lt;/code&gt;, no &lt;code&gt;NCryptDaaSign&lt;/code&gt;, no Windows API at all that exposes ECDAA from a user-mode application. The code shows the &lt;em&gt;logic&lt;/em&gt; an admin or platform engineer would follow when probing a TPM&apos;s reported algorithm set, not a working call against any shipping Windows API.&lt;/p&gt;
&lt;p&gt;{`
// Logic only. Microsoft ships no Windows API that surfaces TPM_ALG_ECDAA.
// In practice an admin would parse the output of Get-TpmEndorsementKeyInfo
// or use a vendor-specific tool to inspect the TPM&apos;s algorithm capability table.
const TPM_ALG_ECDAA = 0x001A;
const TPM_ECC_BN_P256 = 0x0010;&lt;/p&gt;
&lt;p&gt;function probeECDAA(tpmAlgList, tpmEccCurveList) {
  const hasECDAA = tpmAlgList.includes(TPM_ALG_ECDAA);
  const hasBN256 = tpmEccCurveList.includes(TPM_ECC_BN_P256);
  if (!hasECDAA) return &apos;no ECDAA: chip omits algorithm 0x001A&apos;;
  if (!hasBN256) return &apos;ECDAA without BN-P256: nominally compliant, practically unusable&apos;;
  return &apos;ECDAA + BN-P256 present (Join still requires Issuer infrastructure)&apos;;
}&lt;/p&gt;
&lt;p&gt;// Example: a Pluton-class chip whose published surface does not advertise ECDAA.
const plutonLike = [0x0001 /* RSA &lt;em&gt;/, 0x0008 /&lt;/em&gt; SHA-256 &lt;em&gt;/, 0x0023 /&lt;/em&gt; ECDSA &lt;em&gt;/];
console.log(probeECDAA(plutonLike, [0x0003 /&lt;/em&gt; NIST P-256 */]));
// -&amp;gt; &quot;no ECDAA: chip omits algorithm 0x001A&quot;&lt;/p&gt;
&lt;p&gt;// Example: a discrete Infineon SLB9670 TPM 2.0 (vendor docs list ECDAA + BN-P256).
const discreteTpm = [0x0001, 0x0008, 0x0023, TPM_ALG_ECDAA];
console.log(probeECDAA(discreteTpm, [0x0003, TPM_ECC_BN_P256]));
// -&amp;gt; &quot;ECDAA + BN-P256 present (Join still requires Issuer infrastructure)&quot;
`}&lt;/p&gt;
&lt;p&gt;The spec was written. The chips shipped. The TCG was satisfied. So why does no one verify ECDAA signatures?&lt;/p&gt;
&lt;h2&gt;7. The Standards Bridge: ISO/IEC 20008 and 20009&lt;/h2&gt;
&lt;p&gt;There is a difference between a TCG specification section number and an ISO/IEC mechanism identifier. The difference is the price of admission to a Common Criteria protection profile and to most government procurement contracts.&lt;/p&gt;
&lt;p&gt;ISO/IEC 20008 is the international-standards anchor for anonymous digital signatures. It comes in three parts. Part 1 (&quot;General&quot;) sets the framework and terminology [@iso-20008-1]. Part 2 (&quot;Mechanisms using a group public key&quot;) catalogues the specific anonymous-signature schemes the international community has standardized -- and Mechanism 4 is the EPID-derived elliptic-curve DAA construction that aligns with the TPM 2.0 ECDAA surface [@iso-20008-2]. Part 3 (&quot;Mechanisms using multiple public keys&quot;) catalogues a different family of schemes that is not the focus of this article.&lt;/p&gt;

The international-standards series titled &quot;Information technology -- Security techniques -- Anonymous digital signatures.&quot; Part 1 (general framework) and Part 2 (mechanisms using a group public key) were both published in 2013. Mechanism 4 in Part 2 standardizes EPID-derived elliptic-curve DAA. ISO/IEC 20008 is the bibliographic anchor cited by Common Criteria protection profiles, FIPS 140-3 module-validation evidence, and government procurement specifications that need to reference a *named, internationally agreed* anonymous-signature mechanism rather than a vendor-specific construction [@iso-20008-2].
&lt;p&gt;A note on the title. Earlier drafts of this article carried the title of ISO/IEC 20008-2 as &quot;anonymous signatures with message recovery.&quot; That phrasing belongs to a different standard, ISO/IEC 9796. The verified ISO catalogue title for 20008-2 is, verbatim, &quot;Information technology -- Security techniques -- Anonymous digital signatures -- Part 2: Mechanisms using a group public key&quot; [@iso-20008-2].&lt;/p&gt;
&lt;p&gt;ISO/IEC 20009 is the companion standard for authentication. Where 20008 standardizes signatures, 20009 standardizes the challenge-response protocols that wrap signatures into entity-authentication exchanges. Part 2 (&quot;Mechanisms based on signatures using a group public key&quot;) is where TPM-style attestation lives in ISO terminology [@iso-20009-2]. A FIDO authenticator using an anonymous group-signature attestation (ECDAA or EPID) is, in ISO-speak, executing a 20009-2 mechanism that wraps a 20008-2 signature; ordinary TPM-backed Kerberos and key-attestation flows use non-anonymous keys and are separate protocol designs.&lt;/p&gt;

Intel held patents on the EPID construction. In contributing the EPID 2.0 algorithm to ISO/IEC 20008 and 20009, Intel made the underlying intellectual property available under royalty-free (RAND-Z) terms. The Wikipedia EPID article records the contribution and notes that EPID &quot;complies with international standards ISO/IEC 20008 / 20009&quot; [@wiki-epid]. The licensing structure mattered: it is what made the construction acceptable to the FIDO Alliance, to the TCG for the TPM 2.0 ECDAA surface, and to the European procurement community whose conformance regimes treat royalty-bearing cryptographic primitives differently from royalty-free ones. Exact licensing-event dates are not directly indexed in publicly fetchable Intel materials; this paragraph is inference-grade reconstruction from the Wikipedia citation chain.
&lt;p&gt;The procurement reason ISO standardization mattered is structural. A Common Criteria Protection Profile cannot, in the general case, reference a TCG specification section number. It can reference an ISO mechanism identifier. The Federal Information Processing Standards 140-3 evidence package for a cryptographic module must, in many cases, demonstrate that the cryptographic primitives the module implements are members of an internationally recognized standard family. The European Cyber Resilience Act, drafted in 2024 and applicable in stages from 2027 onward, treats compliance with a recognized international standard as one of the routes to a presumption of conformity. ISO/IEC 20008-2 Mechanism 4 is the door TPM 2.0 ECDAA walks through to be admissible in those regimes.&lt;/p&gt;
&lt;p&gt;Standardization was complete by 2014. Cryptographic primitive: CPS 2010. Security model: BFGSW 2013. ISO mechanism: 20008-2 Mechanism 4. TPM normative surface: &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt;, &lt;code&gt;TPM_ECC_BN_P256&lt;/code&gt;, &lt;code&gt;TPM2_Commit&lt;/code&gt;, &lt;code&gt;TPM2_Sign&lt;/code&gt;. Every box was checked. The next question -- the one the standardization community could not answer on its own -- was whether anyone would write a verifier.&lt;/p&gt;
&lt;h2&gt;8. The FIDO Bet That Failed (2017-2021)&lt;/h2&gt;
&lt;p&gt;In 2018, the FIDO Alliance bet that ECDAA was the missing privacy story for &lt;a href=&quot;https://paragmali.com/blog/webauthn-and-passkeys-on-windows-from-ctap-to-the-credential/&quot; rel=&quot;noopener&quot;&gt;WebAuthn&lt;/a&gt;. Three years later, W3C took the bet off the table.&lt;/p&gt;
&lt;p&gt;The bet was not casual. FIDO had a real problem. WebAuthn authenticators -- the YubiKey hardware tokens, the &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;Microsoft Hello&lt;/a&gt; platform authenticators, the Touch ID and Face ID modules -- need to attest that they are genuine hardware. The attestation surface FIDO Alliance had inherited from U2F was &lt;em&gt;Basic Attestation&lt;/em&gt;: every authenticator in a manufacturing batch of 100,000 or more units shared one attestation key [@fido-cert-levels], so a relying party that checked the attestation learned only &quot;this is one of 100,000-plus YubiKey 5 NFCs,&quot; not which device specifically. The cohort-size rule gave Basic Attestation a workable operational privacy property. But there was an architectural fork in the road for an organization that wanted &lt;em&gt;cryptographic&lt;/em&gt; attestation privacy without the cohort-key fan-out problem.&lt;/p&gt;
&lt;p&gt;FIDO Alliance picked the cryptographic fork. The FIDO ECDAA Algorithm v2.0 specification was published as an Implementation Draft on February 27, 2018 [@fido-ecdaa-v2]. The document is the most carefully written specification of the DAA contract from a deployment perspective; the editor was Rolf Lindemann at Nok Nok Labs. The motivation section we have already quoted in §2 names the Privacy-CA failure mode in unusually direct terms.&lt;/p&gt;
&lt;p&gt;WebAuthn Level 1 reached W3C Recommendation status on March 4, 2019 [@webauthn-1]. Section 8 defined six attestation statement formats by &lt;code&gt;fmt&lt;/code&gt; identifier: &lt;code&gt;packed&lt;/code&gt;, &lt;code&gt;tpm&lt;/code&gt;, &lt;code&gt;android-key&lt;/code&gt;, &lt;code&gt;android-safetynet&lt;/code&gt;, &lt;code&gt;fido-u2f&lt;/code&gt;, and &lt;code&gt;none&lt;/code&gt;. ECDAA was not a separate format; the WebAuthn-1 §6.4.3 attestation-type list (Basic, Self, AttCA, ECDAA, None) carried ECDAA as an attestation &lt;em&gt;type&lt;/em&gt; supported &lt;em&gt;within&lt;/em&gt; the &lt;code&gt;packed&lt;/code&gt; and &lt;code&gt;tpm&lt;/code&gt; formats. An independent verification of the live HTML finds dozens of occurrences of the string &quot;ecdaa&quot; in the Level 1 Recommendation -- ECDAA had its own type identifier, its own signing logic, and its own verification procedure embedded inside the two formats that mattered [@webauthn-1].&lt;/p&gt;
&lt;p&gt;WebAuthn Level 2 reached W3C Recommendation status on April 8, 2021 [@webauthn-2] [@wiki-webauthn]. The same independent verification against the live Level 2 HTML returns zero occurrences of &quot;ecdaa.&quot; Every reference -- the type identifier, the signing rules, the verifier procedure that the &lt;code&gt;packed&lt;/code&gt; and &lt;code&gt;tpm&lt;/code&gt; formats invoked -- was removed in a single editorial pass. The Yubico migration guide for its Java WebAuthn server library makes the vendor view explicit: &quot;This attestation type was removed from WebAuthn Level 2. ECDAA support has not been implemented in this library, so this value could in practice never be returned&quot; [@yubico-migration].&lt;/p&gt;
&lt;p&gt;Why did the bet fail? Four reasons, each visible from the public record.&lt;/p&gt;
&lt;p&gt;First, no major browser ever shipped an ECDAA verifier inside the &lt;code&gt;packed&lt;/code&gt; or &lt;code&gt;tpm&lt;/code&gt; statement format paths. Chromium, Firefox, and Safari implemented WebAuthn with &lt;code&gt;packed&lt;/code&gt;, &lt;code&gt;tpm&lt;/code&gt;, &lt;code&gt;fido-u2f&lt;/code&gt;, and &lt;code&gt;android-safetynet&lt;/code&gt; attestation, but the ECDAA branch within &lt;code&gt;packed&lt;/code&gt; and &lt;code&gt;tpm&lt;/code&gt; stayed unimplemented. The Yubico migration guide quoted above is the vendor-side confirmation of an industry-wide outcome [@yubico-migration].&lt;/p&gt;
&lt;p&gt;Second, the largest authenticator vendors picked the Basic and AttCA attestation types instead of ECDAA. YubiKey 5 series ships with the &lt;code&gt;packed&lt;/code&gt; format using a Basic Attestation key shared across a 100,000+-unit cohort [@yubico-yk5-attestation] [@fido-cert-levels]. Feitian, Google Titan, and other major FIDO2 authenticator vendors ship Basic Attestation under the same FIDO certification-policy cohort rule [@fido-cert-levels]. Microsoft Hello platform authenticators on Windows TPM-backed devices use the &lt;code&gt;tpm&lt;/code&gt; attestation statement format with an AIK that a Microsoft-operated CA certifies -- the AttCA type, functionally a Privacy-CA [@ms-hello-doc] [@azure-attestation]. The vendor base from which a WebAuthn relying party would actually see an attestation statement, in practice, never produced an ECDAA one.&lt;/p&gt;
&lt;p&gt;Third, FIDO ECDAA v2.0 never advanced beyond Implementation Draft. The URL slug for the document literally encodes its status: &lt;code&gt;fido-v2.0-id-20180227&lt;/code&gt; -- the &lt;code&gt;id-20180227&lt;/code&gt; segment names the format &lt;code&gt;&amp;lt;status&amp;gt;-&amp;lt;date&amp;gt;&lt;/code&gt;, and &quot;id&quot; is &quot;Implementation Draft.&quot; It never reached &quot;Proposed Standard&quot; or &quot;Approved Specification&quot; in FIDO&apos;s process [@fido-ecdaa-v2]. A relying party making a long-term technology bet on an attestation statement format that has never advanced past Implementation Draft has no reason to invest in a verifier library.&lt;/p&gt;
&lt;p&gt;Fourth, FIDO Basic Attestation&apos;s cohort-size rule (100,000+ authenticators per attestation group key, enforced contractually on the certified-authenticator side) gave the underlying privacy concern an &lt;em&gt;operational&lt;/em&gt; answer [@fido-cert-levels]. A WebAuthn relying party that sees a Basic Attestation signature learns &quot;this is one of at least 100,000 identical authenticators&quot; -- a cohort large enough that the relying party cannot, in practice, recover individual identifying information from the attestation alone. The cohort rule does not require pairing arithmetic, does not need a verifier library, and works with the same &lt;code&gt;packed&lt;/code&gt; and &lt;code&gt;tpm&lt;/code&gt; attestation formats every relying party already implements.&lt;/p&gt;

The FIDO Basic Attestation cohort minimum is a particularly clean example of how operational rules can compete directly with cryptographic primitives. The privacy property a relying party wants -- &quot;I cannot single out this device from its peers&quot; -- can be obtained by (a) hardware-anchored zero-knowledge proofs that mathematically forbid linkage (cryptographic DAA), or (b) a contractual obligation that every batch of attestation keys covers at least 100,000 devices (FIDO Basic Attestation) [@fido-cert-levels]. The cryptographic answer is mathematically stronger. The operational answer is dramatically easier to debug, audit, and revoke. Production has consistently chosen the latter.
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; ECDAA shipped chips. It never shipped verifiers. Standardization is necessary but not sufficient for production deployment: production cryptography needs verifier libraries, and verifier libraries are &lt;em&gt;social&lt;/em&gt; phenomena -- they emerge from relying-party demand, SDK presence, incident-response tooling, and library-maintainer attention, none of which the cryptography itself produces. Cryptographic excellence does not predict deployment; library availability does.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the second aha. The reader entered §8 believing that a standardized cryptographic primitive backed by FIDO, three browser vendors, and a publicly authored attestation format would deploy. They exit understanding that ECDAA standardized everything except the social machinery -- and the social machinery is where production attestation actually lives.&lt;/p&gt;
&lt;p&gt;If a consortium with FIDO&apos;s privacy mandate, browser-vendor coalition, and authenticator-vendor base could not generate enough relying-party momentum to keep ECDAA in WebAuthn, what chance did the silent option in TPM 2.0 ever have? The answer requires walking the Microsoft attestation stack.&lt;/p&gt;
&lt;h2&gt;9. Windows: A Billion Chips, Zero Production Use (2014-Present)&lt;/h2&gt;
&lt;p&gt;Microsoft has shipped over a billion Windows TPM 2.0 platforms [@ms-pluton-blog] [@wiki-windows-11]. Microsoft has not shipped a Windows DAA API. The two facts are not in tension. They are the story.&lt;/p&gt;
&lt;p&gt;The shipping Windows attestation stack is documented and unambiguous. Microsoft Azure Attestation is the production-grade attestation service. Its public architecture document describes the protocol in five paragraphs that read, line for line, like TPM 1.1 from 2003 [@azure-attestation]:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Every TPM ships with a unique asymmetric key called the endorsement key (EK)... A certification authority (CA) establishes trust in the TPM either via EKPub or EKCert... A device proves to the CA that the key for which the certificate is being requested is cryptographically bound to the EKPub and that the TPM owns the EKPriv. The CA issues a certificate with a special issuance policy to denote that the key is now attested as protected by a TPM.&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The architecture is the Privacy-CA architecture. The Microsoft-operated CA inputs an EK certificate and outputs a JWT that downstream Microsoft services (Defender for Endpoint device-compliance, Intune Conditional Access policies, Entra ID conditional access, customer-defined Azure Attestation policies) consume. The Windows Health Attestation Service, the older Microsoft surface that predated Azure Attestation, used the same broker model with different deployment shape. The Defender for Endpoint device-compliance flow that gates Conditional Access on attested TPM boot state consumes WHAS or Azure Attestation JWTs, not raw DAA quotes.&lt;/p&gt;
&lt;p&gt;Microsoft Pluton&apos;s published surface tells the same story from the silicon side. Pluton is the security processor Microsoft has been shipping in successive Windows-class platforms. Its Microsoft Learn page enumerates the cryptographic algorithms and platform-security primitives the processor exposes [@pluton]. The page is exhaustive about TPM 2.0 baseline algorithms (RSA-2048, ECDSA over NIST P-256, SHA-2 family). It contains zero occurrences of &lt;code&gt;ECDAA&lt;/code&gt;, of &lt;code&gt;TPM_ALG_ECDAA&lt;/code&gt;, or of any phrase like &quot;anonymous attestation.&quot; Insufficient public evidence to assert that Pluton implements ECDAA; sufficient evidence to assert that Pluton&apos;s published surface does not advertise it.&lt;/p&gt;
&lt;p&gt;The Windows API surface gap is the third piece of evidence. The TPM Base Services (&lt;code&gt;Tbsi_*&lt;/code&gt; functions in &lt;code&gt;Tbs.dll&lt;/code&gt;) expose &lt;code&gt;TPM2_Commit&lt;/code&gt; and &lt;code&gt;TPM2_Sign&lt;/code&gt; to user-mode applications -- but only as raw command-buffer submissions. There is no &lt;code&gt;BCryptDirectAnonymousAttestation&lt;/code&gt;. There is no &lt;code&gt;NCryptDaaSign&lt;/code&gt;. There is no Web Authentication API wrapper that surfaces ECDAA.&lt;/p&gt;
&lt;p&gt;The TPM Platform Crypto Provider (PCP) that Windows ships as part of the Cryptography Next Generation (CNG) framework supports RSA and ECDSA TPM-backed keys but does not surface ECDAA. The TSS.MSR open-source TPM stack from Microsoft Research does not ship a DAA wrapper. An application developer who wants ECDAA on Windows today writes raw &lt;code&gt;TBS_SUBMIT_COMMAND&lt;/code&gt; byte buffers against the documented TPM 2.0 command numbering, manages the Join protocol against an Issuer of their own provisioning, and verifies the resulting signatures with a library they wrote themselves or pulled from a research-grade implementation.&lt;/p&gt;
&lt;p&gt;The interesting question is why. Microsoft has never published a &quot;we considered DAA and chose the broker model because...&quot; statement. Treating that absence honestly, the four reasons below are &lt;em&gt;inferences&lt;/em&gt; from observable architecture decisions, not Microsoft-engineer-published rationales. The article labels them as such.&lt;/p&gt;
&lt;p&gt;First, &lt;em&gt;operational simplicity&lt;/em&gt;. A hosted CA with audit logs is more debuggable than a per-relying-party DAA verifier with no central audit point. When a device fails attestation in production, the on-call engineer reading the Azure Attestation logs can answer &quot;why did this device fail?&quot; in seconds; the same question against a DAA verifier requires reasoning about pairing arithmetic, basename derivation, and Issuer-credential validity. Engineering organizations choose architectures whose failure modes they can debug.&lt;/p&gt;
&lt;p&gt;Second, &lt;em&gt;revocation economics&lt;/em&gt;. A Privacy-CA can revoke an AIK by removing one certificate from its issued-certificate store. Revoking a DAA credential, in the construction TPM 2.0 ships, requires either EPID-style signature-based revocation -- which the TPM 2.0 ECDAA scheme does not provide -- or a private-key list distributed to every relying party (extracting the private key from the misbehaving TPM is presumed possible after compromise, and verifiers then check that the signing key is not on the list). The CA&apos;s revocation primitive is a database delete. The DAA revocation primitive is an SDK rollout to every consumer of the verification library.&lt;/p&gt;
&lt;p&gt;Third, &lt;em&gt;the relying-party stack&lt;/em&gt;. DAA verifier libraries are not present in any mainstream cloud platform&apos;s SDK. The .NET CNG surface, the Java JCA, the Python &lt;code&gt;cryptography&lt;/code&gt; library, the Go &lt;code&gt;crypto&lt;/code&gt; standard library, the Rust &lt;code&gt;ring&lt;/code&gt; and &lt;code&gt;dalek&lt;/code&gt; ecosystems -- none ship an ECDAA verifier. X.509 / PKI verifier libraries, by contrast, are everywhere. A relying party building on top of mainstream SDKs gets PKI verification for free; gets DAA verification for nothing close to free.&lt;/p&gt;
&lt;p&gt;Fourth, &lt;em&gt;the Windows API surface gap is itself the obstacle&lt;/em&gt;. Adding a &lt;code&gt;BCrypt&lt;/code&gt; / &lt;code&gt;NCrypt&lt;/code&gt; / WebAuthn DAA wrapper to Windows requires designing a new key-storage provider contract, defining the JOIN-protocol service interface, writing the conformance test suite, drafting the security documentation, and rolling it out on the Windows release calendar. That is a project the size of Windows Hello&apos;s. Microsoft has not, to public knowledge, prioritized it.&lt;/p&gt;

flowchart TD
    HW[&quot;TPM 2.0 hardware&lt;br /&gt;(discrete or Pluton)&lt;br /&gt;TPM_ALG_ECDAA may be present&quot;]
    TBS[&quot;TPM Base Services&lt;br /&gt;(Tbs.dll, kernel)&quot;]
    PCP[&quot;TPM Platform Crypto Provider&lt;br /&gt;(BCrypt / NCrypt)&lt;br /&gt;RSA and ECDSA only&quot;]
    AZ[&quot;Microsoft Azure Attestation&lt;br /&gt;(Privacy-CA architecture)&quot;]
    WHAS[&quot;Windows Health Attestation Service&lt;br /&gt;(Privacy-CA architecture)&quot;]
    RP[&quot;Intune / Defender / Entra&lt;br /&gt;Conditional Access enforcement&quot;]
    HW --&amp;gt; TBS
    TBS --&amp;gt; PCP
    PCP --&amp;gt; AZ
    PCP --&amp;gt; WHAS
    AZ --&amp;gt; RP
    WHAS --&amp;gt; RP
    HW -.-&amp;gt;|&quot;ECDAA path exists&lt;br /&gt;no Windows API&quot;| HW
&lt;p&gt;The deeper reading -- the one that makes Microsoft&apos;s choice look structural rather than accidental -- starts from a comparison the four inferences above already pointed toward.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Privacy-CA brokers and DAA solve the same problem -- prove the TPM is genuine without disclosing which TPM. They differ only in &lt;em&gt;where the trust assumption lives&lt;/em&gt;. The broker treats privacy as an operational policy (the CA promises not to log, audit logs prove it kept the promise, regulators enforce the promise). DAA treats privacy as a mathematical property (the issuer cannot link, period, no audit needed). The architecture that wins in production is the one with the &lt;em&gt;smaller operational surface&lt;/em&gt;, not the one with the &lt;em&gt;better cryptographic guarantee&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the third aha. The reader entered §9 believing that cryptographic superiority should eventually win in production, and that Microsoft&apos;s non-adoption of DAA must be an oversight or a missed product opportunity. They exit understanding that the deployment-economics asymmetry is structural: a broker-mediated attestation flow reduces, end-to-end, to standard X.509 plumbing every cloud SDK already ships, while a DAA-mediated flow requires bespoke verifier libraries, bespoke revocation infrastructure, bespoke debugging tooling, and bespoke incident-response runbooks. Cloud-platform organizations have spent the last ten years building world-class operational machinery for X.509 attestation. They will not throw it away for a cryptographic property no compliance regime currently demands.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The four reasons compound. The broker model gives a single audit point, a database-delete revocation primitive, an SDK that ships in every major language, and a debugging story the on-call engineer can walk through at 3 a.m. DAA gives mathematical privacy and requires every one of those operational properties to be rebuilt from scratch. Cloud platforms have, repeatedly and consistently, picked the architecture whose operational properties are easier to ship -- not because they do not understand the cryptographic alternative, but because the cryptographic alternative would require them to discard the operational machinery they already have. This is the structural reason DAA has stayed in firmware on a billion chips and out of production attestation flows on all of them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If the broker calculus is this durable, is there any future world in which DAA wins? Two, and both are research-stage with decade-long horizons.&lt;/p&gt;
&lt;h2&gt;10. Theoretical Limits and Open Problems&lt;/h2&gt;
&lt;p&gt;What can DAA never do? Where does the next decade of research go? Three open problems organize the active research community in 2026.&lt;/p&gt;
&lt;h3&gt;10.1 What DAA cannot do&lt;/h3&gt;
&lt;p&gt;The first honest statement is the negative one. A correctly implemented DAA scheme does not prevent a &lt;em&gt;compromised TPM&lt;/em&gt; from signing for the cohort it belongs to. The EK certificate attestation must be honest at manufacture time; if a TPM&apos;s secret membership value &lt;code&gt;f&lt;/code&gt; leaks to an attacker (through fault injection, through side-channel extraction, through a firmware backdoor), the attacker can produce ECDAA signatures indistinguishable from legitimate ones until the TPM&apos;s &lt;code&gt;f&lt;/code&gt; is added to a revocation list. The same constraint applies to every group-signature scheme.&lt;/p&gt;
&lt;p&gt;A second hard limit is per-basename linkability. The user-controlled-linkability property gives a TPM the choice of linkable or unlinkable signing -- but once a verifier has seen the pseudonym &lt;code&gt;N_V = ζ^f mod Γ&lt;/code&gt; for a particular &lt;code&gt;(TPM, bsn)&lt;/code&gt; pair, the linkage for that basename is permanent. A misbehaving TPM that wants its history with a particular relying party forgotten cannot, by signing under a different basename, retroactively unlink past sessions.&lt;/p&gt;
&lt;p&gt;A third limit is rogue-key scalability. The TPM 2.0 ECDAA scheme detects rogue keys by checking each signature against a list of compromised-&lt;code&gt;f&lt;/code&gt; values the verifier maintains. For small lists this is cheap. For very large lists -- imagine a deployment where 1% of the chip population leaks &lt;code&gt;f&lt;/code&gt; to attackers and the verifier must check every signature against ten million revoked values -- the constant factor matters. EPID&apos;s Sig-RL mechanism uses signature-based revocation that scales better; the TPM 2.0 ECDAA scheme does not include it.&lt;/p&gt;
&lt;h3&gt;10.2 The One-TPM-to-Bind-Them-All fix&lt;/h3&gt;
&lt;p&gt;In 2017 a team consisting of Jan Camenisch, Liqun Chen, Manu Drijvers, Anja Lehmann, David Novick, and Rainer Urian published &quot;One TPM to Bind Them All: Fixing TPM 2.0 for Provably Secure Anonymous Attestation&quot; at IEEE S&amp;amp;P 2017 [@one-tpm-2017]. The paper demonstrated a Diffie-Hellman-oracle attack against the TPM 2.0 ECDAA interface as shipped: a malicious host could query the TPM in a way that gave the host a DH-oracle relative to the TPM&apos;s secret &lt;code&gt;f&lt;/code&gt;, effectively breaking the unlinkability property. The proposed fix had been published the previous year by Camenisch, Drijvers, and Lehmann at TRUST 2016 [@cdl-2016] [@cdl-2016-eprint]; library implementations of DAA published from 2017 onward incorporate the fix.The CDL16 fix is library-level, not silicon-level. The TPM 2.0 ECDAA command surface in the chip remains as shipped; the &lt;em&gt;software&lt;/em&gt; that drives it must use the corrected protocol sequence to avoid presenting the host-controlled DH oracle. As of late 2025, the TCG normative TPM 2.0 Library Specification text has not been amended to require the corrected sequence. Implementations of DAA on top of TPM 2.0 -- the FIDO ECDAA v2.0 library, the Camenisch-Drijvers-Lehmann reference code, modern academic ECDAA implementations -- follow CDL16. Implementations written against the bare TPM 2.0 Library Specification without reading CDL16 are vulnerable.&lt;/p&gt;
&lt;h3&gt;10.3 Post-quantum DAA&lt;/h3&gt;
&lt;p&gt;Shor&apos;s algorithm is fatal to DAA. Every classical DAA construction -- BCC 2004, BCL 2008, CPS 2010, CDL 2016 -- relies on the hardness of discrete logarithms in elliptic-curve groups, the hardness of strong-RSA factoring, or both. A cryptographically relevant quantum computer breaks all of them. &lt;a href=&quot;https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/&quot; rel=&quot;noopener&quot;&gt;Post-quantum&lt;/a&gt; DAA is therefore active research, with no production deployment as of 2026. Three candidate families are being actively explored:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Symmetric-primitive DAA.&lt;/strong&gt; Dan Boneh, Saba Eskandarian, and Ben Fisch presented &quot;Post-quantum EPID Signatures from Symmetric Primitives&quot; at CT-RSA 2019 [@bef-2019], building a post-quantum group signature from one-way functions and Merkle trees. The construction has classical post-quantum security guarantees but pays a steep size cost.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lattice-based DAA.&lt;/strong&gt; Rachid El Bansarkhani and Ali El Kaafarani published &quot;Direct Anonymous Attestation from Lattices&quot; as IACR ePrint 2017/1022 [@bk-2017-eprint], the earliest such proposal in the literature. The state-of-the-art lattice DAA construction is the 2024 Collaborative Segregated NIZK (&quot;CoSNIZK&quot;) work by Liqun Chen, Patrick Hough, and Nada El Kassem [@cosnizk-2024], achieving signatures of approximately 38 kilobytes -- an order of magnitude smaller than the earliest lattice proposals but still two orders of magnitude larger than CPS 2010 ECDAA.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hash-based DAA.&lt;/strong&gt; Liqun Chen, Changyu Dong, Nada El Kassem, Christopher Newton, and Yalan Wang published &quot;Hash-Based Direct Anonymous Attestation&quot; at PQCrypto 2023 [@hashdaa-2023], building DAA from SPHINCS+-style stateless hash-based signatures. Size and speed remain unfavorable for TPM 2.0 firmware budgets.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The blocker for any of these reaching production TPM firmware is not academic. The TPM 2.0 normative algorithm set does not include lattice primitives. A post-quantum DAA in TPM 2.0 would require introducing &lt;code&gt;TPM_ALG_DILITHIUM&lt;/code&gt;, &lt;code&gt;TPM_ALG_FALCON&lt;/code&gt;, &lt;code&gt;TPM_ALG_KYBER&lt;/code&gt;, or some equivalent into the spec, mandating support in the PC Client Platform TPM Profile, and rolling out across the OEM TPM-vendor base. That is, at minimum, a three-to-five-year standards effort that the TCG has not, as of late 2025, publicly committed to. CoSNIZK at 38 kilobytes is also two to three times larger than the largest signature any deployed TPM 2.0 firmware budgets for; the TPM-side compute time at quantum-safe parameter sets is currently measured in seconds rather than tens of milliseconds.&lt;/p&gt;
&lt;h3&gt;10.4 DAA for confidential computing&lt;/h3&gt;
&lt;p&gt;The other future-world thread is confidential computing -- the family of CPU-anchored isolated-execution primitives (Intel SGX, Intel TDX, AMD SEV-SNP, ARM CCA) that need their own attestation surfaces. Intel SGX attestation initially used EPID and has since migrated to DCAP, a vendor-CA broker similar in shape to Microsoft Azure Attestation. AMD SEV-SNP and Intel TDX use vendor-rooted PKI from the start.&lt;/p&gt;
&lt;p&gt;Whether DAA-style group-signature schemes are appropriate for VM-level attestation -- where cohorts are small (per-region TDX hosts in a given hyperscaler datacenter), where the verifier is often a small set of well-known cloud-platform endpoints, and where traffic-analysis leakage between confidential VMs and Privacy-CA-like services is itself a threat -- is an open architectural question. The 2026 default is &quot;vendor-CA broker&quot;; the academic community continues to argue that cryptographic DAA would be a better match for the threat model. Production has not, so far, agreed.&lt;/p&gt;
&lt;p&gt;A note on Java Card DAA prototypes. A small number of academic implementations of DAA on Java Card secure elements appeared between 2014 and 2017 -- Camenisch and others published smartcard-class implementations as proofs of concept. None reached production deployment. The reasons appear to be the same operational-economics asymmetry that limits TPM 2.0 ECDAA adoption: Java Card environments lack the relying-party verifier libraries that would consume the output. This is inference; no Java Card vendor has, to public knowledge, published a &quot;we evaluated DAA and chose not to ship it&quot; statement.&lt;/p&gt;
&lt;p&gt;These are the open problems for researchers. What about the rest of us, on Monday morning?&lt;/p&gt;
&lt;h2&gt;11. Practical Guide and Frequently Asked Questions&lt;/h2&gt;
&lt;p&gt;Five roles, one Monday morning. Where does this leave you?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For a Windows platform engineer.&lt;/strong&gt; The minimum viable Windows DAA API surface is approximately a &lt;code&gt;BCryptCreateDaaContext&lt;/code&gt;, &lt;code&gt;BCryptDaaJoin&lt;/code&gt;, &lt;code&gt;BCryptDaaSign&lt;/code&gt;, and &lt;code&gt;BCryptDaaVerify&lt;/code&gt; set, plus an &lt;code&gt;NCryptDaaKeyHandle&lt;/code&gt; for key-storage-provider lifecycle, plus a Web Authentication API surface that consumes ECDAA attestation. Shipping all of that costs a Hello-sized engineering investment. If Pluton&apos;s published surface ever advertises ECDAA, an OEM-side integration becomes possible. Today the answer is that DAA is not available through any supported Windows API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For an attestation-provider product engineer.&lt;/strong&gt; Pick a Privacy-CA broker architecture for production. The comparison table below makes the trade-offs explicit. Cryptographic DAA does not pay for the architectural switch unless the relying-party privacy threat is specifically the broker itself -- a threat model that, in 2026, no shipping production attestation product publicly assumes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For a FIDO authenticator vendor.&lt;/strong&gt; ECDAA attestation is not a viable production choice in 2026. The path to it becoming viable runs through verifier libraries in Chromium, Firefox, and Safari; relying-party SDK support across Auth0, Okta, Microsoft Entra, and Google Identity Platform; and a non-deprecated WebAuthn Level N specification that re-adds the format. None of those preconditions are visibly in progress.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For an academic zero-knowledge-proof researcher.&lt;/strong&gt; Four open problems map onto production needs: post-quantum DAA at TPM-firmware-shippable signature sizes (the current state-of-the-art at 38 kilobytes is too large), threshold-issuer DAA (no single party can issue a credential), confidential-computing DAA (for small-cohort VM attestation), and IoT DAA (for milliwatt-class energy budgets). Each is publishable; none yet has a deployment path.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;For a privacy-tech advocate or policymaker.&lt;/strong&gt; The framing that helps Microsoft, Google, and AWS engineering teams hear the request is &quot;the broker can be compelled by a subpoena; the math cannot.&quot; The framing that does not help is &quot;your cryptography is worse than the academic alternative.&quot; The first is a threat-model conversation that engineering organizations can engage with; the second is a technology conversation they have already had and decided.&lt;/p&gt;
&lt;h3&gt;Comparison: four production architectures for attested privacy&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Privacy-CA broker&lt;/th&gt;
&lt;th&gt;TPM 2.0 ECDAA&lt;/th&gt;
&lt;th&gt;EPID 2.0&lt;/th&gt;
&lt;th&gt;Vendor-CA (Apple, AWS Nitro, Google)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Trust assumption&lt;/td&gt;
&lt;td&gt;Operational (CA promises not to log)&lt;/td&gt;
&lt;td&gt;Cryptographic (issuer cannot link)&lt;/td&gt;
&lt;td&gt;Cryptographic (issuer cannot link)&lt;/td&gt;
&lt;td&gt;Operational (vendor CA promises not to log)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anonymity from verifier?&lt;/td&gt;
&lt;td&gt;If CA does not log&lt;/td&gt;
&lt;td&gt;Yes (per-basename)&lt;/td&gt;
&lt;td&gt;Yes (per-basename)&lt;/td&gt;
&lt;td&gt;If vendor does not log&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TPM-side sign time&lt;/td&gt;
&lt;td&gt;Milliseconds (AIK signing)&lt;/td&gt;
&lt;td&gt;Tens of milliseconds&lt;/td&gt;
&lt;td&gt;Tens of milliseconds&lt;/td&gt;
&lt;td&gt;N/A (signing on vendor silicon)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signature size&lt;/td&gt;
&lt;td&gt;Hundreds of bytes (AIK)&lt;/td&gt;
&lt;td&gt;Hundreds of bytes&lt;/td&gt;
&lt;td&gt;Hundreds of bytes&lt;/td&gt;
&lt;td&gt;Hundreds of bytes (X.509 over signed JWT)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Revocation&lt;/td&gt;
&lt;td&gt;Centralized (refuse / CRL / OCSP)&lt;/td&gt;
&lt;td&gt;Private-key list (TPM 2.0)&lt;/td&gt;
&lt;td&gt;Sig-RL (signature-based)&lt;/td&gt;
&lt;td&gt;Vendor revocation list&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Implementer complexity&lt;/td&gt;
&lt;td&gt;Low (X.509 PKI everywhere)&lt;/td&gt;
&lt;td&gt;High (BN-P256 pairing libraries)&lt;/td&gt;
&lt;td&gt;High (vendor SDK required)&lt;/td&gt;
&lt;td&gt;Low (vendor SDK ships it)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Standardization&lt;/td&gt;
&lt;td&gt;TCG (2003)&lt;/td&gt;
&lt;td&gt;TPM 2.0 + ISO 20008-2 Mech 4&lt;/td&gt;
&lt;td&gt;ISO 20008-2 Mech 4&lt;/td&gt;
&lt;td&gt;Vendor-proprietary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best suited for&lt;/td&gt;
&lt;td&gt;Cloud attestation at hyperscaler scale&lt;/td&gt;
&lt;td&gt;Hardware-anchored attestation where broker is the threat&lt;/td&gt;
&lt;td&gt;Intel-deployed enclave attestation&lt;/td&gt;
&lt;td&gt;Vendor-platform attestation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2026 deployment scale&lt;/td&gt;
&lt;td&gt;Billions of attestations per day&lt;/td&gt;
&lt;td&gt;Essentially zero production verifiers&lt;/td&gt;
&lt;td&gt;2.4B+ EPID keys per RSAC 2016&lt;/td&gt;
&lt;td&gt;Billions of attestations per day&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The &quot;essentially zero production verifiers&quot; entry for TPM 2.0 ECDAA is the deployment story this article exists to explain. The cryptography is in firmware on hundreds of millions of devices; the verifier side, in 2026, is research-grade libraries and the FIDO ECDAA-Verify reference code. No production cloud-platform SDK ships an ECDAA verifier.&lt;/p&gt;

Four things, in order. First, Pluton&apos;s published surface advertises `TPM_ALG_ECDAA` and an Issuer key-management story (a Microsoft-operated DAA Issuer for Windows devices, with documented enrollment and revocation flows). Second, a Cryptography Next Generation API surface (`BCryptDaaSign`, `NCryptDaaKey*`) that exposes the TPM2_Commit / TPM2_Sign sequence behind a single managed-language call. Third, a Web Authentication API extension that surfaces ECDAA attestation as a first-class statement format the same way the `tpm` format is today. Fourth, an Azure Attestation policy mode that consumes ECDAA signatures and produces JWT outputs downstream Microsoft services already understand. None of these are technically blocking; all four require a multi-year roadmap commitment that, as of late 2025, Microsoft has not publicly made. This is a thought experiment about technical feasibility, not a forecast about Microsoft strategy.
&lt;p&gt;The companion piece to this article is the &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM in Windows&lt;/a&gt; article, which walks the broader TPM 2.0 command surface ECDAA sits inside.&lt;/p&gt;


It depends on what the laptop ships. The TPM 2.0 Library Specification names `TPM_ALG_ECDAA`. The TCG PC Client Platform TPM Profile made the algorithm optional in v1.04 (February 2020) and has carried that designation through v1.07 RC1 (December 2025), so a conformant Windows-class platform is allowed to omit it. Many discrete TPM 2.0 modules (Infineon, STMicroelectronics, Nuvoton) do implement the algorithm; Microsoft Pluton&apos;s published documentation does not advertise it. The honest answer is &quot;look at your specific TPM vendor&apos;s algorithm capability table&quot; -- and that even if your TPM does support the algorithm, Windows ships no API to use it [@tpm-library-spec] [@tcg-ptp] [@pluton] [@wiki-daa].


Microsoft has not published an explicit rationale. Four inferable reasons are visible from the architecture: (1) operational simplicity -- a hosted CA is easier to debug than a per-relying-party DAA verifier; (2) revocation economics -- a CA can revoke an AIK by deleting a certificate, while DAA revocation requires a private-key list distributed to every verifier; (3) a missing relying-party verifier-library stack; (4) no Windows API surface for ECDAA. All four are inferences. The shipped architecture is the Privacy-CA-shaped flow documented at the Microsoft Learn attestation page [@azure-attestation].


WebAuthn Level 1 (March 2019) registered ECDAA as an attestation *type* (Basic, Self, AttCA, ECDAA, None) carried inside the `packed` and `tpm` attestation statement formats. The Level 1 specification text contains 63 references to &quot;ecdaa.&quot; WebAuthn Level 2 (April 2021) removed the type entirely; an independent grep of the Level 2 Recommendation HTML returns zero occurrences of &quot;ecdaa.&quot; The Yubico migration guide for its WebAuthn server library states verbatim that &quot;this attestation type was removed from WebAuthn Level 2&quot; and that &quot;ECDAA support has not been implemented in this library.&quot; The format has not been resurrected as of 2026 [@webauthn-1] [@webauthn-2] [@yubico-migration].


EPID is a DAA variant with one cryptographic addition: signature-based revocation (Sig-RL), which lets a verifier prove that a candidate signature was not produced by the same signer as any signature on a revocation list. The TPM 2.0 ECDAA scheme is the Chen-Page-Smart 2010 construction; EPID 2.0 is essentially the same construction with Sig-RL added. Intel positions EPID separately because of its production deployment (2.4 billion-plus keys shipped per Intel&apos;s RSAC 2016 disclosure, used for SGX attestation, Widevine, and several Intel chipsets), its specific licensing structure (royalty-free under Intel&apos;s contribution to ISO/IEC 20008 / 20009), and its open-source SDK that Intel maintained until archiving in 2023 [@brickell-li-epid-2007] [@brickell-li-tdsc-2012] [@wiki-epid] [@epid-sdk].


Active research, no production deployment as of 2026. The leading constructions are lattice-based (CoSNIZK 2024 at approximately 38 kilobytes per signature [@cosnizk-2024]), hash-based (the 2023 PQCrypto paper from SPHINCS+ [@hashdaa-2023]), and symmetric-primitive-based (Boneh-Eskandarian-Fisch CT-RSA 2019 [@bef-2019]). The barriers to shipping any of them in a TPM are fundamental: TPM 2.0 firmware does not implement lattice primitives, signature sizes at 30+ kilobytes are incompatible with current attestation-latency budgets, and no relying-party verifier library exists. A post-quantum DAA TPM is a 2030s project at the earliest.


No. The Stage 0a focus-premise audit of this article demoted that framing as not supported by evidence. The accurate claim is &quot;standardized in the TPM 2.0 Library Specification (2014); optional in the TCG PC Client Platform TPM Profile since February 2020; present on many discrete TPMs (vendor documentation confirms); absent from Microsoft Pluton&apos;s published algorithm surface; supported by no Windows API.&quot; That hedged statement is the one the article carries from its first 200 words through to this FAQ [@tpm-library-spec] [@tcg-ptp] [@pluton].

&lt;p&gt;The cryptography is finished. The standardization is finished. The hardware is in the field. What is missing is the social machinery -- the verifier libraries, the SDK presence, the operational tooling, the incident-response runbooks, the regulator demand -- that turns cryptography into deployment. Direct Anonymous Attestation is the cleanest example in platform security of a primitive that won every standardization fight and lost every deployment one. The lesson is not that the cryptography is wrong. The lesson is that cryptography is necessary but never sufficient. Production systems are social systems whose mathematical components, however elegant, must compete with operational alternatives whose properties are easier to ship.&lt;/p&gt;
&lt;p&gt;The companion pieces in this series are &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;The TPM in Windows&lt;/a&gt; (the cryptographic primitive plumbing TPM 2.0 ECDAA sits inside) and the Microsoft Pluton continuation article (Pluton&apos;s published capability surface and the negative claim this article rests its §9 hedge on). The Measured Boot piece -- forthcoming -- walks the data that a hypothetical DAA quote would attest. If those three articles arrive together, the picture of Windows attestation as a &lt;em&gt;system&lt;/em&gt; rather than a primitive becomes complete.&lt;/p&gt;
</content:encoded><category>tpm</category><category>attestation</category><category>zero-knowledge-proofs</category><category>cryptography</category><category>windows-security</category><category>pluton</category><category>webauthn</category><category>fido</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Edge&apos;s Two Password Cryptographies: A Beautiful PSI on the Wire, and Plaintext RAM by Design</title><link>https://paragmali.com/blog/edge-two-password-cryptographies/</link><guid isPermaLink="true">https://paragmali.com/blog/edge-two-password-cryptographies/</guid><description>Microsoft Edge ships a homomorphic-encryption PSI for breach checking and decrypts every saved password into process RAM at launch. Both designs are deliberate. They defend different threat models.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>
Microsoft Edge ships two cryptographic designs for &quot;passwords&quot; inside the same `msedge.exe` binary, owned by the same product team, with radically different threat models. The first -- Password Monitor -- is a deployed Private Set Intersection protocol built on Microsoft SEAL, the first production consumer homomorphic-encryption deployment in a browser, and is state-of-the-art cryptography for defending against a compromised breach-corpus server. The second -- Edge&apos;s local credential storage -- decrypts every saved password into process memory at browser launch and keeps it there for the lifetime of the session, a design Tom Joran Sonstebyseter Ronning&apos;s `EdgeSavedPasswordsDumper` (May 4, 2026) made legible and that Microsoft classified as &quot;by design.&quot; These are not incompatible designs. They are precise statements about which threat models the Edge product team is and is not defending against, and treating them as one unified &quot;password security&quot; story masks where the actual compromise happens in 2026.
&lt;h2&gt;1. Two cryptographies, one product, one week&lt;/h2&gt;
&lt;p&gt;On &lt;strong&gt;January 21, 2021&lt;/strong&gt;, Microsoft Research&apos;s Cryptography and Privacy group announced Edge&apos;s Password Monitor ships a homomorphic-encryption-based Private Set Intersection protocol built on Microsoft SEAL. The post is explicit that this is &quot;possible due to pioneering cryptography research and technology incubation done here at Microsoft Research,&quot; that it is the result of a collaboration &quot;between [the] Cryptography and Privacy Research Group, and Edge product team,&quot; and that the protocol descends from two specific papers: &quot;Fast Private Set Intersection from Homomorphic Encryption&quot; and &quot;Labeled PSI from Fully Homomorphic Encryption with Malicious Security&quot; [@msr-password-monitor-2021]. It is, by a comfortable margin, the first production consumer deployment of homomorphic encryption in a browser.&lt;/p&gt;
&lt;p&gt;On &lt;strong&gt;May 4, 2026, at 14:29:51 UTC&lt;/strong&gt;, a researcher in Oslo named Tom Joran Sonstebyseter Ronning posted to X: &quot;Microsoft Edge loads all your saved passwords into memory in cleartext -- even when you&apos;re not using them&quot; [@ronning-x]. He linked a GitHub repository called &lt;code&gt;EdgeSavedPasswordsDumper&lt;/code&gt;: roughly 230 lines of C# that opens a handle to the parent &lt;code&gt;msedge.exe&lt;/code&gt; process and reads every saved credential as plaintext, with no kernel exploit, no admin (against same-user processes), and no &lt;a href=&quot;https://paragmali.com/blog/dpapi-and-dpapi-ng-the-credential-vault-under-everything/&quot; rel=&quot;noopener&quot;&gt;DPAPI&lt;/a&gt; bypass [@ronning-github]. The README confirms the behaviour is present in &quot;any Edge versions that&apos;s Chromium based (from version 79 and newer, including 147.0.3912.98 and any future version)&quot; [@ronning-github]. Two days later, on &lt;strong&gt;May 6, 2026&lt;/strong&gt;, Microsoft told Forbes that the in-memory behaviour is &quot;an expected feature of the application&quot; and &quot;by design&quot; [@forbes-winder].&lt;/p&gt;
&lt;p&gt;Both designs ship in the same binary. Both are owned by the same product team. Both can be defended on technical grounds. And both stories get told about the same word: &quot;passwords.&quot;&lt;/p&gt;
&lt;p&gt;This article argues they are about two different threat models, and the apparent contradiction in the headline disappears once you separate them.&lt;/p&gt;

A two-party cryptographic protocol in which Alice holds a set $S_A$, Bob holds a set $S_B$, and they jointly compute $S_A \cap S_B$ such that each party learns the intersection (or, in some variants, only one party does) and nothing else about the other party&apos;s set beyond what the intersection implies.
&lt;p&gt;The PSI story is genuinely beautiful. It begins in 1986 with a paper hardly anyone reads, climbs through a 35-year cryptographic engineering effort to make oblivious transfer cheap enough to be free, lands in 2017 on a homomorphic-encryption breakthrough whose cost curve fits the breach-checking problem exactly, and ships on consumers&apos; desktops by 2021 [@msr-password-monitor-2021]. The endpoint-storage story is genuinely awkward. Edge unwraps every DPAPI-encrypted saved credential into process memory when the password feature first activates, keeps the plaintext resident for the lifetime of the session, and accepts that any same-user process can read it back out [@ronning-github]. Microsoft&apos;s official position is that local code execution on the user&apos;s machine is &quot;outside the threat model&quot; of the browser password store -- a position that is internally consistent with a decade of MSRC policy and also true [@forbes-winder].&lt;/p&gt;
&lt;p&gt;The thesis: &lt;strong&gt;&quot;password security&quot; is at least two threat models&lt;/strong&gt;, and Microsoft has chosen to deploy genuinely state-of-the-art cryptography against one of them while explicitly conceding the other. Treating both as one unified story is how product narratives obscure where the actual compromise happens in 2026.&lt;/p&gt;

timeline
    title PSI and browser credential storage, 1986 to 2026
    1986 : Meadows PSI
         : NRL matchmaking
    1999 : Huberman-Franklin-Hogg
         : DH meet-in-the-middle
    2003 : IKNP03 OT extension
    2004 : FNP04 polynomial PSI
    2015 : KOS15 active security
    2016 : KKRT16 OPRF-PSI
    2017 : CLR17 HE-PSI
         : Signal SGX contact discovery
    2018 : CHLR18 Labeled PSI
         : HIBP v2 k-anonymity
    2019 : Google Password Checkup
         : Silent OT
    2021 : Edge Password Monitor ships
         : Cong et al. CCS 2021
    2024 : Chrome App-Bound Encryption
    2026 : Ronning EdgeSavedPasswordsDumper
         : Microsoft &quot;by design&quot;
&lt;p&gt;To see why the two designs are not a contradiction, we need to understand how PSI got to be deployable at all -- a story that begins thirty-five years before Edge Password Monitor existed.&lt;/p&gt;
&lt;h2&gt;2. Why Private Set Intersection exists at all&lt;/h2&gt;
&lt;p&gt;Imagine two parties, neither of whom trusts the other. Alice has a set of identifiers; Bob has a set of identifiers. They want to learn which identifiers they share -- and only that. Each party wants to learn nothing about elements not in the intersection.&lt;/p&gt;
&lt;p&gt;This is not an obvious problem to need a protocol for. If Alice and Bob trusted a third party, they would hand over their sets. If they trusted each other, they would compare directly. The problem only becomes interesting when neither assumption holds. The 2026 canonical version looks like this: &lt;em&gt;Microsoft holds a curated set of roughly five billion breached credentials, and the user holds a few hundred passwords saved in their browser. The user wants to know which of their passwords appear in Microsoft&apos;s breach corpus -- and they want Microsoft to learn nothing about the passwords that do not.&lt;/em&gt; (Throughout this article the &quot;five billion&quot; figure is the author&apos;s 2026 forward projection of Microsoft&apos;s compromised-credential corpus; the contemporaneous Microsoft Research figure used in the 2021 Password Monitor announcement is four billion [@msr-password-monitor-2021], and the §3 sidenote spells out the inference.)&lt;/p&gt;
&lt;p&gt;That framing did not exist in 1986. The original motivation was much weirder.&lt;/p&gt;
&lt;h3&gt;The 1986 paper&lt;/h3&gt;
&lt;p&gt;Catherine Meadows, then at the U.S. Naval Research Laboratory, published &quot;A More Efficient Cryptographic Matchmaking Protocol for Use in the Absence of a Continuously Available Third Party&quot; at the 1986 IEEE Symposium on Security and Privacy [@meadows-1986][@ieee-6234864]. The titular &quot;matchmaking&quot; problem was prosaic. Two parties want to learn whether they share an interest in some sensitive list -- classified-mailing-list membership, intelligence-source overlap, the everyday work of compartmented information systems -- without revealing anything else.&lt;/p&gt;
&lt;p&gt;Meadows&apos;s protocol uses commutative encryption. Alice and Bob both raise the elements of their sets to private exponents over a Diffie-Hellman group. After two rounds, both parties hold the doubly-blinded versions of both sets. Equal underlying elements produce equal doubly-blinded values, because exponentiation in an Abelian group commutes. Unequal elements look like uniform-random group elements to both sides. The intersection comes out; the rest does not.&lt;/p&gt;
&lt;p&gt;Meadows wrote this ten years before Diffie-Hellman key exchange shipped in SSL 3.0 (November 1996), the protocol family TLS would standardise in 1999 [@wikipedia-tls], and thirty-five years before her protocol&apos;s intellectual descendants would ship in a consumer browser.&lt;/p&gt;
&lt;h3&gt;The 1999 revival&lt;/h3&gt;
&lt;p&gt;The same protocol shape was rediscovered and given its modern formulation by Bernardo Huberman, Matthew Franklin, and Tad Hogg at Xerox PARC in 1999. Their paper &quot;Enhancing Privacy and Trust in Electronic Communities&quot; was published at the First ACM Conference on Electronic Commerce [@hfh-1999]. The motivations were online-community problems that look quaint today: which of your friends are on this matchmaking site, do we share interests on a sensitive bulletin board, can two early-internet communities establish trust without leaking their member lists. The protocol they wrote down -- usually called &quot;DH meet-in-the-middle&quot; or just &quot;the Huberman-Franklin-Hogg protocol&quot; -- is the canonical PSI shape every security engineer still reaches for first.The dblp BibTeX record gives the canonical DOI as 10.1145/336992.337012, which the ACM Digital Library 403s to most non-browser User-Agents. The dblp HTML mirror returns 200 and confirms the citation [@dblp-hfh].&lt;/p&gt;
&lt;h3&gt;What had to exist before&lt;/h3&gt;
&lt;p&gt;PSI predates breach checking by twenty years. The cryptographers who built PSI did not know they were building Edge Password Monitor. They were building a protocol primitive that happened, much later, to map cleanly onto a problem the world did not yet have.&lt;/p&gt;
&lt;p&gt;The first such mapping landed in 2018 with HIBP&apos;s k-anonymity API and the cluster of academic and industry PSI deployments that followed [@hunt-pwned-v2-2018]. The primitive predated the killer application by two and a half decades. This is the normal shape of cryptographic engineering: the primitive sits on the shelf until the world needs it.&lt;/p&gt;
&lt;p&gt;The DH meet-in-the-middle protocol is elegant. It is also catastrophically wrong for the breach-checking use case. To see why, we have to count exponentiations.&lt;/p&gt;
&lt;h2&gt;3. Early approaches: DH meet-in-the-middle and FNP04&lt;/h2&gt;
&lt;p&gt;Let us walk the DH meet-in-the-middle protocol step by step. Alice holds a set $S_A$. Bob holds a set $S_B$. Both parties agree on a Diffie-Hellman group $G$ of prime order $q$ with generator $g$, and on a cryptographic hash function $H: {0,1}^* \to G$ that maps set elements into the group. Alice picks a private exponent $a \in \mathbb{Z}_q$; Bob picks $b$.&lt;/p&gt;
&lt;p&gt;The protocol proceeds in two rounds:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Alice computes ${H(x)^a : x \in S_A}$, shuffles, and sends to Bob.&lt;/li&gt;
&lt;li&gt;Bob computes ${H(y)^b : y \in S_B}$, shuffles, and sends to Alice.&lt;/li&gt;
&lt;li&gt;Alice exponentiates every value Bob sent her by $a$, producing ${H(y)^{ab} : y \in S_B}$. She sends this set back to Bob &lt;em&gt;in the order Bob originally sent his&lt;/em&gt; ${H(y)^b}$, so Bob can index by his own set.&lt;/li&gt;
&lt;li&gt;Bob exponentiates Alice&apos;s first-round values by $b$, producing ${H(x)^{ab} : x \in S_A}$. (In the symmetric variant Bob also sends his doubly-blinded set back to Alice; either side can then perform the match.)&lt;/li&gt;
&lt;li&gt;Both parties now hold the same doubly-blinded set for both sides. Equal underlying elements collide; unequal ones do not. They intersect locally.&lt;/li&gt;
&lt;/ol&gt;

sequenceDiagram
    participant A as Alice (set S_A)
    participant B as Bob (set S_B)
    Note over A,B: shared group G, hash H, private exponents a (Alice), b (Bob)
    A-&amp;gt;&amp;gt;B: shuffled set of H(x)^a for x in S_A
    B-&amp;gt;&amp;gt;A: shuffled set of H(y)^b for y in S_B
    A-&amp;gt;&amp;gt;A: compute H(y)^(ba) for each item from Bob
    B-&amp;gt;&amp;gt;B: compute H(x)^(ab) for each item from Alice
    Note over A,B: equal underlying elements yield equal doubly-blinded values
    A-&amp;gt;&amp;gt;B: ordered set of H(y)^(ab) for matching
    Note over A,B: intersection computed locally
&lt;p&gt;Why this is right. Under the Decisional Diffie-Hellman (DDH) assumption in $G$, the doubly-blinded values $H(x)^{ab}$ for $x \notin S_A \cap S_B$ look uniformly random to the other side. Equal underlying elements collide; unequal underlying elements do not. The reader can verify this for themselves in the demonstration below, which is the canonical pedagogical version of the protocol.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Toy PSI -- pedagogical only, NOT secure (small modulus, weak hash). const p = 2n ** 61n - 1n;      // Mersenne prime, small for demo const g = 37n;                  // generator over Z_p* (toy) const H = s =&amp;gt; {   // toy hash: deterministic map string -&amp;gt; [1, p-1]   let h = 1469598103934665603n;   for (const c of s) h = ((h ^ BigInt(c.charCodeAt(0))) * 1099511628211n) % p;   return h === 0n ? 1n : h; }; const expMod = (base, exp, mod) =&amp;gt; {   let r = 1n, b = base % mod, e = exp;   while (e &amp;gt; 0n) {     if (e &amp;amp; 1n) r = (r * b) % mod;     b = (b * b) % mod;     e &amp;gt;&amp;gt;= 1n;   }   return r; }; const S_A = [&quot;alice-at-example.com&quot;, &quot;bob-at-example.com&quot;, &quot;carol-at-example.com&quot;]; const S_B = [&quot;dave-at-example.com&quot;, &quot;bob-at-example.com&quot;, &quot;carol-at-example.com&quot;]; const a = 0xC0FFEEn, b = 0xBADCAFEn; const A1 = S_A.map(x =&amp;gt; expMod(H(x), a, p));   // Alice -&amp;gt; Bob const B1 = S_B.map(y =&amp;gt; expMod(H(y), b, p));   // Bob -&amp;gt; Alice const A2 = A1.map(v =&amp;gt; expMod(v, b, p));       // Bob blinds Alice&apos;s set const B2 = B1.map(v =&amp;gt; expMod(v, a, p));       // Alice blinds Bob&apos;s set // Reveal intersection by matching doubly-blinded values const setA2 = new Set(A2.map(String)); const intersection = S_B.filter((_, i) =&amp;gt; setA2.has(String(B2[i]))); console.log(&quot;Intersection:&quot;, intersection);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;The protocol does work. It gives semi-honest security under DDH and costs $O(|S_A| + |S_B|)$ group exponentiations per side, plus the same again to blind the received set. In a balanced setting -- two sets of similar size, perhaps a few thousand elements each -- it is genuinely deployable.&lt;/p&gt;
&lt;h3&gt;The three things that go wrong at scale&lt;/h3&gt;
&lt;p&gt;For breach checking, the protocol breaks in three documented ways.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Set-cardinality leakage.&lt;/strong&gt; The shuffled lists Alice and Bob send each other have lengths $|S_A|$ and $|S_B|$. Bob learns precisely how many passwords Alice has saved; Alice learns precisely how big Bob&apos;s breach corpus is. The first leak is small but real; the second is fine when the server publishes its corpus size anyway (HIBP does), but the protocol does not hide it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Online-cost asymmetry.&lt;/strong&gt; The server pays $O(|S_B|)$ exponentiations per client query. At the five-billion-element scale of Microsoft&apos;s compromised-credential corpus, no realistic group exponentiation cost makes this feasible per query: even at 100 microseconds per exponentiation (optimistic for $|q| = 256$), five billion exponentiations is more than five days of single-core CPU. Sharding helps. Caching helps. Pre-computation helps. None makes the asymptotic curve workable as the corpus grows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;No labeled variant.&lt;/strong&gt; The protocol returns set membership, not associated metadata. Edge Password Monitor wants to tell you &quot;this credential appeared in breach X&quot; -- so the protocol has to support associating a server-side label with each set element and returning the label for matched elements. DH meet-in-the-middle does not, without unpleasant extensions.&lt;/p&gt;
&lt;h3&gt;FNP04: the right idea, wrong substrate&lt;/h3&gt;
&lt;p&gt;The algebraic alternative arrived in 2004 with Freedman, Nissim, and Pinkas&apos;s &quot;Efficient Private Matching and Set Intersection&quot; at EUROCRYPT [@fnp-2004]. The idea is gorgeous. Alice encodes her set $S_A = {x_1, \dots, x_n}$ as the polynomial whose roots are her set:&lt;/p&gt;
&lt;p&gt;$$p(z) = \prod_{i=1}^{n} (z - x_i)$$&lt;/p&gt;
&lt;p&gt;Alice encrypts each coefficient of $p$ under an additively-homomorphic encryption scheme (Paillier, in the paper). She sends the encrypted coefficients to Bob. Bob homomorphically evaluates $p(y)$ for every element $y$ of his set $S_B$ and returns the encrypted results. If $y \in S_A$, then $p(y) = 0$, and after decryption Alice sees a zero in the corresponding position. If $y \notin S_A$, then $p(y)$ is a non-trivial polynomial evaluation that, randomized correctly, decrypts to a uniform value Alice cannot interpret.&lt;/p&gt;
&lt;p&gt;This is the first asymmetric PSI -- the first protocol where one party can do most of the work while the other sends only a small encrypted query. It is also, in deployment terms, structurally infeasible at scale. Paillier ciphertexts live in $\mathbb{Z}^*_{n^2}$ and are $2 \cdot |n|$ bits each [@paillier-1999] (2048 bits for FNP04&apos;s $|n| = 1024$ default; 4096 bits for the $|n| \geq 2048$ that modern security demands). Paillier homomorphic evaluation needs full-size modular exponentiation per coefficient, and the server compute scales as $O(|S_A| \cdot |S_B|)$. At Edge Password Monitor&apos;s target scale -- a client set of a few hundred passwords against a five-billion-element server corpus -- a single query would take minutes to hours of server compute and tens of megabytes of round-trip data per query.The 5-billion-element estimate is INFERRED, not measured (see §2 for the article-wide projection disclosure). No published source benchmarks FNP04 at that scale; the inference combines the $O(|S_A| \cdot |S_B|)$ asymptotic with measured Paillier throughput on commodity hardware. The order-of-magnitude conclusion is sound; treat the precise number as a back-of-envelope.The polynomial-roots idea is not dead. Thirteen years later, Chen, Laine, and Rindal will revive exactly this construction inside CLR17 [@clr-2017], with Paillier replaced by BFV-style somewhat-homomorphic encryption and a partition-and-evaluate trick that fixes the $O(|S_A| \cdot |S_B|)$ blow-up. The structure survives; the substrate gets swapped.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Protocol&lt;/th&gt;
&lt;th&gt;Era&lt;/th&gt;
&lt;th&gt;Server cost&lt;/th&gt;
&lt;th&gt;Communication&lt;/th&gt;
&lt;th&gt;Verdict at 5B-element scale&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;DH meet-in-the-middle [@hfh-1999]&lt;/td&gt;
&lt;td&gt;1999&lt;/td&gt;
&lt;td&gt;$O(&lt;/td&gt;
&lt;td&gt;S_B&lt;/td&gt;
&lt;td&gt;)$ DH exponentiations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;FNP04 [@fnp-2004]&lt;/td&gt;
&lt;td&gt;2004&lt;/td&gt;
&lt;td&gt;$O(&lt;/td&gt;
&lt;td&gt;S_A&lt;/td&gt;
&lt;td&gt;\cdot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;(preview) HE-PSI on BFV [@clr-2017]&lt;/td&gt;
&lt;td&gt;2017&lt;/td&gt;
&lt;td&gt;$O(&lt;/td&gt;
&lt;td&gt;S_B&lt;/td&gt;
&lt;td&gt;\log&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The naive approach -- &quot;just hash and compare&quot; -- leaks set cardinality at a minimum, and plain hashing is brute-forceable against any password the server can guess. Doing PSI properly under encryption requires either $O(|S_A| \cdot |S_B|)$ server work and 2048-bit Paillier ciphertexts (FNP04, dead at billions), or a new primitive: oblivious transfer at scale. It takes a decade of engineering to make that primitive free.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;FNP04&apos;s polynomial idea will turn out to be the right idea -- but only after we replace Paillier with somewhat-homomorphic encryption thirteen years later. Before we can get there, we need a different breakthrough: making the underlying oblivious-transfer primitive cheap enough that &lt;em&gt;every&lt;/em&gt; PSI in the literature can ride on it.&lt;/p&gt;
&lt;h2&gt;4. The evolution: oblivious transfer extension&lt;/h2&gt;
&lt;p&gt;Here is the central fact that drove a decade of cryptographic engineering: &lt;strong&gt;every PSI protocol that scales eventually reduces to &quot;many oblivious transfers.&quot;&lt;/strong&gt; OT is the universal building block. Once you can do millions of OTs per second, you can do nearly any two-party secure computation, including PSI. The question is how cheap &quot;many OTs&quot; can become.&lt;/p&gt;

A two-party primitive. In 1-out-of-2 OT, the sender holds two messages $(m_0, m_1)$, the receiver chooses a bit $b$, and after the protocol runs the receiver learns $m_b$ while the sender learns nothing about $b$. OT is universal -- it suffices for secure two-party computation of any function -- and it is also expensive: implemented directly from public-key primitives, each OT costs at least one Diffie-Hellman exponentiation, on the order of a millisecond per OT on commodity hardware.

A two-phase protocol that performs $m$ OTs at the cost of $\kappa$ &quot;base&quot; OTs (typically $\kappa = 128$, implemented with public-key crypto) plus $O(m)$ symmetric primitive calls. Because $\kappa$ is small and constant, the per-OT cost drops from public-key cost (~1 ms) to symmetric-crypto cost (~100 ns) -- roughly three orders of magnitude, and the asymptotic gap widens with $m$.
&lt;h3&gt;Generation 1: IKNP03&lt;/h3&gt;
&lt;p&gt;In 2003, Yuval Ishai, Joe Kilian, Kobbi Nissim, and Erez Petrank published &quot;Extending Oblivious Transfers Efficiently&quot; at CRYPTO 2003 [@iknp-2003].&lt;/p&gt;
&lt;p&gt;Ishai and Petrank are at Technion; Kilian and Nissim were at NEC Labs America at the time. The construction is short enough to summarize in one paragraph and important enough to be called &lt;em&gt;the&lt;/em&gt; OT extension: start with $\kappa$ &quot;base&quot; OTs done the expensive way (one public-key operation each), then use them to seed pseudorandom generators and a clever transposition trick that, with $O(m)$ hash-function calls, produces $m$ effective OTs. The base cost stays fixed at $\kappa$ public-key operations; the per-OT marginal cost collapses to a few hash invocations.&lt;/p&gt;
&lt;p&gt;The numerical impact: before IKNP, secure-computation researchers cited oblivious-transfer cost in milliseconds; after IKNP, in hundreds of nanoseconds. Three orders of magnitude is the difference between &quot;research artifact&quot; and &quot;this protocol can ship.&quot;No IACR ePrint preprint existed at publication time; a post-conference upload appeared in 2008 as ePrint 2008/508. ePrint 2003/052 is a different paper (Klima, Pokorny, Rosa). The Springer LNCS chapter [@iknp-2003] is the canonical reference.&lt;/p&gt;
&lt;h3&gt;Generation 2: KOS15&lt;/h3&gt;
&lt;p&gt;IKNP03 is secure against a &lt;em&gt;semi-honest&lt;/em&gt; adversary -- one who follows the protocol but tries to learn extra information from the transcript. Real-world deployments often need &lt;em&gt;active&lt;/em&gt; security: protection against an adversary who deviates to extract information or bias the output.&lt;/p&gt;
&lt;p&gt;In 2015, Marcel Keller, Emmanuela Orsini, and Peter Scholl published &quot;Actively Secure OT Extension with Optimal Overhead&quot; [@kos-2015]. The construction adds a correlation-check phase on top of IKNP03 that catches active deviations with overwhelming probability. The paper&apos;s own abstract: &quot;no more than 5% more time than the passively secure IKNP extension, in both LAN and WAN environments, and thus is essentially optimal with respect to the passive protocol.&quot; Modern implementations (libOTe, EMP-toolkit, MP-SPDZ) report on the order of 10-20% wall-clock overhead and 5-10% communication overhead over semi-honest IKNP03 in production -- the &quot;optimal overhead&quot; in the title is the claim that this margin vanishes as the OT count grows.&lt;/p&gt;
&lt;p&gt;After KOS15, &quot;active security is free&quot; became the industry default. Every modern OT-extension library -- libOTe, EMP-toolkit, MP-SPDZ -- ships KOS15 (or a close variant) as the production-grade default. The earlier semi-honest-only choice is a research artifact.&lt;/p&gt;
&lt;h3&gt;Generation 3: Silent OT&lt;/h3&gt;
&lt;p&gt;In 2019, a six-author collaboration -- Elette Boyle, Geoffroy Couteau, Niv Gilboa, Yuval Ishai, Lisa Kohl, and Peter Scholl -- published &quot;Efficient Pseudorandom Correlation Generators: Silent OT Extension and More&quot; at CRYPTO 2019 [@silent-ot-2019]. The construction replaces the communication-heavy IKNP/KOS phase with a &lt;em&gt;Pseudorandom Correlation Generator&lt;/em&gt; (PCG): the two parties exchange a few-kilobyte seed and locally expand it into millions of correlated OTs.&lt;/p&gt;

A protocol primitive that, given a short shared seed, lets two parties locally expand the seed into long correlated random strings -- in the OT case, the random correlations needed to &quot;consume&quot; each OT call. Once the seed is exchanged, no further communication is needed to produce more OTs; the parties simply expand more locally. PCGs reduce the per-OT wire cost to zero in the post-seed phase.
&lt;p&gt;The numerical impact this time is &lt;em&gt;bandwidth&lt;/em&gt;. Pre-Silent-OT, OT-extension protocols sent on the order of $\kappa$ bits per OT. Silent OT sends a polylogarithmic amount of data total for the entire extension. The precursor construction &quot;Compressing Vector OLE&quot; by Boyle, Couteau, Gilboa, Ishai, Kohl, and Rindal [@boyle-vector-ole-2019] laid the algebraic foundation.&lt;/p&gt;
&lt;p&gt;For Edge Password Monitor&apos;s deployment shape (small client set, large server set), Silent OT does not land in the production protocol -- HE-PSI provides the asymmetric communication scaling -- but its existence in 2019 is part of why the industry treats OT extension as essentially solved engineering and feels free to ride a higher-layer protocol on top.&lt;/p&gt;
&lt;h3&gt;The OPRF-PSI plateau: KKRT16&lt;/h3&gt;
&lt;p&gt;Pure OT-extension is one substrate; the other is the Oblivious Pseudorandom Function.&lt;/p&gt;

A two-party protocol in which the sender holds a key $k$, the receiver holds an input $x$, and after the protocol the receiver learns $F_k(x)$ while the sender learns nothing about $x$. The receiver gets the PRF output without giving up the input; the sender keeps the key without giving up the output. OPRFs are the building block under most modern PSI: each party evaluates the OPRF on their set, then plaintext-compares the outputs.
&lt;p&gt;In 2016, Vladimir Kolesnikov, Ranjit Kumaresan, Mike Rosulek, and Ni Trieu published &quot;Efficient Batched Oblivious PRF with Applications to Private Set Intersection&quot; at CCS 2016 [@kkrt-2016]. The paper builds a batched OPRF directly on top of KOS-style OT extension. The reported benchmark: intersecting two $2^{20}$-element sets on a LAN took about 3.8 seconds total. For several years, KKRT16 was the deployment-grade symmetric-PSI protocol.&lt;/p&gt;
&lt;p&gt;KKRT16 is great if your two sets are roughly the same size. The Edge Password Monitor problem is fundamentally asymmetric -- the client holds a few hundred saved passwords, the server holds billions of breached credentials. For &lt;em&gt;asymmetric&lt;/em&gt; PSI, the OT-extension lineage hits a wall the next generation has to climb.&lt;/p&gt;

flowchart LR
    A[&quot;Kappa base OTs&lt;br /&gt;(public-key)&quot;] --&amp;gt; B[&quot;IKNP03 extension&lt;br /&gt;(symmetric)&quot;]
    B --&amp;gt; C[&quot;KOS15 active security&quot;]
    C --&amp;gt; D[&quot;Silent OT&lt;br /&gt;(PCG-based)&quot;]
    B --&amp;gt; E[&quot;KKRT16 OPRF-PSI&quot;]
    C --&amp;gt; F[&quot;CHLR18 HE-PSI&lt;br /&gt;+ OPRF wrapping&quot;]
    D --&amp;gt; G[&quot;Modern OT-extension libraries:&lt;br /&gt;libOTe, EMP-toolkit&quot;]
    E --&amp;gt; H[&quot;Symmetric balanced PSI&quot;]
    F --&amp;gt; I[&quot;Edge Password Monitor&quot;]
&lt;h2&gt;5. The breakthrough: HE-based PSI&lt;/h2&gt;
&lt;p&gt;Asymmetric PSI requires that the server&apos;s heavy compute stays on the server, and that the client send only a tiny encrypted query whose size is independent of $|S_B|$. That is exactly what fully homomorphic encryption -- or, more precisely, &lt;em&gt;somewhat-homomorphic&lt;/em&gt; encryption -- can offer.&lt;/p&gt;

An encryption scheme is *homomorphic* if operations on ciphertexts decrypt to the corresponding operations on plaintexts. *Somewhat-homomorphic encryption* (SWHE) supports a bounded depth of operations (typically additions and multiplications) before noise growth requires re-encryption. *Fully homomorphic encryption* (FHE) supports arbitrary-depth circuits via bootstrapping. The BFV scheme (Brakerski-Fan-Vercauteren), implemented in Microsoft SEAL [@ms-seal], is the SWHE variant Edge Password Monitor uses; FHE is the popular term but the actual deployed circuit depth fits comfortably within SWHE.
&lt;h3&gt;CLR17: the cost curve flips&lt;/h3&gt;
&lt;p&gt;In 2017, Hao Chen, Kim Laine, and Peter Rindal published &quot;Fast Private Set Intersection from Homomorphic Encryption&quot; at CCS 2017 [@clr-2017]. The construction is a clean revival of FNP04&apos;s polynomial-roots idea, with three engineering moves that fix every reason FNP04 was infeasible.&lt;strong&gt;Move 1: SWHE instead of Paillier.&lt;/strong&gt; BFV ciphertexts pack many plaintext slots and support SIMD-style homomorphic operations. A single ciphertext can encrypt and evaluate over thousands of plaintext values in parallel; the slot count is set at scheme-parameter time.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Move 2: Cuckoo hash partitioning.&lt;/strong&gt; The receiver Cuckoo-hashes its set $S_R$ into bins. The sender hashes each element of $S_S$ to the same set of bins. Instead of one giant polynomial whose roots are all of $S_S$, the sender builds one small polynomial per bin -- typically a few thousand bins, each holding a few hundred elements.&lt;/p&gt;

A hashing scheme that uses $k$ hash functions and inserts each element into one of $k$ candidate bins, displacing existing occupants if necessary (the displaced element finds another of its candidate bins). Cuckoo hashing achieves $O(1)$ worst-case lookup; the achievable load factor depends on the number of hash functions and the bucket size -- roughly 49% with $k=2$ (the original Pagh-Rodler 2001 construction [@pagh-rodler-2001]), roughly 91% with $k=3$, and higher with a stash of evicted elements or $k \geq 4$. CLR17 and CHLR18 use parameter choices in the high-load regime. In CLR17, Cuckoo hashing pairs the receiver&apos;s set with the sender&apos;s set so that two equal elements end up in the same bin with overwhelming probability.
&lt;p&gt;&lt;strong&gt;Move 3: Partition-and-evaluate.&lt;/strong&gt; The receiver encrypts its bins under BFV and sends them. The sender homomorphically evaluates its per-bin polynomial at the encrypted receiver&apos;s bin. Because of SIMD slot packing, each bin&apos;s polynomial is evaluated in parallel across all slots, and the sender&apos;s total work is $O(|S_S| \log |S_R|)$ FHE operations instead of FNP04&apos;s $O(|S_R| \cdot |S_S|)$.&lt;/p&gt;
&lt;p&gt;The headline benchmark from the paper, on the MSR publication page: &quot;36 seconds of online-computation and 12.5 MB of round trip communication to intersect five thousand 32-bit strings with 16 million 32-bit strings&quot; [@msr-clr17-pub]. Communication scales linearly in the small set and logarithmically in the large set. The cost curve is finally right.&lt;/p&gt;

sequenceDiagram
    participant C as Client (Edge)
    participant S as Server (Microsoft)
    Note over C,S: stage 1 -- OPRF preprocessing (binds queries to server&apos;s secret key)
    C-&amp;gt;&amp;gt;S: blinded credential beta * H(cred)
    S-&amp;gt;&amp;gt;C: alpha * (blinded H(cred)) using server key alpha
    C-&amp;gt;&amp;gt;C: unblind, obtain F_alpha(cred)
    Note over C,S: stage 2 -- HE-PSI on sharded corpus
    C-&amp;gt;&amp;gt;S: BFV ciphertext encrypting F_alpha(cred), sharded by 2-byte prefix
    S-&amp;gt;&amp;gt;S: Cuckoo-hash shard, evaluate per-bin polynomial homomorphically
    S-&amp;gt;&amp;gt;C: encrypted match result + label ciphertext
    C-&amp;gt;&amp;gt;C: decrypt result, then if match surface breach metadata
&lt;h3&gt;CHLR18: labeled, malicious, deployable&lt;/h3&gt;
&lt;p&gt;The next year, the same authors plus Zhicong Huang published &quot;Labeled PSI from Fully Homomorphic Encryption with Malicious Security&quot; at CCS 2018 [@chlr-2018]. The paper adds three production-grade properties.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Labels.&lt;/strong&gt; Each element in the server&apos;s set can carry an associated label (which breach, when, severity). When the receiver finds a match, they also recover the label.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Malicious security.&lt;/strong&gt; The protocol is secure against an actively malicious sender, layered on top of the underlying semi-honest construction via an OPRF preprocessing step. The OPRF is the same primitive we met in §4; here it does double duty: it prevents the client from brute-forcing the server&apos;s corpus offline (the client cannot evaluate $F_k(\cdot)$ without server interaction) and provides the malicious-security guarantee.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Arbitrary-length items.&lt;/strong&gt; The protocol handles long inputs (full URLs plus usernames, in the breach-checking case), not just short fixed-width keys.&lt;/p&gt;
&lt;p&gt;The headline benchmark: &quot;for an intersection of $2^{20}$ and 512 size sets of arbitrary length items our protocol has a total online running time of just 1 second (single thread), and a total communication cost of 4 MB&quot; [@msr-chlr18-pub]. A larger benchmark of $2^{28}$ and 1024 takes 12 seconds multithreaded with less than 18 MB of communication.&lt;/p&gt;
&lt;h3&gt;Cong et al. 2021: the production-grade successor&lt;/h3&gt;
&lt;p&gt;The protocol that ships in Edge Password Monitor today is the descendant published by Kelong Cong, Radames Cruz Moreno, Mariana Botelho da Gama, Wei Dai, Ilia Iliashenko, Kim Laine, and Michael Rosenberg at CCS 2021: &quot;Labeled PSI from Homomorphic Encryption with Reduced Computation and Communication&quot; [@cong-2021]. The paper is the basis for Microsoft&apos;s open-source APSI library [@ms-apsi], whose README states verbatim that it &quot;provides a PSI functionality for asymmetric set sizes based on the protocol described in eprint.iacr.org/2021/1116&quot; and that it &quot;uses the BFV encryption scheme implemented in the Microsoft SEAL library.&quot;The Cong et al. 2021 byline is seven authors: Kelong Cong, Radames Cruz Moreno, Mariana Botelho da Gama, Wei Dai, Ilia Iliashenko, Kim Laine, Michael Rosenberg. Some upstream reporting conflates a different author list onto the same URL; the citation_author meta-tags returned by ePrint 2021/1116 confirm this seven-author septuple [@cong-2021].&lt;/p&gt;
&lt;h3&gt;The OPRF wrapping and corpus sharding&lt;/h3&gt;
&lt;p&gt;Two practical layers on top of the bare HE-PSI protocol turn the academic construction into a production deployment, and the Microsoft Research Password Monitor blog is explicit about both [@msr-password-monitor-2021].&lt;/p&gt;
&lt;p&gt;First, the OPRF preprocessing. Without it, a malicious client could send candidate passwords one at a time and observe match results, brute-forcing the server&apos;s corpus. With it, every client query passes through $F_k(\cdot)$ where $k$ is a server secret. The MSR blog states: &quot;the client communicates with the server to obtain a hash $H$ of the credential, where $H$ denotes a hash function that only the server knows... using an OPRF... the client is prevented from performing an efficient dictionary attack on the server&quot; [@msr-password-monitor-2021].&lt;/p&gt;
&lt;p&gt;Second, corpus sharding. The MSR blog notes that the corpus is sharded by the first two bytes of a username-hash. The blog&apos;s verbatim example: &quot;Suppose the database $D$ consists of 4 billion credentials, then after sharding each subset, it will contain about 60,000 credentials on average.&quot; At the article&apos;s 2026 5-billion projection the math is the same -- corpus divided by $2^{16}$ -- and per-shard work is closer to 76,000 credentials. Either way, the per-query homomorphic evaluation runs against tens of thousands of credentials instead of the full corpus. This is the same engineering trade as Apple&apos;s 15-bit bucketing -- a small information leak (the client reveals which shard their query lives in) in exchange for tractable per-query compute.&lt;/p&gt;

A Windows facility, introduced in Windows 2000, that encrypts arbitrary blobs under a user-derived key chain (ultimately rooted in the user&apos;s password, with hardware-bound variants under DPAPI-NG) and exposes a simple `CryptProtectData` / `CryptUnprotectData` API [@ms-learn-dpapi-ng]. Browsers including Chromium store the symmetric key that wraps their saved-password database under DPAPI at rest. This protects the on-disk database when the user is not logged in, but it does not protect process memory after the same user has unwrapped the data into a running browser.

This unique security feature is possible due to pioneering cryptography research and technology incubation done here at Microsoft Research. -- Microsoft Research, January 21, 2021 [@msr-password-monitor-2021]
&lt;p&gt;The administrator-visible group policy that controls this feature is &lt;code&gt;PasswordMonitorAllowed&lt;/code&gt;, documented on Microsoft Learn [@ms-learn-edge-pm].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Asymmetric PSI on somewhat-homomorphic encryption flips the cost curve so that communication scales with the small client set, not the enormous server set. That is why a homomorphic-encryption protocol can ship on a consumer browser in 2021 without melting the user&apos;s CPU. The cryptographic case for Edge Password Monitor is auditable down to the published papers and unequivocally well-engineered.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft has shipped the first production consumer homomorphic-encryption deployment in a browser, against the threat &quot;server-side breach corpus leakage,&quot; on the same browser that, in §7, will turn out to hold every saved credential in plaintext RAM the entire time you have it open. To make sense of that contrast, we need to see what the rest of the industry did with the same problem.&lt;/p&gt;
&lt;h2&gt;6. State of the art: four deployed compromised-credential services&lt;/h2&gt;
&lt;p&gt;PSI on paper is one thing. PSI in production is another. The &quot;pure PSI&quot; ideal -- both parties learn the intersection and &lt;em&gt;nothing else&lt;/em&gt;, no information leaks on either side -- is impractical at planetary scale. Every deployed compromised-credential service in 2026 makes a concession somewhere.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The concessions reveal which threats each provider takes most seriously. Read the next table by column 3 (&quot;what is revealed on the wire&quot;) and column 6 (&quot;dictionary-attack hardening&quot;) side by side: that pair tells you the threat model each provider chose.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The four services compared here are HIBP Pwned Passwords v3, Google Password Checkup, Apple Password Monitoring (iCloud Keychain), and Microsoft Edge Password Monitor. A fifth, Signal contact discovery, is technically a contact-discovery service rather than a breach checker, but it sits on the same protocol map and is the canonical &quot;we used a TEE instead of pure crypto&quot; data point.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Protocol family&lt;/th&gt;
&lt;th&gt;What&apos;s revealed on the wire&lt;/th&gt;
&lt;th&gt;Server trust&lt;/th&gt;
&lt;th&gt;Bandwidth at scale&lt;/th&gt;
&lt;th&gt;Dictionary-attack hardening&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;HIBP Pwned Passwords v3 [@hibp-api-v3]&lt;/td&gt;
&lt;td&gt;Pure SHA-1 5-hex-char k-anonymity&lt;/td&gt;
&lt;td&gt;A 20-bit hash prefix per query&lt;/td&gt;
&lt;td&gt;None (zero-trust API)&lt;/td&gt;
&lt;td&gt;Trivial (a few KB per query)&lt;/td&gt;
&lt;td&gt;None on the wire; SHA-1 hash makes corpus searchable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Password Checkup [@thomas-usenix-2019]&lt;/td&gt;
&lt;td&gt;k-anonymity + blinded-hash OPRF&lt;/td&gt;
&lt;td&gt;A small hash prefix per query&lt;/td&gt;
&lt;td&gt;Honest-but-curious&lt;/td&gt;
&lt;td&gt;Tens of KB per query&lt;/td&gt;
&lt;td&gt;OPRF prevents corpus enumeration by the client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple Password Monitoring [@apple-password-monitoring]&lt;/td&gt;
&lt;td&gt;EC-based PSM on NIST P-256 + 15-bit bucket&lt;/td&gt;
&lt;td&gt;A 15-bit prefix + double-blinded EC point&lt;/td&gt;
&lt;td&gt;Honest-but-curious&lt;/td&gt;
&lt;td&gt;A few hundred KB per query (padded)&lt;/td&gt;
&lt;td&gt;OPRF + double-blinding + padding-to-fixed-count&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signal contact discovery (2017) [@signal-private-contact-2017]&lt;/td&gt;
&lt;td&gt;SGX enclave + ORAM (no pure crypto)&lt;/td&gt;
&lt;td&gt;Nothing visible to Signal staff&lt;/td&gt;
&lt;td&gt;TEE attestation&lt;/td&gt;
&lt;td&gt;Negligible (single SGX RPC)&lt;/td&gt;
&lt;td&gt;Enclave isolation rather than crypto hardening&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Microsoft Edge Password Monitor [@msr-password-monitor-2021]&lt;/td&gt;
&lt;td&gt;HE-PSI on Microsoft SEAL + OPRF + 2-byte shard&lt;/td&gt;
&lt;td&gt;2-byte username-hash prefix + BFV ciphertext&lt;/td&gt;
&lt;td&gt;Honest-but-curious&lt;/td&gt;
&lt;td&gt;Single MB-range round trip&lt;/td&gt;
&lt;td&gt;OPRF binds queries to server key; HE prevents transcript leaks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;h3&gt;HIBP Pwned Passwords v3: the k-anonymity baseline&lt;/h3&gt;
&lt;p&gt;Troy Hunt launched Pwned Passwords v2 in 2018 with the help of Junade Ali at Cloudflare, adding a k-anonymity API; the design is published in two posts, Hunt&apos;s &quot;I&apos;ve Just Launched Pwned Passwords Version 2&quot; and Ali&apos;s &quot;Validating Leaked Passwords with k-Anonymity&quot; [@hunt-pwned-v2-2018][@ali-cloudflare-2018]. The protocol is delightfully simple: the client hashes the candidate password under SHA-1, sends the first 5 hex characters (20 bits) of the hash to the API, and the API returns every suffix in that bucket. The client compares locally.&lt;/p&gt;

A privacy property: each query produces output that is consistent with at least $k$ other potential queries the client could have made. In the HIBP context, $k$ is the number of distinct password hashes that share the same 20-bit SHA-1 prefix -- typically a few hundred. The server learns the bucket but not which specific password the client cares about, and (because hashes are sparse over the prefix space) cannot easily distinguish &quot;the user has password X&quot; from &quot;the user has password Y&quot; if X and Y share the prefix.
&lt;p&gt;The HIBP corpus serves &quot;18B+ Monthly Requests&quot; against roughly a billion hashes [@hibp-passwords]. Operationally, this is a one-shot HTTP GET. There is no PSI on the wire beyond TLS. The whole protocol fits on the back of an envelope. The cost: each query leaks the 20-bit prefix, which is enough to identify the user&apos;s password if the attacker has independent information narrowing the candidate space.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;async function sha1Hex(s) {   const buf = new TextEncoder().encode(s);   const hash = await crypto.subtle.digest(&quot;SHA-1&quot;, buf);   return [...new Uint8Array(hash)].map(b =&amp;gt; b.toString(16).padStart(2, &quot;0&quot;)).join(&quot;&quot;).toUpperCase(); } async function showBucket(password) {   const h = await sha1Hex(password);   const prefix = h.slice(0, 5);    // 5 hex chars -- 20 bits sent to server   const suffix = h.slice(5);   console.log(&quot;Password:&quot;, password);   console.log(&quot;SHA-1:   &quot;, h);   console.log(&quot;Prefix (leaves your device):&quot;, prefix);   console.log(&quot;Suffix (compared locally):  &quot;, suffix);   console.log(&quot;Approx bucket size: ~&quot;, Math.round(847_223_402 / (1&amp;lt;&amp;lt;20)), &quot;entries&quot;); } showBucket(&quot;hunter2&quot;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;The mental model the runnable above gives is the precise shape of the trade. Every HIBP query says &quot;I am asking about a password whose SHA-1 starts with these 20 bits.&quot; There are roughly $2^{20} \approx 1{,}048{,}576$ possible prefixes, so each query narrows the server&apos;s posterior over your password by exactly that much.&lt;/p&gt;
&lt;h3&gt;Google Password Checkup: k-anonymity with an OPRF on top&lt;/h3&gt;
&lt;p&gt;In August 2019, Kurt Thomas and colleagues at Google published &quot;Protecting Accounts from Credential Stuffing with Password Breach Alerting&quot; at USENIX Security [@thomas-usenix-2019]. The accompanying blog post on the Google Security Blog announces the deployment [@google-blog-password-checkup-2019]. The numbers are familiar at this point: &quot;a cloud service that mediates access to over 4 billion credentials found in breaches and a Chrome extension serving as an initial client. Based on anonymous telemetry from nearly 670,000 users and 21 million logins, we find that 1.5% of logins on the web involve breached credentials&quot; [@thomas-usenix-2019].&lt;/p&gt;
&lt;p&gt;The protocol upgrades HIBP&apos;s k-anonymity baseline with an OPRF preprocessing round: instead of hashing under SHA-1 locally and sending the prefix, the client first obtains $F_k(\text{password})$ via an OPRF interaction, where $k$ is a Google-held key. The OPRF output is then bucketed and matched against Google&apos;s corpus. The OPRF prevents the client from enumerating Google&apos;s corpus offline; the bucketing limits per-query server work.&lt;/p&gt;
&lt;h3&gt;Apple Password Monitoring: PSM with double-blinding&lt;/h3&gt;
&lt;p&gt;Apple&apos;s protocol is the most cryptographically elaborate of the four. The Apple Platform Security guide is unusually explicit [@apple-password-monitoring][@apple-security-guide-pdf]. From the guide, verbatim: &quot;a form of cryptographic private set intersection is deployed that compares the users&apos; passwords against a large set of leaked passwords&quot;; the corpus is &quot;approximately 1.5 billion passwords... into $2^{15}$ different buckets&quot;; and the protocol uses elliptic-curve PSM on NIST P-256 with a double-blinded structure.&lt;/p&gt;
&lt;p&gt;The math, slightly compressed. Let $H_{\text{SWU}}$ be the Shallue-van de Woestijne-Ulas hash-to-curve.&lt;/p&gt;
&lt;p&gt;Google publishes an open-source PSM construction at &lt;code&gt;google/private-membership&lt;/code&gt; [@google-private-membership-github]; Apple&apos;s protocol shares the EC double-blinding skeleton. Apple computes a per-corpus-element representation:&lt;/p&gt;
&lt;p&gt;$$P_{pw} = \alpha \cdot H_{\text{SWU}}(pw)$$&lt;/p&gt;
&lt;p&gt;where $\alpha$ is a secret random key known only to Apple. The client computes its query:&lt;/p&gt;
&lt;p&gt;$$P_c = \beta \cdot H_{\text{SWU}}(pw)$$&lt;/p&gt;
&lt;p&gt;with $\beta$ chosen randomly per-query. The interaction lets the client recover $\alpha \cdot H_{\text{SWU}}(pw)$ and check it against a 15-bit bucket of $P_{pw}$ values. (Apple&apos;s public documentation hashes only the password; whether the production implementation includes additional salting is not disclosed.) The double-blinding is the point: $\alpha$ stays Apple&apos;s secret (so the client cannot enumerate); $\beta$ stays per-query random (so Apple cannot link two queries from the same client).Apple&apos;s PSM defends &lt;em&gt;also&lt;/em&gt; against the server learning how many unique passwords a user has, by padding-to-fixed-count with random queries: &quot;if a user has fewer than this number, random passwords are generated and added to the queries to make up the difference&quot; [@apple-password-monitoring]. None of the other four services deploys this defence. The padding cost is the price.&lt;/p&gt;
&lt;h3&gt;Signal contact discovery: the TEE outlier&lt;/h3&gt;
&lt;p&gt;In September 2017, Moxie Marlinspike published &quot;Technology Preview: Private Contact Discovery&quot; on Signal&apos;s blog [@signal-private-contact-2017]. The post is candid about the cost calculation that drove Signal toward Intel SGX rather than pure cryptographic PSI:&lt;/p&gt;

Signal clients will be able to efficiently and scalably determine whether the contacts in their address book are Signal users *without revealing the contacts in their address book to the Signal service*. -- Moxie Marlinspike, September 2017 [@signal-private-contact-2017]
&lt;p&gt;Marlinspike is explicit about the cost calculation: &quot;Doing better is difficult. There are a range of options that don&apos;t work... like using bloom filters, encrypted bloom filters, sharded bloom filters.&quot; Signal examines pure cryptographic PSI and decides, given its scale and latency requirements, that an SGX enclave running a constant-time ORAM-protected lookup is the better engineering trade [@signal-cds-github].&lt;/p&gt;

The SGX choice came with side-channel debt that subsequent literature made expensive. Foreshadow (2018) [@foreshadow-2018], SgxPectre (2018) [@sgxpectre-2018], SGAxe (2020) [@sgaxe-2020], and AEPIC Leak (2022) [@aepic-leak-2022] all targeted SGX directly. Each disclosure prompted Signal to publish a re-evaluation. Signal eventually migrated to the second-generation Contact Discovery Service (CDSI), which continues to rely on TEEs but with a hardened threat model. The point for our story is not that SGX is bad. It is that &quot;pure crypto vs. TEE&quot; is not a settled question; every provider revisits it under their own latency, corpus, and threat-model constraints, and each makes a different decision.
&lt;h3&gt;Microsoft Edge Password Monitor&lt;/h3&gt;
&lt;p&gt;The Microsoft deployment is the only one shipping a full HE-PSI protocol on the wire against the full corpus. As established in §5, the protocol stack is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Two-byte username-hash shard selection (corpus partitioned, sender does work only against tens of thousands of elements per query -- the MSR blog&apos;s 2021 example uses 4 billion / $2^{16} \approx$ 60,000; the article&apos;s 2026 5-billion projection yields $\approx$ 76,000).&lt;/li&gt;
&lt;li&gt;OPRF preprocessing (binds queries to a server secret; prevents client-side enumeration).&lt;/li&gt;
&lt;li&gt;BFV-encrypted query, evaluated against the Cuckoo-hashed per-bin polynomials, returned as a single ciphertext per shard.&lt;/li&gt;
&lt;li&gt;Client decrypts; if matched, decrypts the associated label (the breach metadata).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All four moving parts are described in the MSR blog, the Cong et al. 2021 paper, and the open-source APSI library [@msr-password-monitor-2021][@cong-2021][@ms-apsi]. Communication scales with the small client set; sender compute scales with the (sharded) server set. The Microsoft Edge enterprise documentation says the feature &quot;helps Microsoft Edge users protect their online accounts by informing them if any of their passwords are found in an online leak&quot; [@ms-learn-edge-pm].&lt;/p&gt;
&lt;h3&gt;Four products, four concessions&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Primary concession&lt;/th&gt;
&lt;th&gt;What it defends against&lt;/th&gt;
&lt;th&gt;What it does not&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;HIBP&lt;/td&gt;
&lt;td&gt;20-bit prefix leak per query&lt;/td&gt;
&lt;td&gt;Server learning the password&lt;/td&gt;
&lt;td&gt;A linkage attack on repeated queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google PCU&lt;/td&gt;
&lt;td&gt;OPRF transcript + prefix&lt;/td&gt;
&lt;td&gt;Client-side corpus enumeration&lt;/td&gt;
&lt;td&gt;Server-side query inference if the prefix is rare&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apple PSM&lt;/td&gt;
&lt;td&gt;15-bit bucket + double-blinding overhead&lt;/td&gt;
&lt;td&gt;Both client and server enumeration; query linkage&lt;/td&gt;
&lt;td&gt;Side-channels on the EC implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Signal CDS&lt;/td&gt;
&lt;td&gt;TEE attestation trust&lt;/td&gt;
&lt;td&gt;Server-side mass-data exfiltration&lt;/td&gt;
&lt;td&gt;SGX side-channel attacks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edge PM&lt;/td&gt;
&lt;td&gt;2-byte shard leak + OPRF transcript&lt;/td&gt;
&lt;td&gt;Anything short of breach corpus leakage from inside Microsoft&lt;/td&gt;
&lt;td&gt;Endpoint compromise -- see §7&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Four products. Four different concessions. Each is internally coherent. None of them solves a problem that Tom Joran Sonstebyseter Ronning&apos;s PoC will turn out to make trivial.&lt;/p&gt;
&lt;h2&gt;7. The other half: plaintext RAM &quot;by design&quot;&lt;/h2&gt;
&lt;p&gt;Now we turn the article inside out.&lt;/p&gt;
&lt;p&gt;Everything we have built so far -- IKNP, KOS, KKRT, CLR17, CHLR18, Cong et al., Apple&apos;s PSM, Google&apos;s k-anonymity wrapping, the entire CCS-grade cryptographic stack inside Edge -- assumes the &lt;em&gt;endpoint&lt;/em&gt; is trustworthy. The question Ronning asked on May 4, 2026 is what happens when it is not.&lt;/p&gt;
&lt;h3&gt;The disclosure&lt;/h3&gt;
&lt;p&gt;The X post arrived at 14:29:51 UTC on May 4, 2026 [@ronning-x]: &quot;Microsoft Edge loads all your saved passwords into memory in cleartext -- even when you&apos;re not using them.&quot; Five hours later the GitHub repository went public, with a complete C# proof-of-concept and a long README [@ronning-github]. PCWorld picked up the story two days later under the byline of Laura Pippig, summarising Microsoft&apos;s response in English [@pcworld-pippig-2026]. The Norwegian origin, ITavisen, carried the verbatim &quot;by design&quot; rendering and named Ronning&apos;s affiliation with the transmission-system operator Statnett [@itavisen-2026]. Davey Winder at Forbes reached Microsoft and obtained the official spokesperson statement on May 6, 2026 [@forbes-winder].Ronning works at Statnett and presented this finding at BigBiteOfTech (Palo Alto Networks Norway) on April 29, 2026 -- five days before the public X-post disclosure [@itavisen-2026]. The X bio describes &quot;#PenetrationTesting using only tools that are already on the system.&quot;&lt;/p&gt;
&lt;h3&gt;What the PoC does&lt;/h3&gt;
&lt;p&gt;The C# program is short. &lt;code&gt;OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, ...)&lt;/code&gt; against the parent &lt;code&gt;msedge.exe&lt;/code&gt;. A walk over committed memory regions via &lt;code&gt;VirtualQueryEx&lt;/code&gt;. &lt;code&gt;ReadProcessMemory&lt;/code&gt; reads of every region. Pattern matching for stored credential structures. Output: every saved Edge password as plaintext.&lt;/p&gt;
&lt;p&gt;The README is explicit on the constraints. &quot;Can be run without Adminstrator rights, but will only be able to access Edge processes ran by the same user. If run with Administrator privileges, the program can access and read memory from other users&apos; Edge processes on the same machine&quot; [@ronning-github]. No kernel exploit. No DPAPI bypass. The DPAPI unwrap happened earlier, when Edge launched (or when the password feature first activated); the cleartext has been sitting in &lt;code&gt;msedge.exe&lt;/code&gt;&apos;s heap ever since.&lt;/p&gt;
&lt;p&gt;The tested target is Edge 147.0.3912.98, but the README explicitly generalises: &quot;Any Edge versions that&apos;s Chromium based (from version 79 and newer, including 147.0.3912.98 and any future version, as Microsoft won&apos;t change this feature)&quot; [@ronning-github].&lt;/p&gt;
&lt;h3&gt;What the architectural choice is&lt;/h3&gt;
&lt;p&gt;The behaviour Ronning identifies is not a memory-safety bug. It is a design choice: Edge unwraps every DPAPI-protected saved credential into process memory when the password manager activates, and keeps the plaintext resident for the lifetime of the session.&lt;/p&gt;

flowchart TD
    A[&quot;Saved password file&lt;br /&gt;(SQLite blob)&quot;] --&amp;gt;|&quot;DPAPI-wrapped at rest&quot;| B[&quot;Disk: encrypted with&lt;br /&gt;per-user DPAPI key&quot;]
    B --&amp;gt;|&quot;msedge.exe launches&quot;| C[&quot;DPAPI CryptUnprotectData()&lt;br /&gt;unwraps key&quot;]
    C --&amp;gt;|&quot;password manager activates&quot;| D[&quot;Plaintext credentials&lt;br /&gt;resident in msedge.exe heap&quot;]
    D --&amp;gt;|&quot;OpenProcess + ReadProcessMemory&lt;br /&gt;(same-user, no admin)&quot;| E[&quot;EdgeSavedPasswordsDumper&lt;br /&gt;reads cleartext&quot;]
    D --&amp;gt;|&quot;autofill flow&quot;| F[&quot;Plaintext copied into&lt;br /&gt;renderer / web form&quot;]
    classDef warn fill:#7a3030,stroke:#a04848,color:#fce8e8
    class D warn,stroke:#c33
    classDef accent fill:#5d3a5d,stroke:#8a5a8a,color:#fde0fd
    class E accent,stroke:#939
&lt;p&gt;Chrome and Brave do not do this. Both browsers decrypt credentials only at the autofill RPC -- the password manager fetches the DPAPI-wrapped blob, decrypts in a narrow window, hands the plaintext to the relevant renderer, and zeroes the buffer [@chromium-os-crypt]. PCWorld corroborates: &quot;Other password managers, including those that are built into browsers, don&apos;t operate in this way -- Ronning says Edge is the only Chromium-based browser he&apos;s tested with this behavior&quot; [@pcworld-pippig-2026].&lt;/p&gt;
&lt;p&gt;In July 2024, Google announced Chrome App-Bound Encryption -- a further hardening of exactly this same-user-LCE threat model. ABE binds the on-disk key unwrap to a verified Chrome process identity, so a malicious program impersonating Chrome cannot ask DPAPI to unwrap Chrome&apos;s data even if it runs as the same user [@google-chrome-abe-2024]. Microsoft has the same DPAPI substrate; Edge has not adopted the equivalent control.&lt;/p&gt;
&lt;h3&gt;The .NET runtime tie-in to AMSI&lt;/h3&gt;
&lt;p&gt;The PoC&apos;s original implementation language is a noteworthy detail. Ronning&apos;s README states: &quot;.NET Framework 4.8.1 (changed from 3.5 originally)&quot; [@ronning-github]. The original .NET 3.5 choice was deliberate. The Antimalware Scan Interface (AMSI) [@ms-amsi-portal] scans .NET 4.8+ assemblies before execution; .NET 3.5 predates AMSI&apos;s &lt;code&gt;Amsi*&lt;/code&gt; API surface entirely [@ms-amsi-dotnet48].The current GitHub README has changed the framework version to .NET 4.8.1 (likely to ensure the PoC runs out-of-the-box on modern Windows), but the original framing -- and the original threat-model point -- was the AMSI evasion that .NET 3.5 enables. The sibling AMSI post in this series explains why the 3.5 framing matters.&lt;/p&gt;
&lt;h3&gt;Microsoft&apos;s response, verbatim&lt;/h3&gt;

Safety and security are foundational to Microsoft Edge. Access to browser data as described in the reported scenario would require the device to already be compromised. Design choices in this area involve balancing performance, usability, and security, and we continue to review it against evolving threats. Browsers access password data in memory to help users sign in quickly and securely -- this is an expected feature of the application. We recommend users install the latest security updates and antivirus software to help protect against security threats. -- Microsoft spokesperson, via Forbes, May 6, 2026 [@forbes-winder]

The statement is technically defensible. The threat model is exactly what the spokesperson says: an attacker who can already execute code as the user on the user&apos;s machine. In MSRC&apos;s published servicing criteria [@msrc-servicing-criteria], &quot;exploitation requires local code execution&quot; is a recurring boundary line -- the same line MSRC applied to Mimikatz against LSASS in the pre-Credential-Guard era, and that Microsoft eventually crossed by shipping [Credential Guard](/blog/the-empty-hash-credential-guard-the-lsaiso-trustlet-and-the-/). The &quot;by design&quot; framing is consistent with a decade of precedent.
Microsoft&apos;s response is internally consistent with a decade of MSRC policy. The &quot;exploitation requires local code execution&quot; framing was applied to Mimikatz against LSASS for years before Credential Guard arrived. It is applied to &quot;give me a debugger and I can read anything&quot; type attacks generally. The position is not improvised, and it is not a special accommodation for Edge. The question this article asks is not &quot;is the position internally consistent&quot; -- it is -- but &quot;what threat model does the position concede.&quot; The answer is the same-user local-code-execution threat model. Whether the concession is acceptable depends on whether the user&apos;s environment makes same-user LCE rare (a single-user Surface) or routine (a Citrix farm, an AVD pool, a shared family computer).
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you operate a multi-user Windows host -- RDS, AVD, Citrix, a shared lab, a family PC with multiple sign-in accounts -- every Edge session&apos;s saved credentials are recoverable by any same-user process during that session, and by an administrator across sessions. The &quot;the device is already compromised&quot; framing is asymmetric: a same-user LCE event on a multi-user host is structurally more common than on a single-user laptop, because there are more identities sharing the same physical machine.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Edge does not have a credential-storage vulnerability. Edge has a credential-storage architectural choice. The choice is to spend the entire browser session&apos;s worth of plaintext-in-RAM budget on autofill UX latency. The choice is defensible. It is also a precise statement of which threats the Edge product team is and is not defending against.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Microsoft&apos;s response is technically defensible. It is also a precise statement of which threat model the Edge product team is and is not defending against. To see why both halves of this article describe the same product, we need to look at the architectural alternatives.&lt;/p&gt;
&lt;h2&gt;8. Competing approaches: where should the secret store live?&lt;/h2&gt;
&lt;p&gt;Three architectural positions present themselves, as siblings rather than as a generational ladder:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Browser-as-secret-store, decrypt-on-launch&lt;/strong&gt; (Edge today). Plaintext-in-RAM window: the entire session. Autofill latency: a memcpy.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Browser-as-secret-store, decrypt-on-autofill&lt;/strong&gt; (Chrome, Brave). Plaintext-in-RAM window: the autofill RPC. Autofill latency: one DPAPI unwrap per fill (microseconds).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OS-as-secret-broker&lt;/strong&gt; (the design the Windows Credential Manager and DPAPI-NG already implement for native apps). Plaintext never crosses into the browser&apos;s process; a higher-privileged broker holds the plaintext at autofill time and the browser receives a handle, not the secret.&lt;/li&gt;
&lt;/ol&gt;

flowchart LR
    subgraph &quot;Decrypt-on-launch (Edge)&quot;
        A1[Disk: DPAPI blob] --&amp;gt; A2[Browser process&lt;br /&gt;plaintext, full session]
        A2 --&amp;gt; A3[Renderer: autofill memcpy]
    end
    subgraph &quot;Decrypt-on-autofill (Chrome / Brave)&quot;
        B1[Disk: DPAPI blob] --&amp;gt; B2[Browser process&lt;br /&gt;plaintext, narrow RPC]
        B2 --&amp;gt; B3[Renderer: autofill]
    end
    subgraph &quot;OS-as-secret-broker&quot;
        C1[Disk: DPAPI-NG blob] --&amp;gt; C2[OS broker process&lt;br /&gt;plaintext only here]
        C2 -.handle.-&amp;gt; C3[Browser receives handle]
        C3 -.autofill via broker.-&amp;gt; C4[Renderer]
    end
&lt;h3&gt;Six-axis comparison&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Axis&lt;/th&gt;
&lt;th&gt;Decrypt-on-launch (Edge)&lt;/th&gt;
&lt;th&gt;Decrypt-on-autofill (Chrome, Brave)&lt;/th&gt;
&lt;th&gt;OS-as-broker&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Plaintext-in-RAM window&lt;/td&gt;
&lt;td&gt;Full session&lt;/td&gt;
&lt;td&gt;Autofill RPC (~ms)&lt;/td&gt;
&lt;td&gt;Never, in the browser process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Autofill latency&lt;/td&gt;
&lt;td&gt;Memcpy (nanoseconds)&lt;/td&gt;
&lt;td&gt;DPAPI unwrap (~10s of microseconds)&lt;/td&gt;
&lt;td&gt;IPC + broker policy check (~ms)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Same-user-LCE attack surface&lt;/td&gt;
&lt;td&gt;High (ReadProcessMemory exposes all)&lt;/td&gt;
&lt;td&gt;Low (must catch the RPC window)&lt;/td&gt;
&lt;td&gt;Negligible (no plaintext in the browser)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory-scraping forensics&lt;/td&gt;
&lt;td&gt;Trivial (any same-user dump works)&lt;/td&gt;
&lt;td&gt;Hard (must dump during fill)&lt;/td&gt;
&lt;td&gt;Impossible (no plaintext to dump)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sync UX with cloud account&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;Standard&lt;/td&gt;
&lt;td&gt;Standard (broker handles sync)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering cost to ship&lt;/td&gt;
&lt;td&gt;Already shipped&lt;/td&gt;
&lt;td&gt;Already shipped (Chromium baseline)&lt;/td&gt;
&lt;td&gt;High (broker IPC, signed code path, extension renegotiation)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The OS-broker position is not hypothetical. The Windows Credential Manager already provides this property for Windows-app credentials. &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;WebAuthn and passkeys&lt;/a&gt; provide it for sites that have adopted the standard. The DPAPI-NG protection descriptors include a &lt;code&gt;WEBCREDENTIALS=&lt;/code&gt; variant [@ms-learn-dpapi-ng][@ms-learn-dpapi-ng-descriptors]. &lt;a href=&quot;https://paragmali.com/blog/pluton-a-tpm-on-silicon-microsoft-can-patch/&quot; rel=&quot;noopener&quot;&gt;Pluton&lt;/a&gt;-anchored vTPM key unwrap provides a hardware-rooted broker substrate [@ms-learn-pluton]. Credential Guard&apos;s LSAISO trustlet is architecturally an isolated secret-broker for LSASS-derived secrets [@ms-learn-credential-guard].&lt;/p&gt;
&lt;p&gt;None of these primitives are wired into Edge&apos;s saved-passwords path. The engineering cost is non-trivial: the broker needs an IPC contract, the browser needs a signed-and-attested code path that talks to the broker, and the renderer extension API surface needs renegotiation. But the cost is finite, and the alternative is what Google has been shipping in Chrome since 2024 -- Chrome&apos;s App-Bound Encryption (see §7) is exactly a step toward the broker model, and Microsoft has the same DPAPI substrate but no equivalent control for Edge [@google-chrome-abe-2024].&lt;/p&gt;
&lt;h3&gt;What &quot;by design&quot; means structurally&lt;/h3&gt;
&lt;p&gt;Microsoft can take the &quot;by design&quot; position because they are not wrong about cryptography. They are right about the bound. No protocol can autofill plaintext into a child renderer without &lt;em&gt;some&lt;/em&gt; process in the chain holding plaintext at the moment of fill. The architectural question is &lt;em&gt;which&lt;/em&gt; process and &lt;em&gt;for how long&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Edge&apos;s answer: &quot;the parent browser process, for the entire session.&quot; Chrome and Brave&apos;s answer: &quot;the parent browser process, for the autofill RPC.&quot; The broker design&apos;s answer: &quot;a separate, higher-privileged process, for the autofill RPC, and never the browser at all.&quot;&lt;/p&gt;
&lt;p&gt;All three are valid points in the design space. The question is not &quot;which is right&quot; -- the answer depends on the user&apos;s environment -- but &quot;what should the default be for a 2026 consumer browser.&quot; The PSI half of the article shows Microsoft can choose the most demanding default when they want to. The endpoint half shows what default they chose here.&lt;/p&gt;
&lt;p&gt;To see why this is a structural property of the problem, not a Microsoft-specific gap, we need to look at the theoretical limits on both sides.&lt;/p&gt;
&lt;h2&gt;9. Theoretical limits&lt;/h2&gt;
&lt;p&gt;Two lower bounds, in parallel: one cryptographic (the PSI side), one architectural (the endpoint side).&lt;/p&gt;
&lt;h3&gt;PSI side: communication and computation lower bounds&lt;/h3&gt;
&lt;p&gt;The communication lower bound for PSI is folklore, used as the comparison baseline in Pinkas-Schneider-Zohner at USENIX Security 2014 [@psz-2014]. Informally: any PSI protocol must transmit at least $\Omega(\min(n_A, n_B) \cdot \kappa)$ bits, where $\kappa$ is the security parameter. The argument is information-theoretic: the receiver has to learn the intersection, which can have size up to $\min(n_A, n_B)$, and each element identifier needs $\Omega(\kappa)$ bits of representation to avoid collisions.&lt;/p&gt;
&lt;p&gt;Silent OT [@silent-ot-2019] meets this lower bound up to polylogarithmic factors in the symmetric balanced setting. HE-PSI in the asymmetric setting gets to $O(n_R \cdot \log n_S)$ ciphertexts via CLR17&apos;s partition-and-evaluate construction [@clr-2017], which is sublinear in $n_S$ -- the breakthrough that makes Edge Password Monitor practical.&lt;/p&gt;
&lt;p&gt;The computation lower bound on the sender side is $\Omega(n_S)$. The sender must, in the limit, touch each element of its set at least once per query. There is no way around this without trading correctness or privacy. Apple &quot;cheats&quot; by reducing the effective $n_S$: their 15-bit bucket cuts the per-query work to roughly $1.5\text{B} / 2^{15} \approx 46{,}000$ elements. Microsoft&apos;s two-byte shard cuts to roughly $5\text{B} / 2^{16} \approx 76{,}000$ elements. The lower bound applies &lt;em&gt;per shard&lt;/em&gt;, not per total corpus.&lt;/p&gt;
&lt;p&gt;The OT-extension lower bound is $\Omega(\kappa)$ base OTs per protocol session, with the bulk of the OT count amortised away by symmetric crypto. KOS15 meets this; Silent OT improves the wire constants further. By 2026, OT extension is essentially solved engineering.&lt;/p&gt;
&lt;h3&gt;Endpoint side: the &quot;no plaintext in process&quot; lower bound&lt;/h3&gt;
&lt;p&gt;The cryptographic side is comfortably tight. The endpoint side is much weirder.For a process $P$ to autofill a credential into a child form, &lt;em&gt;some component&lt;/em&gt; in the trust chain must hold the plaintext at the moment of fill. There are exactly three possible holders:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;$P$ itself.&lt;/strong&gt; The Edge design. Plaintext lives in the parent browser process throughout the session.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A child renderer $Q$.&lt;/strong&gt; The Chrome / Brave design. Plaintext crosses the parent-renderer boundary for the duration of the autofill RPC and gets zeroed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A separate higher-privileged broker $B$.&lt;/strong&gt; The OS-broker design. Plaintext lives in a sibling process that is harder to dump than the browser (in the limit, a PPL or a Credential-Guard-style trustlet).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No general cryptographic primitive lets a process &lt;em&gt;use&lt;/em&gt; a plaintext credential without ever &lt;em&gt;holding&lt;/em&gt; it. The plaintext is a value; the operations on it (paste-into-form, compute-HMAC-with-it, transmit-over-TLS-as-a-bearer-token) all require it in cleartext at some point. This is not a deficiency of any particular cryptosystem. It is the definition of &quot;use.&quot;&lt;/p&gt;
&lt;p&gt;The plaintext-RAM design Edge ships is not a cryptographic failure. It is a deliberate choice to spend the plaintext-in-RAM budget on UX latency. The escape hatch is &lt;em&gt;architectural&lt;/em&gt;: a hardware-isolated broker process. Pluton-anchored vTPM key unwrap [@ms-learn-pluton], Credential Guard&apos;s LSAISO pattern [@ms-learn-credential-guard], DPAPI-NG with the right protection descriptor [@ms-learn-dpapi-ng-descriptors] -- the OS primitives all exist.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; There is no cryptographic primitive that lets a process autofill plaintext without holding it. The only escape hatch is an architectural one: a higher-privileged broker. Microsoft already ships the broker primitives -- DPAPI-NG, Credential Guard, Pluton. They are not wired into Chromium.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The aha moment the rest of the article was built to deliver: the Ronning PoC is not a &quot;bug&quot; in any meaningful sense. The structural question is whether Microsoft should ship the architectural primitive -- which they already have, in DPAPI-NG and Credential Guard -- but have not wired into Chromium&apos;s password store. The &quot;by design&quot; response is technically true and politically convenient simultaneously. Both are correct readings.&lt;/p&gt;
&lt;p&gt;Both lower bounds are tight or near-tight today. The PSI side is essentially solved engineering; the endpoint side is essentially solved policy and unsolved deployment. The open questions are about which side we invest in next.&lt;/p&gt;
&lt;h2&gt;10. Open problems&lt;/h2&gt;
&lt;p&gt;Four open problems, framed as research directions Microsoft, Apple, and Google have not jointly committed to.&lt;/p&gt;
&lt;h3&gt;Open problem 1: post-quantum PSI and OT extension&lt;/h3&gt;
&lt;p&gt;Every deployed breach-checking protocol today rests on assumptions &lt;a href=&quot;https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/&quot; rel=&quot;noopener&quot;&gt;Shor&apos;s algorithm&lt;/a&gt; breaks. The OPRFs in Apple PSM and Google Password Checkup rely on discrete log over elliptic curves; the HE-PSI in Edge Password Monitor relies on BFV-on-classical-parameters; Paillier (historic, FNP04) relies on integer factorisation. Harvest-now-decrypt-later exposure on durable transcripts is the near-term migration question: an adversary capturing PSI transcripts today and storing them until a cryptographically relevant quantum computer arrives could, in principle, reconstruct the queries.&lt;/p&gt;
&lt;p&gt;Lattice-based OT extension exists at currently-secure parameters, at roughly $10\times$ the communication of IKNP per OT in early prototypes. Whether the breach-checking deployments at Microsoft, Apple, and Google migrate on the same timeline as the rest of TLS (the IETF post-quantum-handshake transition) is an open coordination problem.&lt;/p&gt;
&lt;h3&gt;Open problem 2: multi-party breach corpora&lt;/h3&gt;
&lt;p&gt;No production deployment of a $&amp;gt;2$-party breach-checking service exists. HIBP, Google, Apple, and Microsoft each hold corpora that overlap but contain unique breaches. Consolidating them privately -- so a query gets the union of all four corpora&apos;s match metadata without any one provider learning more than their own corpus contributed -- would meaningfully improve detection.&lt;/p&gt;
&lt;p&gt;The academic literature on multi-party PSI is substantial and growing, but the engineering and the governance work has not been done. Each provider has a different commercial relationship with the breach dataset, a different legal posture, and a different operational interest in their corpus being canonical. The cryptographic primitive is the easy part.&lt;/p&gt;
&lt;h3&gt;Open problem 3: sub-linear sender-side amortisation&lt;/h3&gt;
&lt;p&gt;The $\Omega(n_S)$ sender-side computation lower bound is &lt;em&gt;per query&lt;/em&gt;. For a service serving billions of queries against a static $S_S$, can per-query cost be amortised across queries via a preprocessing step the sender pays once?&lt;/p&gt;
&lt;p&gt;Cong et al. 2021 [@cong-2021] reduces constants substantially and pushes the practical envelope. Sub-linear &lt;em&gt;asymptotic&lt;/em&gt; sender-side cost is open. The information-theoretic barrier is real -- the sender must touch any element that could be in the receiver&apos;s query -- but the &lt;em&gt;expected&lt;/em&gt; cost over many queries against a static corpus admits a more aggressive analysis under the right access patterns.&lt;/p&gt;
&lt;h3&gt;Open problem 4: hardware-broker browser secret stores&lt;/h3&gt;
&lt;p&gt;The endpoint architectural problem. Migrate Edge, Chrome, and Brave from &quot;process-RAM plaintext&quot; to &quot;OS-broker (plaintext never crosses into the browser)&quot; using DPAPI-NG with a broker-PPL protection descriptor or a Credential Guard-style trustlet.&lt;/p&gt;
&lt;p&gt;WebAuthn and passkeys offer this property already -- the platform authenticator holds the private key, and the browser receives signed assertions without ever seeing the secret. But passkeys require per-site enrollment that traditional username-password sites have not adopted at scale; the long tail of legacy login forms will remain on saved-passwords-as-strings for years.&lt;/p&gt;
&lt;p&gt;The Windows Credential Manager offers the broker property for &lt;em&gt;Windows-app&lt;/em&gt; credentials -- but it is not wired into Chromium for &lt;em&gt;browser&lt;/em&gt; credentials. The engineering work is real; the cryptographic work is essentially trivial. Whether and when Microsoft, Google, or Brave commit to it is the question.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Open problem&lt;/th&gt;
&lt;th&gt;What&apos;s been tried&lt;/th&gt;
&lt;th&gt;Current best&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Post-quantum PSI&lt;/td&gt;
&lt;td&gt;Lattice-based OT, ring-LWE OPRFs&lt;/td&gt;
&lt;td&gt;Prototype-grade; 10x classical at ~secure parameters&lt;/td&gt;
&lt;td&gt;Harvest-now-decrypt-later on PSI transcripts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multi-party breach corpora&lt;/td&gt;
&lt;td&gt;Multi-party PSI literature (e.g., Kolesnikov et al. CCS 2017)&lt;/td&gt;
&lt;td&gt;Academic constructions; no production deploy&lt;/td&gt;
&lt;td&gt;Each provider&apos;s corpus has unique recall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sub-linear sender cost&lt;/td&gt;
&lt;td&gt;Cong et al. 2021 constants reduction&lt;/td&gt;
&lt;td&gt;Linear $\Omega(n_S)$ per query&lt;/td&gt;
&lt;td&gt;$5 \times 10^9$ corpus, billions of queries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hardware-broker secret stores&lt;/td&gt;
&lt;td&gt;WebAuthn / passkeys; DPAPI-NG, Credential Guard&lt;/td&gt;
&lt;td&gt;Standards exist; wiring into browsers is missing&lt;/td&gt;
&lt;td&gt;The Ronning PoC threat model&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

Open Chromium&apos;s process tree on Windows (Task Manager: Details, group by Path) and look for the `Google Chrome.exe` (or `msedge.exe`) process running with the `--type=` argument absent -- that&apos;s the parent. App-Bound Encryption binds the DPAPI unwrap to that exact parent process&apos;s signature, so a same-user attacker masquerading as Chrome cannot ask DPAPI to unwrap Chrome&apos;s data even with the right user identity. The architectural primitive is sitting in Chrome&apos;s source tree as of July 2024 [@google-chrome-abe-2024]; the equivalent control for Edge&apos;s password store has not shipped.
&lt;p&gt;These four open problems share a structure: each would require coordination across multiple vendors and across the cryptography / platform / browser boundary. None is research-blocked. All are governance-blocked.&lt;/p&gt;
&lt;h2&gt;11. Practical guide&lt;/h2&gt;
&lt;p&gt;What should you do this week?&lt;/p&gt;
&lt;h3&gt;Users&lt;/h3&gt;
&lt;p&gt;If your Edge browser is your password manager and you are on a single-user laptop you control end-to-end, the Ronning PoC&apos;s threat model is &quot;an attacker who can run code as you on your own machine.&quot; If that happens, the attacker is already in a strong position regardless of how Edge holds passwords -- they can keylog the next login, screenshot anything you autofill, or install a malicious browser extension. The marginal risk of the plaintext-RAM design on a single-user laptop is real but bounded.&lt;/p&gt;
&lt;p&gt;If you share a Windows host -- a family PC with multiple accounts, a small-business workstation several employees sign into, a domain-joined laptop on which IT has administrative access -- the calculus changes. Any same-user process can read your Edge plaintext during your session. Any administrator can read it across sessions (the PoC&apos;s &quot;Administrator can access other users&apos; Edge processes&quot; mode). The case for moving saved credentials out of Edge into a dedicated password manager (1Password, Bitwarden, KeePass) is structurally stronger here.&lt;/p&gt;
&lt;p&gt;A dedicated password manager usually still keeps plaintext in &lt;em&gt;its&lt;/em&gt; own process RAM during autofill -- this is the §9 lower bound asserting itself. The difference is the size of the plaintext-in-RAM window: dedicated password managers tend to require an explicit unlock and re-lock after a configurable idle period. Edge&apos;s window is the entire browser session.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; On a multi-user Windows machine -- RDS, AVD, Citrix, a family computer with separate accounts -- disable Edge&apos;s password manager via the &lt;code&gt;PasswordManagerEnabled&lt;/code&gt; group policy and route users to an out-of-process credential broker (1Password&apos;s CLI integration, Bitwarden&apos;s desktop helper, or the platform Credential Manager).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Windows admins&lt;/h3&gt;
&lt;p&gt;The two relevant Edge enterprise policies are documented on Microsoft Learn:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PasswordManagerEnabled&lt;/code&gt;&lt;/strong&gt; [@ms-learn-passwordmanagerenabled] -- turns Edge&apos;s saved-passwords feature on or off entirely. On a multi-user host with sensitive data, the right value is &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;PasswordMonitorAllowed&lt;/code&gt;&lt;/strong&gt; [@ms-learn-edge-pm] -- controls whether Password Monitor&apos;s breach-checking PSI runs at all. The default is &quot;user-controlled&quot;; in a managed enterprise, you may want to mandatorily enable it (independent of whether the password manager itself is enabled, because Password Monitor can check passwords you type into login forms, not just ones you have saved).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For RDS, AVD, and Citrix environments specifically, the threat model is structurally worse than a single-user laptop. Multiple users share a single Windows host. Their Edge profiles are isolated by Windows ACLs but their &lt;em&gt;processes&lt;/em&gt; are not isolated against an administrator. The PoC&apos;s &quot;Administrator privileges can access other users&apos; Edge processes&quot; mode is exactly the privilege available to a session-host administrator who has been compromised, or to a malicious tenant who escalates locally.&lt;/p&gt;

On a shared-tenant Windows host, the question is not whether same-user LCE will occur -- it is structurally more common than on a single-user laptop -- but how containable it is when it does. Saved browser credentials are an outsized lever for an attacker who pivots laterally: a single compromised user account on a session-host can yield every saved corporate credential for that user, and an administrator escalation can yield every saved credential for every user on the host. The hardening recommendation is to disable browser-based password management entirely (`PasswordManagerEnabled=0`) on session-host images, document an approved credential broker for your environment (Windows Credential Manager for native apps; an enterprise password manager with broker integration for browsers), and audit Edge profile directories on session-host images for stale `Login Data` SQLite files left over from earlier deployments.
&lt;h3&gt;Developers&lt;/h3&gt;
&lt;p&gt;If you ship a component that handles credentials, the design lesson from §9 is not &quot;never hold plaintext&quot; -- you cannot avoid it without an OS-level broker -- but &quot;minimise the plaintext-in-RAM window.&quot;&lt;/p&gt;
&lt;p&gt;The Chrome App-Bound Encryption pattern from July 2024 [@google-chrome-abe-2024] is a template: bind your at-rest key unwrap to a verified process identity so an attacker who exfiltrates your wrapped data cannot trivially unwrap it from a different process. If you must hold plaintext in the parent process for the lifetime of the session (the Edge design), make the trade explicit in the threat model documentation and ensure operations consuming the plaintext are auditable.&lt;/p&gt;
&lt;p&gt;If you can architecturally afford a broker, do it. The IPC cost is real (low-microsecond per call) but small compared to the operational reduction in incident severity. WebAuthn / passkeys are the long-term destination for credentials; the broker pattern is the short-term destination for everything else.&lt;/p&gt;
&lt;h2&gt;12. Frequently asked questions&lt;/h2&gt;

Yes, at rest on disk. DPAPI wraps the symmetric key that encrypts Edge&apos;s `Login Data` SQLite file under a key chain rooted in the user&apos;s password. When the user is logged out (or the machine is powered off), the on-disk blob is opaque to anyone who does not have the user&apos;s DPAPI credentials. The protection ends the moment Edge unwraps the DPAPI blob into process memory, which happens during browser launch or the first password-feature activation. Once unwrapped, the credentials sit in `msedge.exe`&apos;s heap until the process exits, and `ReadProcessMemory` from any same-user process reads them as plaintext. DPAPI is an at-rest control, not an in-memory one.

Partially. If Edge ran as a protected process at an appropriate signature level, only an antimalware-PPL-elevated process could open it for `ReadProcessMemory`, which would substantially raise the bar against a same-user attacker. Browser-process PPL has implications for every loaded DLL (each must be signed at or above the host&apos;s PPL level) and every extension API the renderer expects to call. Chrome and Brave have not adopted PPL for the browser process either. Microsoft has the option; they have not used it. PPL would address the same-user-LCE concern but not the administrator-across-sessions concern.

No. Credential Guard isolates LSASS-derived secrets (NTLM hashes, Kerberos tickets, cached credentials) into the LSAISO trustlet running under Virtualization-Based Security, which is unreachable from the normal-world kernel let alone normal-world user-mode processes. It does not cover browser-owned secrets. Saved Edge credentials live in `msedge.exe`&apos;s heap, not in LSASS, and Credential Guard does not extend protection to arbitrary user-mode application secret stores.

No, in the strict cryptographic sense. K-anonymity leaks the bucket index by design -- the server learns a 20-bit (HIBP), 15-bit (Apple), or 16-bit (Edge shard) prefix of the hash being queried, which carries non-trivial information about which password the client is asking about. A proper PSI protocol leaks nothing beyond the intersection itself. The argument for k-anonymity is that the bucket is large enough -- on the order of hundreds to thousands of possible hashes per bucket -- that the residual information is not actionable for the threats most users face. It is a precise statement of &quot;a small leak in exchange for vast practical efficiency&quot;; the cost is documented and bounded, and that is why every deployed service uses some variant of it. But it is not zero-leak.

Threat-model differences and SGX&apos;s well-documented side-channel literature (see the §6 Aside for the four-attack chronology). For a breach-checking service whose threat model is &quot;the corpus must not leak from inside Microsoft,&quot; HE-PSI offers a clean cryptographic argument that does not depend on any TEE&apos;s silicon-level security claims. The MSR Cryptography and Privacy group had been publishing the relevant HE-PSI papers since 2017 and shipping the SEAL library publicly since 2018, so the substrate was in-house. The cost is real (orders of magnitude more compute than an SGX enclave) but tractable at the sharded scale Edge Password Monitor operates at. The trade is reasonable, and it is documented in the MSR blog [@msr-password-monitor-2021].

AMSI evasion. The Antimalware Scan Interface scans .NET 4.8+ assemblies before execution; .NET 3.5 predates AMSI&apos;s API surface entirely. A C# program targeting .NET 3.5 will run on any modern Windows with the legacy framework installed (which is most of them, because .NET 3.5 is shipped as a Windows feature) and will not be subject to the same managed-runtime scanning that 4.8+ assemblies are. The current GitHub README says &quot;.NET Framework 4.8.1 (changed from 3.5 originally)&quot; [@ronning-github] -- likely to ease running on a clean modern Windows -- but the original .NET 3.5 framing was the deliberate AMSI-evasion choice. The sibling AMSI post in this series explains the scanning architecture in detail.

Per the researcher&apos;s claim and the PCWorld relay, no -- see §7 for the decrypt-on-autofill contrast and Chrome&apos;s App-Bound Encryption hardening [@pcworld-pippig-2026][@google-chrome-abe-2024]. The observable difference is direct: a `ReadProcessMemory`-based scrape of an idle Chrome process returns markedly less than the same scrape of an idle Edge process.

Yes (with caveats). See §7&apos;s MSRC-servicing-criteria Aside for the full framing [@msrc-servicing-criteria]: the short answer is that the position is internally consistent with a decade of MSRC policy and with the §9 architectural lower bound, but it concedes the same-user LCE threat model and shifts defence onto endpoint controls (antivirus, application-control policies, PPL on antimalware processes only) that may not exist on the user&apos;s machine. The architectural primitive that would close the gap (an OS broker) exists on Windows but is not wired into Chromium.
&lt;p&gt;The PSI on the wire is real. The plaintext-RAM concession is also real. Both are statements about which threat model the Edge product team is defending against, and the apparent contradiction in the title disappears once you read them as such.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;edges-two-password-cryptographies&quot; keyTerms={[
  { term: &quot;Private Set Intersection (PSI)&quot;, definition: &quot;A two-party cryptographic protocol that computes the intersection of two sets without revealing anything else.&quot; },
  { term: &quot;Oblivious Transfer (OT)&quot;, definition: &quot;A primitive in which a sender holds two messages, a receiver picks one, and neither learns the other&apos;s choice or the unsent message.&quot; },
  { term: &quot;OT extension&quot;, definition: &quot;Two-phase protocol that turns kappa public-key OTs into m symmetric-cost OTs, collapsing per-OT cost by orders of magnitude.&quot; },
  { term: &quot;Oblivious Pseudorandom Function (OPRF)&quot;, definition: &quot;Two-party protocol where the receiver learns F_k(x) without revealing x to the sender or learning k.&quot; },
  { term: &quot;Pseudorandom Correlation Generator (PCG)&quot;, definition: &quot;Primitive that locally expands a short shared seed into long correlated random strings, used to make Silent OT communication-free post-seed.&quot; },
  { term: &quot;Homomorphic encryption (HE / SWHE / FHE)&quot;, definition: &quot;Encryption supporting operations on ciphertexts that decrypt to operations on plaintexts; SWHE is bounded depth, FHE is unbounded via bootstrapping.&quot; },
  { term: &quot;Cuckoo hashing&quot;, definition: &quot;Hashing with k candidate bins per element and displacement, achieving O(1) lookup at high load -- the partitioning trick under CLR17 and CHLR18.&quot; },
  { term: &quot;k-anonymity (password monitoring)&quot;, definition: &quot;A precise small information leak: each query is consistent with at least k possible passwords sharing the bucket prefix.&quot; },
  { term: &quot;Data Protection API (DPAPI)&quot;, definition: &quot;Windows facility that wraps blobs under user-derived keys; an at-rest control, not an in-process-memory control.&quot; },
  { term: &quot;App-Bound Encryption (Chrome, July 2024)&quot;, definition: &quot;Chrome control that binds DPAPI unwrap of saved data to the verified Chrome process identity, blocking same-user impersonation attacks.&quot; }
]} questions={[
  { q: &quot;Why does the DH meet-in-the-middle PSI protocol fail at breach-checking scale?&quot;, a: &quot;It costs O(|S_B|) group exponentiations per query, leaks set cardinality on both sides, and provides no labeled-PSI variant.&quot; },
  { q: &quot;What three engineering moves let CLR17 revive FNP04&apos;s polynomial-roots PSI?&quot;, a: &quot;(1) BFV SWHE replaces Paillier for slot-packed homomorphic evaluation, (2) Cuckoo hash partitioning splits the giant polynomial into per-bin polynomials, (3) partition-and-evaluate bounds server work to O(|S_S| log |S_R|).&quot; },
  { q: &quot;What does the OPRF preprocessing layer add to Edge Password Monitor&apos;s bare HE-PSI?&quot;, a: &quot;It prevents a malicious client from brute-forcing the server&apos;s corpus offline; the client cannot evaluate F_k(x) without server interaction.&quot; },
  { q: &quot;What does the EdgeSavedPasswordsDumper PoC require to read Edge passwords as cleartext?&quot;, a: &quot;Same-user OpenProcess + ReadProcessMemory on the parent msedge.exe. No kernel exploit, no admin (against same-user processes), no DPAPI bypass.&quot; },
  { q: &quot;Why is &apos;no cryptographic primitive closes the plaintext-RAM gap&apos; the structural claim of section 9?&quot;, a: &quot;Using a plaintext value requires holding it; the only escape hatch is moving the holder to a higher-privileged broker process, which is an architectural choice, not a cryptographic one.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>private-set-intersection</category><category>homomorphic-encryption</category><category>microsoft-edge</category><category>password-security</category><category>threat-modeling</category><category>dpapi</category><category>cryptography</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Fuzzy Extractors and the One Inequality That Explains Why Windows Hello Doesn&apos;t Use One</title><link>https://paragmali.com/blog/fuzzy-extractors-windows-hello/</link><guid isPermaLink="true">https://paragmali.com/blog/fuzzy-extractors-windows-hello/</guid><description>Fuzzy extractors turn noisy biometrics into stable cryptographic keys. A single 2004 inequality explains why Windows Hello deliberately does not use one.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>
A fuzzy extractor turns a noisy, low-entropy biometric reading into a stable, uniformly random cryptographic key, with a public helper string that leaks negligibly little about the key. The Dodis-Reyzin-Smith 2004 construction is the canonical primitive: a secure sketch composed with a strong randomness extractor, governed by a single security inequality that bounds the extractable key length by the source min-entropy, minus the code redundancy, minus twice the security parameter. For consumer face and fingerprint at realistic noise levels, that inequality forbids a cryptographically useful key. Windows Hello -- and Apple Face ID -- consequently use a *match-then-unwrap-TPM-sealed-key* architecture instead, in which the biometric is a gate, not an input to key derivation.
&lt;h2&gt;1. Why can&apos;t a fingerprint just be a password?&lt;/h2&gt;
&lt;p&gt;A developer building a login system writes &lt;code&gt;key = SHA256(fingerprint_image)&lt;/code&gt;, ships it, and never logs in again. Two scans of the same finger produce two slightly different images, the hash is avalanche-sensitive by design, and the cryptographic key is unrecoverable on every authentication after the first. The fix is not a bigger hash. The fix is a new cryptographic primitive.&lt;/p&gt;
&lt;p&gt;The mistake is universal because the temptation is universal. A fingerprint feels like a password: it identifies you, it is hard to forge, and you carry it everywhere. So why not just hash it into a 256-bit key the way every developer has hashed a password for thirty years? The answer is mechanical. SHA-256 is an avalanche function: flipping a single input bit flips, on average, half the output bits. A fingerprint sensor returns a slightly different image every time you press your finger to the glass; one stray dust mote, one degree of rotation, one pixel of pressure variation, and the input has changed in thousands of bits. The hash is statistically independent of the previous one. The key is gone.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Two near-identical 128-bit &quot;fingerprint readings&quot; differing in just 5 bits const enc = new TextEncoder(); async function sha256Hex(bytes) {   const h = await crypto.subtle.digest(&apos;SHA-256&apos;, bytes);   return [...new Uint8Array(h)].map(b =&amp;gt; b.toString(16).padStart(2,&apos;0&apos;)).join(&apos;&apos;); } const w1 = new Uint8Array(16); for (let i = 0; i &amp;lt; 16; i++) w1[i] = (i * 37) &amp;amp; 0xff; const w2 = w1.slice(); w2[3] ^= 0x01; w2[7] ^= 0x10; w2[11] ^= 0x02; w2[12] ^= 0x40; w2[15] ^= 0x80; const h1 = await sha256Hex(w1), h2 = await sha256Hex(w2); let diff = 0; for (let i = 0; i &amp;lt; 64; i++) if (h1[i] !== h2[i]) diff++; console.log(&apos;reading 1 hash:&apos;, h1); console.log(&apos;reading 2 hash:&apos;, h2); console.log(&apos;hex digits that differ:&apos;, diff, &apos;/ 64&apos;); console.log(&apos;the second hash shares nothing with the first&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Any biometric authentication scheme has to confront two simultaneous problems. The first is that biometric readings are &lt;em&gt;noisy&lt;/em&gt;: two scans of the same finger differ in many bits, two photos of the same face under different lighting differ in millions. The second is that biometric distributions are &lt;em&gt;low-entropy&lt;/em&gt;: fingerprints, faces, and even irises are far from uniformly random bitstrings; they cluster heavily, and a clever guesser can do much better than brute force.&lt;/p&gt;
&lt;p&gt;The Dodis-Reyzin-Smith framing of these two facts, in the introduction of their 2004 paper, is precise: &quot;strings that are neither uniformly random nor reliably reproducible seem to be more plentiful&quot; than the well-behaved strings classical cryptography assumes [@dors-2008-siamjc]. Hao, Anderson, and Daugman put the engineering version of the problem in one sentence: &quot;the main obstacle to algorithmic combination is that biometric data are noisy; only an approximate match can be expected to a stored template. Cryptography, on the other hand, requires that keys be exactly right, or protocols will fail&quot; [@hao-anderson-daugman-2005-tr].&lt;/p&gt;

A pair of algorithms $(\text{Gen}, \text{Rep})$ such that $\text{Gen}(w) \to (R, P)$ produces a uniformly random key $R \in \{0,1\}^\ell$ and a public helper string $P$, while $\text{Rep}(w&apos;, P) \to R$ recovers the same key $R$ for any $w&apos;$ within distance $t$ of $w$. The helper $P$ may be public; it must leak only negligibly about $R$ under any source $W$ of sufficient min-entropy [@dors-2008-siamjc].
&lt;p&gt;A fuzzy extractor is the primitive built to solve exactly this design problem. Given a noisy source $w$ with at least $m$ bits of min-entropy, $\text{Gen}$ produces a stable key $R$ and a public helper $P$; given any reading $w&apos;$ within Hamming distance $t$ of the original, $\text{Rep}$ recovers $R$ identically. The helper $P$ is allowed to be public; the security guarantee says $P$ leaks at most $\varepsilon$ bits about $R$ in statistical distance. This primitive is the right answer to the developer&apos;s mistake at the top of the section, and it has been the subject of twenty years of beautiful cryptographic theory.&lt;/p&gt;
&lt;p&gt;So here is the puzzle the rest of the article will solve. Every major consumer biometric authentication product -- &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;Windows Hello&lt;/a&gt; (2015), Apple Touch ID (2013), Apple Face ID (2017) -- has explicitly avoided this primitive. None of them derives a cryptographic key from your biometric. Why? The answer takes nine more sections, and it bottoms out on one inequality.&lt;/p&gt;
&lt;h2&gt;2. Historical origins: the 1990s problem statement&lt;/h2&gt;
&lt;p&gt;By the late 1990s the smartcard-and-PKI deployment wave had forced an uncomfortable question on the cryptographic community: how do you bind a long-lived private key to a &lt;em&gt;person&lt;/em&gt; rather than a &lt;em&gt;device&lt;/em&gt;? Smartcards were cheap to mass-produce, but they were also cheap to steal, and PINs got shared the moment any user found them inconvenient. Tying the key to a fingerprint or an iris reading promised a way out, but the underlying mathematics had not yet been written down.&lt;/p&gt;
&lt;p&gt;Two foundational tools were already in the cryptographic toolkit and would later become load-bearing pieces of the fuzzy extractor. The first was the 1979 Carter-Wegman construction of &lt;em&gt;universal hash functions&lt;/em&gt;: a family ${h_s}$ such that for any two distinct inputs $x \ne y$, $\Pr_s[h_s(x) = h_s(y)] \le 1/|\text{range}|$ [@carter-wegman-1979]. The second was the 1989 Impagliazzo-Levin-Luby Leftover Hash Lemma (LHL), which proved that applying a randomly chosen universal hash to any min-entropy source yields an output statistically indistinguishable from uniform, up to a precise entropy budget [@ill-1989]. Together, these two results were a randomness-extraction toolkit waiting for an application.Carter-Wegman 1979 is the deepest ancestor of every information-theoretic fuzzy extractor. The strong extractor at the heart of the Dodis-Reyzin-Smith construction is, mechanically, a Carter-Wegman universal hash with a random seed -- the LHL is what proves its output is uniform.&lt;/p&gt;

The min-entropy of a random variable $W$ is $H_\infty(W) = -\log_2 \max_w \Pr[W = w]$. It is the entropy measure that captures *worst-case* guessing difficulty: a source with $m$ bits of min-entropy cannot be guessed correctly with probability greater than $2^{-m}$ in one try. Min-entropy is the right measure for cryptographic key derivation because Shannon entropy is too generous when the distribution is peaked [@dors-2008-siamjc].
&lt;p&gt;In May 1998, at the IEEE Symposium on Security and Privacy, Davida, Frankel, and Matt published the first formal-cryptographic proposal for binding a private signing key to a biometric. Their scheme used majority-decoding with a BCH error-correcting code to absorb the noise in repeated iris readings, then used the corrected reading to release a stored long-lived signing key [@davida-frankel-matt-1998], [@dblp-davida-frankel-matt-1998]. The construction worked, in the sense that it ran end-to-end on test data. But the paper had no notion of a &lt;em&gt;strong extractor&lt;/em&gt;, no parameter inequality bounding the extractable key length, and no security theorem against a generic adversary. The reader was asked to trust the construction by inspection.&lt;/p&gt;
&lt;p&gt;That same period saw the rise of a completely different approach. In 2001, Ratha, Connell, and Bolle of IBM proposed &lt;em&gt;cancelable biometrics&lt;/em&gt;: instead of trying to derive a cryptographic key from the biometric, apply a non-invertible application-specific transformation $T_i$ to the feature vector before storage, so that a compromised template can be revoked and re-issued under a fresh $T_j$ [@ratha-connell-bolle-2001]. The goal was &lt;em&gt;template protection&lt;/em&gt;, not key derivation.&lt;/p&gt;
&lt;p&gt;The three properties Ratha et al. demanded of $T_i$ -- &lt;em&gt;irreversibility&lt;/em&gt; (the transform cannot be inverted to recover the original feature vector), &lt;em&gt;unlinkability&lt;/em&gt; (two transforms of the same biometric cannot be matched), and &lt;em&gt;renewability&lt;/em&gt; (a compromised transform can be replaced) -- would two decades later be codified verbatim by ISO/IEC 24745:2022 as the universal properties of any biometric template protection scheme [@iso-iec-24745-2022], [@rathgeb-uhl-2011]. Cancelable biometrics partitions the design space alongside fuzzy extractors: the former &lt;em&gt;transforms&lt;/em&gt; a biometric template, the latter &lt;em&gt;derives&lt;/em&gt; a cryptographic key from it.&lt;/p&gt;
&lt;p&gt;Davida, Frankel, and Matt had shipped a working construction without a unifying primitive. Juels and Wattenberg, within twelve months, would publish a cleaner construction with the same gap; and within seven years Dodis, Reyzin, and Smith would close it. The next section is the story of those precursors, and the structural defect they share.&lt;/p&gt;
&lt;h2&gt;3. Early approaches: fuzzy commitment and fuzzy vault&lt;/h2&gt;
&lt;p&gt;Two precursor constructions, six years apart, get most of the way to a fuzzy extractor without naming the primitive. They are simultaneously the foundation everything later builds on and the ad-hoc constructions the 2004 Dodis-Reyzin-Smith paper would retroactively classify as &lt;em&gt;components&lt;/em&gt; of a real abstraction rather than a complete one.&lt;/p&gt;
&lt;h3&gt;3.1 Juels-Wattenberg 1999: fuzzy commitment&lt;/h3&gt;
&lt;p&gt;Ari Juels and Martin Wattenberg, at the 1999 ACM Conference on Computer and Communications Security, introduced the &lt;strong&gt;fuzzy commitment scheme&lt;/strong&gt; [@juels-wattenberg-1999-pdf]. The construction is short enough to write on a napkin. Fix a binary error-correcting code $\mathcal{C} \subseteq {0,1}^n$ that corrects up to $t$ errors. To commit to a noisy biometric reading $w \in {0,1}^n$:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Pick a random codeword $c \stackrel{R}{\leftarrow} \mathcal{C}$.&lt;/li&gt;
&lt;li&gt;Publish the commitment blob $(h(c), \delta)$ where $\delta := w \oplus c$ and $h$ is a cryptographic hash.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To decommit with a fresh reading $w&apos;$ within Hamming distance $t$ of $w$, compute $c&apos; := D(w&apos; \oplus \delta)$ where $D$ is the code&apos;s decoder; check $h(c&apos;) \stackrel{?}{=} h(c)$. If the check passes, the commitment opens. The argument that the scheme is &lt;em&gt;binding&lt;/em&gt; (the committer cannot later open to a different value) and &lt;em&gt;hiding&lt;/em&gt; (the commitment leaks nothing about $w$) goes through in the random-oracle model.&lt;/p&gt;

sequenceDiagram
    participant U as User (commit)
    participant S as Storage
    participant V as Verifier (decommit)
    U-&amp;gt;&amp;gt;U: Pick random codeword c
    U-&amp;gt;&amp;gt;U: Compute delta = w XOR c
    U-&amp;gt;&amp;gt;U: Compute t = hash(c)
    U-&amp;gt;&amp;gt;S: Publish (t, delta)
    Note over V: Time passes, user re-scans
    V-&amp;gt;&amp;gt;S: Fetch (t, delta)
    V-&amp;gt;&amp;gt;V: Read fresh w&apos; near w
    V-&amp;gt;&amp;gt;V: Compute c&apos; = Decode(w&apos; XOR delta)
    V-&amp;gt;&amp;gt;V: Check hash(c&apos;) == t
    V--&amp;gt;&amp;gt;V: Open commitment to c
&lt;p&gt;Fuzzy commitment is elegant, but it has three structural gaps that DRS 2004 will later expose.&lt;/p&gt;
&lt;p&gt;First, the construction is a &lt;em&gt;commitment&lt;/em&gt;, not an &lt;em&gt;extractor&lt;/em&gt;: it binds a hash of a codeword, not a uniformly random key, and it cannot be plugged directly into a key-derivation pipeline. Second, it assumes Hamming-distance noise, which fits iris codes (Daugman&apos;s IrisCodes are fixed-length bitstrings whose pairwise distance is fractional binomial) but does not fit fingerprint minutiae sets or face embeddings. Third, and most damagingly, the construction leaks under correlated re-enrolment. In 2009, Simoens, Tuyls, and Preneel demonstrated &quot;how to link and reverse protected templates produced by code-offset and bit-permutation sketches&quot; [@simoens-tuyls-preneel-2009]; if a user enrols twice with two slightly different readings $w_1, w_2$ of the same finger, the helper pair $(\delta_1, \delta_2)$ leaks $w_1 \oplus w_2$, which is closer to zero than uniform and reveals the noise distribution.&lt;/p&gt;
&lt;h3&gt;3.2 Juels-Sudan 2002 / 2006: fuzzy vault&lt;/h3&gt;
&lt;p&gt;Three years later, Ari Juels and Madhu Sudan extended the same idea to &lt;em&gt;unordered sets&lt;/em&gt;, the natural metric for fingerprint minutiae [@juels-sudan-2002-pdf], [@juels-sudan-2006-dcc]. The &lt;strong&gt;fuzzy vault&lt;/strong&gt; locks a secret $\kappa$ in a vault as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Encode $\kappa$ as the coefficients of a polynomial $p$ of degree $k$ over a finite field.&lt;/li&gt;
&lt;li&gt;For each element $a_i$ of the genuine biometric set $A$, publish the point $(a_i, p(a_i))$.&lt;/li&gt;
&lt;li&gt;Add many &lt;em&gt;chaff points&lt;/em&gt; $(x_j, y_j)$ with $y_j \ne p(x_j)$ to drown the genuine points in noise.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A user whose set $B$ overlaps sufficiently with $A$ identifies enough true points to Reed-Solomon-decode $p$, recovers $\kappa$, and unlocks the vault. The construction handles set-difference noise naturally and was widely deployed in fingerprint authentication research between 2002 and 2010.Watch the citation. The conference version is IEEE ISIT 2002 (single-page proceedings extended abstract; full author PDF is the canonical text). The journal version is &lt;em&gt;Designs, Codes and Cryptography&lt;/em&gt; 38(2):237-257, February 2006 -- not IEEE Transactions on Information Theory as one widely-circulated secondary source claims.&lt;/p&gt;
&lt;p&gt;But the fuzzy vault inherits and amplifies the precursor&apos;s defects. Walter Scheirer and Terrance Boult, in 2007, enumerated three concrete attacks: &lt;em&gt;Attack via Record Multiplicity&lt;/em&gt; (ARM), &lt;em&gt;Surreptitious Key Inversion&lt;/em&gt; (SKI), and &lt;em&gt;Blended Substitution&lt;/em&gt; [@scheirer-boult-2007]. The Attack via Record Multiplicity exploits exactly the same correlated-re-enrolment weakness fuzzy commitment has: two vaults locking the same biometric under different polynomials reveal the underlying set $A$ by intersecting the published points. The Scheirer-Boult paper opens with a sentence that is, in retrospect, the diagnosis of the entire pre-DRS literature: &quot;while many PETs for biometrics have attempted a formal analysis of their security, a significant oversight has been the issue of the risk from attacks that use multiple records&quot; [@scheirer-boult-2007].&lt;/p&gt;
&lt;h3&gt;3.3 The structural defect both constructions share&lt;/h3&gt;
&lt;p&gt;Stand back. Both constructions handle noise tolerance via an error-correcting code, and both produce a security argument by hashing or hiding the result. Neither construction separates these two responsibilities. The noise-tolerance layer (the code) and the uniformity layer (the hash) are entangled in the same blob of public data. That entanglement is structurally why neither can prove a generic security theorem against a generic adversary: every security argument is tied to specific assumptions about the source distribution, the code, and the random oracle, and slight changes to any of them break the analysis. The fix is not a better code or a better hash. The fix has a name: &lt;em&gt;decomposition&lt;/em&gt;.&lt;/p&gt;

A pair of algorithms $(\text{SS}, \text{Rec})$ such that $\text{SS}(w) \to s$ produces a public sketch $s$, and $\text{Rec}(w&apos;, s) \to w$ recovers the original $w$ for any $w&apos;$ within distance $t$ of $w$. The sketch is allowed to leak some information about $w$, but the residual *average min-entropy* $\tilde H_\infty(W \mid \text{SS}(W))$ must remain at least some target $\tilde m$ [@dors-2008-siamjc].
&lt;p&gt;That word -- decomposition -- is what Dodis, Reyzin, and Smith would deliver, on Thursday May 6, 2004, in Interlaken, Switzerland, at EUROCRYPT.&lt;/p&gt;
&lt;h2&gt;4. Evolution: five generations at a glance&lt;/h2&gt;
&lt;p&gt;Before walking through the DRS 2004 decomposition in detail, it helps to see where it sits in the family tree. Every construction the rest of this article mentions belongs to one of five generations, ordered by what failure of the previous generation it closes.&lt;/p&gt;

flowchart LR
    G0[&quot;Gen 0&lt;br /&gt;hash(w)&lt;br /&gt;fails on noise&quot;] --&amp;gt; G1[&quot;Gen 1&lt;br /&gt;Juels-Wattenberg 1999&lt;br /&gt;fuzzy commitment&quot;]
    G1 --&amp;gt; G15[&quot;Gen 1.5&lt;br /&gt;Juels-Sudan 2002/2006&lt;br /&gt;fuzzy vault&quot;]
    G15 --&amp;gt; G2[&quot;Gen 2&lt;br /&gt;Dodis-Reyzin-Smith 2004&lt;br /&gt;fuzzy extractor&quot;]
    G2 --&amp;gt; G3a[&quot;Gen 3a&lt;br /&gt;Boyen 2004&lt;br /&gt;reusable&quot;]
    G2 --&amp;gt; G3b[&quot;Gen 3b&lt;br /&gt;BDKOS 2005 / DKKRS 2012&lt;br /&gt;tamper-resilient&quot;]
    G2 --&amp;gt; G4[&quot;Gen 4&lt;br /&gt;Fuller-Meng-Reyzin 2013&lt;br /&gt;computational, LWE-based&quot;]
    G2 --&amp;gt; G5[&quot;Gen 5&lt;br /&gt;CFPRS 2016&lt;br /&gt;reusable low-entropy&quot;]
&lt;p&gt;The table below names each generation, its central insight, and the new failure mode it exposes that motivates the next generation. Read it top to bottom; each row solves a problem the row above raised.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Gen&lt;/th&gt;
&lt;th&gt;Year&lt;/th&gt;
&lt;th&gt;Authors / venue&lt;/th&gt;
&lt;th&gt;Central insight&lt;/th&gt;
&lt;th&gt;New failure exposed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;--&lt;/td&gt;
&lt;td&gt;folk&lt;/td&gt;
&lt;td&gt;$\text{key} = h(w)$&lt;/td&gt;
&lt;td&gt;Avalanche destroys key on every re-scan&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1999&lt;/td&gt;
&lt;td&gt;Juels-Wattenberg, CCS [@juels-wattenberg-1999-pdf]&lt;/td&gt;
&lt;td&gt;Code-offset: hide $w$ inside $\delta = w \oplus c$ for random codeword $c$&lt;/td&gt;
&lt;td&gt;Hamming-only; no extractor; leaks under re-enrol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;1.5&lt;/td&gt;
&lt;td&gt;2002 / 2006&lt;/td&gt;
&lt;td&gt;Juels-Sudan, ISIT / DCC [@juels-sudan-2002-pdf], [@juels-sudan-2006-dcc]&lt;/td&gt;
&lt;td&gt;Polynomial-on-set with chaff points; handles set-difference&lt;/td&gt;
&lt;td&gt;Vulnerable to record-multiplicity and key-inversion attacks [@scheirer-boult-2007]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;2004 / 2008&lt;/td&gt;
&lt;td&gt;Dodis-Reyzin-Smith, EUROCRYPT / SIAM JC [@drs-2004-eurocrypt], [@dors-2008-siamjc]&lt;/td&gt;
&lt;td&gt;Decomposition: secure sketch + strong extractor; one inequality&lt;/td&gt;
&lt;td&gt;Forbids construction at consumer biometric entropy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3a&lt;/td&gt;
&lt;td&gt;2004&lt;/td&gt;
&lt;td&gt;Boyen, CCS [@boyen-2004-ccs-eprint]&lt;/td&gt;
&lt;td&gt;Reusable fuzzy extractors; chosen-perturbation security&lt;/td&gt;
&lt;td&gt;Outsider model needs XOR-homomorphic sketch; insider model needs RO&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3b&lt;/td&gt;
&lt;td&gt;2005 / 2012&lt;/td&gt;
&lt;td&gt;Boyen-Dodis-Katz-Ostrovsky-Smith, EUROCRYPT [@bdkos-2005-eurocrypt]; DKKRS, IEEE TIT [@dkkrs-2012-ieeetit]&lt;/td&gt;
&lt;td&gt;Tamper-resilient fuzzy extractors; helper-data integrity against active adversary&lt;/td&gt;
&lt;td&gt;Active-adversary lower bound: $\Omega(\log(1/\varepsilon))$ extra entropy&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;2013 / 2020&lt;/td&gt;
&lt;td&gt;Fuller-Meng-Reyzin, ASIACRYPT / I&amp;amp;C [@fmr-2013-asiacrypt-eprint], [@fmr-2020-iandc]&lt;/td&gt;
&lt;td&gt;Skip the sketch; LWE-based computational construction extracts key length equal to source min-entropy&lt;/td&gt;
&lt;td&gt;Negative result: every computational HILL secure sketch still implies an ECC with $2^{m-2}$ codewords&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2016&lt;/td&gt;
&lt;td&gt;Canetti-Fuller-Paneth-Reyzin-Smith, EUROCRYPT [@cfprs-2016-eurocrypt]&lt;/td&gt;
&lt;td&gt;Per-bit digital lockers; sample-then-extract; reusable for low-entropy sources&lt;/td&gt;
&lt;td&gt;Depends on digital-locker idealisation; restricted source class&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Read this way, the family tree tells a story. Each successor generation closes a real defect: Boyen 2004 closes the multi-enrolment leak that Simoens-Tuyls-Preneel would later make concrete; BDKOS 2005 closes the helper-data tampering problem; FMR 2013 attacks the min-entropy floor itself by trading information-theoretic security for an LWE assumption; CFPRS 2016 chases the low-entropy regime where every prior generation gave up. None of them dethrones the foundational decomposition. They all live inside the framework DRS established.Watch two attribution traps. Boyen 2004 is a sole-author paper -- &quot;Reusable Cryptographic Fuzzy Extractors&quot; by Xavier Boyen [@boyen-2004-ccs-eprint], not &quot;Boyen and Reyzin&quot; or &quot;Boyen et al.&quot; And Fuller-Meng-Reyzin 2013 appeared at &lt;em&gt;ASIACRYPT&lt;/em&gt; 2013, not EUROCRYPT 2013; the misattribution is widespread in secondary sources [@fmr-2013-asiacrypt-eprint].&lt;/p&gt;
&lt;p&gt;Generation 2 is the load-bearing entry. Every later claim about what a fuzzy extractor can and cannot do traces back to it. The next section walks through the construction in mechanical detail, because the inequality at its centre is the artefact every later section will reference.&lt;/p&gt;
&lt;h2&gt;5. The breakthrough: Dodis-Reyzin-Smith 2004 in detail&lt;/h2&gt;
&lt;p&gt;May 6, 2004. Interlaken, Switzerland. Session 16 (&quot;New Applications&quot;). Yevgeniy Dodis (NYU), Leonid Reyzin (Boston University), and Adam Smith (then MIT) present a paper that will be widely cited as the foundational work of the area [@drs-2004-eurocrypt]. The journal version, published in 2008 in &lt;em&gt;SIAM Journal on Computing&lt;/em&gt; with Rafail Ostrovsky added as a fourth author, is the canonical reference text for every formal definition the field uses [@dors-2008-siamjc].The conference paper is three-author Dodis-Reyzin-Smith; the 2008 SIAM Journal on Computing version is four-author and adds Ostrovsky. Cite whichever fits your context, but get the author count right.&lt;/p&gt;
&lt;p&gt;The paper&apos;s contribution is not a new algorithm. It is a &lt;em&gt;decomposition&lt;/em&gt; and a &lt;em&gt;security inequality&lt;/em&gt;. The two halves of the decomposition are the secure sketch and the strong randomness extractor, and the inequality bounds the extractable key length in terms of source min-entropy, code redundancy, and security parameter.&lt;/p&gt;
&lt;h3&gt;5.1 The secure sketch: information reconciliation&lt;/h3&gt;
&lt;p&gt;A secure sketch is the noise-tolerance layer. Formally, an $(\mathcal{M}, m, \tilde m, t)$-secure sketch is a pair of functions $(\text{SS}, \text{Rec})$ over a metric space $(\mathcal{M}, \text{dis})$ such that, for any $w, w&apos;$ with $\text{dis}(w, w&apos;) \le t$, $\text{Rec}(w&apos;, \text{SS}(w)) = w$, and for any source $W$ with min-entropy $H_\infty(W) \ge m$, the &lt;em&gt;average min-entropy&lt;/em&gt; $\tilde H_\infty(W \mid \text{SS}(W)) \ge \tilde m$ [@dors-2008-siamjc].&lt;/p&gt;

Average min-entropy, also called conditional min-entropy, generalises min-entropy to the case where partial information $Y$ about $W$ is public. Formally, $\tilde H_\infty(W \mid Y) = -\log_2 \mathbb{E}_{y \leftarrow Y}\!\left[\max_w \Pr[W = w \mid Y = y]\right]$. It is the right entropy measure for sketches because the sketch $\text{SS}(W)$ is public and an adversary&apos;s best guess of $W$ averages over the possible sketch values [@dors-2008-siamjc].
&lt;p&gt;Two canonical sketch constructions matter. The &lt;strong&gt;code-offset sketch&lt;/strong&gt; picks a random codeword $c$ from an $[n, k, 2t+1]$ binary error-correcting code and publishes $s = w \oplus c$. To recover, compute $c&apos; = D(w&apos; \oplus s)$ where $D$ is the code&apos;s decoder; then return $w = s \oplus c&apos;$. The entropy loss is at most $n - k$ bits. The &lt;strong&gt;syndrome sketch&lt;/strong&gt; publishes $s = H \cdot w^T$ where $H$ is the parity-check matrix of the same code; recovery solves a coset-leader problem. The entropy loss is identical; the syndrome variant just publishes a shorter helper. PinSketch, the canonical sketch for &lt;em&gt;set-difference&lt;/em&gt; metrics, lives in section 6 of the journal paper [@dors-2008-siamjc].&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Simulate a tiny [16, 11, 3] code: 11 data bits, 5 parity bits via a fixed generator. // Real code-offset uses BCH/Reed-Solomon; this is a toy that shows the structure. function parity(w, mask) { let p = 0; for (let i = 0; i &amp;lt; 16; i++) if ((mask&amp;gt;&amp;gt;i)&amp;amp;1) p ^= (w&amp;gt;&amp;gt;i)&amp;amp;1; return p; } const masks = [0b1111111111100000, 0b1111110000011110, 0b1111000011111101, 0b1100111111111011, 0b0011111111110111]; function encode(data11) {   let cw = data11 &amp;amp; 0x7FF;   for (let i = 0; i &amp;lt; 5; i++) cw |= parity(data11, masks[i]) &amp;lt;&amp;lt; (11 + i);   return cw; } // Sketch: pick a random codeword c, publish s = w XOR c const w = 0b0110110010110101; // imagine this is the user&apos;s first reading const data = Math.floor(Math.random() * 2048); const c = encode(data); const s = w ^ c; console.log(&apos;First reading w =&apos;, w.toString(2).padStart(16,&apos;0&apos;)); console.log(&apos;Random codeword c =&apos;, c.toString(2).padStart(16,&apos;0&apos;)); console.log(&apos;Public sketch s = w XOR c =&apos;, s.toString(2).padStart(16,&apos;0&apos;)); // Re-scan: the user reads w&apos; with one bit flipped const wp = w ^ (1 &amp;lt;&amp;lt; 7); console.log(&apos;Re-scan reading w\\&apos; =&apos;, wp.toString(2).padStart(16,&apos;0&apos;)); const cp = wp ^ s; console.log(&apos;Decoder input c + e =&apos;, cp.toString(2).padStart(16,&apos;0&apos;)); console.log(&apos;The decoder sees the noisy codeword and corrects it back to c -- so Rec recovers w from w\\&apos; and s.&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;h3&gt;5.2 The strong randomness extractor: from sketch-residual to uniform key&lt;/h3&gt;
&lt;p&gt;A strong randomness extractor is the uniformity layer. The relevant formal statement is the average-case form of the &lt;strong&gt;Leftover Hash Lemma&lt;/strong&gt;.&lt;/p&gt;

A function $\text{Ext}: \{0,1\}^n \times \{0,1\}^d \to \{0,1\}^\ell$ is an *average-case* $(n, \tilde m, \ell, \varepsilon)$-strong extractor if, for every joint distribution $(W, I)$ over $\{0,1\}^n \times \{0,1\}^*$ with $\tilde H_\infty(W \mid I) \ge \tilde m$, the statistical distance $\text{SD}((\text{Ext}(W; S), S, I), (U_\ell, S, I)) \le \varepsilon$ where $S$ is the (public) extractor seed and $U_\ell$ is uniform [@dors-2008-siamjc].

Let $H$ be a universal hash family with output length $\ell$. For any source $W$ with $\tilde H_\infty(W \mid I) \ge \tilde m$, the distribution $(S, H_S(W), I)$ is $\varepsilon$-close in statistical distance to $(S, U_\ell, I)$ whenever $\ell \le \tilde m - 2 \log(1/\varepsilon) + 2$ [@ill-1989], [@dors-2008-siamjc]. The Leftover Hash Lemma is therefore the single inequality that powers every information-theoretic strong extractor used in practice.
&lt;p&gt;The LHL says: take any min-entropy source, hash it with a randomly chosen universal hash, and what comes out is statistically indistinguishable from uniform, up to a precise budget. Pay $2 \log(1/\varepsilon) - 2$ bits of entropy at the door; everything left over is uniform.&lt;/p&gt;
&lt;h3&gt;5.3 Composition&lt;/h3&gt;
&lt;p&gt;The composition is the whole point. Define $\text{Gen}(w) := (R, P)$ where $P = (\text{SS}(w), \text{seed})$ and $R = \text{Ext}(w; \text{seed})$. To recover, $\text{Rep}(w&apos;, P)$ runs $w = \text{Rec}(w&apos;, \text{SS}(w))$ and recomputes $R = \text{Ext}(w; \text{seed})$. The composition is an $(\mathcal{M}, m, \ell, t, \varepsilon)$-fuzzy extractor, and the security proof is now algebraic.&lt;/p&gt;

The helper data $P$ in a fuzzy extractor is the public part of the output of $\text{Gen}$. It consists of the secure sketch $\text{SS}(w)$ plus the extractor seed. It must be available at recovery time, but it need not be secret. The security guarantee says that even an adversary who sees $P$ in full learns at most $\varepsilon$ bits about the extracted key $R$ in statistical distance [@dors-2008-siamjc].

flowchart TD
    W[&quot;Noisy reading w&quot;] --&amp;gt; SS[&quot;Secure sketch SS&quot;]
    W --&amp;gt; EXT[&quot;Strong extractor Ext&quot;]
    SEED[&quot;Random seed&quot;] --&amp;gt; EXT
    SS --&amp;gt; P[&quot;Public helper P = (sketch, seed)&quot;]
    SEED --&amp;gt; P
    EXT --&amp;gt; R[&quot;Uniform key R&quot;]
    P --&amp;gt; REP[&quot;Rep at recovery&quot;]
    WP[&quot;Noisy reading w&apos;&lt;br /&gt;(within distance t)&quot;] --&amp;gt; REP
    REP --&amp;gt; R2[&quot;Same uniform key R&quot;]
&lt;h3&gt;5.4 The load-bearing inequality&lt;/h3&gt;
&lt;p&gt;Compose the two entropy budgets. The sketch starts with $H_\infty(W) \ge m$ bits of min-entropy and leaks at most $n - k$ to its public sketch; what remains is $\tilde H_\infty(W \mid \text{SS}(W)) \ge m - (n - k)$. Feed that residual into the LHL with security parameter $\varepsilon$, and the extractor delivers a uniform key of lengthThe constant $+2$ at the end of the inequality is an artefact of how DORS 2008 states the average-case Leftover Hash Lemma in Lemma 2.4; the conference paper writes it as $-O(1)$.&lt;/p&gt;

$$\ell \;\le\; H_\infty(W) - (n - k) - 2\log(1/\varepsilon) + 2.$$
&lt;p&gt;This inequality is the artefact every later section will reference. Walk it term by term. The first term is the source min-entropy: the actual information content of the biometric. The second term is the code redundancy: the entropy paid to absorb noise. The third term is the security parameter cost: every halving of the adversary&apos;s distinguishing advantage costs two bits. The final $+2$ is a small constant.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;function extractableKeyLen(m, codeRedundancy, epsilon) {   const securityCost = 2 * Math.log2(1 / epsilon);   return m - codeRedundancy - securityCost + 2; } // Iris source (Daugman 2003: ~249 dof = effective bits), 128-bit security, BCH [255,131,37] console.log(&apos;iris @ eps=2^-80:&apos;, extractableKeyLen(249, 124, 2 ** -80).toFixed(1), &apos;bits&apos;); // Fingerprint at the upper end of Pankanti-Prabhakar-Jain 2002 (~70 effective bits) console.log(&apos;fingerprint @ eps=2^-80:&apos;, extractableKeyLen(70, 124, 2 ** -80).toFixed(1), &apos;bits&apos;); // Face embedding under correlated illumination noise (~30-50 effective bits) console.log(&apos;face @ eps=2^-80:&apos;, extractableKeyLen(40, 124, 2 ** -80).toFixed(1), &apos;bits&apos;); // Loosen security to eps=2^-40 and see if fingerprint recovers console.log(&apos;fingerprint @ eps=2^-40:&apos;, extractableKeyLen(70, 124, 2 ** -40).toFixed(1), &apos;bits&apos;);&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;Run that calculator on realistic numbers. At a security parameter of $\varepsilon = 2^{-80}$, the third term alone eats 160 bits. A standard $[255, 131, 37]$ BCH code (which corrects up to 18 errors in 255 bits) burns another 124 bits. To extract a 128-bit AES key, the source must supply at least 410 bits of min-entropy.&lt;/p&gt;

Set $m = 70$ (fingerprint upper bound per Pankanti et al. 2002), $n - k = 124$ (BCH redundancy), and $\varepsilon = 2^{-80}$. The extractable key length becomes $70 - 124 - 160 + 2 = -212$ bits. A negative bound means the construction is not slow or expensive: it is *infeasible* at any parameter setting. Try loosening security to $\varepsilon = 2^{-40}$: still $70 - 124 - 80 + 2 = -132$. Even pushing the security parameter all the way down to $\varepsilon = 2^{-10}$ (laughably weak by OS-authenticator standards) leaves you at \$70 - 124 - 20 + 2 = -72$ bits. The fingerprint source simply does not have the entropy budget for the construction at any meaningful security level.
&lt;p&gt;The iris, at Daugman&apos;s 249 statistical degrees of freedom [@daugman-2003-pdf], [@daugman-2004-csvt], is just barely enough -- and only because Hao, Anderson, and Daugman engineered a careful two-layer Hadamard-then-Reed-Solomon code that exploits the block structure of iris noise to achieve a high error-correction rate per information bit, sufficient to extract 140 bits from the 2048-bit iris code at 99.5% recovery success [@hao-anderson-daugman-2005-tr]. The fingerprint, at 40 to ~70 effective bits per Pankanti, Prabhakar, and Jain [@pankanti-prabhakar-jain-2002], is not even close. The face embedding, at 30 to 50 raw bits and considerably less under correlated illumination and pose noise, is further still.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; The DRS 2004 key-length inequality is the article&apos;s load-bearing artefact. Every later claim that a fuzzy extractor cannot work on consumer biometrics traces back to it. The construction is not slow or expensive on these sources -- it is &lt;em&gt;mathematically forbidden&lt;/em&gt;, in the sense that the extractable key length is negative at the security parameter an operating-system authenticator demands.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This is the inequality that forbids the construction on consumer-grade face or fingerprint at the security bar an operating system authenticator demands. The rest of the article is the four-generation effort to escape the forbidding, and the architectural choice every shipped consumer product made instead.&lt;/p&gt;
&lt;h2&gt;6. State of the art: by metric space and by successor generation&lt;/h2&gt;
&lt;p&gt;The DRS 2004 framework is parameterised by metric space and source class. To navigate the field, think of every fuzzy-extractor instantiation as a pair of choices: pick a sketch suited to the source&apos;s metric, then pick an extractor suited to the source&apos;s entropy profile. The state of the art is best read as a two-axis table.&lt;/p&gt;
&lt;h3&gt;6.1 Sketches by metric space&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric space&lt;/th&gt;
&lt;th&gt;Sketch construction&lt;/th&gt;
&lt;th&gt;Code or technique&lt;/th&gt;
&lt;th&gt;Where it fits&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Hamming distance&lt;/td&gt;
&lt;td&gt;Code-offset / syndrome [@dors-2008-siamjc]&lt;/td&gt;
&lt;td&gt;$[n,k,2t+1]$ BCH&lt;/td&gt;
&lt;td&gt;Iris codes; SRAM PUFs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set difference&lt;/td&gt;
&lt;td&gt;PinSketch (DORS 2008 section 6) [@dors-2008-siamjc], [@reyzin-lab-home]&lt;/td&gt;
&lt;td&gt;Symmetric-difference syndrome decoding; sublinear in universe size&lt;/td&gt;
&lt;td&gt;Fingerprint minutiae sets; many-out-of-many tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Edit distance&lt;/td&gt;
&lt;td&gt;Embed into Hamming via low-distortion encoding&lt;/td&gt;
&lt;td&gt;Ostrovsky-Rabani-style embeddings&lt;/td&gt;
&lt;td&gt;DNA sequences, typed passwords&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Continuous (face / fingerprint embeddings)&lt;/td&gt;
&lt;td&gt;Quantise then Hamming&lt;/td&gt;
&lt;td&gt;Lloyd-Max or learned quantisers&lt;/td&gt;
&lt;td&gt;Face deep-features; the worst empirical entropy profile&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The continuous-source case is where the consumer biometric story gets ugly: quantising a learned embedding loses entropy in proportion to the quantiser&apos;s resolution, and the residual is the entropy budget the sketch has to work with.&lt;/p&gt;
&lt;h3&gt;6.2 Generation 3a: Boyen 2004 reusable fuzzy extractors&lt;/h3&gt;
&lt;p&gt;Xavier Boyen, about five months after the DRS conference paper, attacked the multi-enrolment problem head on [@boyen-2004-ccs-eprint]. A &lt;em&gt;reusable&lt;/em&gt; fuzzy extractor remains secure when the same source is enrolled multiple times under correlated but different readings $w_1, w_2, \ldots, w_q$. Boyen formalises two threat models. The &lt;em&gt;outsider chosen-perturbation&lt;/em&gt; attack allows the adversary to choose the noise patterns between enrolments; Boyen shows that fuzzy extractors built from XOR-homomorphic sketches (code-offset is one) are secure against outsider adversaries with bounded perturbations. The &lt;em&gt;insider chosen-perturbation&lt;/em&gt; attack additionally gives the adversary access to the extracted keys $R_1, \ldots, R_q$; this stronger model requires a random-oracle assumption. The Canetti-Fuller-Paneth-Reyzin-Smith 2016 paper would later argue that the outsider model&apos;s perturbation class is &quot;unlikely to hold for a practical source,&quot; quoting the paper directly [@cfprs-2016-eprint].&lt;/p&gt;
&lt;h3&gt;6.3 Generation 3b: BDKOS 2005 / DKKRS 2012 tamper-resilient fuzzy extractors&lt;/h3&gt;
&lt;p&gt;A different defect of the DRS construction: the public helper $P$ is not authenticated. If an active adversary can rewrite $P$ on its way to the verifier, the verifier reconstructs the wrong key, and the security analysis falls apart. Xavier Boyen, Yevgeniy Dodis, Jonathan Katz, Rafail Ostrovsky, and Adam Smith addressed this in 2005 with the &lt;strong&gt;tamper-resilient&lt;/strong&gt; fuzzy extractor [@bdkos-2005-eurocrypt]. Their Theorem 1 builds a tamper-detecting secure sketch in the random-oracle model: publish $(\text{pub}^&lt;em&gt;, h)$ where $\text{pub}^&lt;/em&gt;$ is a standard sketch and $h = H(w, \text{pub}^*)$; at recovery, recompute the tag and reject on mismatch. The full tamper-resilient fuzzy extractor (BDKOS §3.2) then composes this tamper-detecting sketch with a strong extractor. The standard-model construction came later, in 2012, from Dodis, Kanukurthi, Katz, Reyzin, and Smith, by replacing the random oracle with an &lt;em&gt;algebraic manipulation detection&lt;/em&gt; (AMD) code, with entropy loss $O(\log(1/\varepsilon))$ above the passive bound [@dkkrs-2012-ieeetit], [@cdfpw-2008-eurocrypt].&lt;/p&gt;
&lt;h3&gt;6.4 Generation 4: Fuller-Meng-Reyzin 2013 computational fuzzy extractors&lt;/h3&gt;
&lt;p&gt;By 2013 the field had hit a wall. The DRS inequality forbids information-theoretic constructions on low-entropy consumer biometrics. Fuller, Meng, and Reyzin asked the obvious next question: does the wall come down if you trade information-theoretic security for computational security? Their answer, in &lt;em&gt;Computational Fuzzy Extractors&lt;/em&gt; at ASIACRYPT 2013, is half negative and half positive [@fmr-2013-asiacrypt-eprint], [@fmr-2020-iandc].&lt;/p&gt;
&lt;p&gt;The negative half: &quot;for every secure sketch that retains $m$ bits of computational entropy, there is an error-correcting code with $2^{m-2}$ codewords&quot; [@fmr-2013-asiacrypt-eprint]. The coding-theory lower bound survives the relaxation to computational HILL pseudoentropy. The positive half: skip the sketch entirely. Treat the biometric reading as an LWE error vector, use a random linear code, and base security on the Learning With Errors problem. The construction extracts a key length equal to the source min-entropy, with security under standard LWE assumptions.&lt;/p&gt;
&lt;h3&gt;6.5 Generation 5: Canetti-Fuller-Paneth-Reyzin-Smith 2016 reusable low-entropy&lt;/h3&gt;
&lt;p&gt;The final piece of the contemporary state of the art is CFPRS 2016 [@cfprs-2016-eurocrypt], [@cfprs-2016-eprint]. Ran Canetti, Benjamin Fuller, Omer Paneth, Leonid Reyzin, and Adam Smith built a fuzzy extractor that is reusable, handles low-entropy distributions, and works under realistic correlated noise. The key technique is &lt;em&gt;per-bit digital lockers&lt;/em&gt;: for each bit of the source, store a digital locker keyed on a random subset of input bits. Recovery samples subsets, queries the lockers, and majority-votes. The construction depends on a digital-locker idealisation, but CFPRS show that any reusable fuzzy extractor for low-entropy sources requires either the random-oracle model or an equivalent strong assumption, which limits the room to remove the idealisation.&lt;/p&gt;
&lt;h3&gt;6.6 The one consumer-biometric construction that ever cleared the bar&lt;/h3&gt;
&lt;p&gt;Across two decades of theoretical work, exactly one published consumer-biometric fuzzy extractor has cleared the DRS bar at production-grade parameters. Hao, Anderson, and Daugman, in a 2005 Cambridge tech report and a 2006 IEEE Transactions on Computers paper, presented an iris fuzzy extractor that &quot;can generate up to 140 bits of biometric key, more than enough for 128-bit AES&quot; with &quot;a 99.5% success rate&quot; on 70 eyes [@hao-anderson-daugman-2005-tr], [@hao-anderson-daugman-2006-ieeetc]. The construction layers a Hadamard code (handles single-bit errors) with a Reed-Solomon code (handles burst errors) inside the code-offset sketch, then runs LHL.The Hao-Anderson-Daugman code is a two-layer Hadamard-then-Reed-Solomon composition. The inner Hadamard layer is HC(6) at rate $7/64 \approx 1/9$ (7 bits encoded into 64 bits per block, 32 blocks per 2048-bit iris code), and absorbs noise within each block; the outer RS(32, 20) over $\text{GF}(2^7)$ tolerates up to six block errors across the 32 blocks. The composition costs more redundancy than a single BCH code but matches the iris noise statistics better. The iris is the only common biometric where the entropy budget is generous enough to absorb that much redundancy and still leave 140 bits over.&lt;/p&gt;
&lt;p&gt;The state of the art, taken together, is wide and mature. Every successor either requires the source to have an entropy profile most consumer biometrics lack, or uses idealisations (random oracle, digital locker, LWE-with-specific-error-distribution) that no production cryptosystem wants to depend on. The next two sections make that boundary precise.&lt;/p&gt;
&lt;h2&gt;7. Competing approaches: six paradigms&lt;/h2&gt;
&lt;p&gt;Step back from the fuzzy-extractor lineage and put it in competitive context. There are at least six distinct approaches to binding cryptographic operations to a biometric, and only two of them &lt;em&gt;derive&lt;/em&gt; a key from the biometric. The other four use the biometric as a &lt;em&gt;gate&lt;/em&gt; on a key generated elsewhere. ISO/IEC 24745:2022 codifies three protection properties -- irreversibility, unlinkability, and renewability -- that any biometric template protection scheme should provide [@iso-iec-24745-2022], and the Rathgeb-Uhl 2011 survey is the open-access reference that maps each approach to the three properties [@rathgeb-uhl-2011].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Approach&lt;/th&gt;
&lt;th&gt;Representative work&lt;/th&gt;
&lt;th&gt;Derives key?&lt;/th&gt;
&lt;th&gt;Irreversibility&lt;/th&gt;
&lt;th&gt;Unlinkability&lt;/th&gt;
&lt;th&gt;Renewability&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Information-theoretic fuzzy extractor&lt;/td&gt;
&lt;td&gt;Dodis-Reyzin-Smith 2004 family [@dors-2008-siamjc]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (under min-entropy)&lt;/td&gt;
&lt;td&gt;Hard under correlated re-enrol&lt;/td&gt;
&lt;td&gt;Yes (rotate seed and sketch)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Computational fuzzy extractor&lt;/td&gt;
&lt;td&gt;Fuller-Meng-Reyzin 2013 / CFPRS 2016 [@fmr-2013-asiacrypt-eprint], [@cfprs-2016-eurocrypt]&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes (under LWE / digital locker)&lt;/td&gt;
&lt;td&gt;Improved over information-theoretic&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cancelable biometrics&lt;/td&gt;
&lt;td&gt;Ratha-Connell-Bolle 2001 [@ratha-connell-bolle-2001]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (by transform design)&lt;/td&gt;
&lt;td&gt;Yes (transform key)&lt;/td&gt;
&lt;td&gt;Yes (re-enrol under fresh transform)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Homomorphic encryption biometric matching&lt;/td&gt;
&lt;td&gt;Engelsma-Jain-Boddeti HERS [@engelsma-jain-boddeti-hers-arxiv]&lt;/td&gt;
&lt;td&gt;Partial&lt;/td&gt;
&lt;td&gt;Yes (under HE)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Secure-element match-on-chip&lt;/td&gt;
&lt;td&gt;Apple Secure Enclave [@apple-platform-security], [@apple-secure-enclave]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Hardware-anchored&lt;/td&gt;
&lt;td&gt;Yes (per-device)&lt;/td&gt;
&lt;td&gt;Yes (hardware key rotation)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Match-then-unwrap-TPM-sealed-key&lt;/td&gt;
&lt;td&gt;Windows Hello ESS [@ms-learn-ess], [@ms-learn-hello-business]&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Hardware-anchored&lt;/td&gt;
&lt;td&gt;Yes (per-device)&lt;/td&gt;
&lt;td&gt;Yes (rotate TPM-sealed key)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

A class of biometric template protection schemes in which a non-invertible, application-specific transformation $T_i$ is applied to the feature vector before storage. The stored template is then $T_i(\text{features})$; matching is performed in the transformed space; and a compromised template can be revoked by re-enrolling under a fresh transform $T_j$. The goal is *template protection*, not cryptographic key derivation: no uniformly random key falls out of the construction. ISO/IEC 24745 names three properties such a transform must satisfy: irreversibility, unlinkability, and renewability [@ratha-connell-bolle-2001], [@rathgeb-uhl-2011].

The international standard *Information security, cybersecurity and privacy protection -- Biometric information protection* (ISO/IEC 24745:2022 Edition 2, 63 pages, published February 2022 [@iso-iec-24745-2022]) defines three properties of any biometric protection scheme -- irreversibility, unlinkability, renewability -- without prescribing any specific cryptographic primitive. The standard is paywalled at CHF 204, which is why most academic and engineering treatments cite the open-access Rathgeb-Uhl 2011 survey [@rathgeb-uhl-2011] as a proxy for the property definitions. The three properties are deliberately neutral: a fuzzy extractor, a cancelable transform, a homomorphic-encryption matcher, and a hardware-anchored secure element can all in principle satisfy them, and the standard is silent on which is best.
&lt;p&gt;The two &lt;em&gt;derive&lt;/em&gt; approaches (rows 1 and 2 in the table) follow the genealogy this article has been tracing. The remaining four are &lt;em&gt;gate&lt;/em&gt; approaches: each generates the cryptographic key by some independent means -- a &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM&lt;/a&gt;-sealed asymmetric key, a Secure Enclave-bound key, a homomorphic-encryption keypair -- and uses the biometric only to decide whether to release the key. The cancelable-biometrics approach is even more conservative: it does not even tie a key to the biometric at all; it only protects the template against compromise.&lt;/p&gt;
&lt;p&gt;Why is the &lt;em&gt;derive&lt;/em&gt; versus &lt;em&gt;gate&lt;/em&gt; distinction so deep? Because it determines who is responsible for the key&apos;s secrecy. In a &lt;em&gt;derive&lt;/em&gt; model, the biometric &lt;em&gt;is&lt;/em&gt; the secret; if the biometric leaks (a photo of your face, a latent print on a glass), the cryptographic key is at risk. In a &lt;em&gt;gate&lt;/em&gt; model, the secret is independent of the biometric -- usually a hardware-anchored private key that never leaves the secure element -- and the biometric is just a soft second factor that decides whether the user is allowed to &lt;em&gt;use&lt;/em&gt; the secret.&lt;/p&gt;
&lt;p&gt;Hardware-anchored &lt;em&gt;gate&lt;/em&gt; schemes also get to rely on attestation: a TPM or Secure Enclave can prove to a remote relying party that the key it just used is bound to a specific device, by a specific user, in a specific authentication ceremony. A pure software fuzzy extractor cannot make any of those claims.&lt;/p&gt;
&lt;p&gt;This is the decisive architectural distinction in the field. Every shipped consumer biometric authenticator on the planet picks &lt;em&gt;gate&lt;/em&gt;. The next two sections explain why: section 8 walks through three theoretical lower bounds that draw the perimeter inside which any fuzzy extractor can live, and section 10 walks through the Windows Hello architecture as the concrete embodiment of &lt;em&gt;gate&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;8. Theoretical limits&lt;/h2&gt;
&lt;p&gt;Three lower-bound results, taken together, draw the perimeter inside which any fuzzy extractor can live. The section 5 inequality was the first. Two more come from later papers, and they are sharper than the basic inequality suggests.&lt;/p&gt;
&lt;h3&gt;8.1 The min-entropy floor&lt;/h3&gt;
&lt;p&gt;The DRS section 5 inequality already gives a floor: $\ell \le H_\infty(W) - (n-k) - 2\log(1/\varepsilon) + 2$. Fuller, Reyzin, and Smith in 2020 sharpened this with an impossibility result for &lt;em&gt;universal&lt;/em&gt; information-theoretic fuzzy extractors.&lt;/p&gt;
&lt;p&gt;They define a stronger notion they call &lt;em&gt;fuzzy min-entropy&lt;/em&gt;, $H^{\text{fuzz}}&lt;em&gt;{t,\infty}(W) := -\log \max&lt;/em&gt;{w_0} \Pr[W \in \mathcal{B}&lt;em&gt;t(w_0)]$, and prove that the gap between the universal-construction bound $H&lt;/em&gt;\infty(W) - \log|\mathcal{B}&lt;em&gt;t|$ and the optimal bound $H^{\text{fuzz}}&lt;/em&gt;{t,\infty}(W)$ can be a large fraction of $n$ bits. For Daugman&apos;s iris parameters ($n = 2048$, $H_\infty \approx 249$, $\log|\mathcal{B}_t| \approx 1024$), the universal bound sits more than 1000 bits below the fuzzy-min-entropy upper bound -- a gap of $\approx 0.5n$ -- and Theorem 5.1&apos;s impossibility region pushes the worst-case gap up toward $h_2(\tau) \cdot n$ for higher noise rates [@frs-2020-ieeetit]. The implication: a single universal construction cannot extract the optimal key length from every high-fuzzy-min-entropy source; some sources require source-specific constructions to close the gap, and the DRS bound is essentially tight in the worst case.&lt;/p&gt;
&lt;p&gt;Plug realistic numbers into the floor. The table below is the empirical perimeter the cryptographic community has lived inside for two decades.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;Approx. raw entropy&lt;/th&gt;
&lt;th&gt;Effective entropy under correlated noise&lt;/th&gt;
&lt;th&gt;Clears DRS bar at $\varepsilon = 2^{-80}$ for 128-bit key?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Iris [@daugman-2003-pdf], [@daugman-2004-csvt]&lt;/td&gt;
&lt;td&gt;~249 dof&lt;/td&gt;
&lt;td&gt;~249 dof (matched-illumination scans)&lt;/td&gt;
&lt;td&gt;Yes (demonstrated [@hao-anderson-daugman-2006-ieeetc])&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fingerprint minutiae [@pankanti-prabhakar-jain-2002]&lt;/td&gt;
&lt;td&gt;~70 bits at best image quality&lt;/td&gt;
&lt;td&gt;40-70 bits depending on sensor&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Face deep-feature embeddings&lt;/td&gt;
&lt;td&gt;30-50 bits raw&lt;/td&gt;
&lt;td&gt;Often much less under illumination / pose&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRAM PUF [@intrinsic-id-sram-puf], [@tuyls-skoric-kevenaar-2007-springer]&lt;/td&gt;
&lt;td&gt;thousands of bits (entire SRAM page)&lt;/td&gt;
&lt;td&gt;thousands of bits (controlled noise)&lt;/td&gt;
&lt;td&gt;Yes (deployed in over a billion devices)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Watch Daugman&apos;s 249 figure carefully. It is the number of degrees of freedom in the &lt;em&gt;Hamming distance distribution&lt;/em&gt; between IrisCodes from different irises, fit to a fractional binomial with $N = 249$ and $p = 0.5$. It is not the raw min-entropy of an iris image: an iris sensor returning 249 bits of high-quality iris data is &lt;em&gt;not&lt;/em&gt; the same as 249 bits of min-entropy. Daugman&apos;s 2003 Pattern Recognition paper makes the distinction explicitly [@daugman-2003-pdf].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Across every consumer biometric the industry has deployed, the iris is unique in clearing the DRS bar at production parameters. Daugman&apos;s 249 statistical degrees of freedom give the iris a budget more than three times the budget of fingerprint, and an order of magnitude more than face. Hao, Anderson, and Daugman 2006 demonstrate a 140-bit iris key with 99.5% success on 70 eyes [@hao-anderson-daugman-2006-ieeetc] -- the only published consumer-biometric fuzzy extractor ever to clear the DRS bar at production parameters. The catch: iris sensors are intrusive, expensive, and rarely shipped in consumer phones or laptops.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;8.2 Reusability impossibility&lt;/h3&gt;
&lt;p&gt;Boyen&apos;s 2004 insider chosen-perturbation game is unconditionally insecure for adversaries who can choose enough perturbations [@boyen-2004-ccs-eprint]. CFPRS 2016 cite this impossibility result and work around it by restricting attention to a digital-locker-amenable source class [@cfprs-2016-eprint]. The practical implication is that any fuzzy extractor that wants to be reusable across many enrolments has to either (a) restrict the source class (CFPRS&apos;s path) or (b) accept a security degradation per re-enrol. Neither option is appealing for a consumer device that may see its user re-enrol after every kernel update, every sensor recalibration, or every routine credential rotation.&lt;/p&gt;
&lt;h3&gt;8.3 Active-adversary lower bound&lt;/h3&gt;
&lt;p&gt;A passive adversary sees the helper $P$ but does not modify it; an active adversary can rewrite $P$ between enrolment and recovery. BDKOS 2005 and DKKRS 2012 prove that protecting against active adversaries requires either a one-time setup secret (a shared seed established out of band), an authenticated channel between enrolment and recovery, or a min-entropy surplus of $\Omega(\log(1/\varepsilon))$ above the passive bound [@bdkos-2005-eurocrypt], [@dkkrs-2012-ieeetit]. For $\varepsilon = 2^{-80}$, the active-adversary surcharge is 80 bits.&lt;/p&gt;
&lt;h3&gt;8.4 Combining the three bounds&lt;/h3&gt;
&lt;p&gt;Stack the three bounds on top of each other for a consumer face / fingerprint source. The min-entropy floor is the hardest barrier: with 40 to 80 effective bits and 160 bits of security-parameter cost plus 100-plus bits of code redundancy, the extractable key length is negative. The reusability impossibility forecloses the workaround of pretending that re-enrolments are uncorrelated -- they are not, because real biometric drift is highly correlated. The active-adversary bound forecloses the workaround of pretending the helper data is safe in transit. A software-only fuzzy extractor cannot meet a consumer-OS security bar at consumer biometric quality. What you do &lt;em&gt;instead&lt;/em&gt; is the next section.&lt;/p&gt;
&lt;h2&gt;9. Open problems&lt;/h2&gt;
&lt;p&gt;Four problems remain, ordered by how directly each one blocks deployment in a Windows Hello-class product.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; 1. &lt;strong&gt;Deployable face / fingerprint fuzzy extractors under realistic correlated noise.&lt;/strong&gt; Engelsma, Cao, and Jain&apos;s 2019 &lt;em&gt;DeepPrint&lt;/em&gt; reduces intra-user fingerprint variance via learned representations [@engelsma-cao-jain-2019-arxiv], but no published construction has cleared the DRS bar on a realistic test set under correlated noise. 2. &lt;strong&gt;Reusable computational fuzzy extractors without idealisations.&lt;/strong&gt; CFPRS 2016 uses digital lockers, which require either a random oracle or an equivalent strong assumption [@cfprs-2016-eurocrypt]. Eliminating that idealisation is open. 3. &lt;strong&gt;Post-quantum information-theoretic fuzzy extractors.&lt;/strong&gt; Fuller-Meng-Reyzin&apos;s LWE-based construction is already post-quantum on the computational side [@fmr-2013-asiacrypt-eprint], [@fmr-2020-iandc], but an information-theoretic construction tailored to PQ-style noise distributions is open. 4. &lt;strong&gt;The PUF-to-biometric architectural gap.&lt;/strong&gt; Fuzzy extractors are deployed &lt;em&gt;only&lt;/em&gt; for PUFs (Synopsys PUF IP (including QuiddiKey), over a billion devices [@intrinsic-id-sram-puf]) where the noise model is controlled. Closing the architectural gap to consumer biometrics, where the noise model is adversarial and environmental, is open.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each of these is hard, and none has a credible path to a consumer-OS-grade deployment in the next product cycle. Take them one at a time.&lt;/p&gt;
&lt;p&gt;The first is the most obviously blocking. Even if every fingerprint sensor in the world tomorrow began returning DeepPrint embeddings instead of minutiae sets, the entropy budget would still be tens of bits below the DRS bar. The bottleneck is the source distribution, not the encoder. Improving the encoder helps -- a learned representation with lower intra-user variance shifts the noise distribution toward zero, which lets you use a code with less redundancy -- but the inequality still bites. The community&apos;s working belief is that no consumer fingerprint sensor will ever ship enough min-entropy to clear the bar at the security parameter an OS authenticator demands.&lt;/p&gt;
&lt;p&gt;The second is more nuanced. Digital lockers are &lt;em&gt;useful&lt;/em&gt; in practice -- they are the central tool that lets CFPRS 2016 handle reusability for low-entropy sources -- but they depend on the random-oracle model. The random-oracle model is fine for theoretical work; it is uncomfortable for a production cryptosystem that has to survive an FIPS evaluation and a NIST audit. The hope is that &lt;em&gt;non-malleable extractors&lt;/em&gt; or &lt;em&gt;correlation-resistant universal hash families&lt;/em&gt; can replace digital lockers in the CFPRS construction without losing the reusability guarantee. Promising directions exist; none has matured into a deployable construction.&lt;/p&gt;
&lt;p&gt;The third sounds esoteric but matters. The information-theoretic DRS construction has been quietly post-quantum since 2004: the LHL holds against quantum adversaries up to a constant factor, and BCH decoding is classical [@dors-2008-siamjc]. But once you move to the &lt;em&gt;computational&lt;/em&gt; fuzzy extractors of FMR 2013 or CFPRS 2016, the security argument depends on a hardness assumption (LWE or digital-locker-as-RO) that one wants to be confident survives the post-quantum transition. LWE is widely believed to be PQ-secure; digital lockers are not yet rigorously analysed against quantum adversaries.&lt;/p&gt;
&lt;p&gt;The fourth, &lt;strong&gt;the PUF-to-biometric gap&lt;/strong&gt;, is where the theoretical and engineering communities meet most uncomfortably. The fuzzy extractor &lt;em&gt;works&lt;/em&gt; in practice: Synopsys PUF IP (including QuiddiKey) embeds a code-offset / syndrome-based fuzzy extractor in over a billion devices, &quot;deployed and proven in over a billion devices certified by EMVCo, Visa, CC EAL6+, PSA, ioXt, and governments across the globe&quot; per the vendor [@intrinsic-id-sram-puf]. The SRAM PUF has thousands of bits of min-entropy and a controlled noise model: powering up the SRAM gives a startup pattern that is reliable across temperature and voltage swings to within a few percent of bits. The signal-to-noise ratio is dramatically better than any consumer biometric.Pierre-Alain Dupont, Julia Hesse, David Pointcheval, Leonid Reyzin, and Sophia Yakoubov&apos;s 2018 EUROCRYPT paper &lt;em&gt;Fuzzy Password-Authenticated Key Exchange&lt;/em&gt; [@dupont-hesse-pointcheval-reyzin-yakoubov-2018] is a recent direction that decouples fuzzy extraction from key agreement: rather than extract a key once and use it, two parties run a password-authenticated key exchange whose &quot;password&quot; is a noisy biometric. Fuzzy PAKE sidesteps the helper-data leakage problem because the helper is consumed inside an interactive protocol that does not commit it to long-term storage.&lt;/p&gt;

The bright line between PUF and biometric is the *noise model*. An SRAM PUF lives in a single device, sees temperature and voltage variation between $-40^\circ$C and $+85^\circ$C, and operates against an adversary who can read the SRAM but cannot rewrite the silicon. The noise distribution is empirically measurable, and the entropy budget is enormous -- thousands of bits per page. A consumer fingerprint sensor, by contrast, lives outside the trust boundary: the noise distribution depends on skin moisture, sensor cleanliness, finger angle, and an adversary who can lift a latent print from a glass. The fuzzy-extractor framework is the right answer for the PUF case and the wrong answer for the consumer biometric case, and the difference is the noise model, not the cryptography.
&lt;p&gt;Each of these problems is interesting on its own merits, but none of them has a credible path to a consumer-OS-grade deployment in the next product cycle. So what does a consumer OS &lt;em&gt;actually&lt;/em&gt; do? That is the punchline.&lt;/p&gt;
&lt;h2&gt;10. The punchline: why Windows Hello does not use a fuzzy extractor&lt;/h2&gt;
&lt;p&gt;State the claim flatly. Windows Hello, in every shipping configuration since Enhanced Sign-in Security was introduced with Windows 11, performs &lt;strong&gt;match-then-unwrap&lt;/strong&gt;, not &lt;strong&gt;derive-from-biometric&lt;/strong&gt;. The biometric is a gate, not an input to key derivation. The cryptographic credential a Windows Hello user authenticates with is a TPM-bound asymmetric keypair generated independently during provisioning; the biometric matcher merely decides whether to authorise the TPM to use that key. The full architecture is documented verbatim in Microsoft Learn&apos;s Enhanced Sign-in Security and Windows Hello for Business pages [@ms-learn-ess], [@ms-learn-hello-business].&lt;/p&gt;
&lt;h3&gt;10.1 Enrolment&lt;/h3&gt;
&lt;p&gt;When a Windows user enrols a face or a fingerprint, the biometric data path runs inside a Virtualisation-Based Security (VBS) &lt;a href=&quot;https://paragmali.com/blog/vbs-trustlets-what-actually-runs-in-the-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;trustlet&lt;/a&gt;, not in the kernel and not in the camera driver. Microsoft&apos;s documentation is explicit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;When ESS is enabled, the face algorithm is protected using VBS ... The hypervisor allows the face camera to write to these memory regions providing an isolated pathway to deliver face data from the camera to the face matching algorithm&quot; [@ms-learn-ess].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The face image never lands in regular kernel memory. It is delivered by the hypervisor into a memory region readable only by the VBS-resident face-matching trustlet, which extracts a feature template, encrypts it with VBS-only keys, and writes the encrypted blob to disk. For fingerprint, ESS supports only sensors with on-device matching: &quot;ESS is only supported on fingerprint sensors with match on sensor capabilities&quot; [@ms-learn-ess]. The sensor itself runs the matcher and never exposes the template to the host operating system.&lt;/p&gt;

A user-mode process that runs inside Virtual Trust Level 1 (VTL 1) on Windows, isolated from the normal-world kernel (VTL 0) by the Hyper-V hypervisor. Trustlets are the unit of code that the Secure Kernel hosts and that VBS-protected operations execute inside. Examples include the LSA Isolated process (Credential Guard) and the biometric matcher (Windows Hello with Enhanced Sign-in Security) [@ms-learn-ess].
&lt;p&gt;In parallel, the &lt;em&gt;credential&lt;/em&gt; the user will actually authenticate with is generated. Microsoft Learn&apos;s Windows Hello for Business page describes this verbatim: &quot;The provisioning flow requires a second factor of authentication before it can generate a public/private key pair. The public key is registered with the IdP, mapped to the user account&quot; [@ms-learn-hello-business]. The private key never leaves the TPM. It is sealed against a TPM policy that requires the boot integrity to be intact, the user account to be the same, and the VBS-resident biometric matcher to have signalled a match success. The keypair is a per-user, per-device, per-IdP credential; nothing about it is a function of the user&apos;s biometric.&lt;/p&gt;
&lt;h3&gt;10.2 Authentication&lt;/h3&gt;
&lt;p&gt;At authentication time, the user presents a face or a finger; the VBS-resident matcher compares the live template to the stored template; on success, the matcher signals the TPM via a secure channel to unwrap the asymmetric private key for use in an IdP challenge response. The Microsoft documentation states the architecture in two sentences:&lt;/p&gt;

The Windows biometric components running in VBS establish a secure channel to the TPM ... When a matching operation is a success, the biometric components in VBS use the secure channel to authorize the usage of Windows Hello keys for authenticating the user with their identity provider, applications, and services. -- Microsoft Learn, Windows Hello Enhanced Sign-in Security [@ms-learn-ess]
&lt;p&gt;The authentication ceremony itself is described in the Windows Hello for Business page: &quot;Regardless of the gesture used, authentication occurs using the private portion of the Windows Hello for Business credential. The IdP validates the user identity by mapping the user account to the public key registered during the provisioning phase&quot; [@ms-learn-hello-business]. The IdP sees a cryptographic proof that the user-registered TPM-bound key signed the challenge; it never sees anything that depends on the biometric.&lt;/p&gt;

flowchart LR
    subgraph &quot;DRS fuzzy extractor (theoretical)&quot;
        D1[&quot;Read biometric w&quot;] --&amp;gt; D2[&quot;Gen(w) -&amp;gt; (R, P)&quot;]
        D2 --&amp;gt; D3[&quot;Store helper P on disk&quot;]
        D2 --&amp;gt; D4[&quot;Use R as key&quot;]
        D5[&quot;Re-read w&apos; near w&quot;] --&amp;gt; D6[&quot;Rep(w&apos;, P) -&amp;gt; R&quot;]
        D6 --&amp;gt; D7[&quot;Use R as key&quot;]
    end
    subgraph &quot;Windows Hello (production)&quot;
        W1[&quot;Read biometric w in VBS&quot;] --&amp;gt; W2[&quot;Compute template T&quot;]
        W2 --&amp;gt; W3[&quot;Encrypt and store T with VBS-only key&quot;]
        W4[&quot;Generate TPM-bound keypair (sk, pk)&quot;] --&amp;gt; W5[&quot;Register pk with IdP&quot;]
        W4 --&amp;gt; W6[&quot;Seal sk to TPM with policy&quot;]
        W7[&quot;Re-read w&apos; in VBS&quot;] --&amp;gt; W8[&quot;Match w&apos; against T&quot;]
        W8 --&amp;gt; W9[&quot;Authorise TPM unwrap via secure channel&quot;]
        W6 --&amp;gt; W9
        W9 --&amp;gt; W10[&quot;TPM signs IdP challenge with sk&quot;]
    end
&lt;h3&gt;10.3 Why this is the right design&lt;/h3&gt;
&lt;p&gt;Map each architectural choice to a fuzzy-extractor limit from section 8.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The min-entropy gap is real.&lt;/strong&gt; Face and fingerprint min-entropy under correlated real-world noise is below the DRS bar for any cryptographically meaningful key length at the security parameter an OS authenticator must hit. Section 5&apos;s inequality forbids the construction; no amount of clever engineering moves the constants. Microsoft&apos;s engineers, when faced with the choice between deriving a 128-bit key from a 40-bit source and binding the key to a TPM, made the only choice the math allows.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Helper-data leakage compounds under re-enrolment.&lt;/strong&gt; Every time a user re-enrols (new device, sensor recalibration, post-incident credential refresh), a new helper string would be published. Simoens, Tuyls, and Preneel established that correlated code-offset helpers link and reverse [@simoens-tuyls-preneel-2009]. Hardware-anchored match-then-unwrap rotates the TPM-sealed asymmetric key under standard key-management rules instead, sidestepping the cryptographic reusability problem entirely. Key rotation under a hardware root of trust is a solved problem; reusability in a software fuzzy extractor remains an active research area.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reusability across user-account-rebuild scenarios.&lt;/strong&gt; PIN reset, device wipe-and-restore, and credential rotation become &lt;em&gt;key-management&lt;/em&gt; problems (rotate the TPM-sealed key) rather than &lt;em&gt;cryptographic-reusability&lt;/em&gt; problems (rotate the fuzzy extractor and trust the CFPRS bound). The former has thirty years of operational practice behind it; the latter has none.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hardware-anchored attestation is easier to reason about.&lt;/strong&gt; TPM seal-policy binding gives a hardware-anchored security argument that a relying party can verify: the trustlet measurement, the biometric-match-success signal, and the boot integrity all have to match before the key unwraps. A software-only fuzzy extractor cannot match this attestation chain. The IdP at the other end of an authentication ceremony can ask the TPM for a quote attesting that the key was used inside a specific code module on a specific device; no software construction makes that proof.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; In every shipped consumer biometric authenticator on the planet, the biometric is a gate, not an input. The cryptographic key is generated separately during provisioning -- as a TPM-bound asymmetric keypair on Windows Hello, as a Secure-Enclave-bound key on Apple Face ID, as a StrongBox-bound key on Android [@android-keystore] -- and unwrapped on match success. The key is never derived from the biometric.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;10.4 The sibling case: Apple Face ID and Touch ID&lt;/h3&gt;
&lt;p&gt;Apple&apos;s Secure Enclave Processor performs the same architectural pattern, with the Secure Enclave playing the role Windows assigns to the trustlet-plus-TPM pair. The Apple Platform Security guide is explicit:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Apple&apos;s biometric security architecture relies on a strict separation of responsibilities between the biometric sensor and the Secure Enclave, and a secure connection between the two. The sensor captures the biometric image and securely transmits it to the Secure Enclave. During enrollment, the Secure Enclave processes, encrypts, and stores the corresponding Optic ID, Face ID, and Touch ID template data. During matching, the Secure Enclave compares incoming data from the biometric sensor against the stored templates to determine whether to unlock the device or respond that a match is valid&quot; [@apple-platform-security], [@apple-secure-enclave].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Two vendors, independently, converged on the same architecture. Both vendors hire the strongest cryptographers in the world. Neither built a fuzzy extractor. The architectural pattern is now the consensus answer to the consumer biometric authentication problem.&lt;/p&gt;

Apple&apos;s Secure Enclave Processor is the architectural sibling of the Windows VBS trustlet plus TPM combination. The Secure Enclave is an ARM-based coprocessor with its own kernel, its own memory, and its own boot chain, physically isolated on the Application Processor die. During Face ID or Touch ID enrolment, the biometric sensor transmits its raw image directly to the Secure Enclave over a hardware-isolated link; the Secure Enclave extracts the template, encrypts it under a per-device key sealed to the Secure Enclave&apos;s UID, and stores it. During matching, the Secure Enclave compares the live template against the stored template inside its own memory, and on success authorises the use of cryptographic keys held in the same coprocessor. The pattern is identical to the Windows Hello pattern: derive nothing from the biometric; gate a hardware-bound key on the match decision [@apple-platform-security].
&lt;p&gt;Twenty years of theoretical work; zero production consumer-OS biometric authenticators on the planet use any of it for face or fingerprint key derivation; and the engineers who said no were right, for reasons traceable to a single load-bearing inequality at the heart of the 2004 EUROCRYPT paper.&lt;/p&gt;
&lt;h2&gt;11. Frequently asked questions&lt;/h2&gt;

No. Both perform match-then-unwrap rather than derive. Windows Hello generates a TPM-bound asymmetric keypair during provisioning [@ms-learn-hello-business]; the biometric matcher, running inside a VBS trustlet, authorises the TPM to use that key on a match-success signal [@ms-learn-ess]. Apple Face ID and Touch ID follow the same pattern with a Secure-Enclave-bound key in place of a TPM-bound one [@apple-platform-security]. In neither case is the cryptographic key a function of your biometric reading.

Yes -- in SRAM PUFs. Synopsys PUF IP (including QuiddiKey), built on Intrinsic ID SRAM PUF technology, is &quot;deployed and proven in over a billion devices certified by EMVCo, Visa, CC EAL6+, PSA, ioXt, and governments across the globe&quot; [@intrinsic-id-sram-puf]. The PUF noise distribution is controlled and the entropy budget is enormous, so the DRS construction works exactly as advertised. Consumer face and fingerprint biometrics are a different regime: the noise model is adversarial, the entropy budget is small, and the construction&apos;s inequality forbids the key length an OS authenticator needs.

Because the hash is avalanche-sensitive by design: a single-bit input change flips, on average, half the output bits. Two scans of the same finger differ in many bits, so two hashes differ in roughly half their bits. The cryptographic key is statistically independent of the previous one, and the user can never log in again after their first authentication. This is the failure mode that motivates the fuzzy-extractor primitive in section 1 [@hao-anderson-daugman-2005-tr].

Because of the load-bearing inequality at the heart of the EUROCRYPT 2004 paper. For consumer face and fingerprint biometrics at the security parameter an operating system authenticator demands ($\varepsilon = 2^{-80}$ or stronger), the extractable key length is negative: the source min-entropy is too low to absorb the cost of code redundancy plus the security parameter [@dors-2008-siamjc], [@frs-2020-ieeetit]. No amount of clever engineering moves the constants.

Yes. The iris is the only common biometric that comfortably clears the DRS bar. Daugman&apos;s 2003 Pattern Recognition paper reports 249 statistical degrees of freedom across 9.1 million iris-to-iris comparisons [@daugman-2003-pdf]; Hao, Anderson, and Daugman in 2006 demonstrated a 140-bit iris key with 99.5% recovery success on 70 eyes [@hao-anderson-daugman-2006-ieeetc]. But iris sensors are expensive, intrusive, and rarely shipped in consumer phones or laptops, so the result has not generalised to mainstream consumer authentication.

Deep-learning encoders such as Engelsma-Cao-Jain&apos;s DeepPrint reduce intra-user variance by mapping noisy raw biometric readings into compact embeddings [@engelsma-cao-jain-2019-arxiv]. That reduces the noise the secure sketch has to absorb and lets the code use less redundancy. But the deep encoder does not add min-entropy to the source: the underlying fingerprint is still a 40-to-80-bit source. No published construction has been shown to clear the DRS bar on a realistic correlated-noise test set for any consumer biometric other than iris.

Unlikely without one of two changes. Either (a) the sensor stack would have to gain entropy -- for instance, adding an iris camera to a future Surface device would put the source above the DRS bar -- or (b) a CFPRS-style reusable computational fuzzy extractor would have to mature past the digital-locker idealisation [@cfprs-2016-eurocrypt]. Even then, the operational advantages of hardware-bound asymmetric keys (TPM-anchored attestation, IdP-friendly key rotation, no helper-data leakage on re-enrolment) are large enough that a fuzzy extractor would have to clear a high bar to displace the current architecture.
&lt;p&gt;The fuzzy extractor is the right primitive for the right source. SRAM PUFs are that source; consumer face and fingerprint biometrics are not. The 2004 inequality drew the line, two decades of theory have refined the line, and every shipped consumer biometric authenticator on the planet has chosen to live on the other side of it.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;fuzzy-extractors-windows-hello&quot; keyTerms={[
  { term: &quot;Fuzzy extractor&quot;, definition: &quot;A pair (Gen, Rep) producing a stable key R from a noisy source w plus a public helper P; defined by Dodis-Reyzin-Smith 2004.&quot; },
  { term: &quot;Secure sketch&quot;, definition: &quot;The noise-tolerance half of a fuzzy extractor; SS publishes a sketch s, Rec recovers w from any w&apos; within distance t given s.&quot; },
  { term: &quot;Strong randomness extractor&quot;, definition: &quot;The uniformity half of a fuzzy extractor; turns a high-min-entropy source into a uniform key, via universal hashing and the Leftover Hash Lemma.&quot; },
  { term: &quot;Leftover Hash Lemma (LHL)&quot;, definition: &quot;Impagliazzo-Levin-Luby 1989: a universal hash applied to a min-entropy source is statistically close to uniform, with budget ell &amp;lt;= m - 2 log(1/epsilon) + 2.&quot; },
  { term: &quot;Min-entropy (H_infinity)&quot;, definition: &quot;Worst-case guessing-difficulty entropy measure; the right measure for cryptographic key derivation from a peaked distribution.&quot; },
  { term: &quot;Average min-entropy&quot;, definition: &quot;Conditional min-entropy that averages an adversary&apos;s best guess over the values of a public side-channel; the right measure for secure-sketch composition.&quot; },
  { term: &quot;Helper data (P)&quot;, definition: &quot;The public part of a fuzzy extractor&apos;s output: the sketch plus the extractor seed. Available at recovery time; leaks at most epsilon bits about R.&quot; },
  { term: &quot;Trustlet (VBS)&quot;, definition: &quot;A Virtual Trust Level 1 user-mode process on Windows, isolated from the normal kernel by Hyper-V; Windows Hello runs its biometric matcher inside a trustlet.&quot; }
]} questions={[
  { q: &quot;Why does SHA-256(fingerprint_image) fail as a cryptographic key?&quot;, a: &quot;SHA-256 is avalanche-sensitive: a single-bit input change flips half the output bits. Two scans of the same finger differ in many bits, so two hashes are statistically independent. The key is unrecoverable on the second scan.&quot; },
  { q: &quot;What does the DRS 2004 inequality bound, and what are its three terms?&quot;, a: &quot;It bounds the extractable key length ell &amp;lt;= H_infinity(W) - (n-k) - 2 log(1/epsilon) + 2. The three terms are the source min-entropy, the code redundancy paid to absorb noise, and the security parameter cost paid to the Leftover Hash Lemma.&quot; },
  { q: &quot;What is the architectural difference between deriving a key from a biometric and gating a key on a biometric?&quot;, a: &quot;Deriving makes the biometric itself the secret; if the biometric leaks, the key is at risk. Gating generates a key independently and uses the biometric only to decide whether to release it; the key&apos;s secrecy is anchored in hardware (TPM, Secure Enclave) and is independent of the biometric.&quot; },
  { q: &quot;Why does Windows Hello not use a fuzzy extractor?&quot;, a: &quot;Because the DRS inequality forbids a useful key on consumer face or fingerprint at security parameters an OS demands; because helper-data leakage compounds under re-enrolment; and because hardware-anchored match-then-unwrap gives TPM-backed attestation that no software fuzzy extractor can match.&quot; },
  { q: &quot;Where are fuzzy extractors actually deployed in production?&quot;, a: &quot;In SRAM PUFs. Synopsys PUF IP (including QuiddiKey) embeds a DRS-style fuzzy extractor in over a billion devices certified by EMVCo, Visa, CC EAL6+, PSA, ioXt, and governments. The PUF noise model is controlled and the entropy budget is large enough.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>cryptography</category><category>biometrics</category><category>fuzzy-extractors</category><category>windows-hello</category><category>tpm</category><category>authentication</category><category>information-theory</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>Post-Quantum Cryptography on Windows: The Thirty-Year Migration That Just Arrived</title><link>https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/</link><guid isPermaLink="true">https://paragmali.com/blog/post-quantum-cryptography-on-windows-the-thirty-year-migrati/</guid><description>How NIST FIPS 203/204/205 reaches the Windows platform via SymCrypt, CNG, Schannel, and .NET 10 -- the algorithm internals, the wire format, the migration timeline, and the honest accounting.</description><pubDate>Mon, 11 May 2026 00:00:00 GMT</pubDate><content:encoded>
**Post-quantum cryptography arrived on Windows in 2024-2026.** NIST finalised FIPS 203 (ML-KEM), FIPS 204 (ML-DSA), and FIPS 205 (SLH-DSA) on August 13, 2024 [@nist-fips-approved-news]. SymCrypt has shipped ML-KEM, ML-DSA, LMS, and composite-ML-KEM implementations across versions 103.5.0 through 103.11.0; CNG exposes them as `BCRYPT_MLKEM_ALG_HANDLE` and `BCRYPT_MLDSA_ALGORITHM`; Schannel can negotiate hybrid TLS 1.3 `X25519MLKEM768` (codepoint 0x11EC) on 24H2 behind Group Policy [@symcrypt-changelog, @cng-mlkem-examples, @draft-tls-ecdhe-mlkem]. The migration closes the harvest-now-decrypt-later channel for TLS-protected traffic, leaves the signed-binary persistence channel open, and is structurally constrained by the 4096-byte TPM 2.0 command buffer against which ML-DSA-87&apos;s 4595-byte signatures overflow [@fips-204-pdf, @wolfssl-wolftpm-v185].
&lt;h2&gt;1. The 1184-Byte Field&lt;/h2&gt;
&lt;p&gt;A Windows endpoint opens a connection to &lt;code&gt;cloudflare.com&lt;/code&gt;. In its ClientHello, alongside the 32-byte X25519 public value every TLS 1.3 handshake has carried since 2018, sits a new 1184-byte field whose contents look like uniform noise -- an ML-KEM-768 encapsulation key, the bytes by which Microsoft, Cloudflare, Google, Apple, and OpenSSH have chosen to close a future they cannot yet see [@draft-tls-ecdhe-mlkem, @cloudflare-pq-2024].&lt;/p&gt;
&lt;p&gt;Two adversaries are watching the handshake. The first has 2026 compute and cannot break either share. The second has a hypothetical 2040 fault-tolerant quantum computer, breaks the X25519 share trivially via Shor&apos;s algorithm, and walks away unable to recover the ML-KEM-768 session key. Why does the handshake hold against the second adversary, and what did it take to make that field 1184 bytes long?&lt;/p&gt;

A family of cryptographic algorithms whose security rests on mathematical problems for which no efficient quantum algorithm is known. PQC is a public-key replacement programme: it replaces RSA, Diffie-Hellman, and elliptic-curve discrete-log primitives that Shor&apos;s algorithm collapses in polynomial time on a fault-tolerant quantum computer. Symmetric primitives (AES, SHA-2/3) survive with parameter increases and are not the target of PQC standardisation.
&lt;p&gt;The wire format is concrete and currently shipping. The IETF draft &lt;code&gt;draft-ietf-tls-ecdhe-mlkem-04&lt;/code&gt; (published 8 February 2026) defines three hybrid Supported Groups codepoints in TLS 1.3: &lt;code&gt;X25519MLKEM768&lt;/code&gt; at 0x11EC, &lt;code&gt;SecP256r1MLKEM768&lt;/code&gt; at 0x11EB, and &lt;code&gt;SecP384r1MLKEM1024&lt;/code&gt; at 0x11ED [@draft-tls-ecdhe-mlkem, @iana-tls-parameters]. The ClientHello &lt;code&gt;key_share&lt;/code&gt; extension carries 32 bytes of X25519 public value followed by 1184 bytes of ML-KEM-768 encapsulation key. The ServerHello reply carries 32 bytes of X25519 public value followed by 1088 bytes of ML-KEM-768 ciphertext. Both endpoints derive an X25519 shared secret and an ML-KEM-768 shared secret, concatenate them, and feed both into TLS 1.3&apos;s HKDF-Extract per &lt;code&gt;draft-ietf-tls-hybrid-design-16&lt;/code&gt; [@draft-tls-hybrid]. An adversary who can break either component but not both still learns nothing.&lt;/p&gt;

A threat model in which an adversary records today&apos;s network traffic and stores it for years, decrypting it once a sufficiently capable quantum computer is available. The threat applies to any traffic whose secrecy must survive past the time-to-cryptographically-relevant-quantum-computer; it does not apply to signed-binary integrity, which is validated at load time. Hybrid TLS shifts the boundary from &quot;must trust X25519 forever&quot; to &quot;must trust either X25519 or ML-KEM-768 forever&quot; [@cloudflare-pq-2024, @mosca-2015].
&lt;p&gt;The first internet-scale deployment of the construction landed on October 3, 2022, when Cloudflare turned on hybrid post-quantum key agreement by default for every website and API on its edge [@cloudflare-pq-for-all].Cloudflare&apos;s blog post measured the bytes-on-the-wire cost of the deployment as roughly 1.1 KB per handshake added; by March 2024 nearly two percent of all TLS 1.3 connections to Cloudflare&apos;s edge negotiated post-quantum key agreement, with double-digit adoption forecast by year-end [@cloudflare-pq-2024]. The Cloudflare default-on date predated FIPS 203&apos;s August 2024 finalisation by almost two years, which is why early deployments speak of &quot;Kyber&quot; and &quot;X25519Kyber768Draft00&quot; rather than ML-KEM.&lt;/p&gt;
&lt;p&gt;Apple&apos;s iMessage PQ3 followed in February 2024, framed as &quot;Level 3&quot; -- post-quantum key establishment plus post-quantum ratcheting [@apple-imessage-pq3]. By May 2026, Microsoft, Google, OpenSSH, and Signal have all shipped or announced hybrid post-quantum key agreement; Section 7 catalogues the per-vendor deployments verbatim, anchored to each vendor&apos;s own release artifact [@cloudflare-pq-2024, @signal-pqxdh, @openssh-9-9].&lt;/p&gt;
&lt;p&gt;This article delivers two promises. The first is algorithm-level: by the end of Section 5 you will know ML-KEM, ML-DSA, and SLH-DSA well enough to reason about parameter-set choices, side-channel posture, and FIPS-mandated byte counts. The second is platform-level: by the end of Section 6 you will know which CNG identifier ships in which SymCrypt release, which Schannel toggle gates X25519MLKEM768 on 24H2, and which Windows surfaces (Schannel, AD CS, .NET 10, Azure Key Vault) carry PQC in May 2026 and which (IKEv2, SMB, RDP, &lt;a href=&quot;https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-/&quot; rel=&quot;noopener&quot;&gt;BitLocker network unlock&lt;/a&gt;, Kerberos PKINIT, &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;Windows Hello attestation&lt;/a&gt;) do not.&lt;/p&gt;
&lt;p&gt;Every line of code, every parameter set, every byte of that 1184-byte field has a thirty-year story behind it. To understand what shipped, we start where it began -- with a 1994 paper that put a clock on every public-key cryptosystem then in production.&lt;/p&gt;
&lt;h2&gt;2. Historical Origins&lt;/h2&gt;
&lt;p&gt;Why is replacing public-key cryptography hard? Because in 1976, Whitfield Diffie and Martin Hellman defined the primitive that &lt;em&gt;everything since&lt;/em&gt; has imitated. Their &quot;New Directions in Cryptography&quot; paper, in &lt;em&gt;IEEE Transactions on Information Theory&lt;/em&gt; 22(6), introduced the asymmetric key-agreement model [@dh-1976]: two parties exchange public values, derive a shared secret, and never share the underlying private state. The shared secret was the discrete logarithm of a public element in a finite group. Every public-key construction that followed -- RSA (1977), the Diffie-Hellman variants, DSA (1991), ECDSA and the elliptic-curve variants (mid-1980s into the 1990s, with X25519 standardised in RFC 7748 in 2016) -- inherited one of two hard problems: integer factoring, or the discrete logarithm in some abelian group [@rfc-7748].&lt;/p&gt;
&lt;p&gt;Eighteen years later, Peter Shor at Bell Labs found a polynomial-time quantum algorithm for both [@shor-1996]. The arXiv preprint &lt;code&gt;quant-ph/9508027&lt;/code&gt; dates to August 1995; the journal version appeared as &quot;Polynomial-Time Algorithms for Prime Factorization and Discrete Logarithms on a Quantum Computer&quot; in &lt;em&gt;SIAM Journal on Computing&lt;/em&gt; 26(5) (1997) 1484-1509; DOI 10.1137/S0097539795293172. Shor&apos;s algorithm requires a fault-tolerant quantum computer with thousands of logical qubits -- the kind of machine that does not yet exist, and may never exist in some accounts. But if it does exist, RSA, DH, DSA, ECDSA, and ECDH all collapse simultaneously. Not weakened; &lt;em&gt;broken&lt;/em&gt;. Doubling key sizes does not help; the algorithm&apos;s runtime is polynomial in the key length.&lt;/p&gt;

A polynomial-time quantum algorithm, due to Peter Shor (1994-1996), that solves integer factoring and the discrete logarithm in arbitrary abelian groups [@shor-1996]. The algorithm reduces both problems to finding the period of a function via the Quantum Fourier Transform, which a fault-tolerant quantum computer can compute in time polynomial in the input size. RSA, finite-field Diffie-Hellman, DSA, and the elliptic-curve variants ECDH/ECDSA/X25519 are all structurally retired by Shor&apos;s algorithm; no parameter increase rescues them.
&lt;p&gt;Two years later, Lov Grover (also at Bell Labs) published the symmetric-key counterpart. Grover&apos;s algorithm searches an unstructured database of N items in O(sqrt(N)) quantum steps [@grover-1996]. Applied to AES-128, Grover reduces the effective key strength to roughly $2^{64}$ quantum-search steps -- comparable to a 64-bit symmetric key. Applied to AES-256, it leaves 128 bits of security. The asymmetric lane is fatal; the symmetric lane is a parameter bump. This is why the entire post-quantum programme is a &lt;em&gt;public-key&lt;/em&gt; replacement programme, not a symmetric one.The standard policy response to Grover is to double the symmetric key size. AES-256 retains 128 bits of post-quantum security; SHA-384 retains 192 bits of preimage resistance; SHA-512 retains 256 bits. CNSA 2.0 mandates AES-256 and SHA-384 specifically for this reason [@cnsa20-csa]. Grover-style speedups do not generalise to AEAD constructions in the same way the asymmetric collapse does; the cost of doubling is structural and easy to absorb, which is why no one tries to invent a &quot;post-quantum AES.&quot;&lt;/p&gt;
&lt;p&gt;If Shor and Grover are 1994-1996 results, why is replacing public-key cryptography not a 2040 problem? Michele Mosca&apos;s 2015 ePrint 2015/1075 named the deadline. Mosca&apos;s inequality is one line:&lt;/p&gt;
&lt;p&gt;$$X + Y &amp;gt; Z$$&lt;/p&gt;
&lt;p&gt;where X is the security shelf-life of the data (how long today&apos;s traffic must remain confidential), Y is the migration time (how long it takes to deploy quantum-safe systems), and Z is the time until a cryptographically relevant quantum computer arrives. If X + Y exceeds Z, the adversary harvesting traffic today wins regardless of when the quantum computer arrives [@mosca-2015].&lt;/p&gt;

The deadline relation $X + Y &amp;gt; Z$: if data-secrecy lifetime (X) plus migration time (Y) exceeds time-to-quantum-computer (Z), harvest-now-decrypt-later succeeds. Mosca&apos;s framing turned an open quantum-engineering timeline into an actionable IT-policy lever; if you cannot predict Z, you must minimise Y, which means starting migration now [@mosca-2015].

If the security shelf-life of your data plus the migration time to deploy quantum-safe systems exceeds the time-to-quantum-computer, the adversary harvesting traffic today wins. -- the X + Y &amp;gt; Z framing, Mosca (eprint 2015/1075).
&lt;p&gt;On September 7, 2022, the U.S. National Security Agency turned Mosca&apos;s inequality into national-security policy. The Commercial National Security Algorithm Suite 2.0 (CNSA 2.0) is the algorithm list the NSA requires for protecting U.S. National Security Systems [@nsa-cnsa-news, @cnsa20-csa]. The current revision (May 30, 2025) names ML-KEM-1024 for key establishment, ML-DSA-87 for general digital signatures, LMS and XMSS for firmware signing, AES-256 for symmetric encryption, and SHA-384 for hashing. The policy carries four dates that drive every U.S. vendor roadmap including Microsoft&apos;s: acquisition preference for PQC in new National Security Systems by January 1, 2027; legacy-algorithm phase-out beginning December 31, 2030; mandatory PQC adoption by December 31, 2031; and disallowance of RSA / ECDSA after 2035.&lt;/p&gt;
&lt;p&gt;Shor&apos;s algorithm requires a fault-tolerant quantum computer that does not yet exist. So why isn&apos;t the migration easy? Because cryptographers tried to replace the asymmetric primitive for thirty years before this paper -- and every early attempt failed in a different way.&lt;/p&gt;
&lt;h2&gt;3. Early Approaches and Their Failures&lt;/h2&gt;
&lt;p&gt;Three rejected family trees and one almost-survivor explain why ML-KEM looks the way it does in 2026. Each was tried; each failed in a specific way; the failure shaped what survived.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;McEliece (1978)&lt;/strong&gt; is the oldest post-quantum proposal still under active study. Robert McEliece&apos;s construction uses the hardness of decoding a general linear code -- specifically, a binary Goppa code disguised by random permutations and a scrambling matrix [@mceliece-1978]. The cryptosystem has survived forty-eight years of cryptanalysis with no structural break; its security argument is one of the most conservative in cryptography. The cost is the public key. Classic McEliece at NIST security category 1 has public keys of roughly 261 kilobytes; at category 5, about 1 megabyte [@mceliece-project]. That size makes it unusable in TLS, where the entire ClientHello must fit in one or two IP packets. Classic McEliece survives as a Round-4 NIST candidate; it was &lt;em&gt;not&lt;/em&gt; selected for FIPS standardisation because of the key-size constraint, but is widely cited as the conservative fallback for long-term archival key wrapping.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HFE and multivariate cryptography (1996)&lt;/strong&gt; form the most thoroughly broken family. Jacques Patarin&apos;s Hidden Field Equations (HFE) hide the structure of a univariate polynomial over a small extension field by composing with random linear transformations on each side. Kipnis and Shamir broke the original HFE construction in 1999 [@kipnis-shamir-1999]. The descendant scheme Rainbow advanced through three NIST rounds before Ward Beullens published &quot;Breaking Rainbow Takes a Weekend on a Laptop&quot; in eprint 2022/214 on 25 February 2022, recovering Rainbow&apos;s secret key in 53 hours on a commodity laptop [@beullens-rainbow-2022].53 hours on a commodity laptop is the visceral data point. Rainbow had been a NIST third-round signature finalist; one paper, one weekend of CPU time, retired it. Beullens&apos; result is now the canonical example in PQC pedagogy of how a cryptographic finalist can be retired by an algorithmic insight that nobody noticed during seven years of NIST evaluation. The multivariate signature lane is effectively closed in 2026, with the partial exception of small specialised constructions (UOV-style schemes) that NIST is considering in the additional-signatures onramp [@nist-pqc-dig-sig].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NTRU (1996)&lt;/strong&gt; is the founding lattice cryptosystem. Jeffrey Hoffstein, Jill Pipher, and Joseph Silverman presented &quot;NTRU: A ring-based public key cryptosystem&quot; at ANTS-III in 1998 [@ntru-1996]. The construction works in a polynomial ring $R = \mathbb{Z}[X]/(X^n - 1)$ and offers public keys of roughly 1-2 kilobytes -- the first lattice cryptosystem with sizes competitive with RSA. NTRU was patent-encumbered for two decades (US Patents 6,081,597 and 6,144,740 expired in August and November 2017) [@ntru-patents], which kept it out of standards work for the formative years. Falcon, the NIST-selected lattice signature scheme that became FIPS 206 draft, inherits the NTRU lattice structure directly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SIDH and SIKE (2011-2022)&lt;/strong&gt; were the most efficient post-quantum proposal by public-key size. Supersingular Isogeny Diffie-Hellman, introduced by Jao and De Feo in 2011 [@jao-defeo-2011], achieved public keys of roughly 330 bytes at category 1 [@wp-sidh] -- smaller than ML-KEM-512&apos;s 800 bytes. NIST advanced SIKE to the fourth round of evaluation on 5 July 2022 [@nist-pqc-selection-2022]. On 27 July 2022, twenty-two days later, Wouter Castryck and Thomas Decru published &quot;An efficient key recovery attack on SIDH,&quot; recovering SIKEp434&apos;s secret key in about ten minutes on a single CPU core via a torsion-point exploitation of Kani&apos;s reducibility criterion [@castryck-decru-sidh]. The higher-security parameter set SIKEp751 (NIST category 5) fell in roughly three hours on the same hardware. A concurrent paper by Maino and Martindale extended the attack to arbitrary starting curves [@maino-martindale-sidh]. One paper, one month, the entire isogeny lane retired. SIKE is the canonical example of why NIST&apos;s portfolio rests on multiple unrelated hardness assumptions.&lt;/p&gt;

flowchart TD
    PQ[&quot;Post-Quantum Cryptography&quot;]
    PQ --&amp;gt; Lat[&quot;Lattice&lt;br /&gt;(LWE, Module-LWE, NTRU)&quot;]
    PQ --&amp;gt; Code[&quot;Code-based&lt;br /&gt;(Goppa, Quasi-Cyclic)&quot;]
    PQ --&amp;gt; Multi[&quot;Multivariate&lt;br /&gt;(HFE, Rainbow)&quot;]
    PQ --&amp;gt; Hash[&quot;Hash-based&lt;br /&gt;(XMSS, LMS, SPHINCS+)&quot;]
    PQ --&amp;gt; Iso[&quot;Isogeny&lt;br /&gt;(SIDH, SIKE)&quot;]
    Lat --&amp;gt; LatV[&quot;ACTIVE: ML-KEM, ML-DSA, Falcon&quot;]
    Code --&amp;gt; CodeV[&quot;NICHE: HQC, Classic McEliece&quot;]
    Multi --&amp;gt; MultiV[&quot;DEAD: Rainbow broken 2022&quot;]
    Hash --&amp;gt; HashV[&quot;ACTIVE: SLH-DSA, LMS, XMSS&quot;]
    Iso --&amp;gt; IsoV[&quot;DEAD: SIDH/SIKE broken 2022&quot;]

A proof technique, introduced for lattices by Miklos Ajtai in 1996 and refined for LWE by Oded Regev in 2005, that ties the average-case security of a cryptosystem to the worst-case hardness of an underlying lattice problem [@regev-2005]. The reduction says: solving random instances of the cryptosystem at any non-negligible advantage gives an algorithm for the *worst-case* hard problem. RSA has no analogous reduction; the average factoring instance is conjectured hard, but no theorem ties it to worst-case factoring. The lattice reduction is the structural argument for why post-quantum lattice cryptography may be more conservative, in a formal sense, than RSA.
&lt;p&gt;The portfolio lesson lands here, and it is the article&apos;s first aha moment. Post-quantum cryptography is not a single family; it is a &lt;em&gt;portfolio&lt;/em&gt; across multiple hardness assumptions, because each one has been broken at least once during the modern standardisation effort. The Rainbow break and the SIKE break both happened &lt;em&gt;during&lt;/em&gt; the NIST competition, in 2022, on candidates that NIST had advanced for further study. This is why the eventual slate -- ML-KEM (lattice) plus SLH-DSA (hash) -- sits on &lt;em&gt;two structurally unrelated&lt;/em&gt; foundations. A single mathematical break cannot retire the whole programme.&lt;/p&gt;
&lt;p&gt;Lattices survived. But the lattices of 2005 had megabyte-scale public keys, unusable in TLS. How those keys were compressed to kilobytes is the story of the next section.&lt;/p&gt;
&lt;h2&gt;4. The Evolution -- Lattices in Five Generations&lt;/h2&gt;
&lt;p&gt;In 2005, Oded Regev published a paper that gave lattice cryptography the mathematical foundation RSA never had. By 2010, the same idea had been compressed by a factor of &lt;code&gt;n&lt;/code&gt; via the Number Theoretic Transform; by 2015 it had been generalised with a parameter knob that let one base ring serve every security category; by 2024 it was a Federal Information Processing Standard. This section walks the generation-by-generation story of how lattices got from impossible to inevitable.&lt;/p&gt;
&lt;h3&gt;Generation 0 (1976-1994): the classical baseline&lt;/h3&gt;
&lt;p&gt;Diffie-Hellman, RSA, DSA, ECDH, ECDSA. Five primitives over four decades, all on discrete-log-style hardness in one group or another, all retired in one stroke by Shor&apos;s algorithm. The classical baseline is what PQC replaces. Nothing about post-quantum cryptography innovates on the symmetric side; AES and SHA-2 survive with parameter increases.&lt;/p&gt;
&lt;h3&gt;Generation 1 (1996-2009): plural hard problems, mostly impractical&lt;/h3&gt;
&lt;p&gt;Miklos Ajtai&apos;s 1996 STOC paper &quot;Generating Hard Instances of Lattice Problems&quot; introduced the first worst-case-to-average-case reduction for a lattice problem (the Short Integer Solution problem) [@ajtai-1996]. The reduction was a foundational theoretical result; the cryptographic constructions built from it had public keys in the megabytes.&lt;/p&gt;
&lt;p&gt;Nine years later, Oded Regev published &quot;On Lattices, Learning with Errors, Random Linear Codes, and Cryptography&quot; at STOC 2005 [@regev-2005]. The Learning With Errors problem is simple to state.&lt;/p&gt;

Given a uniformly random matrix $A \in \mathbb{Z}_q^{m \times n}$, a secret vector $s \in \mathbb{Z}_q^n$, and a small noise vector $e$ sampled from a Gaussian-like distribution, distinguish the pair $(A, As + e)$ from a uniformly random pair $(A, b)$ where $b$ is uniform in $\mathbb{Z}_q^m$. LWE is conjectured hard for any polynomial-time algorithm classical or quantum; Regev&apos;s theorem ties LWE to the worst-case hardness of approximating shortest-vector problems on $n$-dimensional lattices, via a quantum reduction [@regev-2005].
&lt;p&gt;LWE was the cryptographic breakthrough. The construction was clean, the reduction tied average-case security to worst-case lattice hardness, and the resulting cryptosystem was simple enough that any cryptographer could implement it. But the public key was a full $n \times n$ matrix over $\mathbb{Z}_q$ -- $O(n^2 \log q)$ bits. At the parameter sizes needed for 128-bit security, that meant several megabytes of public key. Unusable in TLS, unusable in X.509, unusable in any deployment that touches the wire.&lt;/p&gt;
&lt;h3&gt;Generation 2 (2010-2017): the ring-LWE and module-LWE compression&lt;/h3&gt;
&lt;p&gt;The compression that made lattices deployable was a single algebraic move. Lift LWE from $\mathbb{Z}_q$ to a polynomial ring. Lyubashevsky, Peikert, and Regev&apos;s 2010 paper &quot;On Ideal Lattices and Learning with Errors over Rings&quot; (eprint 2012/230) introduced Ring-LWE [@lpr-2010-ringlwe]. The underlying ring is $R_q = \mathbb{Z}_q[X]/(X^n + 1)$ for $n$ a power of two; the secret and noise are now polynomials in $R_q$ rather than vectors over $\mathbb{Z}_q$. Multiplying two ring elements becomes a polynomial multiplication, which the Number Theoretic Transform reduces from $O(n^2)$ scalar multiplications to $O(n \log n)$.&lt;/p&gt;

A discrete Fourier transform over a finite field rather than the complex numbers. For a prime $q$ such that $2n$ divides $q - 1$, NTT converts a polynomial $a(X) \in \mathbb{Z}_q[X]/(X^n + 1)$ into its evaluations at the $2n$-th roots of unity in $\mathbb{Z}_q$. Polynomial multiplication then becomes pointwise multiplication of the NTT vectors. NTT is the speedup that compresses Ring-LWE arithmetic from $O(n^2)$ to $O(n \log n)$ and is the reason ML-KEM-768 encapsulates in tens of microseconds on commodity x86-64 [@fips-203-pdf].
&lt;p&gt;Public keys dropped from megabytes to kilobytes. The 2010 lift is the load-bearing intellectual move; everything subsequent is engineering.&lt;/p&gt;
&lt;p&gt;Adeline Langlois and Damien Stehle&apos;s 2012/2015 Module-LWE paper added a parameter knob [@langlois-stehle-modulelwe]. Module-LWE works over $R_q$ rings of &lt;em&gt;fixed&lt;/em&gt; degree $n$ (typically 256 in ML-KEM), but lifts the secret and matrix into module rank $k$: $A$ is a $k \times k$ matrix of ring elements, $s$ is a $k$-vector of ring elements. Now one base ring of degree 256 can serve every NIST security category by varying $k \in {2, 3, 4}$. ML-KEM-512 uses $k = 2$; ML-KEM-768 uses $k = 3$; ML-KEM-1024 uses $k = 4$. The compiler-style metaphor is exact: Ring-LWE was an over-fitted special case, Module-LWE generalises it.&lt;/p&gt;

A generalisation of Learning With Errors over polynomial rings of fixed degree, in which the secret is a $k$-vector of ring elements and the matrix is $k \times k$. Module-LWE inherits the worst-case-to-average-case reduction from Ring-LWE [@langlois-stehle-modulelwe], offers a finer-grained security knob than either LWE or Ring-LWE, and is the underlying hardness assumption of ML-KEM (FIPS 203) and ML-DSA (FIPS 204) [@fips-203-pdf, @fips-204-pdf].
&lt;p&gt;The first TLS deployment of a Ring-LWE key exchange landed in 2014, and Microsoft Research was at the centre of it.&lt;/p&gt;

The BCNS 2014 paper &quot;Post-quantum key exchange for the TLS protocol from the ring learning with errors problem&quot; by Joppe Bos, Craig Costello, Michael Naehrig, and Douglas Stebila (eprint 2014/599) was the first end-to-end TLS implementation of a Ring-LWE key exchange [@bcns-2014]. Two of the four authors -- Costello and Naehrig -- were Microsoft Research Redmond. Two years later, the same Microsoft Research group plus collaborators published Frodo (CCS 2016, eprint 2016/659), the unstructured-LWE conservative fallback design with no ring algebra [@frodo-2016]. Frodo became FrodoKEM in the NIST process; FrodoKEM was selected as a Round-3 alternate but not advanced to standardisation [@frodokem-project]. Microsoft Research&apos;s own retrospective spans this work: &quot;Our PQC effort began in 2014 when we published research on post-quantum algorithms and later quantum cryptanalysis ... we participated in four submissions to the original 2017 NIST PQC call and one submission to the current call. Since 2018 we have been experimenting with verified versions of PQC algorithms and in 2019 Microsoft Research completed testing of an experimental PQC-protected VPN tunnel between Redmond, Washington, and Scotland&quot; [@ms-quantum-safe-blog]. The 2024 FIPS publication did not surprise Microsoft.
&lt;p&gt;Google&apos;s Chrome team deployed the construction in production first. CECPQ1 (&quot;Combined Elliptic-Curve and Post-Quantum 1&quot;) shipped in Chrome Canary in July 2016, combining X25519 with NewHope [@google-cecpq1]. NewHope was a Ring-LWE construction by Alkim, Ducas, Poppelmann, and Schwabe; CECPQ1 ran for several months as an experiment, measured the cost of an extra ~2 KB on each handshake, and was retired. CECPQ2 replaced it with NTRU-HRSS -- the announcement post by Adam Langley names the lineage explicitly (&quot;CECPQ1 was the experiment ... It&apos;s about time for CECPQ2&quot;) and the NTRU-HRSS basis -- and was wound down in 2022 as Chrome migrated to the X25519+Kyber-768 hybrid following NIST&apos;s July 2022 selection [@cloudflare-pq-2024]. The parallel CECPQ2b experiment paired X25519 with SIKE; the Castryck-Decru break that same month retired CECPQ2b along with the entire isogeny lane. The Cloudflare-Microsoft-Google triad has been iterating in production since.&lt;/p&gt;
&lt;h3&gt;Generation 3 (2017-2022): the NIST competition&lt;/h3&gt;
&lt;p&gt;NIST issued the formal call for post-quantum public-key submissions in December 2016. Eighty-two submissions arrived by the November 2017 deadline; sixty-nine were judged complete and proper, advancing into Round 1 (announced December 2017; narrowed to 26 in Round 2, January 2019) [@wp-nist-pqc].The 82-vs-69 discrepancy is a frequent source of confusion in PQC pedagogy. Eighty-two total submissions, sixty-nine deemed &quot;complete and proper&quot; by NIST&apos;s intake review, advanced to Round 1. The remaining thirteen had documentation defects or were withdrawn. Wikipedia&apos;s &quot;NIST Post-Quantum Cryptography Standardization&quot; article spells out both numbers verbatim [@wp-nist-pqc]. The field narrowed to 26 algorithms in Round 2 (January 2019), then to 7 finalists plus 8 alternates in Round 3 (July 2020). NIST IR 8413 (July 2022) is the canonical status report on Round 3 [@nist-ir-8413].&lt;/p&gt;
&lt;p&gt;On 5 July 2022, NIST announced the first four standardisation selections: CRYSTALS-Kyber for key encapsulation, plus CRYSTALS-Dilithium, FALCON, and SPHINCS+ for signatures [@nist-pqc-selection-2022]. Three were lattice schemes; one (SPHINCS+) was hash-based. The same announcement moved Classic McEliece, BIKE, HQC, and SIKE to a fourth round for further evaluation. Twenty-five days later, the Castryck-Decru attack retired SIKE. NIST IR 8545 documents the eventual fourth-round selection of HQC (announced 7 March 2025) over BIKE, with Classic McEliece left as a candidate for niche-use standardisation due to its key size [@nist-hqc-news].&lt;/p&gt;

gantt
    dateFormat YYYY
    axisFormat %Y
    section Algorithm research
    Diffie-Hellman           :milestone, dh, 1976, 0
    Shor&apos;s algorithm         :milestone, shor, 1994, 0
    NTRU                     :milestone, ntru, 1996, 0
    Regev LWE                :milestone, lwe, 2005, 0
    Ring-LWE (LPR)           :milestone, rlwe, 2010, 0
    Module-LWE               :milestone, mlwe, 2012, 0
    BCNS / Frodo / NewHope   :milestone, bcns, 2014, 0
    section NIST process
    PQC call announced       :milestone, call, 2016, 0
    Round 1 (69 candidates)  :milestone, r1, 2017, 0
    Round 2 (26 candidates)  :milestone, r2, 2019, 0
    Round 3 finalists        :milestone, r3, 2020, 0
    Selections + Round 4     :milestone, sel, 2022, 0
    Rainbow + SIKE broken    :milestone, brk, 2022, 0
    FIPS 203 / 204 / 205     :milestone, fips, 2024, 0
    HQC selected             :milestone, hqc, 2025, 0
    section Windows shipping
    SymCrypt v103.5.0 ML-KEM :milestone, sc1, 2024, 0
    Insider Canary CNG PQ    :milestone, can, 2025, 0
    .NET 10 GA               :milestone, dn, 2025, 0
    Schannel X25519MLKEM768  :milestone, sch, 2026, 0
    TPM 2.0 v1.85 PQC        :milestone, tpm, 2026, 0
&lt;h3&gt;Generation 4 (2023-2024): standardisation&lt;/h3&gt;
&lt;p&gt;The draft FIPS standards published in August 2023; the final versions landed on 13 August 2024, when the Secretary of Commerce approved FIPS 203, FIPS 204, and FIPS 205 [@nist-fips-approved-news]. Names changed in the transition: CRYSTALS-Kyber [@crystals-kyber-paper] became Module-Lattice-Based Key-Encapsulation Mechanism (ML-KEM); CRYSTALS-Dilithium became Module-Lattice-Based Digital Signature Algorithm (ML-DSA); SPHINCS+ [@sphincsplus-framework] became Stateless Hash-Based Digital Signature Algorithm (SLH-DSA). The renaming was deliberate; NIST wanted standard names that described the construction rather than the project. Falcon&apos;s standardisation slipped to FIPS 206 in draft, principally because the floating-point Gaussian sampler required for Falcon&apos;s compact signatures is unusually hard to make both fast and constant-time [@nist-pqc-dig-sig].&lt;/p&gt;
&lt;h3&gt;Generation 5 (2024-2026): shipping on Windows&lt;/h3&gt;
&lt;p&gt;SymCrypt v103.5.0 added ML-KEM &quot;per final FIPS 203&quot; along with XMSS and XMSS^MT [@symcrypt-changelog]. Subsequent versions added LMS (v103.6.0), ML-DSA (v103.7.0), FIPS-approved-services indicator (v103.8.0), ML-DSA External-Mu sign/verify (v103.9.0), FIPS CAST plus ML-KEM/ML-DSA keygen pairwise consistency tests (v103.9.1), and Composite ML-KEM (v103.11.0). The Windows Insider Canary channel exposed the CNG identifiers in May 2025 [@ms-pqc-windows-insider]. .NET 10 (GA November 2025) shipped managed types &lt;code&gt;System.Security.Cryptography.MLKem&lt;/code&gt;, &lt;code&gt;MLKemCng&lt;/code&gt;, &lt;code&gt;MLDsa&lt;/code&gt;, &lt;code&gt;MLDsaCng&lt;/code&gt; [@dotnet-10-launch, @dotnet-mlkem, @dotnet-mlkemcng]. Schannel hybrid TLS 1.3 X25519MLKEM768 reached Server 2025 and 24H2 in preview behind Group Policy in early 2026.&lt;/p&gt;
&lt;p&gt;The competition is over. The standards are published. The SymCrypt versions are shipping. We have arrived at the moment where the algorithm internals matter -- because every Windows engineer now writes code against &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt;, and code that uses an algorithm should know how it works.&lt;/p&gt;
&lt;h2&gt;5. The Breakthrough -- ML-KEM, ML-DSA, SLH-DSA at Engineer Depth&lt;/h2&gt;
&lt;p&gt;Three FIPS standards. Three algorithms. Three Windows API surfaces. Each rests on a different hardness assumption. Each has its own parameter zoo, key sizes, and side-channel surface. This section walks all three at the level a Windows engineer needs to make procurement, audit, and migration decisions.&lt;/p&gt;
&lt;h3&gt;5.1 ML-KEM (FIPS 203) -- the default KEM&lt;/h3&gt;
&lt;p&gt;ML-KEM is the only NIST-finalised key-encapsulation mechanism. It is the encryption primitive of the post-quantum era on Windows. The algebra is Module-LWE / Module-LWR over $R_q = \mathbb{Z}_q[X]/(X^{256} + 1)$ with $q = 3329$ -- a 12-bit prime chosen to make NTT arithmetic fast on 16-bit and 32-bit lanes [@fips-203-pdf]. The base ring has degree 256; the module rank $k$ selects the parameter set.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter set&lt;/th&gt;
&lt;th&gt;$k$&lt;/th&gt;
&lt;th&gt;NIST category&lt;/th&gt;
&lt;th&gt;Encapsulation key (bytes)&lt;/th&gt;
&lt;th&gt;Ciphertext (bytes)&lt;/th&gt;
&lt;th&gt;Shared secret (bytes)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ML-KEM-512&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1 (AES-128 equivalent)&lt;/td&gt;
&lt;td&gt;800&lt;/td&gt;
&lt;td&gt;768&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-KEM-768&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;3 (AES-192 equivalent)&lt;/td&gt;
&lt;td&gt;1184&lt;/td&gt;
&lt;td&gt;1088&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-KEM-1024&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;5 (AES-256 equivalent)&lt;/td&gt;
&lt;td&gt;1568&lt;/td&gt;
&lt;td&gt;1568&lt;/td&gt;
&lt;td&gt;32&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The byte counts in the table are verbatim from the FIPS 203 standard [@fips-203-pdf, @wp-kyber]. Cloudflare&apos;s October 2022 deployment and Schannel&apos;s X25519MLKEM768 both target ML-KEM-768 specifically -- the category-3 sweet spot that survives even an aggressive cryptanalytic improvement against Module-LWE [@cloudflare-pq-for-all, @draft-tls-ecdhe-mlkem]. Apple&apos;s PQ3 splits its parameter selection: ML-KEM-1024 for the initial key exchange and Kyber-768 for the ongoing asymmetric ratchet [@apple-imessage-pq3]. OpenSSH 9.0+ deployed a different post-quantum primitive entirely -- Streamlined NTRU Prime in &lt;code&gt;sntrup761x25519-sha512&lt;/code&gt; [@openssh-9-0] -- and OpenSSH 9.9 (released 19 September 2024) added the ML-KEM-768-based group &lt;code&gt;mlkem768x25519-sha256&lt;/code&gt; available by default alongside it [@openssh-9-9].&lt;/p&gt;

A generic construction that converts an IND-CPA-secure public-key encryption scheme into an IND-CCA2-secure key-encapsulation mechanism. The transform re-encrypts the plaintext during decapsulation and verifies the resulting ciphertext bit-for-bit; any mismatch causes decapsulation to return an implicit-rejection pseudorandom value rather than the real shared secret. ML-KEM wraps an IND-CPA-secure scheme called K-PKE with the FO transform; the FO wrapper is what makes ML-KEM safe to use with long-term keys [@fips-203-pdf].
&lt;p&gt;ML-KEM has three operations: &lt;code&gt;KeyGen&lt;/code&gt; produces $(ek, dk)$; &lt;code&gt;Encaps(ek)&lt;/code&gt; produces $(K, c)$ where $K$ is the 32-byte shared secret and $c$ is the ciphertext; &lt;code&gt;Decaps(dk, c)&lt;/code&gt; recomputes $K$. The CNG surface mirrors this exactly. The canonical Microsoft idiom (from Microsoft Learn&apos;s CNG ML-KEM examples, currently marked prerelease) is &lt;code&gt;BCryptGenerateKeyPair&lt;/code&gt; with the pseudo-handle &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt;, followed by &lt;code&gt;BCryptSetProperty&lt;/code&gt; setting &lt;code&gt;BCRYPT_PARAMETER_SET_NAME&lt;/code&gt; to &lt;code&gt;BCRYPT_MLKEM_PARAMETER_SET_768&lt;/code&gt;, followed by &lt;code&gt;BCryptFinalizeKeyPair&lt;/code&gt;, followed by &lt;code&gt;BCryptExportKey&lt;/code&gt; to extract the encapsulation key as a &lt;code&gt;BCRYPT_MLKEM_ENCAPSULATION_BLOB&lt;/code&gt; [@cng-mlkem-examples]. The new verbs &lt;code&gt;BCryptEncapsulate&lt;/code&gt; and &lt;code&gt;BCryptDecapsulate&lt;/code&gt; complete the picture; neither existed in CNG before the ML-KEM surface was added.&lt;/p&gt;

sequenceDiagram
    participant C as Client (Windows / Schannel)
    participant S as Server (Cloudflare / IIS)
    C-&amp;gt;&amp;gt;S: ClientHello key_share = X25519 (32B) || ML-KEM-768 ek (1184B)
    Note over S: Generates X25519 keypair
    Note over S: Computes ML-KEM Encaps(ek)
    Note over S: Yields ct and K_pq
    S-&amp;gt;&amp;gt;C: ServerHello key_share = X25519 (32B) || ML-KEM-768 ct (1088B)
    Note over C: Derives ECDH shared secret K_ecdh
    Note over C: Computes ML-KEM Decaps for K_pq
    Note over C,S: HKDF-Extract IKM = K_ecdh concat K_pq
    Note over C,S: Yields TLS 1.3 traffic secrets
&lt;p&gt;The internal construction of ML-KEM combines an IND-CPA-secure public-key encryption scheme called K-PKE with the Fujisaki-Okamoto-Hofheinz transform to produce an IND-CCA2 KEM. K-PKE is a Regev-style encryption with module structure; the encryption is illustrative-grade simple.&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative ML-KEM K-PKE encryption. // The FIPS 203 standard is the normative source for byte-exact operations. // q = 3329, n = 256, ring R_q = Z_q[X] / (X^n + 1), module rank k in {2, 3, 4}. function kpkeEncrypt(ek: PublicKey, message: number[], seed: Uint8Array) {   const { A, t, k } = ek;           // A is k x k matrix in R_q, t is k-vector in R_q   const r  = sampleSmallCBD(seed, k);     // r:  k-vector, centred binomial noise   const e1 = sampleSmallCBD(seed, k);     // e1: k-vector, fresh noise   const e2 = sampleSmallSingle(seed);     // e2: scalar polynomial, fresh noise   const u = ringMatVecMul(transpose(A), r);   addInPlace(u, e1);                       // u = A^T r + e1   const v = ringDot(t, r);                 // v = t . r   addInPlace(v, e2);                       // v = t.r + e2   const mEncoded = encodeMessage(message); // 256-bit message -&amp;gt; R_q element   addInPlace(v, mEncoded);                 // v += Encode(message)   return { u, v };                          // ciphertext (u in R_q^k, v in R_q) }&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;The IND-CCA2 wrapper that becomes ML-KEM proper is the FO transform: hash the message and randomness into the encapsulation, then re-encrypt during decapsulation and reject if the ciphertext does not match. Decapsulation on a tampered ciphertext returns a pseudorandom shared secret derived from the secret key -- implicit rejection -- rather than an error code that an attacker could observe. This is what gives ML-KEM CCA2 security suitable for static keys in TLS, X.509, and CNG.&lt;/p&gt;
&lt;h3&gt;5.2 ML-DSA (FIPS 204) -- the default lattice signature&lt;/h3&gt;
&lt;p&gt;ML-DSA is the general-purpose lattice signature scheme. Same base ring of degree 256 as ML-KEM, but with a &lt;em&gt;different&lt;/em&gt; prime: $q = 8380417$, a 23-bit prime [@fips-204-pdf]. The disparity is intentional; ML-KEM and ML-DSA do not share keys, so their NTT parameter choices are independently optimised. The construction is Fiat-Shamir-with-aborts over Module-LWE and Module-SIS.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Parameter set&lt;/th&gt;
&lt;th&gt;NIST category&lt;/th&gt;
&lt;th&gt;Public key (bytes)&lt;/th&gt;
&lt;th&gt;Signature (bytes)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ML-DSA-44&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;1312&lt;/td&gt;
&lt;td&gt;2420&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-DSA-65&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;1952&lt;/td&gt;
&lt;td&gt;3293&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ML-DSA-87&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;2592&lt;/td&gt;
&lt;td&gt;4595&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Numbers verbatim from FIPS 204 [@fips-204-pdf]. CNSA 2.0 selects ML-DSA-87 specifically as the general-signature algorithm for U.S. National Security Systems [@cnsa20-csa].&lt;/p&gt;

A signature construction in which the prover commits to a masking value, hashes the message and commitment to derive a challenge, computes a response that depends on the secret and the challenge, and *aborts and retries* if the response would leak the secret. The &quot;abort&quot; probability is bounded so signing completes in a small constant expected number of restarts. The technique, due to Lyubashevsky, is the foundation of ML-DSA&apos;s security argument; the rejection-sampling loop is also the source of a measurable timing variance that constant-time implementations must handle carefully [@fips-204-pdf].

flowchart TD
    Start[&quot;Begin sign(message, sk)&quot;] --&amp;gt; Sample[&quot;Sample masking vector y (small ball)&quot;]
    Sample --&amp;gt; Commit[&quot;Compute w = Ay in R_q&quot;]
    Commit --&amp;gt; Hash[&quot;c = H(message || HighBits(w))&quot;]
    Hash --&amp;gt; Resp[&quot;Compute response z = y + c*s_1&quot;]
    Resp --&amp;gt; Check{&quot;||z||_inf &amp;lt; bound?&lt;br /&gt;||LowBits(w - c*s_2)||_inf &amp;lt; bound?&quot;}
    Check --&amp;gt;|no| Sample
    Check --&amp;gt;|yes| Out[&quot;Return signature (z, c, h)&quot;]
&lt;p&gt;ML-DSA-87&apos;s 4595-byte signature is the bottom-of-stack constraint that drives every TPM and Pluton roadmap [@fips-204-pdf]. The default TPM 2.0 command and response buffers, fixed by historical compatibility decisions, are 4096 bytes. ML-DSA-65&apos;s 3293-byte signature fits; ML-DSA-87&apos;s 4595-byte signature does not. The TCG TPM 2.0 Library Specification v1.85 (March 2026) introduces a streaming Sign/Verify family and ML-KEM &lt;code&gt;Encapsulate&lt;/code&gt;/&lt;code&gt;Decapsulate&lt;/code&gt; opcodes that resolve the overflow; the full opcode inventory and the new &lt;code&gt;TPM2B_KEM_CIPHERTEXT&lt;/code&gt; / &lt;code&gt;TPM2B_SHARED_SECRET&lt;/code&gt; / &lt;code&gt;TPM_ST_MESSAGE_VERIFIED&lt;/code&gt; structures are catalogued in Section 9.1 [@wolfssl-wolftpm-v185]. Until v1.85 chips ship in retail volume, ML-DSA-87 cannot live on a commodity TPM. Cross-reference the &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM&lt;/a&gt; and &lt;a href=&quot;https://paragmali.com/blog/pluton-a-tpm-on-silicon-microsoft-can-patch/&quot; rel=&quot;noopener&quot;&gt;Pluton&lt;/a&gt; sibling articles for the silicon-side mechanics.Pluton&apos;s firmware-update agility -- the firmware ships through the existing Microsoft Update channel -- is the reason Pluton can move on PQ adoption faster than discrete TPM 2.0 chips, whose firmware updates depend on each TPM vendor&apos;s release cadence. The cross-reference to the Pluton sibling article in this series spells out the firmware-update mechanism in detail [@ms-quantum-safe-blog].&lt;/p&gt;
&lt;p&gt;The CNG surface mirrors ML-KEM&apos;s idiom with the signature primitives. &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt; with &lt;code&gt;BCRYPT_MLDSA_ALGORITHM = L&quot;ML-DSA&quot;&lt;/code&gt; and &lt;code&gt;MS_PRIMITIVE_PROVIDER&lt;/code&gt; returns a handle; &lt;code&gt;BCryptSetProperty&lt;/code&gt; selects &lt;code&gt;BCRYPT_MLDSA_PARAMETER_SET_44&lt;/code&gt;, &lt;code&gt;_65&lt;/code&gt;, or &lt;code&gt;_87&lt;/code&gt;; &lt;code&gt;BCryptGenerateKeyPair&lt;/code&gt; plus &lt;code&gt;BCryptFinalizeKeyPair&lt;/code&gt; produces the keypair; key blobs are &lt;code&gt;BCRYPT_PQDSA_PUBLIC_KEY_BLOB&lt;/code&gt; and &lt;code&gt;BCRYPT_PQDSA_PRIVATE_KEY_BLOB&lt;/code&gt;; signing and verification go through &lt;code&gt;BCryptSignHash&lt;/code&gt; and &lt;code&gt;BCryptVerifySignature&lt;/code&gt; with a &lt;code&gt;BCRYPT_PQDSA_PADDING_INFO&lt;/code&gt; struct that selects pure-mode or pre-hash-mode (External-Mu, per FIPS 204&apos;s HashML-DSA variants) and carries an optional context string [@cng-mldsa-examples].&lt;/p&gt;
&lt;p&gt;{&lt;code&gt;// Illustrative ML-DSA signing. // Constants gamma1, gamma2, beta are parameter-set dependent. function mlDsaSign(message: Uint8Array, sk: SecretKey): Signature {   const { A, s1, s2, t0 } = sk;   let attempt = 0;   while (attempt &amp;lt; 1000) {     attempt++;     const y  = sampleMaskingVector(gamma1);     // y in R_q^l, ||y||_inf &amp;lt; gamma1     const w  = ringMatVecMul(A, y);              // w = A*y in R_q^k     const w1 = highBits(w, 2 * gamma2);     const c  = hashToChallenge(message, w1);     // c in B_tau     const z  = addVec(y, scalarMul(c, s1));      // z = y + c*s1     if (infNorm(z) &amp;gt;= gamma1 - beta) continue;   // reject if response too large     const r0 = lowBits(subVec(w, scalarMul(c, s2)), 2 * gamma2);     if (infNorm(r0) &amp;gt;= gamma2 - beta) continue;  // reject if low bits leak     return { z, c, h: makeHint(t0, c, w) };   }   throw new Error(&quot;ML-DSA signing exceeded attempt budget (statistically improbable)&quot;); }&lt;/code&gt;}&lt;/p&gt;
&lt;p&gt;ML-DSA sign time on x86-64 is in the low single-digit milliseconds; verify time is in the hundreds of microseconds. The rejection-sampling loop creates a measurable variance in sign-time -- a side channel that secret-key recovery exploits if the loop count, branch, or memory-access pattern leak.&lt;/p&gt;
&lt;h3&gt;5.3 SLH-DSA (FIPS 205) -- the conservative hash-based signature&lt;/h3&gt;
&lt;p&gt;SLH-DSA&apos;s security rests on hash-function security alone. No lattice. No code. No multivariate. No isogeny. Just preimage resistance and collision resistance of an underlying hash function (SHA-2 or SHAKE). If every algebraic post-quantum assumption breaks tomorrow, hash-based signatures still hold. The cost is signature size and signing time [@fips-205-pdf].&lt;/p&gt;
&lt;p&gt;The construction is a hypertree -- a tree of XMSS subtrees, with WOTS+ (Winternitz One-Time Signature Plus) leaves at each subtree level, and FORS (Forest of Random Subsets) few-time signatures at the bottom layer signing the actual message. The hypertree is sampled fresh per signature via a pseudorandom function of the message, which is what makes SPHINCS+ -&amp;gt; SLH-DSA stateless. Unlike LMS or XMSS, which require the signer to track a counter (because re-using a one-time key reveals the secret), SLH-DSA derives the leaf address from the message hash and a per-signature randomness; no signer state survives between signatures.&lt;/p&gt;

A one-time signature scheme. The signer publishes a public key consisting of hash-chain endpoints; the private key is the chain starts. Signing reveals intermediate chain values that depend on the message digest. A WOTS+ key signs exactly one message; signing a second message with the same key reveals enough chain values to forge any signature. WOTS+ is the leaf primitive of XMSS and SLH-DSA [@fips-205-pdf].

A few-time signature scheme built from $k$ independent hash trees of depth $t$. To sign a message, the signer hashes the message to obtain $k$ leaf indices and reveals the leaf preimage plus authentication path in each tree. Signing many messages with the same FORS key eventually reveals enough leaves to forge, but the few-times threshold is high enough to be tolerable when FORS is the bottom layer of an SLH-DSA hypertree whose root is signed by the layer above [@fips-205-pdf].

flowchart TD
    Root[&quot;SLH-DSA public key = root of top XMSS tree (32-64 bytes)&quot;]
    Root --&amp;gt; T1[&quot;Top XMSS subtree (WOTS+ leaves)&quot;]
    T1 --&amp;gt; T2[&quot;Middle XMSS subtrees (WOTS+ leaves)&quot;]
    T2 --&amp;gt; T3[&quot;More XMSS layers (parameter d controls depth)&quot;]
    T3 --&amp;gt; Bot[&quot;Bottom XMSS subtree&quot;]
    Bot --&amp;gt; FORS[&quot;FORS forest (k trees of depth t)&quot;]
    FORS --&amp;gt; Msg[&quot;Message digest derived from per-signature randomness&quot;]
&lt;p&gt;Twelve parameter sets ship in FIPS 205: every combination of &lt;code&gt;{SHA2, SHAKE}&lt;/code&gt; × &lt;code&gt;{128s, 128f, 192s, 192f, 256s, 256f}&lt;/code&gt; where &lt;code&gt;s&lt;/code&gt; is &quot;small signature, slow signing&quot; and &lt;code&gt;f&lt;/code&gt; is &quot;fast signing, larger signature&quot; [@fips-205-pdf]. Public keys are 32-64 bytes; signatures range from 7,856 bytes (SLH-DSA-SHA2-128s) to 49,856 bytes (SLH-DSA-SHA2-256f). Signing time ranges from ~10 ms (SLH-DSA-SHA2-128f) to several hundred milliseconds at the high end. The use case is &lt;em&gt;code signing&lt;/em&gt;: sign once, verify a billion times. CNG plans &lt;code&gt;BCRYPT_SLHDSA_ALGORITHM&lt;/code&gt; with the same &lt;code&gt;BCRYPT_PQDSA_KEY_BLOB&lt;/code&gt; and &lt;code&gt;BCRYPT_PQDSA_PADDING_INFO&lt;/code&gt; plumbing as ML-DSA [@cng-algorithm-ids].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; ML-KEM is the only NIST-finalised KEM. ML-DSA is the general-purpose lattice signature. SLH-DSA is the conservative hash-based fallback. They are not interchangeable; an engineer picks one (or all three) per use case. Hybrid TLS key agreement uses ML-KEM-768; X.509 end-entity signatures use ML-DSA-65 or ML-DSA-87; long-lived code signing where signature size is tolerable uses SLH-DSA; firmware signing with build-counter discipline uses LMS or XMSS.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;All three algorithms are FIPS-standardised. All three have CNG identifiers in Insider Canary builds. But until SymCrypt ships them, until Schannel negotiates them, until AD CS issues certificates that carry them, none of this exists for the Windows engineer in production. So what does Microsoft actually ship in May 2026?&lt;/p&gt;
&lt;h2&gt;6. State of the Art -- What Windows Ships in May 2026&lt;/h2&gt;
&lt;p&gt;Algorithms are not products. Microsoft ships SymCrypt, CNG, NCrypt, Schannel, .NET, AD CS, CertEnroll, and &lt;a href=&quot;https://paragmali.com/blog/windows-app-identity-33-year-reinvention/&quot; rel=&quot;noopener&quot;&gt;Authenticode&lt;/a&gt; -- and post-quantum cryptography arrives in each surface on its own clock.&lt;/p&gt;
&lt;h3&gt;SymCrypt -- the FIPS-validated foundation&lt;/h3&gt;
&lt;p&gt;SymCrypt is Microsoft&apos;s primary cryptographic library. The repository description states it directly: &quot;SymCrypt is the core cryptographic function library currently used by Windows ... started in late 2006 with the first sources committed in Feb 2007 ... Since the 1703 release of Windows 10, SymCrypt has been the primary crypto library for all algorithms in Windows&quot; [@symcrypt-repo]. Microsoft open-sourced SymCrypt in March 2019 [@symcrypt-repo]. It is the FIPS 140-validated module that backs CNG; if CNG ships a post-quantum algorithm on Windows, SymCrypt is the implementation underneath.&lt;/p&gt;

Microsoft&apos;s open-source cryptographic library, used by Windows, Azure Linux, Xbox, and other Microsoft platforms. SymCrypt has been Windows&apos;s primary cryptographic library since Windows 10 1703 (April 2017); its FIPS 140-validated module is the implementation backing CNG (the Win32 API surface) and the NCrypt KSP infrastructure (the key-storage-provider surface). SymCrypt is currently written predominantly in cross-platform C, with an in-progress Rust rewrite for memory-safety reasons [@symcrypt-repo, @ms-research-symcrypt-rust].
&lt;p&gt;The SymCrypt release history through May 2026 is verbatim from the public CHANGELOG [@symcrypt-changelog].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Post-quantum change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;103.5.0&lt;/td&gt;
&lt;td&gt;Add ML-KEM per final FIPS 203; add XMSS / XMSS^MT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.6.0&lt;/td&gt;
&lt;td&gt;Add LMS implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.7.0&lt;/td&gt;
&lt;td&gt;Add ML-DSA implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.8.0&lt;/td&gt;
&lt;td&gt;Add FIPS approved-services indicator&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.9.0&lt;/td&gt;
&lt;td&gt;Add ML-DSA Sign / Verify with External Mu&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.9.1&lt;/td&gt;
&lt;td&gt;Add FIPS CAST for ML-DSA, plus ML-KEM and ML-DSA keygen pairwise-consistency tests&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;103.11.0&lt;/td&gt;
&lt;td&gt;Add Composite ML-KEM implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The SymCrypt releases page lists the binary artefacts at each version for Windows AMD64/ARM64, generic Linux AMD64/ARM64, and OpenEnclave AMD64 [@symcrypt-releases]. Microsoft has also begun rewriting SymCrypt in Rust; the Microsoft Research blog post describes the rationale (memory safety in a TCB-grade library) and confirms the algorithm coverage includes &quot;AES-GCM, SHA, ECDSA, and the more recent post-quantum algorithms ML-KEM and ML-DSA&quot; [@ms-research-symcrypt-rust].&lt;/p&gt;
&lt;h3&gt;CNG, NCrypt, and .NET 10&lt;/h3&gt;
&lt;p&gt;The Windows Insider Canary channel introduced post-quantum CNG identifiers in May 2025 [@ms-pqc-windows-insider]. The new pseudo-handle &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt;, the algorithm-name strings &lt;code&gt;BCRYPT_MLKEM_ALGORITHM = L&quot;ML-KEM&quot;&lt;/code&gt; and &lt;code&gt;BCRYPT_MLDSA_ALGORITHM = L&quot;ML-DSA&quot;&lt;/code&gt;, the prerelease &lt;code&gt;BCRYPT_SLHDSA_ALGORITHM&lt;/code&gt;, and the existing &lt;code&gt;BCRYPT_LMS_ALGORITHM&lt;/code&gt; are documented on the CNG Algorithm Identifiers page [@cng-algorithm-ids]. NCrypt KSPs expose the same algorithm names; an application that previously called &lt;code&gt;NCryptCreatePersistedKey&lt;/code&gt; against an RSA KSP can do the equivalent against an ML-KEM KSP with no plumbing changes beyond the algorithm identifier and parameter set.&lt;/p&gt;
&lt;p&gt;.NET 10 (GA November 2025) exposes the managed surface [@dotnet-10-launch]. &lt;code&gt;System.Security.Cryptography.MLKem&lt;/code&gt; is an abstract base class with &lt;code&gt;KeyGen&lt;/code&gt;, &lt;code&gt;Encapsulate&lt;/code&gt;, &lt;code&gt;Decapsulate&lt;/code&gt;, &lt;code&gt;ExportEncapsulationKey&lt;/code&gt;, and &lt;code&gt;ImportEncapsulationKey&lt;/code&gt; instance methods [@dotnet-mlkem]. &lt;code&gt;MLKemCng&lt;/code&gt; is the CNG-backed concrete subclass that forwards to SymCrypt via CNG [@dotnet-mlkemcng]. Equivalent &lt;code&gt;MLDsa&lt;/code&gt; / &lt;code&gt;MLDsaCng&lt;/code&gt; and &lt;code&gt;SlhDsa&lt;/code&gt; / &lt;code&gt;SlhDsaCng&lt;/code&gt; pairs cover the signature primitives. The &lt;code&gt;*Cng&lt;/code&gt; subclasses are sealed; the abstract base classes are subclassable for non-CNG implementations.&lt;/p&gt;
&lt;h3&gt;Schannel hybrid TLS 1.3&lt;/h3&gt;
&lt;p&gt;Schannel is the Windows TLS stack. The hybrid TLS 1.3 Supported Groups are defined by IETF &lt;code&gt;draft-ietf-tls-ecdhe-mlkem-04&lt;/code&gt; (8 February 2026) [@draft-tls-ecdhe-mlkem]:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Group&lt;/th&gt;
&lt;th&gt;Codepoint&lt;/th&gt;
&lt;th&gt;Construction&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;code&gt;X25519MLKEM768&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x11EC&lt;/td&gt;
&lt;td&gt;RFC 7748 X25519 plus ML-KEM-768&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SecP256r1MLKEM768&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x11EB&lt;/td&gt;
&lt;td&gt;NIST P-256 plus ML-KEM-768&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;SecP384r1MLKEM1024&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;0x11ED&lt;/td&gt;
&lt;td&gt;NIST P-384 plus ML-KEM-1024&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The IANA TLS Parameters registry lists all three (registry last updated 2026-04-29) [@iana-tls-parameters]. Schannel preview on 24H2 and Server 2025 gates these behind Group Policy in early 2026; default-on is the May 2026 -&amp;gt; November 2026 milestone per Microsoft&apos;s Quantum-Safe Security blog [@ms-quantum-safe-blog]. The actual TLS key schedule combines the two shared secrets with a concatenation combiner: $\text{HKDF-Extract}(\text{salt} = 0, \text{IKM} = K_{\text{ecdh}} | K_{\text{pq}})$, per &lt;code&gt;draft-ietf-tls-hybrid-design-16&lt;/code&gt; [@draft-tls-hybrid]. The combiner is correct as long as either component is unbroken; an adversary breaking only ECDH cannot recover the session key, nor can one who breaks only ML-KEM.&lt;/p&gt;
&lt;h3&gt;AD CS, CertEnroll, and Azure Key Vault&lt;/h3&gt;
&lt;p&gt;The X.509 side of the migration lags TLS by a year. Active Directory Certificate Services supports ML-DSA certificate templates via the CertEnroll API, conditional on a CSP or KSP exposing &lt;code&gt;BCRYPT_MLDSA_ALGORITHM&lt;/code&gt;. The practical migration mechanism is &lt;em&gt;composite&lt;/em&gt; signatures per &lt;code&gt;draft-ietf-lamps-pq-composite-sigs-19&lt;/code&gt; (21 April 2026), which combines ML-DSA with RSA-PKCS#1-v1.5, RSA-PSS, ECDSA, Ed25519, or Ed448 in a single &lt;code&gt;SubjectPublicKeyInfo&lt;/code&gt; and requires both components to verify [@draft-lamps-composite]. Downlevel verifiers that do not recognise the composite OID can still validate the inner classical chain; uplevel verifiers validate both. Pure post-quantum X.509 chains are in preview for closed pilots, not in general use. Azure Key Vault&apos;s managed-HSM exposes post-quantum keys in preview for Q1 2026.&lt;/p&gt;

flowchart TD
    ISV[&quot;ISV applications (browsers, services, SDKs)&quot;]
    Schannel[&quot;Schannel (TLS 1.3 with X25519MLKEM768)&quot;]
    ADCS[&quot;AD CS / CertEnroll (ML-DSA, composite signatures)&quot;]
    DotNet[&quot;.NET 10 (MLKem, MLDsa, SlhDsa managed types)&quot;]
    KSP[&quot;NCrypt KSPs (Microsoft Software KSP, Pluton KSP, vendor KSPs)&quot;]
    CNG[&quot;CNG Win32 API (BCryptEncapsulate, BCryptSignHash, ...)&quot;]
    SymCrypt[&quot;SymCrypt (FIPS-validated, primary crypto library since Win10 1703)&quot;]
    HW[&quot;Hardware (CPU AES-NI, Pluton, TPM 2.0, IOMMU)&quot;]
    ISV --&amp;gt; Schannel
    ISV --&amp;gt; ADCS
    ISV --&amp;gt; DotNet
    Schannel --&amp;gt; CNG
    ADCS --&amp;gt; CNG
    DotNet --&amp;gt; CNG
    Schannel --&amp;gt; KSP
    KSP --&amp;gt; CNG
    CNG --&amp;gt; SymCrypt
    SymCrypt --&amp;gt; HW
&lt;h3&gt;What is NOT shipping in May 2026&lt;/h3&gt;
&lt;p&gt;The honest accounting matters. Several load-bearing Windows surfaces have no post-quantum path as of May 2026.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The post-quantum migration is partial. As of May 2026, none of the following has a published Microsoft post-quantum specification or shipping implementation: IKEv2 PQ key exchange; SMB hybrid (&lt;code&gt;X25519MLKEM768&lt;/code&gt; over SMB 3.1.1); RDP hybrid; BitLocker network unlock (still RSA-2048 + AES-256); Kerberos PKINIT (no PQ certificate path for the KDC bootstrap); Windows Hello attestation (TPM-bound RSA-2048 / ECDSA-P256). Authenticode signatures on drivers and binaries remain RSA-2048 + SHA-256 with no published PQ Authenticode specification. Premature migration of these surfaces is &lt;em&gt;worse&lt;/em&gt; than no migration, because there is no downlevel-compatible composite story for them. The discipline is: hybrid TLS first, composite X.509 chain second, firmware signing pilot third. Leave the rest alone until Microsoft publishes specifications [@ms-quantum-safe-blog].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;CNSA 2.0 -- the policy clock&lt;/h3&gt;
&lt;p&gt;CNSA 2.0 turns the technical timeline into an acquisition mandate. The four authoritative dates from the May 30, 2025 revision of the Cybersecurity Advisory [@cnsa20-csa]:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Milestone&lt;/th&gt;
&lt;th&gt;Date&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Acquisition preference for PQ in new National Security Systems&lt;/td&gt;
&lt;td&gt;January 1, 2027&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy algorithm phase-out begins&lt;/td&gt;
&lt;td&gt;December 31, 2030&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Mandatory PQ adoption in National Security Systems&lt;/td&gt;
&lt;td&gt;December 31, 2031&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;RSA / ECDSA disallowed in National Security Systems&lt;/td&gt;
&lt;td&gt;After 2035&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;

The U.S. National Security Agency&apos;s Commercial National Security Algorithm Suite 2.0, announced September 7, 2022 [@nsa-cnsa-news]. CNSA 2.0 mandates post-quantum algorithms for U.S. National Security Systems by 2031 and disallows RSA / ECDSA after 2035. Specific algorithm selections (May 30, 2025 revision): ML-KEM-1024 for key establishment, ML-DSA-87 for general signing, LMS and XMSS for firmware signing, AES-256 for symmetric encryption, SHA-384 for hashing [@cnsa20-csa]. The CNSA 2.0 dates drive every U.S. vendor&apos;s PQC roadmap including Microsoft&apos;s.

A FIPS 140-3 validation is a Cryptographic Module Validation Program certificate that asserts a cryptographic module (a binary, with a specific version, a specific build, and a specific tested configuration) implements specific algorithms correctly and has been tested by an accredited lab. SymCrypt&apos;s FIPS validation is what makes CNG-backed cryptography acceptable for U.S. federal procurement; without validation, the same algorithm implemented in the same byte-exact code is not FIPS-validated. The cadence matters because algorithm-implementation cadence (new ML-DSA External-Mu support in v103.9.0) and module-validation cadence (a new CMVP certificate per validated build) are different clocks. SymCrypt v103.8.0 explicitly added a FIPS approved-services indicator [@symcrypt-changelog] -- the runtime hook by which an application can ask &quot;am I operating in FIPS-validated mode?&quot; and reject non-FIPS algorithms accordingly. CMVP queue times in 2026 are running 9-18 months, which means the published SymCrypt version is typically two or three versions ahead of the FIPS-validated version at any given moment.
&lt;p&gt;ML-KEM is the only NIST-finalised KEM. ML-DSA and SLH-DSA are the only NIST-finalised signature schemes. But the NIST portfolio still has Falcon in FIPS 206 draft, HQC for code-based diversification, LMS / XMSS for firmware -- and the IETF still has composite signatures and hybrid TLS layered on top. What else is shipping, and why?&lt;/p&gt;
&lt;h2&gt;7. Competing Approaches -- Inside the Lattice Lane and Outside It&lt;/h2&gt;
&lt;p&gt;ML-KEM is the only KEM in FIPS 203, but it is not the only KEM in the portfolio. Several other algorithms compete for adjacent niches, and the engineer who treats &quot;PQ&quot; as one thing misses the architectural choices that CNSA 2.0 and NIST actually make.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Falcon (FN-DSA, FIPS 206 draft).&lt;/strong&gt; NTRU-lattice signatures with fast Fourier sampling. Signature sizes range from 666 bytes (Falcon-512, category 1) to 1280 bytes (Falcon-1024, category 5) -- three to five times smaller than ML-DSA-65 at comparable security; the byte counts are verbatim from the Falcon Round-3 specification&apos;s recommended-parameters table [@falcon-spec]. The cost is Falcon&apos;s Gaussian sampler, which requires floating-point arithmetic and is notoriously hard to make constant-time. Microsoft has signalled support; SymCrypt has not shipped Falcon as of May 2026. FIPS 206 finalisation is the precondition; NIST&apos;s &lt;code&gt;pqc-dig-sig&lt;/code&gt; project page lists Falcon (renamed FN-DSA) as the standard whose finalisation has been pushed past initial timelines pending the constant-time sampler question [@nist-pqc-dig-sig].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;HQC (Hamming Quasi-Cyclic).&lt;/strong&gt; NIST selected HQC as the fourth-round standardisation choice on 7 March 2025 [@nist-hqc-news]. HQC is code-based -- its security rests on the hardness of decoding random quasi-cyclic codes -- which is structurally unrelated to lattice cryptography. NIST IR 8545 documents the rationale: HQC offers diversification away from lattices in case future cryptanalysis makes Module-LWE less conservative than it now appears. HQC was chosen over BIKE; Classic McEliece remains a candidate but was not selected because of key size. NIST is expected to publish the HQC standard around 2027.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Classic McEliece.&lt;/strong&gt; ~1 MB public keys at NIST category 5; ~261 KB at category 1 [@mceliece-project]. Forty-eight years of cryptanalysis without a structural break. Not selected by NIST for general standardisation. Survives as a niche choice for long-term archival key wrapping, where the key transfer happens once and the ciphertext is small.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LMS, XMSS, XMSS^MT (stateful hash-based, NIST SP 800-208).&lt;/strong&gt; Already in SymCrypt. The NIST SP 800-208 specification names &quot;two algorithms ... stateful hash-based signature schemes: the Leighton-Micali Signature (LMS) system and the eXtended Merkle Signature Scheme (XMSS), along with their multi-tree variants (HSS and XMSS_MT)&quot; [@nist-sp-800-208]. CNSA 2.0 specifies LMS and XMSS for firmware signing: UEFI capsule signing, OEM driver signing, secure-boot dbx revocation entries [@cnsa20-csa]. The stateful-counter requirement -- the signer must track a build counter and never reuse a leaf index -- is acceptable in build pipelines that already track build numbers monotonically. SymCrypt v103.5.0 added XMSS and XMSS^MT; v103.6.0 added LMS [@symcrypt-changelog].&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; CNSA 2.0 names stateful hash-based signatures for firmware signing and only firmware signing. The reason is operational. LMS and XMSS are state-leaking: signing twice with the same leaf index reveals the secret. A general-purpose signing surface (X.509 end-entity certificates, code signing for arbitrary developers, document signing) cannot guarantee state discipline. A firmware-build pipeline that issues build numbers monotonically and operates under hardware-security-module discipline can. The narrow scope is what makes LMS / XMSS safe to deploy now -- the stateless SLH-DSA story is the general-purpose alternative for everything that cannot guarantee counter discipline [@cnsa20-csa, @nist-sp-800-208].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Composite signatures.&lt;/strong&gt; &lt;code&gt;draft-ietf-lamps-pq-composite-sigs-19&lt;/code&gt; (21 April 2026) defines combinations of ML-DSA with each of RSA-PKCS#1-v1.5, RSA-PSS, ECDSA, Ed25519, and Ed448 [@draft-lamps-composite]. The X.509 SubjectPublicKeyInfo contains both component public keys; verification requires both component signatures to succeed; an attacker must break both algorithms. Composite is the &lt;strong&gt;load-bearing migration mechanism for 2026-2030&lt;/strong&gt;, because it is deployable against today&apos;s PKI. Downlevel verifiers ignore the composite OID and trust the inner classical chain; uplevel verifiers validate both.&lt;/p&gt;

A signature construction that combines two component signature algorithms -- one classical, one post-quantum -- such that both signatures must verify for the composite signature to validate. The composite public key is the concatenation of the two component public keys plus a composite-OID wrapper; the composite signature is the concatenation of the two component signatures. The construction provides &quot;either-component&quot; security: an adversary must break both algorithms to forge. Composite is the practical migration on-ramp for X.509 PKI during 2026-2030 [@draft-lamps-composite].
&lt;p&gt;&lt;strong&gt;Hybrid TLS X25519MLKEM768.&lt;/strong&gt; Already in production at internet scale. Cloudflare since October 2022; Apple iMessage PQ3 since February 2024; Signal PQXDH since September 2023 [@signal-pqxdh]; OpenSSH since version 9.0 (April 2022) via &lt;code&gt;sntrup761x25519-sha512&lt;/code&gt; (Streamlined NTRU Prime + X25519) [@openssh-9-0], with the ML-KEM-768-based group &lt;code&gt;mlkem768x25519-sha256&lt;/code&gt; added in OpenSSH 9.9 (September 2024) [@openssh-9-9]; Google Chrome; Microsoft Edge. Schannel preview on 24H2 and Server 2025 in early 2026 [@cloudflare-pq-for-all, @cloudflare-pq-2024, @apple-imessage-pq3].Apple&apos;s framing of PQ3 as &quot;Level 3&quot; is the policy-marketing achievement of the post-quantum era. Level 1 is no post-quantum; Level 2 is post-quantum key establishment for the &lt;em&gt;initial&lt;/em&gt; handshake; Level 3 is post-quantum key establishment for &lt;em&gt;both&lt;/em&gt; the initial handshake and ongoing message-key ratcheting. iMessage PQ3 reached Level 3 in February 2024 -- six months before ML-KEM was even FIPS-finalised. The companion symbolic-analysis PDF Apple commissioned confirms the parameter split: ML-KEM-1024 for the initial key exchange, Kyber-768 for the ongoing ratchet [@apple-imessage-pq3].&lt;/p&gt;

A key-exchange construction that combines a classical key-agreement algorithm (X25519, ECDH-P256, RSA) with a post-quantum KEM (ML-KEM-768) in such a way that the final shared secret depends on both components. An adversary who breaks either component but not both learns nothing. The most common combiner is HKDF-Extract over the concatenation of the two shared secrets, per `draft-ietf-tls-hybrid-design-16` [@draft-tls-hybrid]. Hybrid is the migration choice during the period when neither classical nor post-quantum primitives can be trusted standalone -- classical because of harvest-now-decrypt-later, post-quantum because of the narrower cryptanalytic margin (see Section 8).
&lt;p&gt;Compact portfolio comparison for the KEM side (HQC sizes are current Round-4 parameters from the HQC project specification [@hqc-project]):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Public key&lt;/th&gt;
&lt;th&gt;Ciphertext&lt;/th&gt;
&lt;th&gt;Hardness&lt;/th&gt;
&lt;th&gt;NIST status&lt;/th&gt;
&lt;th&gt;Microsoft adoption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ML-KEM-768&lt;/td&gt;
&lt;td&gt;1184 B&lt;/td&gt;
&lt;td&gt;1088 B&lt;/td&gt;
&lt;td&gt;Module-LWE&lt;/td&gt;
&lt;td&gt;FIPS 203&lt;/td&gt;
&lt;td&gt;SymCrypt 103.5.0; CNG; Schannel hybrid&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HQC-1&lt;/td&gt;
&lt;td&gt;2241 B (cat 1)&lt;/td&gt;
&lt;td&gt;4433 B (cat 1)&lt;/td&gt;
&lt;td&gt;Quasi-cyclic codes&lt;/td&gt;
&lt;td&gt;Round-4 selected (March 2025)&lt;/td&gt;
&lt;td&gt;Not yet in SymCrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Classic McEliece&lt;/td&gt;
&lt;td&gt;~261 KB to ~1 MB&lt;/td&gt;
&lt;td&gt;~128 B to ~240 B&lt;/td&gt;
&lt;td&gt;Goppa codes&lt;/td&gt;
&lt;td&gt;Round-4 niche&lt;/td&gt;
&lt;td&gt;Not in SymCrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid X25519+MLKEM768&lt;/td&gt;
&lt;td&gt;1216 B&lt;/td&gt;
&lt;td&gt;1120 B&lt;/td&gt;
&lt;td&gt;X25519 OR Module-LWE&lt;/td&gt;
&lt;td&gt;TLS 1.3 IETF draft&lt;/td&gt;
&lt;td&gt;Schannel preview, default-on roadmap&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;Signature side:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Algorithm&lt;/th&gt;
&lt;th&gt;Public key&lt;/th&gt;
&lt;th&gt;Signature&lt;/th&gt;
&lt;th&gt;Hardness&lt;/th&gt;
&lt;th&gt;NIST status&lt;/th&gt;
&lt;th&gt;Microsoft adoption&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;ML-DSA-65&lt;/td&gt;
&lt;td&gt;1952 B&lt;/td&gt;
&lt;td&gt;3293 B&lt;/td&gt;
&lt;td&gt;Module-LWE / Module-SIS&lt;/td&gt;
&lt;td&gt;FIPS 204&lt;/td&gt;
&lt;td&gt;SymCrypt 103.7.0; CNG&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Falcon-512&lt;/td&gt;
&lt;td&gt;897 B&lt;/td&gt;
&lt;td&gt;666 B [@falcon-spec]&lt;/td&gt;
&lt;td&gt;NTRU lattices&lt;/td&gt;
&lt;td&gt;FIPS 206 draft&lt;/td&gt;
&lt;td&gt;Not in SymCrypt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SLH-DSA-SHA2-128f&lt;/td&gt;
&lt;td&gt;32 B&lt;/td&gt;
&lt;td&gt;17088 B&lt;/td&gt;
&lt;td&gt;SHA-2 collision resistance&lt;/td&gt;
&lt;td&gt;FIPS 205&lt;/td&gt;
&lt;td&gt;Planned &lt;code&gt;BCRYPT_SLHDSA_ALGORITHM&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LMS / HSS&lt;/td&gt;
&lt;td&gt;60 B&lt;/td&gt;
&lt;td&gt;4-50 KB&lt;/td&gt;
&lt;td&gt;Hash preimage&lt;/td&gt;
&lt;td&gt;NIST SP 800-208&lt;/td&gt;
&lt;td&gt;SymCrypt 103.6.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Composite ML-DSA-65 + ECDSA-P256&lt;/td&gt;
&lt;td&gt;~2 KB&lt;/td&gt;
&lt;td&gt;~3.4 KB&lt;/td&gt;
&lt;td&gt;ML-DSA AND ECDSA&lt;/td&gt;
&lt;td&gt;LAMPS draft-19&lt;/td&gt;
&lt;td&gt;AD CS pilot path&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The portfolio works as long as one of its families holds. But what if it doesn&apos;t? What does cryptography &lt;em&gt;not&lt;/em&gt; tell us about the future, and what are the structural limits of even the strongest post-quantum primitive?&lt;/p&gt;
&lt;h2&gt;8. Theoretical Limits -- What PQC Does and Does Not Solve&lt;/h2&gt;
&lt;p&gt;Post-quantum cryptography is not magic. It closes one specific channel of one specific threat model, and engineers who treat it as &quot;now we&apos;re quantum-safe&quot; miss the four limits the cryptographers themselves keep flagging.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. The cryptanalysis margin is narrower than for RSA or ECDH.&lt;/strong&gt; The best classical algorithm for solving Module-LWE at NIST parameter sizes (BKZ with sieving) runs in roughly $2^{0.292 n}$ operations, where $n$ is the lattice dimension; the best quantum variant runs in roughly $2^{0.257 n}$. That is a 12% exponent reduction -- not a Shor-style polynomial-time collapse. NIST parameter sizes carry a small but measurable margin to absorb future BKZ-with-sieving improvements. The hardness conjecture is stronger than RSA&apos;s (worst-case-to-average-case reduction), but the cryptanalytic frontier is thinner. Lattice cryptanalysis has improved continuously since Ajtai 1996; whether the asymptotic exponent further drops in the next decade is an open problem [@regev-2005, @langlois-stehle-modulelwe].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. The side-channel surface is larger.&lt;/strong&gt; ML-DSA&apos;s rejection-sampling loop is secret-correlated; Falcon&apos;s Gaussian sampler requires floating-point arithmetic; ML-KEM&apos;s polynomial operations can leak through cache-timing channels. The most visceral example is &lt;strong&gt;KyberSlash&lt;/strong&gt; (eprint 2024/1049, advisory GHSA-x5j2-g63m-f8g4), in which Bernstein and collaborators demonstrated that the &lt;em&gt;official Kyber reference implementation&lt;/em&gt; contained a secret-dependent division-timing leak that survived multiple rounds of NIST review and recovered secret keys in minutes on a Raspberry Pi 2 [@kyberslash-2024].KyberSlash is the most important data point in PQC implementation security. The leak was a one-line &lt;code&gt;/&lt;/code&gt; operator that compiled to a variable-time integer division on ARM and on older x86-64. The vulnerability survived years of formal NIST review, multiple academic implementations, and several vendor ports. Constant-time discipline is more fragile in PQ primitives than in classical primitives -- both because the algorithms are newer and because the ring arithmetic offers many more variable-time corners than the simpler scalar arithmetic of ECDH or RSA. The KyberSlash site, authored by Bernstein, documents specific implementations affected [@kyberslash-2024].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. The signed-binary harvest is not closed by PQ.&lt;/strong&gt; This is the article&apos;s third aha moment, and the one most readers miss. A 2026 Authenticode signature on a 2026 Windows driver uses RSA-2048 + SHA-256. In 2035, the verifier may no longer trust RSA-2048 -- but the binary has already been loaded by every machine that downloaded it. Authenticode is not a transport channel. There is no migration-window analogue of harvest-now-decrypt-later because the signature was already validated at load time. The threat model is &quot;an adversary in 2035 forges a &lt;em&gt;new&lt;/em&gt; signature on a &lt;em&gt;new&lt;/em&gt; binary,&quot; not &quot;an adversary in 2035 decrypts a 2026 conversation.&quot; The two threat models call for different migration disciplines.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Key idea:&lt;/strong&gt; Post-quantum cryptography closes the harvest-now-decrypt-later channel for transport-protected traffic (TLS, IPsec, SSH, iMessage). It does not close the signed-binary persistence channel; a 2035 quantum-forged signature on a 2035 driver is a &lt;em&gt;new&lt;/em&gt; attack, not a retroactive decryption of a 2026 signature. It does not close the algorithm-agility gap; CNG ships per-algorithm identifiers, not per-algorithm-class. Plan migration accordingly. Hybrid TLS first; composite X.509 chain second; firmware signing pilot third. Authenticode and PKINIT can wait for Microsoft&apos;s published specifications -- and premature migration in those surfaces is &lt;em&gt;worse&lt;/em&gt; than no migration.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;4. The algorithm-agility problem persists.&lt;/strong&gt; Microsoft has shipped CNG identifiers &lt;em&gt;per algorithm&lt;/em&gt; (&lt;code&gt;BCRYPT_MLKEM_ALGORITHM&lt;/code&gt;, &lt;code&gt;BCRYPT_MLDSA_ALGORITHM&lt;/code&gt;) rather than per algorithm-class (a hypothetical &lt;code&gt;BCRYPT_PQ_KEM_ALGORITHM&lt;/code&gt; that selected the underlying primitive at runtime). The IETF treats algorithm agility as a load-bearing concern in &lt;code&gt;draft-ietf-pquip-pqc-engineers-14&lt;/code&gt; (26 August 2025), the IETF informational document on engineering PQC into existing protocol surfaces [@draft-pquip-engineers]. CNG does not yet treat it as load-bearing; the engineering consequences for the next migration are discussed in Section 9.4.&lt;/p&gt;

The property of a cryptographic protocol or library that lets the underlying algorithm change without changing the protocol or API surface. A protocol that names &quot;AES-256-GCM&quot; instead of &quot;an AEAD with at least 128-bit security&quot; has poor algorithm agility; replacing AES-256-GCM with ChaCha20-Poly1305 requires the entire protocol to be re-negotiated. CNG&apos;s `BCRYPT_MLKEM_ALGORITHM` is per-algorithm rather than per-algorithm-class; a future Round-5 KEM will require new CNG plumbing rather than a parameter change. The IETF `pqc-engineers` document treats algorithm agility as the load-bearing engineering concern for the post-2030 migration window [@draft-pquip-engineers].

A common confusion is the simultaneous truth that lattice cryptography has a *stronger* hardness argument than RSA (the worst-case-to-average-case reduction) and a *narrower* cryptanalytic margin (the 12% exponent gap). Both are true. The strength claim is structural: every average-case Module-LWE instance is hard if any worst-case lattice instance is hard. The narrowness claim is empirical: the best known algorithm is closer to a feasibility threshold than the best known algorithm for factoring or for elliptic-curve discrete log. The conservative McEliece line trades the strength claim (no analogous reduction) for an even wider empirical margin (no progress on Goppa-code decoding in 48 years). Engineers who treat &quot;stronger hardness&quot; and &quot;wider margin&quot; as synonyms get the post-quantum picture backwards. The honest framing: lattice is the deployable post-quantum, McEliece is the conservative fallback, and the portfolio exists because no one assumption carries everything.
&lt;p&gt;These four limits are not bugs; they are structural. But they are not the only open problems. What is the cryptographer&apos;s current research frontier, and where will the next migration begin?&lt;/p&gt;
&lt;h2&gt;9. Open Problems -- Where the Active Research Is&lt;/h2&gt;
&lt;p&gt;What does Microsoft, NIST, and the IETF still not know? Five open problems whose resolution will define the next decade of Windows cryptography.&lt;/p&gt;
&lt;h3&gt;9.1 TPM 2.0 and Pluton blob-size constraints&lt;/h3&gt;
&lt;p&gt;Default &lt;code&gt;MAX_COMMAND_SIZE&lt;/code&gt; and &lt;code&gt;MAX_RESPONSE_SIZE&lt;/code&gt; on TPM 2.0 are 4096 bytes. ML-DSA-87 signatures (4595 bytes) overflow the response buffer; ML-DSA-65 (3293 bytes) fits. NV memory budgets on commodity TPMs are tightly constrained, which means storing a single ML-DSA-87 keypair (2592-byte public key plus a multi-kilobyte private state) consumes a meaningful fraction of the available NV slot space [@wolfssl-wolftpm-v185, @fips-204-pdf]. The TCG TPM 2.0 Library Specification v1.85 (March 2026) introduces the streaming command family that resolves the buffer overflow; the cited wolfSSL secondary source enumerates the new commands verbatim as &lt;code&gt;TPM2_SignSequenceStart&lt;/code&gt; / &lt;code&gt;TPM2_VerifySequenceStart&lt;/code&gt;, &lt;code&gt;TPM2_SignSequenceComplete&lt;/code&gt; / &lt;code&gt;TPM2_VerifySequenceComplete&lt;/code&gt;, and &lt;code&gt;TPM2_SignDigest&lt;/code&gt; / &lt;code&gt;TPM2_VerifyDigestSignature&lt;/code&gt; for digest-mode operations, plus &lt;code&gt;TPM2_Encapsulate&lt;/code&gt; and &lt;code&gt;TPM2_Decapsulate&lt;/code&gt; for ML-KEM, with the new structures &lt;code&gt;TPM2B_KEM_CIPHERTEXT&lt;/code&gt;, &lt;code&gt;TPM2B_SHARED_SECRET&lt;/code&gt;, and &lt;code&gt;TPM_ST_MESSAGE_VERIFIED&lt;/code&gt; (the matching &lt;code&gt;*SequenceUpdate&lt;/code&gt; opcode is implied by analogy with the existing TPM 2.0 hash-sequence command family but is not enumerated in the available secondary source pending TCG primary access) [@wolfssl-wolftpm-v185]. Commodity v1.85-capable chips are entering early sampling in 2026; &lt;strong&gt;Pluton&apos;s Rust firmware can move faster&lt;/strong&gt; but is locked to specific SoC generations.Pluton&apos;s SoC-generation locking is the structural cost of its update-channel advantage. The Microsoft Learn Pluton page enumerates the currently supported families (AMD Ryzen 6000, 7000, 8000, 9000, and Ryzen AI Series; Intel Core Ultra 200V Series, Ultra Series 3, and (non-Ultra) Series 3 processors; Qualcomm Snapdragon 8cx Gen 3 and Snapdragon X Series) [@pluton-microsoft-learn]; OEMs without those silicon options cannot ship Pluton-backed PQC even when the firmware-update mechanism is ready. The cross-reference to the Pluton sibling article spells out the silicon-side mechanics.&lt;/p&gt;
&lt;h3&gt;9.2 Kerberos PKINIT&lt;/h3&gt;
&lt;p&gt;RFC 4556&apos;s certificate-of-the-KDC bootstrap currently uses RSA-OAEP or pre-shared-secret-via-ECDH for AS-REP key establishment. The KDC certificate could be composite-ML-DSA-signed, but the AS-REP encryption key derivation has no IETF post-quantum migration draft as of May 2026. Every Windows domain join, every smart-card logon, every Kerberos-authenticated SMB or RDP or IIS session depends on PKINIT -- and PKINIT has no PQ path. The NTLM-to-PKINIT migration (the subject of a sibling article on &lt;a href=&quot;https://paragmali.com/blog/ntlmless-the-death-of-ntlm-in-windows/&quot; rel=&quot;noopener&quot;&gt;NTLM deprecation&lt;/a&gt;) was hard enough; the PKINIT-to-PQ-PKINIT migration has not started.&lt;/p&gt;
&lt;h3&gt;9.3 Authenticode and the EFI signature database&lt;/h3&gt;
&lt;p&gt;A Windows machine that boots in 2035 must verify boot loaders signed between 2010 and 2035. The EFI signature-database revocation list (&lt;code&gt;dbx&lt;/code&gt;) is roughly 32 KB on commodity platforms [@uefi-dbx]. Replacing each entry&apos;s RSA-2048 signature with ML-DSA-65 multiplies the per-entry signature size by ~1.6×; with SLH-DSA-SHA2-128f, by ~50×. No public Microsoft &lt;a href=&quot;https://paragmali.com/blog/secure-boot-in-windows-the-chain-from-sector-zero-to-userini/&quot; rel=&quot;noopener&quot;&gt;Secure Boot&lt;/a&gt; post-quantum roadmap exists as of May 2026. LMS is the obvious candidate -- CNSA 2.0 mandates LMS or XMSS for firmware signing -- but the dbx-size question remains open. Cross-reference the Secure Boot sibling article in this series.&lt;/p&gt;
&lt;h3&gt;9.4 Algorithm agility as a separately engineered property&lt;/h3&gt;
&lt;p&gt;Section 8 limit-4 introduced algorithm agility as a structural property the IETF treats as load-bearing [@draft-pquip-engineers]. The open engineering problem is the CNG provider-interface design. Today every consumer -- Schannel, AD CS, IKEv2, SMB, RDP, Authenticode -- is wired to a specific algorithm identifier (&lt;code&gt;BCRYPT_MLKEM_ALGORITHM&lt;/code&gt;, &lt;code&gt;BCRYPT_MLDSA_ALGORITHM&lt;/code&gt;). A future migration to a NIST Round-5 KEM has to re-do every one of those wiring points, the same shape of problem CNG had with the RSA-to-ECDSA transition. Solving algorithm agility means redesigning the CNG provider interface around algorithm &lt;em&gt;families&lt;/em&gt; rather than algorithm &lt;em&gt;names&lt;/em&gt; -- a multi-year engineering programme that nobody has publicly committed to, and that the post-2030 migration window depends on.&lt;/p&gt;
&lt;h3&gt;9.5 The PKI rebuild before 2035&lt;/h3&gt;
&lt;p&gt;Every TLS server certificate, every code-signing certificate, every smart-card user certificate has to be re-issued in a post-quantum algorithm before the legacy algorithm is disallowed. The throughput of the global public-CA system is the limiting factor. Commercial CAs are pilot-issuing composite-signed roots in 2026; volume issuance lags by years. &lt;strong&gt;NIST IR 8547 (12 November 2024)&lt;/strong&gt; proposes deprecating quantum-vulnerable algorithms in NIST standards by 2035 [@nist-ir-8547].&lt;/p&gt;

NIST IR 8547 proposes the timeline; CNSA 2.0 imposes it on U.S. National Security Systems; CA/Browser Forum will eventually impose it on public web PKI. The unfunded part is the operational work. Every organisation operating an internal Windows AD CS hierarchy has to re-issue its root, its issuing CAs, and every end-entity certificate. The Microsoft tooling for this rebuild is the AD CS composite-signature support and the CertEnroll ML-DSA template path. The CA throughput question is real -- a typical commercial CA issues at peak in the low hundreds of thousands of certificates per day, and the global web PKI runs at orders of magnitude more -- which is why composite signatures are the deployment story for 2026-2030 and pure-PQ X.509 is the post-2030 story [@nist-ir-8547, @draft-lamps-composite].

flowchart TD
    OP1[&quot;TPM / Pluton blob-size limits (v1.85)&quot;]
    OP2[&quot;Kerberos PKINIT bootstrap&quot;]
    OP3[&quot;Authenticode and EFI dbx&quot;]
    OP4[&quot;CNG algorithm agility&quot;]
    OP5[&quot;Global PKI rebuild by 2035&quot;]
    OP1 --&amp;gt; S1[&quot;Hello attestation&quot;]
    OP1 --&amp;gt; S2[&quot;BitLocker network unlock&quot;]
    OP1 --&amp;gt; S3[&quot;Trustlet attestation&quot;]
    OP2 --&amp;gt; S4[&quot;Domain join&quot;]
    OP2 --&amp;gt; S5[&quot;Smart-card logon&quot;]
    OP2 --&amp;gt; S6[&quot;Kerberos-mediated SMB / RDP / IIS&quot;]
    OP3 --&amp;gt; S7[&quot;Driver loading&quot;]
    OP3 --&amp;gt; S8[&quot;Secure Boot revocation&quot;]
    OP4 --&amp;gt; S9[&quot;Schannel / AD CS / IKEv2 / SMB / RDP&quot;]
    OP5 --&amp;gt; S10[&quot;Every X.509 certificate in the estate&quot;]
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; ML-DSA-87 keys cannot live on a TPM 2.0 chip whose firmware predates Library Specification v1.85. Three Windows surfaces are stuck on RSA-2048 / ECDSA-P256 until v1.85-capable chips reach retail volume: Windows Hello attestation (TPM-bound), BitLocker network unlock (depends on TPM key sealing), and &lt;a href=&quot;https://paragmali.com/blog/vbs-trustlets-what-actually-runs-in-the-secure-kernel/&quot; rel=&quot;noopener&quot;&gt;Trustlet attestation&lt;/a&gt; (LSAISO / Credential Guard). Pluton can move faster than discrete TPMs because its firmware ships through Windows Update; the cross-reference to the Pluton sibling article explains the firmware-update agility mechanism [@wolfssl-wolftpm-v185, @ms-quantum-safe-blog].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Five open problems, five decade-scale research programmes, five places where a Windows engineer&apos;s procurement decision in 2026 will be visible in 2035. So what does that engineer do on Monday morning?&lt;/p&gt;
&lt;h2&gt;10. Practical Guide -- What an Engineer Does Monday Morning&lt;/h2&gt;
&lt;p&gt;Six actions, in priority order. Each is doable in May 2026. Each closes a real gap. None requires a procurement cycle -- those start at Action 4.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Run a CNG inventory against &lt;code&gt;HKLM\SYSTEM\CurrentControlSet\Control\Cryptography&lt;/code&gt; and the registered providers. Catalogue certificate templates with &lt;code&gt;certutil -template&lt;/code&gt;. Enumerate Schannel cipher suites with &lt;code&gt;Get-TlsCipherSuite&lt;/code&gt; on every Schannel-using service. Identify every place RSA-2048, ECDSA-P256, ECDH/X25519, RSA-PSS, and DSA appear in your estate. Output is a CSV. The CSV is the input to every subsequent action. Without inventory, the migration is a guessing game [@cng-algorithm-ids].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;{`
// Pseudo-code for a Schannel cipher-suite inventory.
// In real PowerShell: Get-TlsCipherSuite | Select-Object Name, Hash, Cipher, Exchange
// This logic flags quantum-vulnerable Exchange groups (RSA, ECDH-*-without-PQ-companion).
type CipherSuite = {
  name: string;
  exchange: string;   // &quot;ECDHE&quot;, &quot;DHE&quot;, &quot;RSA&quot;, &quot;X25519MLKEM768&quot;, ...
  cipher: string;     // &quot;AES-256-GCM&quot;, &quot;ChaCha20-Poly1305&quot;, ...
  hash: string;       // &quot;SHA-384&quot;, &quot;SHA-256&quot;, ...
};&lt;/p&gt;
&lt;p&gt;const QUANTUM_VULNERABLE_EXCHANGE = new Set([
  &quot;RSA&quot;, &quot;DHE&quot;, &quot;ECDHE&quot;, &quot;ECDH&quot;,                // classical key agreement
  &quot;X25519&quot;, &quot;SecP256r1&quot;, &quot;SecP384r1&quot;,            // unwrapped classical EC groups
]);&lt;/p&gt;
&lt;p&gt;const QUANTUM_SAFE_EXCHANGE = new Set([
  &quot;X25519MLKEM768&quot;, &quot;SecP256r1MLKEM768&quot;, &quot;SecP384r1MLKEM1024&quot;,
  &quot;MLKEM768&quot;, &quot;MLKEM1024&quot;,
]);&lt;/p&gt;
&lt;p&gt;function audit(suites: CipherSuite[]) {
  for (const s of suites) {
    if (QUANTUM_SAFE_EXCHANGE.has(s.exchange)) {
      console.log(&quot;OK    &quot; + s.name + &quot;  (&quot; + s.exchange + &quot;)&quot;);
    } else if (QUANTUM_VULNERABLE_EXCHANGE.has(s.exchange)) {
      console.log(&quot;FLAG  &quot; + s.name + &quot;  (&quot; + s.exchange + &quot; is harvest-now-decrypt-later exposed)&quot;);
    } else {
      console.log(&quot;?     &quot; + s.name + &quot;  (unknown exchange &quot; + s.exchange + &quot;)&quot;);
    }
  }
}
`}&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Cloudflare-fronted endpoints already negotiate &lt;code&gt;X25519MLKEM768&lt;/code&gt; by default [@cloudflare-pq-for-all, @cloudflare-pq-2024]. For Windows servers using OpenSSL 3.5+, enable hybrid. For Schannel-only servers, monitor the Group Policy toggle on 24H2 and the documented Schannel curve preference order. Hybrid is the immediate harvest-now-decrypt-later defence -- the one place where a single configuration change measurably reduces today&apos;s exposure to a future quantum break.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Issue one composite root, one composite issuing CA, one composite end-entity certificate in a non-production lab. Validate consumption by an updated Schannel client and Microsoft Edge. The point is to surface the operational rough edges -- template definition, key-archival behaviour with PQ keys, certificate-validation timing on uplevel and downlevel clients -- before they hit production. This is the load-bearing 2026-2030 PKI migration on-ramp; the composite OID is downlevel-compatible, so the failure mode of an uplevel client validating an unaltered classical chain is the baseline, not a regression [@draft-lamps-composite].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Current TPM 2.0 v1.84 chips on most Windows 11 endpoints will not accept ML-DSA-87 keys without a firmware update. Use &lt;code&gt;Get-Tpm&lt;/code&gt; and the TBS API to enumerate supported algorithms; if &lt;code&gt;BCryptOpenAlgorithmProvider&lt;/code&gt; for ML-DSA returns &lt;code&gt;NTE_NOT_SUPPORTED&lt;/code&gt; against the platform crypto provider, the underlying TPM does not yet expose the PQ surface. If your hardware lifetime extends past 2030, wait for v1.85-capable chips (early sampling 2026) or Pluton (already shipping with on-die firmware updates). Inventory your fleet&apos;s TPM firmware versions today; the migration plan needs to know the floor [@wolfssl-wolftpm-v185].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; UEFI capsule signing, OEM driver signing, and secure-boot dbx revocation entries are all candidates for LMS or XMSS [@cnsa20-csa]. The stateful-counter requirement is acceptable because build pipelines already track build numbers monotonically. The CNG identifier &lt;code&gt;BCRYPT_LMS_ALGORITHM&lt;/code&gt; is prerelease; SymCrypt v103.6.0 ships LMS [@symcrypt-changelog]. Start the pilot in a non-production signing service that has secure HSM custody of the counter state. The firmware-signing migration is the only place where CNSA 2.0 explicitly &lt;em&gt;prefers&lt;/em&gt; stateful hash-based signatures over ML-DSA, because firmware signing is the use case where state discipline is realistic.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Post-quantum Authenticode is not specified by Microsoft as of May 2026. Premature migration breaks downlevel verification. The discipline is: hybrid TLS first, composite X.509 chain second, AD CS pilot third, firmware-signing pilot fourth, and &lt;strong&gt;leave Authenticode alone&lt;/strong&gt; until Microsoft publishes the post-quantum Authenticode specification. Authenticode signatures are validated at binary load time; harvest-now-decrypt-later does not apply, and there is no urgency that justifies risking downlevel-verifier breakage. This is the action that takes restraint rather than effort.&lt;/p&gt;
&lt;/blockquote&gt;

The priority ordering follows the threat model. Hybrid TLS is first because it closes harvest-now-decrypt-later on transport traffic with no compatibility cost (the classical share remains in the handshake; an uplevel server downgrades to classical-only cleanly). Composite X.509 is second because it lets you build a post-quantum-ready PKI hierarchy now and surfaces operational rough edges before pure-PQ deployments. Firmware signing is third because the stateful-counter discipline requires HSM-mediated key custody and a long lead time for the signing pipeline. Authenticode is last because there is no specification and no urgency.
&lt;p&gt;One quarter to inventory, two quarters to pilot, two years to volume. Now for the questions every engineer asks after reading.&lt;/p&gt;
&lt;h2&gt;11. Frequently Asked Questions and Closing&lt;/h2&gt;


Yes, when the server supports `X25519MLKEM768` and your Edge build negotiates it. Check via the Edge devtools&apos; Security panel, or test against a Cloudflare-fronted endpoint with the Edge URL bar&apos;s connection information popup. Cloudflare reported nearly two percent of all TLS 1.3 connections to its edge were post-quantum-protected in March 2024, with a forecast of double-digit adoption by year-end [@cloudflare-pq-2024]. Endpoints that have not enabled hybrid TLS still negotiate X25519 alone, which leaves you exposed to harvest-now-decrypt-later.

Not as of May 2026. Schannel&apos;s hybrid TLS 1.3 preview is gated behind Group Policy on 24H2 and Server 2025. Microsoft&apos;s Quantum-Safe Security blog frames May 2026 to November 2026 as the milestone window for default-on negotiation [@ms-quantum-safe-blog]. Until then, the Schannel side has to be explicitly opted in via the cipher-preference order and the Group Policy toggle.

No, not on the public record. Post-quantum Authenticode is not yet specified. The CNG `BCRYPT_MLDSA_ALGORITHM` exists, and SymCrypt 103.7.0 implements ML-DSA, but the Authenticode signature format and the verifier policy have not been updated to accept post-quantum algorithms. Premature migration breaks downlevel verification on every Windows machine that has not received the PQ Authenticode update -- which today is *every* Windows machine. Do not migrate Authenticode prematurely.

Only against harvest-now-decrypt-later. Today&apos;s TLS is not under quantum attack -- quantum computers capable of breaking RSA-2048 or ECDH-X25519 do not exist in 2026. But today&apos;s TLS *traffic can be recorded* today, and if a sufficient quantum computer exists in 2040 the recorded traffic can be decrypted then. Enabling hybrid TLS now closes that window; enabling it in 2035 does not retroactively protect the traffic recorded in 2026 [@mosca-2015].

Nobody knows. CNSA 2.0 picks 2035 as the policy deadline, not a technical forecast [@cnsa20-csa]. Mosca&apos;s 2015 estimate, widely reproduced in PQC literature, was a 1/2 chance of breaking RSA-2048 by 2031. Quantum-engineering progress between 2015 and 2026 has been substantial on the qubit-count axis and modest on the error-correction axis; the underlying question -- when a thousand-logical-qubit fault-tolerant device becomes available -- has no consensus answer in 2026. CNSA 2.0&apos;s job is to make the answer not matter.

Hybrid `X25519MLKEM768` protects you against a break of either component. The HKDF combiner in `draft-ietf-tls-hybrid-design-16` requires both component shared secrets to be uniform-looking from the adversary&apos;s perspective; an attacker who breaks only ML-KEM still cannot recover the session key without also breaking X25519 [@draft-tls-hybrid]. Pure ML-KEM (no hybrid) does not have this property. The central design choice of the IETF hybrid construction is that it pays the byte cost (~1.2 KB extra per handshake) to buy the safety margin.

The kilobyte scale is structural to lattice mathematics; it is not a parameter-tuning issue. The minimum key size for a Module-LWE-based KEM at NIST category 3 is set by the dimension required for security under the best known lattice-sieving attacks. ML-KEM-768 at 1184 bytes is already aggressively tuned [@wp-kyber]. The alternatives that offer smaller keys (Falcon at 897 bytes, SIKE-when-it-was-alive at ~330 bytes) buy that size with either constant-time difficulty (Falcon&apos;s Gaussian sampler) or fragility (SIKE collapsed in July 2022). Classical comparisons: X25519&apos;s 32-byte public value is the floor; Classic McEliece&apos;s ~1 MB is the ceiling.

Only if your procurement timeline extends past 2030. As of May 2026, TPM 2.0 v1.85-PQ-spec-compliant chips are in announcement and early-sampling stages, not in retail volume [@wolfssl-wolftpm-v185]. Pluton is shipping, but is locked to specific SoC generations (current Intel Core Ultra series, AMD Ryzen 8000-series, Qualcomm Snapdragon X) [@pluton-microsoft-learn]. If you replace endpoints every three years, your 2026 procurement decision will be visible in 2029, before any post-quantum TPM mandate bites. If your refresh cycle is five-to-seven years, the calculus changes -- but the answer is still &quot;wait for v1.85 silicon to ship at volume&quot; unless you can write a specific business case for the early-adopter risk.

&lt;h3&gt;Closing&lt;/h3&gt;
&lt;p&gt;A Windows endpoint opens a connection to &lt;code&gt;cloudflare.com&lt;/code&gt;. The 1184-byte field on the wire is no longer a curiosity. It is a thirty-year migration in a single TLS extension. The bytes have a history: Diffie and Hellman in 1976; Shor in 1994; McEliece&apos;s megabyte keys; HFE and its descendants broken by Beullens; NTRU patented for two decades; Regev&apos;s quantum-reduction LWE in 2005; the Ring-LWE compression of 2010; the Module-LWE knob of 2012; BCNS 2014 from Microsoft Research Redmond; Cloudflare-by-default on October 3, 2022; the Castryck-Decru break twenty-five days after NIST&apos;s July 2022 selection; SymCrypt 103.5.0; the FIPS publications of August 13, 2024; the CNG &lt;code&gt;BCRYPT_MLKEM_ALG_HANDLE&lt;/code&gt; exposed in Insider Canary in May 2025; Schannel preview behind Group Policy in early 2026.&lt;/p&gt;
&lt;p&gt;The work is not done. Kerberos PKINIT has no PQ path. Authenticode has no PQ specification. BitLocker network unlock is still RSA-2048. The EFI signature database is still RSA-2048. Every signed binary already on every Windows disk in the world is signed with an algorithm whose 2035 status is uncertain. The TPM 4096-byte buffer cannot fit an ML-DSA-87 signature. CNG ships per-algorithm identifiers, not per-algorithm-class, which guarantees that the &lt;em&gt;next&lt;/em&gt; migration will hit the same surfaces from the same angle. CNSA 2.0 picks 2035; NIST IR 8547 picks 2035 [@cnsa20-csa, @nist-ir-8547]; the global public-CA infrastructure has nine years to rebuild every certificate it has ever issued.&lt;/p&gt;

Migration to post quantum cryptography (PQC) is not a flip-the-switch moment, it&apos;s a multiyear transformation that requires immediate planning and coordinated execution to avoid a last-minute scramble. -- Microsoft Quantum-Safe Security blog, 20 August 2025 [@ms-quantum-safe-blog].

The cryptographic transition described here runs in parallel to the architectural transition documented across this blog&apos;s sibling articles. The hypervisor article explains the substrate on which the Secure Kernel and trustlets sit. The VBS trustlets article explains where Credential Guard lives. The NTLM-to-Kerberos article documents the protocol migration that PQ Kerberos PKINIT will eventually re-do. The Adminless article addresses the local-administrator surface; the Pluton and TPM articles cover the silicon-side roots of trust; the Secure Boot article covers the static measured boot chain that meets the dynamic measured boot chain at hypervisor load. Read them in any order. They share the same migration calendar, the same engineering discipline, and the same honesty about the gaps.
&lt;p&gt;Above all, the bytes are real. The CNG handle exists. The SymCrypt release is shipping. The migration has started. The next decade is the engineering. Every line of code, every parameter set, every byte of that 1184-byte field has thirty years of work behind it, and the Windows engineer of 2026 is the one who carries it the next mile.&lt;/p&gt;
&lt;p&gt;&amp;lt;StudyGuide slug=&quot;post-quantum-cryptography-on-windows&quot; keyTerms={[
  { term: &quot;ML-KEM (FIPS 203)&quot;, definition: &quot;Module-Lattice-Based Key-Encapsulation Mechanism. The only NIST-finalised post-quantum KEM. Parameter sets ML-KEM-512/768/1024 over R_q = Z_q[X]/(X^256 + 1) with q = 3329. ML-KEM-768 public key is 1184 bytes, ciphertext 1088 bytes, shared secret 32 bytes.&quot; },
  { term: &quot;ML-DSA (FIPS 204)&quot;, definition: &quot;Module-Lattice-Based Digital Signature Algorithm. Fiat-Shamir-with-aborts over Module-LWE / Module-SIS, q = 8380417. Parameter sets ML-DSA-44/65/87; signatures 2420 / 3293 / 4595 bytes.&quot; },
  { term: &quot;SLH-DSA (FIPS 205)&quot;, definition: &quot;Stateless Hash-Based Digital Signature Algorithm. SPHINCS+ lineage; security rests on hash-function security alone. Twelve parameter sets across SHA-2 and SHAKE; signatures 7,856 to 49,856 bytes.&quot; },
  { term: &quot;LWE / Ring-LWE / Module-LWE&quot;, definition: &quot;Learning With Errors: distinguish (A, As + e) from uniform when e is small. Ring-LWE lifts to a polynomial ring; Module-LWE generalises to module-rank-k. The 2010-2012 algebraic lift compressed lattice key sizes from megabytes to kilobytes.&quot; },
  { term: &quot;NTT&quot;, definition: &quot;Number Theoretic Transform. The finite-field analogue of the FFT; reduces polynomial multiplication in R_q from O(n^2) to O(n log n). The reason ML-KEM is fast enough for TLS.&quot; },
  { term: &quot;Fujisaki-Okamoto-Hofheinz transform&quot;, definition: &quot;Generic IND-CPA-to-IND-CCA2 transform for KEMs. Re-encrypts the plaintext during decapsulation and returns implicit-rejection pseudorandom output on mismatch. ML-KEM wraps K-PKE with FO to become IND-CCA2.&quot; },
  { term: &quot;Mosca&apos;s inequality&quot;, definition: &quot;X + Y &amp;gt; Z. If data-secrecy lifetime plus migration time exceeds time-to-quantum-computer, harvest-now-decrypt-later succeeds. The framing that made post-quantum migration an actionable IT-policy lever.&quot; },
  { term: &quot;CNSA 2.0&quot;, definition: &quot;U.S. NSA Commercial National Security Algorithm Suite 2.0. Mandates ML-KEM-1024, ML-DSA-87, LMS/XMSS for firmware, AES-256, and SHA-384 in National Security Systems. Acquisition preference 2027; mandatory adoption 2031; RSA / ECDSA disallowed after 2035.&quot; },
  { term: &quot;Hybrid key agreement&quot;, definition: &quot;Combines a classical key-agreement primitive (X25519) with a post-quantum KEM (ML-KEM-768) so the session key depends on both. An adversary must break both components to forge or recover. Used by Cloudflare since October 2022, Apple iMessage PQ3 since February 2024, Schannel preview in 2026.&quot; },
  { term: &quot;Composite signature&quot;, definition: &quot;X.509 signature that combines a classical and a post-quantum component such that both must verify. The deployment story for 2026-2030 X.509 PKI migration, per draft-ietf-lamps-pq-composite-sigs-19. Downlevel verifiers ignore the composite OID; uplevel verifiers validate both.&quot; },
  { term: &quot;Algorithm agility&quot;, definition: &quot;The property that protocols and APIs can change the underlying algorithm without re-engineering the consumer. CNG ships per-algorithm identifiers (BCRYPT_MLKEM_ALGORITHM) rather than per-algorithm-class identifiers; a future Round-5 KEM will require new CNG plumbing.&quot; },
  { term: &quot;X25519MLKEM768&quot;, definition: &quot;Hybrid TLS 1.3 Supported Group, codepoint 0x11EC, defined in draft-ietf-tls-ecdhe-mlkem-04. Concatenates X25519 (32-byte) and ML-KEM-768 (1184-byte ek / 1088-byte ct) shares. ClientHello key_share is 1216 bytes; ServerHello key_share is 1120 bytes.&quot; }
]} questions={[
  { q: &quot;Why is the post-quantum programme a public-key replacement programme and not a symmetric one?&quot;, a: &quot;Shor&apos;s algorithm breaks RSA / DH / ECDSA / ECDH in polynomial time on a fault-tolerant quantum computer, with no parameter increase rescuing them. Grover&apos;s algorithm gives only a quadratic speedup on symmetric primitives, which is absorbed by doubling key sizes (AES-256, SHA-384). The asymmetric lane is fatal; the symmetric lane is a parameter bump.&quot; },
  { q: &quot;What single algebraic move compressed lattice public keys from megabytes to kilobytes?&quot;, a: &quot;Lifting Learning With Errors from Z_q to a polynomial ring R_q = Z_q[X]/(X^n + 1), per Lyubashevsky-Peikert-Regev 2010. Polynomial multiplication via NTT becomes O(n log n) instead of O(n^2). Module-LWE (Langlois-Stehle 2012/2015) added a module-rank parameter knob that lets one base ring serve every NIST security category.&quot; },
  { q: &quot;Why does the NIST FIPS slate combine a lattice scheme and a hash scheme rather than two lattice schemes?&quot;, a: &quot;Diversification. Both the Rainbow break (Beullens, February 2022) and the SIKE break (Castryck-Decru, July 2022) happened during the NIST competition. The portfolio rests on two structurally unrelated foundations (lattice + hash) so that a single mathematical break cannot retire the whole programme. SLH-DSA&apos;s security rests on hash-function security alone.&quot; },
  { q: &quot;Which SymCrypt version first added ML-KEM?&quot;, a: &quot;Version 103.5.0, which &apos;Add ML-KEM per final FIPS 203&apos; along with XMSS and XMSS^MT. Subsequent versions added LMS (103.6.0), ML-DSA (103.7.0), the FIPS approved-services indicator (103.8.0), ML-DSA External-Mu (103.9.0), FIPS CAST for ML-DSA (103.9.1), and Composite ML-KEM (103.11.0).&quot; },
  { q: &quot;What TLS Supported Group does Schannel preview-negotiate on 24H2, and what is its codepoint?&quot;, a: &quot;X25519MLKEM768, codepoint 0x11EC, per draft-ietf-tls-ecdhe-mlkem-04 (8 February 2026). The ClientHello carries 32 bytes X25519 + 1184 bytes ML-KEM-768 encapsulation key (1216 bytes total); the ServerHello carries 32 bytes X25519 + 1088 bytes ML-KEM-768 ciphertext (1120 bytes total).&quot; },
  { q: &quot;Why does ML-DSA-87 not fit on a commodity TPM 2.0 chip?&quot;, a: &quot;ML-DSA-87 signatures are 4595 bytes. Default TPM 2.0 MAX_COMMAND_SIZE and MAX_RESPONSE_SIZE are 4096 bytes. TCG TPM 2.0 Library Specification v1.85 (March 2026) introduces a streaming TPM2_SignSequence Start / Complete family (with TPM2_SignDigest / TPM2_VerifyDigestSignature for digest-mode operations) and ML-KEM TPM2_Encapsulate / Decapsulate, but v1.85-capable chips are in early sampling in 2026, not retail volume.&quot; },
  { q: &quot;What is the difference between LMS / XMSS and SLH-DSA, and when does CNSA 2.0 prefer each?&quot;, a: &quot;LMS and XMSS are stateful: the signer must track a counter and never reuse a leaf index. SLH-DSA derives the leaf address from the message hash and per-signature randomness, making it stateless. CNSA 2.0 specifies LMS or XMSS for firmware signing (where build pipelines already track counters under HSM custody) and ML-DSA-87 for general signing.&quot; },
  { q: &quot;Why does PQC not close the signed-binary persistence channel?&quot;, a: &quot;Authenticode signatures are validated at binary load time. A 2026 RSA-2048 signature has already been verified by every machine that downloaded the binary; a 2035 quantum break does not retroactively decrypt anything because Authenticode is not an encryption channel. The threat model is forgery of new signatures on new binaries, not retroactive decryption. Harvest-now-decrypt-later does not apply.&quot; }
]} /&amp;gt;&lt;/p&gt;
</content:encoded><category>windows</category><category>cryptography</category><category>post-quantum</category><category>pqc</category><category>tls</category><category>symcrypt</category><category>cng</category><category>fips</category><author>noreply@paragmali.com (Parag Mali)</author></item><item><title>BitLocker on Windows: Architecture, Attacks, and the Limits of Full-Disk Encryption</title><link>https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-/</link><guid isPermaLink="true">https://paragmali.com/blog/bitlocker-on-windows-architecture-attacks-and-the-limits-of-/</guid><description>How BitLocker evolved from an optional enterprise feature to encryption-by-default, its cryptographic architecture, every known attack, and what FDE still cannot protect against.</description><pubDate>Sun, 26 Apr 2026 00:00:00 GMT</pubDate><content:encoded>
**Windows 11 now encrypts your drive with BitLocker by default -- no action required.** The encryption itself (XTS-AES) is solid, but the default TPM-only configuration is vulnerable to physical attacks costing as little as \$5 in hardware. For real security, add a pre-boot PIN, verify software encryption is enforced, and know where your recovery key is stored. This article traces BitLocker&apos;s 20-year evolution from an optional enterprise feature to encryption-by-default, explains its cryptographic architecture, catalogs every known attack, and compares it to VeraCrypt, LUKS, and FileVault.
&lt;h2&gt;Your Laptop Is Already Encrypted&lt;/h2&gt;
&lt;p&gt;Your Windows laptop is almost certainly encrypting every sector of its drive right now -- and you probably never turned that on. Since late 2024, every clean install of Windows 11 activates BitLocker by default, silently encrypting the entire volume and uploading a recovery key to Microsoft&apos;s cloud [@computerworld-24h2]. This is the culmination of a 20-year engineering effort that began with a stolen laptop containing 26.5 million veterans&apos; medical records.&lt;/p&gt;
&lt;p&gt;In May 2006, an employee of the U.S. Department of Veterans Affairs took home a laptop and an external hard drive containing the personal data of 26.5 million veterans and active-duty military personnel. Both were stolen from his home. The data was not encrypted. The breach later resulted in a $20 million class-action settlement and became the defining example of why laptops need full-disk encryption [@ms-bitlocker].&lt;/p&gt;
&lt;p&gt;That event accelerated a shift already underway inside Microsoft: from &quot;encryption is something you turn on&quot; to &quot;encryption is something that&apos;s always there.&quot; Today, the 24H2 update means every new Windows 11 installation has BitLocker active with no user action [@computerworld-24h2]. On Windows Home editions, this takes the form of Device Encryption -- a streamlined subset of BitLocker without Group Policy control or configurable pre-boot PIN.&lt;/p&gt;
&lt;p&gt;But here is the tension at the heart of this story. BitLocker encrypts everything by default, yet security researchers keep finding ways past it -- with a $5 chip, a network cable, or a can of compressed air. If your drive is encrypted, why should you care?&lt;/p&gt;
&lt;p&gt;The answer lies in how the keys are managed. And that story starts 25 years ago.&lt;/p&gt;
&lt;h2&gt;Before BitLocker: File-Level Encryption and Its Fatal Flaw&lt;/h2&gt;
&lt;p&gt;Before BitLocker, Windows had encryption. The Encrypting File System (EFS) shipped with Windows 2000 in 1999, and it encrypted individual files beautifully [@ms-efs]. So why wasn&apos;t that enough?&lt;/p&gt;

A file-level encryption feature built into NTFS since Windows 2000. EFS encrypts individual files using a randomly generated File Encryption Key (FEK), which is then wrapped with the user&apos;s RSA public key certificate. Decryption is transparent while the user is logged in.
&lt;p&gt;EFS worked at the NTFS layer. Each file got its own symmetric key (DESX by default in Windows 2000; AES became the default EFS cipher in Windows XP SP1 and Windows Server 2003), wrapped with the user&apos;s public key and stored in a special NTFS attribute. When you were logged in, decryption happened transparently [@ms-efs].&lt;/p&gt;
&lt;p&gt;The problem was where it didn&apos;t go. EFS encrypts files and folders -- not volumes. The operating system itself, the page file, the hibernation file, the temp directory -- all of that remained in plaintext. An attacker who booted from a USB stick could read the Windows directory, extract cached credentials from the SAM and SECURITY registry hives, and access fragments of &quot;encrypted&quot; files that the OS had swapped to disk [@ms-bitlocker].&lt;/p&gt;
&lt;p&gt;The failure mode was concrete: steal a laptop, mount the drive on another machine, and everything the OS touched is readable. EFS protected the documents you remembered to encrypt. It did nothing for the thousands of files the OS created on your behalf.
Microsoft also explored a far more ambitious approach. The &quot;Next-Generation Secure Computing Base&quot; (NGSCB), originally codenamed Palladium, proposed a hardware-rooted secure computing environment -- a tamper-resistant &quot;nexus&quot; running alongside Windows. Announced in 2002 under Peter Biddle&apos;s leadership, the full vision was scaled back around 2004-2005 under a wave of industry resistance and public backlash over DRM fears. But its core idea -- a Trusted Platform Module providing hardware-rooted key storage -- survived and became BitLocker&apos;s anchor [@ngscb-wiki].&lt;/p&gt;

A dedicated hardware chip (or firmware module) that provides cryptographic key storage, platform integrity measurement, and sealed storage. The TPM can &quot;seal&quot; a secret to specific Platform Configuration Register (PCR) values, releasing it only when the boot chain matches the expected measurements. Standardized by the Trusted Computing Group (TCG), first as TPM 1.2 and later TPM 2.0.
&lt;p&gt;Microsoft needed something that encrypted &lt;em&gt;everything&lt;/em&gt; on the disk -- the OS, the swap file, the hibernation file, the temp files, all of it. But who would design the cipher, and how would the keys be protected without making the user type a 48-character password at every boot?&lt;/p&gt;
&lt;h2&gt;The Architecture: How BitLocker Actually Works&lt;/h2&gt;
&lt;p&gt;In 2006, a Dutch cryptographer named Niels Ferguson published a paper describing a new disk encryption scheme [@ferguson-2006]. Two decades later, the core architecture he designed is still running on hundreds of millions of machines -- structurally unchanged.&lt;/p&gt;
&lt;h3&gt;The Three-Layer Key Hierarchy&lt;/h3&gt;
&lt;p&gt;BitLocker&apos;s design centers on a three-layer key hierarchy that solves an otherwise painful problem: how do you let a user change their password without re-encrypting an entire 2 TB drive?&lt;/p&gt;

The symmetric key that directly encrypts and decrypts disk sectors. In XTS-AES-256 mode, this is effectively two 256-bit AES keys -- one for block encryption, one for the XTS &quot;tweak&quot; computation that ensures identical plaintext at different sector locations produces different ciphertext.

A key-encrypting key that wraps the FVEK. The VMK itself is encrypted by one or more key protectors. This indirection allows changing authentication methods (adding a PIN, rotating a recovery key) without re-encrypting the entire volume -- only the VMK wrapping changes.

An authentication mechanism that independently encrypts the VMK. Common protectors include TPM-only (VMK sealed to Platform Configuration Registers), TPM+PIN (adds a user-supplied PIN), and Recovery Password (a 48-digit numerical backup code). Multiple protectors can coexist on the same volume.
&lt;p&gt;The indirection is the key insight. Your data is encrypted with the FVEK. The FVEK is wrapped by the VMK. The VMK is wrapped by one or more key protectors -- your TPM, your PIN, your recovery password. When you change your PIN, only the VMK wrapper changes. The FVEK stays the same. The entire volume never needs re-encryption [@ms-bitlocker].&lt;/p&gt;

flowchart TD
    A[&quot;Disk Sectors&lt;br /&gt;(encrypted data)&quot;] --&amp;gt;|&quot;decrypted by&quot;| B[&quot;FVEK&lt;br /&gt;(Full Volume Encryption Key)&quot;]
    B --&amp;gt;|&quot;wrapped by&quot;| C[&quot;VMK&lt;br /&gt;(Volume Master Key)&quot;]
    C --&amp;gt;|&quot;protected by&quot;| D[&quot;TPM-Only&lt;br /&gt;Protector&quot;]
    C --&amp;gt;|&quot;protected by&quot;| E[&quot;TPM+PIN&lt;br /&gt;Protector&quot;]
    C --&amp;gt;|&quot;protected by&quot;| F[&quot;Recovery Password&lt;br /&gt;(48-digit)&quot;]
    C --&amp;gt;|&quot;protected by&quot;| G[&quot;Recovery Key&lt;br /&gt;(file/cloud escrow)&quot;]
&lt;h3&gt;TPM Sealing: The Hardware Root of Trust&lt;/h3&gt;
&lt;p&gt;At boot time, the TPM measures each component of the boot chain -- firmware, bootloader, boot configuration -- and records hashes in its Platform Configuration Registers (PCRs). During BitLocker setup, the TPM seals the VMK to the current PCR values. On every subsequent boot, the TPM re-measures the chain. If the measurements match, the TPM releases the VMK. If anything has changed -- a modified bootloader, a BIOS update, a different boot device -- the TPM refuses, and the user must provide a recovery key [@ms-bitlocker].&lt;/p&gt;

A register inside the TPM that stores a hash representing the state of a specific component of the boot chain (firmware, bootloader, OS kernel, etc.). PCR values are &quot;extended&quot; at each boot step -- the new measurement is hashed together with the previous value, creating a tamper-evident chain. BitLocker seals the VMK to specific PCR values so that any modification to the boot process blocks key release.

sequenceDiagram
    participant UEFI as UEFI Firmware
    participant TPM as TPM Chip
    participant BL as Windows Boot Manager
    participant OS as Windows OS
    UEFI-&amp;gt;&amp;gt;TPM: Extend PCR 0-7 (firmware measurements)
    BL-&amp;gt;&amp;gt;TPM: Extend PCR 8-11 (bootloader measurements)
    BL-&amp;gt;&amp;gt;TPM: Request VMK unseal
    alt PCR values match sealed state
        TPM-&amp;gt;&amp;gt;BL: Release VMK (cleartext)
        BL-&amp;gt;&amp;gt;OS: Decrypt FVEK, mount volume
    else PCR mismatch (tampered boot chain)
        TPM-&amp;gt;&amp;gt;BL: Refuse unseal
        BL-&amp;gt;&amp;gt;OS: Prompt for recovery key
    end
&lt;p&gt;Ferguson&apos;s Elephant diffuser served BitLocker well from Vista through Windows 7, but it was a custom, non-standard construction. Government compliance frameworks (FIPS 140-2) and the emerging IEEE 1619 standard required a published, peer-reviewed cipher mode [@nist-800-38e].
Ferguson&apos;s original 2006 design used AES-128 in CBC mode with a custom &quot;Elephant&quot; diffuser -- a two-pass diffusion algorithm (one pass forward, one pass backward) applied to each 512-byte sector. The Elephant diffuser ensured that flipping one bit of ciphertext would scramble the entire sector of plaintext, raising the bar for sector manipulation attacks. The name was reportedly a nod to the animal&apos;s famously long memory -- a reference to diffusion spreading changes across the entire sector.&lt;/p&gt;

The removal of the Elephant diffuser in Windows 8 was controversial. Microsoft cited FIPS 140-2 certification requirements and alignment with standardized disk-encryption modes as the primary motivations. Windows 8 kept AES-CBC but removed the proprietary whole-sector diffuser; Windows 10 version 1511 later moved new volumes to XTS-AES. Critics pointed out that both post-Elephant designs lost the diffuser&apos;s whole-sector avalanche behavior. Microsoft chose standards compliance over a proprietary advantage -- a pragmatic decision that made BitLocker deployable in regulated environments worldwide [@ms-bitlocker].
&lt;p&gt;Windows 10 version 1511 (November 2015) completed the transition by making XTS-AES-128 the default for new volumes, with XTS-AES-256 available via Group Policy [@ms-bitlocker]. XTS stands for XEX-based Tweaked-codebook mode with ciphertext Stealing, built on Rogaway&apos;s XEX (XOR-Encrypt-XOR) construction [@rogaway-2004]. The core XEX pattern works like this: each sector gets a unique &quot;tweak&quot; derived from its sector number, and each 16-byte block within that sector is XORed with the tweak, AES-encrypted, then XORed with the tweak again. XTS extends XEX by adding ciphertext stealing to handle sectors that are not an exact multiple of 16 bytes. The result: identical plaintext at different disk locations produces different ciphertext, and there is no IV management problem like CBC had [@nist-800-38e].&lt;/p&gt;

flowchart LR
    SN[&quot;Sector Number&quot;] --&amp;gt; TW[&quot;Tweak Generation&lt;br /&gt;(AES encrypt sector #&lt;br /&gt;with tweak key)&quot;]
    TW --&amp;gt; X1a[&quot;XOR with&lt;br /&gt;Tweak&quot;]
    TW --&amp;gt; X2a[&quot;XOR with&lt;br /&gt;Tweak&quot;]
    TW --&amp;gt; XNa[&quot;XOR with&lt;br /&gt;Tweak&quot;]
    P1[&quot;Plaintext&lt;br /&gt;Block 1&quot;] --&amp;gt; X1a --&amp;gt; AES1[&quot;AES Encrypt&quot;] --&amp;gt; X1b[&quot;XOR with&lt;br /&gt;Tweak&quot;] --&amp;gt; C1[&quot;Ciphertext&lt;br /&gt;Block 1&quot;]
    P2[&quot;Plaintext&lt;br /&gt;Block 2&quot;] --&amp;gt; X2a --&amp;gt; AES2[&quot;AES Encrypt&quot;] --&amp;gt; X2b[&quot;XOR with&lt;br /&gt;Tweak&quot;] --&amp;gt; C2[&quot;Ciphertext&lt;br /&gt;Block 2&quot;]
    PN[&quot;Plaintext&lt;br /&gt;Block N&quot;] --&amp;gt; XNa --&amp;gt; AESN[&quot;AES Encrypt&quot;] --&amp;gt; XNb[&quot;XOR with&lt;br /&gt;Tweak&quot;] --&amp;gt; CN[&quot;Ciphertext&lt;br /&gt;Block N&quot;]
&lt;p&gt;This architecture is elegant. The TPM releases the key automatically at boot if nothing has changed. The key hierarchy means operational changes (new PIN, new recovery key) never touch the encrypted data. The cipher mode is standardized, hardware-accelerated via AES-NI on virtually all modern CPUs [@intel-aesni], and FIPS-validated.&lt;/p&gt;
&lt;p&gt;But what happens if an attacker gets to the hardware? Princeton researchers were about to find out.&lt;/p&gt;
&lt;h2&gt;The Cold Wake-Up Call: Attacks That Changed Everything&lt;/h2&gt;
&lt;p&gt;In 2008, a team at Princeton did something unsettling. They sprayed a laptop&apos;s RAM with compressed air, yanked the power cord, and recovered the BitLocker encryption key from the memory that should have been erased. It wasn&apos;t [@halderman-2008].&lt;/p&gt;

A physical attack exploiting the fact that DRAM retains its contents for seconds to minutes after power loss (a property called &quot;data remanence&quot;). By cooling RAM to extend retention time and quickly rebooting into an attacker-controlled OS, encryption keys can be extracted from residual memory contents. First demonstrated against BitLocker, FileVault, and dm-crypt by Halderman et al. at Princeton in 2008.
&lt;p&gt;J. Alex Halderman and his team demonstrated that DRAM does not instantly lose its contents when power is removed. At room temperature, data decays over seconds. Cooled with compressed air (around -50C), retention extends to minutes. That is enough time to reboot into a USB-based attack environment, scan memory for AES key schedules, and extract the FVEK [@halderman-2008].&lt;/p&gt;

DRAM retains its contents for seconds to minutes after power loss... enough time to extract full encryption keys. -- Halderman et al., 2008, adapted from the paper&apos;s findings.
&lt;p&gt;The cold boot attack revealed a fundamental truth about every full-disk encryption system: the keys must live in RAM while the OS is running. There is no way around this. Encrypt the keys in memory and you need a key to decrypt &lt;em&gt;those&lt;/em&gt; keys -- turtles all the way down. The cold boot paper did not just break BitLocker; it defined the boundary of what FDE can and cannot do.&lt;/p&gt;
&lt;h3&gt;The $5 Chip That Reads Your Encryption Key&lt;/h3&gt;
&lt;p&gt;In 2019, Denis Andzakovic at Pulse Security demonstrated something even more unsettling: in BitLocker&apos;s default TPM-only configuration, the VMK travels in cleartext on the communication bus (SPI or LPC, depending on hardware) between the discrete TPM chip and the CPU [@andzakovic-2019]. A $40 NZD FPGA, clipped to the right pins, captured the VMK during boot. The drive was decrypted offline in under five minutes.&lt;/p&gt;

sequenceDiagram
    participant CPU as CPU
    participant SPI as SPI / LPC Bus
    participant TPM as Discrete TPM
    participant ATK as Attacker (logic analyzer)
    Note over CPU,TPM: Normal boot sequence
    CPU-&amp;gt;&amp;gt;SPI: Request VMK unseal
    SPI-&amp;gt;&amp;gt;TPM: Forward request
    TPM-&amp;gt;&amp;gt;SPI: Return VMK (cleartext!)
    ATK-&amp;gt;&amp;gt;SPI: Sniff VMK from bus traffic
    SPI-&amp;gt;&amp;gt;CPU: Deliver VMK
    Note over ATK: Attacker now has VMK -- Drive can be decrypted offline
&lt;p&gt;The SCRT security team independently reproduced this attack in 2021 [@scrt-tpm-2021], and by 2024, the community had replaced the FPGA with a $5 Raspberry Pi Pico [@tpm-sniffing-repo]. Compass Security documented successful attacks on Lenovo ThinkPad, HP EliteBook, and Microsoft Surface models -- the most common enterprise laptops [@compass-bypasses].
The $40 FPGA that Andzakovic used in 2019 has since been replaced by a $5 Raspberry Pi Pico in community tooling. The TPM-Sniffing repository on GitHub maintains a list of tested laptop models and TPM chips, with specific pinout diagrams for each.&lt;/p&gt;
&lt;p&gt;Even adding a TPM+PIN does not fully close this gap. In October 2024, SCRT Security demonstrated that if an attacker knows (or coerces) the PIN, the VMK can still be extracted via SPI bus sniffing [@scrt-tpm-pin-2024]. The real mitigation is a firmware TPM (fTPM, integrated into the CPU) -- when the TPM is on the same die as the processor, there is no external bus to sniff.&lt;/p&gt;
&lt;h3&gt;The SSD Betrayal&lt;/h3&gt;
&lt;p&gt;In 2019, Carlo Meijer and Bernard van Gastel at Radboud University published findings that shocked the storage industry. Multiple popular SSDs from Samsung and Critical had critically flawed hardware encryption. In some cases, the data encryption key was stored in plaintext on the drive. In others, the user&apos;s password was never actually used for key derivation [@meijer-2019].&lt;/p&gt;

Multiple popular SSDs had critically flawed hardware encryption -- in some cases, the data encryption key was stored in plaintext or could be recovered by manipulating firmware. -- Meijer and van Gastel, 2019, summarizing their IEEE S&amp;amp;P findings.

This was not a theoretical weakness. BitLocker&apos;s &quot;eDrive&quot; mode trusted SSD firmware to perform encryption. On a Samsung 850 EVO or Critical MX300 with eDrive enabled, BitLocker reported the drive as &quot;encrypted&quot; -- but an attacker with physical access could use JTAG debugging or firmware manipulation to read all data in plaintext. The data was never actually encrypted by the hardware [@meijer-2019]. Microsoft issued Security Advisory ADV180028 in November 2018, recommending administrators enforce software encryption, and later changed the default so BitLocker no longer trusts hardware encryption [@adv180028].
&lt;h3&gt;DMA and Bitpixie: The Attacks Keep Coming&lt;/h3&gt;
&lt;p&gt;Ulf Frisk&apos;s PCILeech tool (2016) turned DMA attacks into a scriptable operation. Any system with unprotected Thunderbolt, ExpressCard, or PCIe slots was vulnerable to direct memory reads while powered on or in sleep mode [@pcileech]. Björn Ruytenberg&apos;s Thunderspy research (2020) found seven vulnerabilities in Intel&apos;s Thunderbolt protocol that enabled evil-maid DMA attacks bypassing disk encryption and screen locks. Microsoft introduced Kernel DMA Protection in Windows 10 version 1803, using IOMMU to block unauthorized DMA from external Thunderbolt and PCIe devices -- closing this vector on supported hardware [@ms-kernel-dma].&lt;/p&gt;
&lt;p&gt;And in 2023, the &quot;bitpixie&quot; vulnerability (CVE-2023-21563) introduced a purely software-based bypass. An attacker with physical access triggers a PXE network boot, which performs a soft reboot -- leaving the VMK in RAM. The attacker boots into a Linux environment (or, in newer variants using the WinPE approach, a custom Windows PE image that avoids Secure Boot restrictions on third-party Linux loaders), scans memory, and extracts the VMK. The entire attack takes about five minutes and requires no hardware modifications [@cve-2023-21563].&lt;/p&gt;
&lt;p&gt;Each attack reinforced the same lesson: full-disk encryption only protects data at rest. The default TPM-only configuration was never designed to resist a determined attacker with physical access. So how did Microsoft respond?&lt;/p&gt;
&lt;h2&gt;The Evolution: Generation by Generation&lt;/h2&gt;
&lt;p&gt;What makes BitLocker&apos;s history unusual is that each generation was a direct response to a specific failure. The evolution was not planned from the start -- it was forced by attackers.&lt;/p&gt;

flowchart TD
    G0[&quot;Gen 0: EFS (2000)&lt;br /&gt;File-level encryption&quot;] --&amp;gt;|&quot;OS/swap/temp left exposed&quot;| G1[&quot;Gen 1: Vista BitLocker (2006)&lt;br /&gt;AES-CBC + Elephant diffuser&quot;]
    G1 --&amp;gt;|&quot;OS drive only; non-standard cipher&quot;| G2[&quot;Gen 2: Win 7 (2009)&lt;br /&gt;+ BitLocker To Go&quot;]
    G2 --&amp;gt;|&quot;Elephant blocks FIPS/IEEE compliance&quot;| G3[&quot;Gen 3: Win 8/8.1 (2012-2013)&lt;br /&gt;No Elephant + eDrive + Device Encryption&quot;]
    G3 --&amp;gt;|&quot;SSD hardware encryption broken (2018)&quot;| G4[&quot;Gen 4: Win 10 (2015-2021)&lt;br /&gt;Software-first + cloud management&quot;]
    G4 --&amp;gt;|&quot;Still not default for consumers&quot;| G5[&quot;Gen 5: Win 11 24H2 (2024)&lt;br /&gt;Encryption by default&quot;]
&lt;p&gt;&lt;strong&gt;Generation 0 -- EFS (2000):&lt;/strong&gt; File-level encryption on NTFS. Left the OS, swap, and temp files exposed. Superseded because volume-level encryption was needed [@ms-efs].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generation 1 -- Vista (2006):&lt;/strong&gt; Ferguson&apos;s AES-CBC + Elephant diffuser. First volume-level encryption in Windows. OS drive only. The cold boot attack (2008) revealed that FDE keys in RAM are vulnerable [@ferguson-2006, @halderman-2008].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generation 2 -- Windows 7 (2009):&lt;/strong&gt; Added BitLocker To Go for USB drives and data volumes. The 2006 VA breach had involved a stolen external hard drive, making removable media encryption urgent. The Elephant diffuser persisted, blocking compliance certification [@ms-bitlocker].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generation 3 -- Windows 8/8.1 (2012-2013):&lt;/strong&gt; Removed the Elephant diffuser while retaining AES-CBC, introduced eDrive hardware offload in Windows 8, and added consumer Device Encryption in Windows 8.1. The SSD betrayal (2018) destroyed the eDrive trust model [@ms-bitlocker, @meijer-2019].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generation 4 -- Windows 10 (2015):&lt;/strong&gt; XTS-AES-128 became the default for new volumes in version 1511. Software encryption was enforced after the Radboud findings. Cloud-based key escrow via Azure AD. TPM sniffing attacks (2019) showed the default TPM-only configuration was vulnerable [@adv180028, @andzakovic-2019].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Generation 5 -- Windows 11 24H2 (2024):&lt;/strong&gt; Encryption by default on clean installs. Relaxed hardware requirements (no Modern Standby/HSTI needed). &lt;a href=&quot;https://paragmali.com/blog/the-tpm-in-windows-one-primitive-twenty-five-years-and-the-c/&quot; rel=&quot;noopener&quot;&gt;TPM 2.0&lt;/a&gt; mandatory. Recovery keys escrowed to Microsoft Account or Azure AD. Hardware-accelerated encryption available in 25H2 [@techpowerup-hwaccel].&lt;/p&gt;

gantt
    title BitLocker Evolution and Attack Timeline
    dateFormat YYYY
    axisFormat %Y
    section Generations
        EFS (file-level)           :g0, 2000, 2006
        Vista: AES-CBC+Elephant    :g1, 2006, 2009
        Win 7: +BitLocker To Go   :g2, 2009, 2012
        Win 8/8.1: no Elephant+eDrive :g3, 2012, 2015
        Win 10: Software-first    :g4, 2015, 2024
        Win 11 24H2: Default      :g5, 2024, 2026
    section Attacks
        Cold boot (Princeton)     :milestone, 2008, 0d
        DMA/PCILeech              :milestone, 2016, 0d
        SSD bypass (Radboud)      :milestone, 2018, 0d
        TPM sniffing              :milestone, 2019, 0d
        Thunderspy                :milestone, 2020, 0d
        Bitpixie (CVE-2023-21563) :milestone, 2023, 0d
&lt;p&gt;By 2024, BitLocker had evolved from an enterprise opt-in feature to encryption that activates without asking. But how does it compare to what Linux and macOS users have?&lt;/p&gt;
&lt;h2&gt;BitLocker vs. the Competition&lt;/h2&gt;
&lt;p&gt;BitLocker is not the only option. Every major OS now ships with full-disk encryption, and the open-source world offers capabilities that BitLocker cannot match.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;BitLocker (Win 11)&lt;/th&gt;
&lt;th&gt;VeraCrypt&lt;/th&gt;
&lt;th&gt;LUKS2 (dm-crypt)&lt;/th&gt;
&lt;th&gt;FileVault 2&lt;/th&gt;
&lt;th&gt;SEDs (TCG Opal)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Platform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Windows&lt;/td&gt;
&lt;td&gt;Windows, macOS, Linux&lt;/td&gt;
&lt;td&gt;Linux&lt;/td&gt;
&lt;td&gt;macOS&lt;/td&gt;
&lt;td&gt;Any (firmware)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Cipher&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;XTS-AES-128/256&lt;/td&gt;
&lt;td&gt;XTS-AES, Serpent, Twofish, cascades&lt;/td&gt;
&lt;td&gt;XTS-AES (default)&lt;/td&gt;
&lt;td&gt;XTS-AES-256 (APFS)&lt;/td&gt;
&lt;td&gt;AES-256 (vendor)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Key derivation&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;TPM sealing + optional PIN&lt;/td&gt;
&lt;td&gt;PBKDF2 + PIM&lt;/td&gt;
&lt;td&gt;Argon2id (memory-hard)&lt;/td&gt;
&lt;td&gt;Secure Enclave&lt;/td&gt;
&lt;td&gt;Firmware-specific&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open source&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Audited&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FIPS 140-2 certified&lt;/td&gt;
&lt;td&gt;Quarkslab 2016; Fraunhofer/BSI 2020&lt;/td&gt;
&lt;td&gt;Community-audited&lt;/td&gt;
&lt;td&gt;Apple docs published&lt;/td&gt;
&lt;td&gt;Radboud found flaws&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enterprise mgmt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Intune, GPO, SCCM&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;td&gt;Scriptable&lt;/td&gt;
&lt;td&gt;MDM (Jamf)&lt;/td&gt;
&lt;td&gt;Vendor-specific&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Plausible deniability&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes (hidden volumes)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Minimal (sequential); up to 50% random IOPS loss&lt;/td&gt;
&lt;td&gt;Similar&lt;/td&gt;
&lt;td&gt;Similar&lt;/td&gt;
&lt;td&gt;Near-zero (hardware)&lt;/td&gt;
&lt;td&gt;Near-zero (hardware)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;VeraCrypt&lt;/strong&gt; (2013) is the open-source TrueCrypt fork, maintained by Mounir Idrassi [@veracrypt-docs]. It offers cascaded ciphers (AES-Twofish-Serpent), hidden volumes for plausible deniability, and cross-platform support. Its hidden volume feature is particularly notable: a single encrypted container holds two volumes with different passwords, and the outer volume&apos;s free space is cryptographically indistinguishable from the hidden volume&apos;s ciphertext. An adversary who compels you to reveal a password gets the decoy; the hidden volume remains undetectable. VeraCrypt was audited by Quarkslab in 2016, which found 8 critical and 3 medium-severity vulnerabilities; v1.19 fixed many high-priority issues, while OSTIF noted that some complex findings were handled with documented workarounds rather than complete fixes. It was audited by Fraunhofer SIT, commissioned by the German BSI, in 2020 [@ostif-veracrypt]. What it lacks is enterprise management -- no centralized key escrow, no MDM integration, no Group Policy. For individual privacy, it is unmatched. For a fleet of 10,000 laptops, it is impractical.
VeraCrypt&apos;s hidden volumes provide plausible deniability -- you can be compelled to reveal a password, and it unlocks a decoy volume while the real data remains hidden in space that is statistically indistinguishable from random data. No other mainstream FDE tool offers this capability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;LUKS2 with dm-crypt&lt;/strong&gt; is Linux-native full-disk encryption, maintained as part of the cryptsetup project [@cryptsetup-faq]. Its standout feature is the Argon2id key derivation function -- a memory-hard, GPU/ASIC-resistant password hash that provides superior brute-force resistance compared to BitLocker&apos;s TPM-based approach or VeraCrypt&apos;s PBKDF2 [@argon2-spec]. Why does memory-hardness matter? GPUs and ASICs can test billions of PBKDF2 hashes per second because each attempt needs little memory. Argon2id forces each attempt to allocate a configurable amount of RAM; LUKS2 calibrates the parameters to a target unlock time on the host system rather than using a fixed 1 GB default. That makes parallel brute-force attacks on GPUs prohibitively expensive. LUKS2 supports up to 32 keyslots, allowing multiple users or recovery methods per volume [@cryptsetup-faq]. It integrates with systemd-cryptenroll for TPM2 and &lt;a href=&quot;https://paragmali.com/blog/your-face-is-not-your-password-inside-windows-hellos-hardwar/&quot; rel=&quot;noopener&quot;&gt;FIDO2&lt;/a&gt; token support. But it is Linux-only, and setup requires comfort with the command line.&lt;/p&gt;

A memory-hard key derivation function (KDF) that won the Password Hashing Competition in 2015. Argon2id combines the data-dependent memory access pattern of Argon2d with the data-independent access pattern of Argon2i, providing resistance to both GPU-based brute-force attacks and side-channel attacks. Used by LUKS2 as its default KDF, replacing the older PBKDF2.
&lt;p&gt;&lt;strong&gt;FileVault 2&lt;/strong&gt; is macOS-native full-disk encryption, deeply integrated with Apple&apos;s hardware. On APFS volumes (macOS High Sierra and later), FileVault uses XTS-AES-256. On T2-equipped Intel Macs and all Apple Silicon machines, the Secure Enclave -- a dedicated security coprocessor physically isolated from the main CPU -- manages encryption keys in hardware. Unlike a discrete TPM where keys transit an external bus (the vulnerability that enables TPM sniffing), Secure Enclave keys never leave the chip. The SSD controller handles encryption in real-time using dedicated AES hardware. The result is near-zero measurable performance impact. FileVault is what happens when one company controls both the hardware and software.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-Encrypting Drives (SEDs)&lt;/strong&gt; conforming to TCG Opal perform encryption in the drive&apos;s firmware with zero CPU overhead. But after the Radboud findings, trusting opaque SSD firmware for security is a gamble. The Meijer and van Gastel research affected drives from Samsung and Critical that, according to the researchers, represented roughly 60% of the consumer SSD market at the time [@meijer-2019].&lt;/p&gt;
&lt;p&gt;Each approach makes different trade-offs between transparency, performance, enterprise management, and trust. But they all share one theoretical limitation.&lt;/p&gt;
&lt;h2&gt;The Limits: What No Full-Disk Encryption Can Do&lt;/h2&gt;
&lt;p&gt;Here is the uncomfortable truth that no vendor will put on the box: full-disk encryption cannot protect your data while your computer is running. That is not a bug. It is a fundamental impossibility.&lt;/p&gt;

Any full-disk encryption system must hold decryption keys in volatile memory while the OS is running. Full protection is available only when the device is powered off.
&lt;p&gt;Any FDE system -- BitLocker, VeraCrypt, LUKS, FileVault -- must load the decryption key into RAM to service I/O requests. While the key is in RAM, it is vulnerable to cold boot attacks, DMA attacks, and any malware running with kernel privileges. This is not a design flaw. It is an impossibility result. You cannot encrypt and decrypt data simultaneously without holding a key somewhere accessible [@halderman-2008].&lt;/p&gt;
&lt;h3&gt;The XTS Semantic Security Gap&lt;/h3&gt;
&lt;p&gt;XTS mode has a narrower theoretical limitation. It uses a sector-derived tweak, so identical plaintext at the same block offset in different sectors does &lt;strong&gt;not&lt;/strong&gt; produce identical ciphertext. The limitation is narrower diffusion: each 16-byte block is protected independently within the sector, so a targeted ciphertext modification corrupts the corresponding 16-byte plaintext block rather than avalanching across the entire sector. Phillip Rogaway&apos;s tweakable-block-cipher work explains why narrow-block disk modes have this shape [@rogaway-2004].&lt;/p&gt;
&lt;p&gt;Wide-block modes like EME and CMC, proposed by Shai Halevi and Rogaway, encrypt an entire sector as a single block -- changing one bit of plaintext changes &lt;em&gt;every&lt;/em&gt; bit of ciphertext across the sector [@halevi-rogaway-eme]. These modes provide stronger semantic security but at higher computational cost, and no major FDE system has adopted them.&lt;/p&gt;

NIST SP 800-111 recommends pre-boot authentication for full-disk encryption in government systems [@nist-sp800-111]. Organizations subject to NIST guidelines may find TPM-only mode insufficient for compliance -- both because of the bus sniffing vulnerability and because of the XTS narrow-block limitation. The practical recommendation is TPM+PIN with XTS-AES-256, which satisfies most regulatory frameworks even if it does not close the theoretical semantic security gap.
&lt;h3&gt;The Performance Reality&lt;/h3&gt;
&lt;p&gt;Software-based BitLocker on fast NVMe SSDs carries a real cost. TechPowerUp measured a 375% increase in CPU cycles per I/O when software BitLocker is active, with random 4K IOPS dropping by up to 50% [@techpowerup-hwaccel]. Sequential throughput impact is typically under 5% thanks to AES-NI acceleration, but the random I/O penalty matters for database workloads and developer builds.&lt;/p&gt;
&lt;p&gt;Microsoft&apos;s hardware-accelerated BitLocker, available in Windows 11 25H2, uses CPU crypto instructions and NVMe controller integration to close this gap. Early benchmarks show random 4K IOPS doubling compared to software mode, with CPU usage dropping by over 70% -- approaching unencrypted performance [@techpowerup-hwaccel].&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;No BitLocker&lt;/th&gt;
&lt;th&gt;Software BitLocker&lt;/th&gt;
&lt;th&gt;HW-Accelerated BitLocker (25H2)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;&lt;tr&gt;
&lt;td&gt;Sequential read/write&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;~5% overhead&lt;/td&gt;
&lt;td&gt;~0% overhead&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Random 4K IOPS&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;Up to 50% loss&lt;/td&gt;
&lt;td&gt;~10% loss&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU cycles per I/O&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;td&gt;+375%&lt;/td&gt;
&lt;td&gt;~+30%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;If the fundamental limits are known, what problems remain unsolved?&lt;/p&gt;
&lt;h2&gt;Open Problems: What Keeps Researchers Awake&lt;/h2&gt;
&lt;p&gt;BitLocker is mature, battle-tested, and now ubiquitous. Yet at least five significant problems remain open -- and some of them could invalidate the entire trust model.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Post-quantum readiness.&lt;/strong&gt; AES-256 survives Grover&apos;s algorithm with an effective 128-bit security level -- still intractable for foreseeable quantum computers [@nist-800-38e]. But the key &lt;em&gt;management&lt;/em&gt; layer is not quantum-safe. TPM 2.0 attestation and Azure AD key escrow use RSA-2048 or ECC P-256, both vulnerable to Shor&apos;s algorithm. NIST finalized post-quantum key encapsulation (ML-KEM) and digital signature (ML-DSA) standards in 2024 [@nist-pqc], but no shipping FDE system integrates post-quantum cryptography into its key management chain yet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SSD firmware auditability.&lt;/strong&gt; There is no standard mechanism to verify that a self-encrypting drive&apos;s firmware actually implements encryption correctly. The Radboud findings proved this is not hypothetical. Microsoft&apos;s policy of defaulting to software encryption works around the problem but does not solve it for hardware-only use cases [@meijer-2019].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Secure Boot trust chain revocation.&lt;/strong&gt; The bitpixie attack persists because older, vulnerable bootloaders remain trusted by Secure Boot. Revoking old certificates via DBX updates is slow and risks bricking devices on heterogeneous hardware fleets. Microsoft&apos;s KB5025885 provides a phased revocation approach, but the transition from the 2011 Microsoft UEFI CA to the 2023 CA is incomplete [@cve-2023-21563].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recovery key awareness.&lt;/strong&gt; Encryption-by-default means millions of non-technical users have BitLocker active without understanding recovery key management. If a TPM is replaced, a BIOS update changes PCR values, or the motherboard is swapped, users who do not know they have a recovery key -- or where it is stored -- may permanently lose access to all their data [@elcomsoft-forensics].&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Forensic and legal implications.&lt;/strong&gt; As every Windows device becomes encrypted by default, law enforcement and digital forensics face increasing difficulty accessing data on seized devices. Courts are grappling with compelled decryption and Fifth Amendment implications. Forensic vendors maintain tools that exploit known weaknesses (like bitpixie), but these become less effective as mitigations are deployed [@elcomsoft-forensics].
Twenty years after BitLocker first shipped, the symmetric encryption itself (AES-256) is solved. The unsolved problems are all about trust -- in hardware, in firmware, in cloud key escrow, and in users&apos; ability to manage their own recovery keys.&lt;/p&gt;
&lt;h2&gt;Practical Guide: Deploying BitLocker Correctly&lt;/h2&gt;
&lt;p&gt;Theory is useful. Configuration is what actually protects your data. Here is how to deploy BitLocker in a way that addresses the known attack surface.&lt;/p&gt;
&lt;h3&gt;Recommended Configuration&lt;/h3&gt;
&lt;p&gt;For sensitive systems, the recommended configuration is:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;TPM+PIN&lt;/strong&gt; -- Closes the TPM bus sniffing gap. The PIN adds a knowledge factor that the TPM requires before releasing the VMK. Even on discrete TPM chips, the attacker cannot extract the VMK without the PIN (though the PIN itself must be strong enough to resist brute-force) [@andzakovic-2019, @scrt-tpm-pin-2024].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;XTS-AES-256&lt;/strong&gt; -- The maximum key length available. Configure via Group Policy: &lt;code&gt;Computer Configuration &amp;gt; Administrative Templates &amp;gt; Windows Components &amp;gt; BitLocker Drive Encryption &amp;gt; Operating System Drives &amp;gt; Choose drive encryption method and cipher strength&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Software encryption mode&lt;/strong&gt; -- Do not trust SSD hardware encryption. Force software mode via Group Policy: &lt;code&gt;Configure use of hardware-based encryption for operating system drives: Disabled&lt;/code&gt; [@adv180028].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Verify recovery key location&lt;/strong&gt; -- Check that your recovery key is stored somewhere you can access. For consumers: &lt;code&gt;https://account.microsoft.com/devices/recoverykey&lt;/code&gt;. For enterprises: verify Azure AD / Entra ID escrow.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Verification Commands&lt;/h3&gt;
&lt;p&gt;{`
// Simulates the logic of manage-bde -status and Get-BitLockerVolume
// In production, run these PowerShell commands:
// manage-bde -status C:
// Get-BitLockerVolume -MountPoint C: | Select-Object *&lt;/p&gt;
&lt;p&gt;const config = {
  volume: &quot;C:&quot;,
  encryptionMethod: &quot;XTS-AES-256&quot;,
  protectors: [&quot;Tpm&quot;, &quot;RecoveryPassword&quot;],
  encryptionPercentage: 100,
  lockStatus: &quot;Unlocked&quot;,
  hardwareEncryption: false
};&lt;/p&gt;
&lt;p&gt;// Check 1: Is a PIN configured?
const hasPIN = config.protectors.some(p =&amp;gt; 
  p === &quot;TpmPin&quot; || p === &quot;TpmPinStartupKey&quot;
);
console.log(hasPIN 
  ? &quot;OK: Pre-boot PIN is configured&quot; 
  : &quot;WARNING: No pre-boot PIN -- vulnerable to TPM bus sniffing&quot;
);&lt;/p&gt;
&lt;p&gt;// Check 2: Is encryption AES-256?
const is256 = config.encryptionMethod.includes(&quot;256&quot;);
console.log(is256 
  ? &quot;OK: Using AES-256&quot; 
  : &quot;INFO: Using AES-128 (consider upgrading to 256)&quot;
);&lt;/p&gt;
&lt;p&gt;// Check 3: Software encryption?
console.log(!config.hardwareEncryption
  ? &quot;OK: Software encryption (not trusting SSD firmware)&quot;
  : &quot;WARNING: Hardware encryption in use -- verify SSD firmware integrity&quot;
);&lt;/p&gt;
&lt;p&gt;// Check 4: Recovery password exists?
const hasRecovery = config.protectors.includes(&quot;RecoveryPassword&quot;);
console.log(hasRecovery
  ? &quot;OK: Recovery password protector exists&quot;
  : &quot;WARNING: No recovery password -- data may be irrecoverable&quot;
);&lt;/p&gt;
&lt;p&gt;// Check 5: Fully encrypted?
console.log(config.encryptionPercentage === 100
  ? &quot;OK: Volume is fully encrypted&quot;
  : &quot;WARNING: Encryption is &quot; + config.encryptionPercentage + &quot;% complete&quot;
);&lt;/p&gt;
&lt;p&gt;console.log(&quot;\n--- Recommendation ---&quot;);
if (!hasPIN) {
  console.log(&quot;Run: manage-bde -protectors -add C: -TPMAndPIN&quot;);
  console.log(&quot;This adds a pre-boot PIN to mitigate bus sniffing attacks.&quot;);
}
`}&lt;/p&gt;
&lt;h3&gt;Common Pitfalls&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Firmware updates triggering recovery mode:&lt;/strong&gt; BIOS/UEFI updates change PCR values, causing the TPM to refuse VMK release. Always suspend BitLocker before firmware updates: &lt;code&gt;manage-bde -protectors -disable C:&lt;/code&gt; [@ms-bitlocker-ps].&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dual-boot scenarios:&lt;/strong&gt; BitLocker encrypts the Windows volume; Linux cannot natively read BitLocker partitions (though the &lt;code&gt;dislocker&lt;/code&gt; tool provides limited read support [@dislocker-github]). Plan partition layouts carefully.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Hardware swaps:&lt;/strong&gt; Replacing the motherboard or TPM chip triggers recovery mode. Ensure the recovery key is accessible &lt;em&gt;before&lt;/em&gt; hardware changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Recovery key in deleted Microsoft Account:&lt;/strong&gt; If the Microsoft Account used during setup is later deleted, the escrowed recovery key is lost. Verify the key is backed up to multiple locations.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

No. Full-disk encryption protects data at rest -- meaning when the device is powered off. While the OS is running, the decryption key is in RAM and vulnerable to DMA attacks, cold boot attacks, and malware with kernel access. While in sleep mode, the key is still in RAM. The protection window is narrower than most people assume: it really only covers a powered-off device stolen by someone without your recovery key.

BitLocker&apos;s source code is not publicly available, so independent code audits are not possible. However, the cryptographic design is published (Ferguson&apos;s 2006 paper), the implementation is FIPS 140-2 certified by an independent lab, and the XTS-AES cipher mode is standardized by IEEE and NIST. The recovery key escrow to Microsoft&apos;s cloud is not a &quot;backdoor&quot; -- it is a documented feature. If you do not want Microsoft to hold your recovery key, you can store it locally or in on-premises Active Directory instead.

No -- not without independent verification. The Radboud University research (Meijer and van Gastel, 2019) proved that Samsung and Critical SSDs had critically broken hardware encryption, affecting drives that the researchers said represented roughly 60% of the consumer SSD market. Microsoft now defaults to software encryption and recommends against relying on SSD hardware encryption unless independently validated. Force software mode via Group Policy.

Yes. Your Windows login password protects against *online* attacks -- someone trying to log in while the OS is running. It does nothing against *offline* attacks. An attacker who removes your drive and connects it to another machine bypasses the login screen entirely. Without encryption, your data is readable. A strong login password and full-disk encryption solve different problems.

It depends on the workload. Sequential read/write performance drops by about 5% with software BitLocker, barely noticeable in daily use thanks to AES-NI hardware acceleration. Random 4K IOPS -- important for databases and development builds -- can drop by up to 50%, with CPU cycles per I/O increasing by 375%. Microsoft&apos;s hardware-accelerated BitLocker in Windows 11 25H2 closes most of this gap, bringing random IOPS to within 10% of unencrypted performance.

Your data is permanently inaccessible. There is no master key, no backdoor, and no way to brute-force AES-256. Check these locations for your recovery key: (1) Microsoft Account at account.microsoft.com/devices/recoverykey, (2) Azure AD / Entra ID if your device is enterprise-managed, (3) a printed copy if you saved one during setup. If none of these have the key, the data is gone.

Yes, especially on laptops with discrete TPM chips. Without a PIN, the TPM releases the encryption key automatically at every boot with no human input. An attacker with brief physical access can sniff the key from the TPM&apos;s SPI bus using a \$5 Raspberry Pi Pico. A pre-boot PIN adds a knowledge factor that the TPM requires before release. Run: `manage-bde -protectors -add C: -TPMAndPIN` to enable it.
&lt;h2&gt;What Comes Next&lt;/h2&gt;
&lt;p&gt;Twenty years of BitLocker history tell a consistent story: the encryption itself was never the weak link. AES has held. XTS mode is mathematically sound. Ferguson&apos;s key hierarchy design from 2006 still works.&lt;/p&gt;
&lt;p&gt;Every real-world failure -- every headline, every conference demo, every CVE -- targeted the trust boundary around the encryption. The trust that RAM clears instantly (it does not). The trust that SSD firmware encrypts honestly (it did not). The trust that the TPM bus is not observable (it is). The trust that users understand their recovery keys (they do not).&lt;/p&gt;
&lt;p&gt;The next chapter of this story will be written by quantum computers and post-quantum cryptography. AES-256 will survive Grover&apos;s algorithm. But the RSA and ECC that protect key exchange, TPM attestation, and cloud escrow will not survive Shor&apos;s. No shipping FDE system has integrated post-quantum key management yet [@nist-pqc]. The clock is ticking -- and the question is whether the industry will act before the deadline, or after the first quantum key recovery makes the news.&lt;/p&gt;
&lt;p&gt;For now, the practical advice is simple: enable TPM+PIN, enforce software encryption, know where your recovery key is, and remember that BitLocker protects your data only when your machine is off.&lt;/p&gt;
</content:encoded><category>bitlocker</category><category>full-disk-encryption</category><category>windows-security</category><category>tpm</category><category>cryptography</category><category>xts-aes</category><category>disk-encryption</category><author>noreply@paragmali.com (Parag Mali)</author></item></channel></rss>