DPAPI and DPAPI-NG: The Credential Vault Under Everything
A 25-year tour of Windows Data Protection API: the four-stage classic chain, the 2012 DPAPI-NG redesign, the KDS root key, and the five structural ceilings the design cannot close.
Permalink1. A Thumb Drive, a Profile Directory, and Three Mimikatz Commands
A DFIR analyst slides a stolen laptop's drive into a write-blocker. The volume mounts because BitLocker's recovery key was already in the corporate KMS. Six files in C:\Users\alice\AppData\Roaming\Microsoft\Protect\S-1-5-21-... -- five GUIDs and a Preferred pointer -- are the only thing standing between the analyst and a decade of Alice's saved Windows secrets.
Three Mimikatz commands later (dpapi::masterkey /in:<GUID> /sid:S-1-5-21-... /password:<known>, then dpapi::cred /in:<credfile> /masterkey:<unwrapped>, then dpapi::chrome /in:"Login Data") the analyst has Alice's saved RDP password to the production jump host, her Microsoft 365 session cookie, the home WiFi PSK, the KeePass "Windows User Account" master password, and the EFS keys for her protected documents (each item is itemised with primary citations in §5's eleven-row credential-vault inventory). No kernel exploit. No live login. Just one (account-password, SID) pair recovered offline from last week's NTDS.dit backup.
The trick that makes that scene possible is older than most working engineers. It shipped in Windows 2000 GA on February 17, 2000, it has been the same shape for 25 years, and its single-secret design assumption has been public and tractable since February 3, 2010. The trick has a name: the Data Protection API, or DPAPI.
This article walks the API end-to-end at the level of detail an academic survey would, but with the working-engineer's framing the topic deserves. We open the four-stage classic-DPAPI chain at the SHA-1-of-NT-hash-into-PBKDF2-with-the-SID-as-UTF-16LE-salt level. We open the 2012 DPAPI-NG redesign and the Microsoft Key Distribution Service's L0/L1/L2 derivation chain at the same level of precision.
We name the four production consumers that ride the new chain in 2026: gMSAs, dMSAs, Windows Hello for Business software-KSP credentials, and Credential Guard's isolated-secret persistence. We name the five structural ceilings the 2022 Golden gMSA, 2024 Chromium app-bound encryption, and 2025 Golden dMSA disclosures all admit out loud. And we close with what a 2026 practitioner -- developer, defender, red-teamer, platform engineer -- actually does with all of it.
A note on adjacent topics: the companion Credential Guard article in this series covers the LSAISO trustlet's isolation boundary; the companion VBS Trustlets article covers the trustlet model itself; the TPM in Windows article covers TPM-bound key storage providers; the BitLocker on Windows article covers full-volume encryption; the NTLMless article covers Kerberoasting and Golden Ticket disambiguation. Where we touch those topics, we touch them briefly and refer out -- the goal here is to make DPAPI's chain legible from CryptProtectData all the way to the four-phase GoldenDMSA pipeline.
If you can read those six files in Alice's Protect directory and you have her password's SHA-1 hash, you have everything Windows ever encrypted for her. The next eleven sections explain why -- and why the 2012 redesign that was supposed to fix it produced a new ceiling that, by 2022, turned out to be even harder to live with.
2. Why Windows 2000 Needed a Credential Vault: Generation 0 and Generation 1
Three years before DPAPI shipped, an attacker with a logged-in user's session could read every Internet Explorer auto-complete password, every Outlook Express account password, every saved-FTP credential, and every dial-up RAS phonebook entry on the machine -- without breaking any cryptography. The late-1990s reversing tradition (the original pwdump, L0phtCrack, Cain & Abel's "Protected Storage PassView," the ad-hoc Outlook Express and IE 4 form-fill stealers documented across Bugtraq and ntbugtraq mailing lists at the time) defeated all of it uniformly. The "encryption" applications used was honoured by the OS, faithfully, for the attacker's process as for the legitimate one -- because there was no system primitive that distinguished one from the other. Each application baked its own key into the binary; every reverser who pulled the binary apart pulled the key out with it.
The system-provided per-user and per-machine secret-storage primitive that ships in every Windows release from Windows 2000 onward. The classic API surface is two flat-C functions -- CryptProtectData and CryptUnprotectData -- that take a plaintext, an optional caller entropy parameter, and return a self-contained opaque BLOB. The cryptographic chain inside those two functions is rooted at the user's login password and is what every "encrypted" Windows secret of the next 25 years sits on top of.
If the attacker is the user's session, what does "encrypt this for the user" even mean? That is the question every Generation 0 design dodged and every modern credential-vault design has to answer head-on. The 1990s answer (XOR-with-a-baked-in-key) and Microsoft's first attempt at a real system primitive (Protected Storage / PStore) both missed the same point in different ways.
Generation 1: Protected Storage
Protected Storage shipped with Internet Explorer 4 and stayed in the OS until Microsoft formally deprecated it. The pstore.dll item taxonomy is a four-tuple (Key, Type, Subtype, Name) -- a folder hierarchy of named secret entries. The API was the first system-level secret store on Windows that any application could use without writing its own key derivation; the conceptual contribution survived even after the implementation was abandoned.
PStore had two ideas the post-Vista world kept and one it dropped. The two it kept: secrets live in a system primitive, not in each application; secrets are addressed by name, not by raw key handle. The one it dropped: an Authenticode access-rule clause that would have bound a stored item to the signing identity of the application that created it. No application ever used the access-rule clause in production. Microsoft's developer notes are blunt about how the story ended: "Pstore uses an older implementation of data protection. Developers are strongly encouraged to take advantage of the stronger data protection provided by the CryptProtectData and CryptUnprotectData functions"; PStore is "only available for read-only operations in Windows Server 2008 and Windows Vista, but may be unavailable in subsequent versions."
What survived into DPAPI
The Generation 2 design that shipped with Windows 2000 in February 2000 made four moves at once. It kept the two PStore ideas worth keeping ("system-level, not per-application" and "secret addressed by structured name"). It dropped the unused Authenticode access-rule clause. It pushed the cryptographic chain down to a key derived directly from the user's login password. And it added a domain-recovery sidecar (the BackupKey Remote Protocol, which we open in §5) so managed enterprises would adopt it.
The canonical first public design document -- NAI Labs / Network Associates' "Windows Data Protection" whitepaper, MSDN ms995355, October 2001 -- is unambiguous about the layering: "DPAPI initially generates a strong key called a MasterKey, which is protected by the user's password. DPAPI uses a standard cryptographic process called Password-Based Key Derivation, described in PKCS #5, to generate a key from the password." And: "DPAPI is a password-based data protection service. It requires a password to provide protection."
Some secondary sources attribute a "Microsoft Windows Data Protection API" whitepaper to Niels Ferguson atniels.ferguson.com/research/dpapi.html. That URL has been TCP-unreachable for years, has zero Wayback captures across four candidate variants, and the Wikipedia Niels Ferguson article lists no DPAPI publication for him -- his named Microsoft paper is the 2006 BitLocker / Elephant-diffuser paper, not anything DPAPI-related. The verifiable Microsoft-blessed first design document is the NAI Labs ms995355 whitepaper.
The two-function flat-C API Microsoft shipped with Windows 2000 in February 2000 is what every Windows secret of the next 25 years has been encrypted with. The four-stage chain it hides behind those two function names is what we open up next.
3. The Four-Stage Chain: How CryptProtectData Actually Encrypts
A CryptProtectData call goes in with a plaintext buffer and an optional entropy parameter; out comes a self-contained opaque BLOB whose header adds roughly 100-150 bytes to the plaintext (the exact size depends on the algorithm choice and the master-key GUID encoding; the field-by-field BLOB layout is documented in the Bursztein-Picod 2010 paper and parsed by the Mimikatz dpapi::blob module). There is no pszProvider argument, no hKey, no algorithm choice exposed to the caller. Behind those two parameters is a four-stage cryptographic chain that has been the same shape for a quarter-century. Each stage takes the previous stage's output and one new input; the only secret in the entire chain that an offline attacker has to guess is the user's password.
Diagram source
flowchart TD
Password["User password"] --> NTHash["NT hash
(MD4 of UTF-16LE password)"]
NTHash --> Sha1NT["SHA-1(NT hash)"]
SID["User SID
(UTF-16LE)"] --> PBKDF2
Sha1NT --> PBKDF2["Stage 1: PBKDF2
(HMAC-SHA1 / HMAC-SHA512)"]
PBKDF2 --> PreKey["Pre-key"]
MK["Stage 2: Master key
(64 random bytes)"] -->|encrypted under| AESCBC["AES-CBC wrap"]
PreKey --> AESCBC
AESCBC --> MKFile["%APPDATA%/Microsoft/Protect/<SID>/<GUID>"]
MK --> SessionKey["Stage 3: Session key
HMAC(MK, salt || entropy)"]
SessionKey --> Wrap["Stage 4: BLOB wrap
(3DES or AES-256, salt, HMAC)"]
Plaintext["Plaintext"] --> Wrap
Wrap --> Blob["DPAPI BLOB"] Stage 1: Pre-key derivation
The pre-key is a function of three values. The user-account password (or its NT-hash equivalent, supplied by LSASS to the local DPAPI provider) is hashed with SHA-1; the SHA-1 result is fed into PBKDF2 as the input keying material; the user's security identifier (SID) UTF-16LE-encoded is the salt; and a Windows-version-dependent iteration count completes the call. The output is a fixed-width pre-key that Stage 2 will use to wrap the master key.
The chain has changed parameters across Windows versions but has kept the four-stage shape since 2000. The Passcape master-key analysis table records the migration verbatim:
| Windows version | Symmetric cipher | HMAC | PBKDF2 iterations |
|---|---|---|---|
| Windows 2000 | RC4 | SHA-1 | 1 |
| Windows XP | 3DES | SHA-1 | 4 000 |
| Windows Vista | 3DES | SHA-1 | 24 000 |
| Windows 7 | AES-256 | SHA-512 | 5 600 |
| Windows 10 / 11 | AES-256 | SHA-512 | 8 000 |
The shift from PBKDF2-HMAC-SHA1 / 3DES (Windows 2000 -- Vista) to PBKDF2-HMAC-SHA512 / AES-256 (Windows 7 onward) happened at Windows 7, not Windows 10; Bursztein and Picod's 2010 USENIX WOOT paper documented the SHA-1 / 3DES / 4 000-iteration era because their study predated Windows 7's release.
Stage 2: The master key
The master key is a 64-byte cryptographically random secret; one is generated per user, on first use of DPAPI, and a fresh one is generated every 90 days by default. Master keys live as files inside %APPDATA%\Microsoft\Protect\<SID>\<GUID>, where the GUID is the master-key identifier embedded in every BLOB that uses it. The file is divided into four slots: a header (with the iteration count and algorithm IDs), the user-master-key blob (encrypted under the Stage 1 pre-key with AES-CBC), the local-encryption-key or CREDHIST GUID, and the domain-backup-key blob (encrypted to the DC's backup public key, see §5).
A 64-byte random secret per user, persisted as a file under %APPDATA%\Microsoft\Protect\<SID>\<GUID>, encrypted under a pre-key derived from the user's password and SID. Every DPAPI BLOB the user ever creates is wrapped under a session key derived from one of these master keys. Master keys rotate every 90 days by default per the NAI Labs design document and the Passcape master-key analysis, but old master keys remain on disk so old BLOBs can still be decrypted.
Stage 3: The per-call session key
For every call to CryptProtectData, DPAPI generates a fresh per-blob salt, computes an HMAC of the master key with the salt and the optional caller entropy, and uses that HMAC as the session key for the actual symmetric encryption. The session key is never stored; it is derivable from (master key, salt, entropy) at unwrap time per the Bursztein-Picod 2010 paper §3.3 and the Mimikatz dpapi::blob parser. The salt is in the BLOB header; the entropy, if any, must be supplied by the caller at unwrap time.
Stage 4: The BLOB wrap
The output BLOB is a self-describing structure with a fixed header. The provider GUID {df9d8cd0-1501-11d1-8c7a-00c04fc297eb} identifies it as a classic-DPAPI blob; the master-key GUID names the master key under which it was wrapped; an algCrypt algorithm identifier records which symmetric cipher was used (0x6603 for CALG_3DES on legacy builds, 0x6610 for CALG_AES_256 on later builds); the salt, ciphertext, and HMAC fill the rest. The Mimikatz dpapi module wiki documents the verbatim field layout that every offline DPAPI tool parses to this day.
The algCrypt field in the DPAPI BLOB header is a CryptoAPI algorithm identifier from wincrypt.h: 0x6603 is CALG_3DES (the historical default, encoded as ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_3DES); 0x6610 is CALG_AES_256 (used on Windows 7 and later, encoded as ALG_CLASS_DATA_ENCRYPT | ALG_TYPE_BLOCK | ALG_SID_AES_256). The provider GUID {df9d8cd0-1501-11d1-8c7a-00c04fc297eb} is the magic constant that marks every classic-DPAPI BLOB and is the same value the Mimikatz dpapi::blob module and the Bursztein-Picod 2010 paper §3.3.1 print when they parse one.
import hashlib
# Synthetic inputs (not real credentials)
nt_hash_hex = "8846f7eaee8fb117ad06bdd830b7586c" # MD4("password") -- example only
sid_text = "S-1-5-21-1234567890-1234567890-1234567890-1001"
iterations = 8000 # Windows 10/11 default per Passcape table
# Stage 1: SHA-1 of the NT hash, then PBKDF2-HMAC-SHA512 with the SID as UTF-16LE salt
nt_hash = bytes.fromhex(nt_hash_hex)
sha1_of_nt = hashlib.sha1(nt_hash).digest()
salt_utf16le = sid_text.encode("utf-16le")
pre_key = hashlib.pbkdf2_hmac(
"sha512", sha1_of_nt, salt_utf16le, iterations, dklen=64
)
print("Pre-key:", pre_key.hex())
# This pre-key is what Stage 2 (AES-CBC wrap of the master key) consumes. Press Run to execute.
The chain is honest about its single secret. The pre-key is a function of (NT hash, SID, iteration count). The first two are public to the OS at every login; the iteration count is in the master-key file. The only thing the offline attacker needs to guess is the password. Bursztein and Picod published the algorithm that proved it -- and that is the next section.
4. The 2010 Disclosure: Bursztein, Picod, and Why DPAPI's Strength Is Exactly the User's Password's Strength
On Wednesday, February 3, 2010, in Washington DC, two researchers released a slide deck titled Decrypting DPAPI data and an open-source Python toolkit named DPAPIck. The talk was Black Hat DC 2010 -- not Black Hat USA, which is held in Las Vegas in late July; the actual venue is on Bursztein's elie.net talk page tagged "Black Hat DC - 2010" and the slide-deck cover page is dated "Wednesday, February 3, 2010." They did not break a cryptographic primitive. They published the algorithm.
What the contribution actually was
What Bursztein and Picod published was a complete public algorithm description for offline DPAPI master-key recovery, plus a tool that exercised it. The talk page is verbatim about the tool: "We will demonstrate and release DPAPick (...) which we believe, is the first tool that allows to decrypt offline data encrypted with DPAPI." The paper itself ran six pages and documented the PBKDF2 chain, the master-key file layout, the CREDHIST chain, and the offline EFS-certificate recovery path that follows from holding the master key.
Passcape's prior art -- the DPAPI Black Box paper from 2003 and the 2005 commercial Outlook Password Recovery product -- predated the academic disclosure by seven years. Passcape's own write-up is verbatim: "For the first time, DPAPI was analyzed by Passcape Software in 2003. In 2005, the company released the first commercial application (Outlook Password Recovery) based on that recovery, which could decrypt DPAPI blobs offline, i.e. without logging on to owner's account." The academic community treats Bursztein-Picod 2010 as the canonical reference rather than Passcape 2003 because of three differences: peer review, an open-source tool, and acceptance into a USENIX workshop.
What it did not do
The 2010 disclosure did not weaken DPAPI's cryptography. The cryptography was already sound; the master-key chain was already what it had been since 2000. What the disclosure made operationally true is that any attacker with the master-key file plus the user's password (or NT hash) could now decrypt every BLOB. This had been the design intent since the Windows 2000 release. The chain was never stronger than the password.
The 2010 Bursztein-Picod disclosure did not make DPAPI weaker. It made the strength of DPAPI legible: as strong as the user's password is, no more, no less.
The 2014-2018 toolchain wave
From Bursztein-Picod the operational chain progressed through three distinct waves. The first was Mimikatz's dpapi::* module family, pushed by Benjamin Delpy when the GitHub repository went public on April 6, 2014; the GitHub API record preserves the timestamp to the second ("created_at":"2014-04-06T18:30:02Z"). Mimikatz's DPAPI wiki page enumerates the twelve sub-modules that emerged from this work: blob, protect, masterkey, credhist, cache, capi, cng, cred, vault, wifi, wwan, chrome. Each one is a parser for a different artefact in the credential vault inventory we open in §5.
The second wave was Will Schroeder's SharpDPAPI, a C# port of the Mimikatz DPAPI logic that survived the mimikatz.exe-by-name AV blocks of the 2017-2018 era. Its triage, masterkeys, credentials, vaults, rdg, keepass, certificates, machinemasterkeys, machinecredentials, machinevaults, machinetriage, backupkey, blob, search, and SCCM commands form the operational vocabulary every red team uses today. Schroeder's companion harmj0y blog documents the four scenarios the toolchain has to handle: "user-with-password," "user-without-password-but-with-NT-hash," "offline disk image," and "domain backup-key path."
The third wave was the conference-talk corridor that closed the public-documentation gap: Bartosz Inglot at OPCDE Dubai 2017 with The Blackbox of DPAPI: the gift that keeps on giving (a 40-page deck out of Mandiant's incident-response practice), and Jean-Christophe Delaunay at UniverShell 2017 with the first open-source DPAPI master-key brute-forcer.
The first public Mimikatz release was actually May 2011 as a closed-source binary; the open-source GitHub repository on April 6, 2014 is the history-of-Windows-security marker that downstream tools cite. Delpy's own framing, recorded by Wired: "Because you don't want to fix it, I'll show it to the world to make people aware of it."The 2010 paper closed the file on classic-DPAPI's secrecy. By 2014 every operator had Mimikatz dpapi::*; by 2018 every operator had SharpDPAPI. The next question was no longer "how does it work" but "what is in the vault, and which sidecars rescue or expose it after a password change."
5. What Lives in the Vault, the [MS-BKRP] Sidecar, and the CREDHIST Cliff
When you save a WiFi network's pre-shared key, that PSK is wrapped in a DPAPI BLOB. When Outlook caches your Exchange account password, that password is wrapped in a DPAPI BLOB. When Chrome stores a cookie, the per-cookie AES-GCM key is wrapped under a DPAPI-protected state-key BLOB. EFS's per-file encryption keys are wrapped by per-user RSA private keys that are themselves wrapped by DPAPI. Twenty-five years of "encrypted" Windows secrets all walk the same four-stage chain.
The credential-vault inventory
Schroeder's operational guide is the most-cited inventory: "At a high level, for the user scenario, a user's password is used to derive a user-specific 'master key'. These keys are located at C:\Users\<USER>\AppData\Roaming\Microsoft\Protect\<SID>\<GUID>"; the operational scope he names is "Chrome Cookies/Login Data, the Windows Credential Manager/Vault (e.g. saved IE/Edge logins and file share/RDP passwords), and Remote Desktop". Walking outward from the master-key directory:
| File-system or store path | What lives there | Who can decrypt |
|---|---|---|
%APPDATA%\Microsoft\Protect\<SID>\<GUID> | The user's master keys (one per 90-day period) | Any process holding the user's password / NT hash |
%APPDATA%\Microsoft\Protect\CREDHIST | SHA-1 chain of previous passwords | Any holder of the current password walks the chain backward |
%APPDATA%\Microsoft\Credentials\ (and %LOCALAPPDATA%\...) | Credential Manager generic / domain credentials | Any process running as the user |
%LOCALAPPDATA%\Microsoft\Vault\ | Windows Vault items (web credentials, app credentials) | Same |
%LOCALAPPDATA%\Google\Chrome\User Data\<profile>\Cookies, Login Data | DPAPI-wrapped state key wraps per-row AES-256-GCM keys | Same (until July 2024 app-bound encryption) |
| EFS per-file FEKs | Wrapped by user RSA private key, itself DPAPI-protected | Same |
| WiFi profile EAP / PSK secrets | DPAPI-protected per-system or per-user | SYSTEM or the profile owner |
| Outlook PST / OST account passwords | DPAPI-protected | Same |
| RDP saved credentials | Credential Manager generic credential, DPAPI-wrapped | Same |
| KeePass Windows User Account key option | Composite key includes a DPAPI-protected blob | Same |
| LSASS (in-memory) | Cached DPAPI_SYSTEM secret used by SYSTEM-context unprotect | Any code running in or able to dump lsass.exe |
For the offline path, the Mimikatz DPAPI wiki documents the full set of unwrap inputs the operator can supply: "masterkey via SID + (password SHA1 OR previous SHA1 from CREDHIST OR NTLM hash OR domain backup key OR DPAPI_SYSTEM secret)." Five paths to the same place.
The two sidecars that rescue and expose
Two sidecars exist because the master-key chain has two recovery problems. The first is the enterprise problem: an admin needs to be able to recover a user's secrets when the user forgets the password. The second is the user problem: a user needs to keep their secrets across self-initiated password changes.
The LSASS-hosted RPC interface (UUID {3dde7c30-165d-11d1-ab8f-00805f14db40} over \PIPE\protected_storage, per the [MS-BKRP] specification PDF §1.9 Standards Assignments) that lets a member computer hand its master key to a domain controller for RSA-public-key wrap. On first creation of any user master key in a domain-joined environment, the master key is encrypted both under the user's password-derived pre-key and to the DC's RSA backup public key, then both wraps are stored in the master-key file. The RSA-backup wrap is the recoverability path for password resets; it is also the universal-decryption primitive any Domain Admin can use against any user in the forest. See the [MS-BKRP] landing page (revision 27.0, April 2024).
The previous-password hash chain stored in %APPDATA%\Microsoft\Protect\CREDHIST. One entry per self-initiated password change; SHA-1 hashes of every previous password; chained backwards so that a master key wrapped under password P_n-1 can be re-derived after a self-initiated change to P_n. Self-initiated change appends; administrative reset and Microsoft Account online reset break the chain irrecoverably for any master key that depended on a pre-reset password.
Diagram source
flowchart LR
Client["Member computer
(user session)"] --> NewMK["Generate new
master key"]
NewMK --> WrapPwd["Wrap under
user password pre-key"]
NewMK --> RPC["RPC: BackuprKey
(over PIPE protected_storage)"]
RPC --> DC["Domain controller
(LSASS)"]
DC --> WrapRSA["Wrap under DC
RSA backup public key"]
WrapRSA --> ReturnBlob["Return wrapped blob"]
WrapPwd --> File["Master-key file
(both wraps stored)"]
ReturnBlob --> File The Microsoft Learn DPAPI backup-keys page is verbatim explicit about the lifecycle ceiling: "There currently is no officially supported way of changing or rotating these DPAPI backup keys on the domain controllers... Should the DPAPI Backup keys for the domain be compromised, the recommendation is to create a new domain and migrate users to that new domain." Burn-the-forest-and-rebuild is the documented remediation.
Classic DPAPI's two sidecars -- [MS-BKRP] for the enterprise, CREDHIST for the user -- save secrets that would otherwise be lost on a password change. The [MS-BKRP] sidecar is also, structurally, the universal-decryption primitive any Domain Admin can use against any user in the forest. Microsoft documents both halves of that trade-off plainly. The 2012 redesign tried to escape it -- and gave us a new universal-decryption primitive in exchange.
6. DPAPI-NG: From "User on Machine" to "Whoever Satisfies the Descriptor"
With Windows 8 / Server 2012 (RTM August 1, 2012), Microsoft did not patch classic DPAPI. They added a parallel API. NCryptProtectSecret takes a protection descriptor like "SID=S-1-5-21-...XYZ AND SID=S-1-5-21-...ABC" or "CERTIFICATE=HashID:abc123..." instead of an implicit (user, machine) binding. The blob it returns is self-describing; any device that can satisfy the descriptor can decrypt without out-of-band knowledge.
The CNG DPAPI overview page is verbatim about the design intent: "This new API, called DPAPI-NG, enables you to securely share secrets (keys, passwords, key material) and messages by protecting them to a set of principals that can be used to remove protection from them on different computers after proper authentication and authorization." The two halves of that sentence -- "set of principals," "different computers" -- are exactly the two things classic DPAPI cannot do.
The descriptor grammar
The protection-descriptors page is the verbatim source for the grammar. "the left side of the equals sign (=) must be SID, SDDL, LOCAL, WEBCREDENTIALS, or CERTIFICATE." Five descriptor keywords; three logical authorisation classes; a small set of well-shaped rule strings.
| Descriptor keyword | Authorisation class | Example rule string |
|---|---|---|
SID | Active Directory group | SID=S-1-5-21-4392301 AND SID=S-1-5-21-3101812 |
SDDL | AD security descriptor | SDDL=O:BAG:BAD:(A;;CCDC;;;BA) |
LOCAL | Per-user / per-machine local | LOCAL=user, LOCAL=machine |
WEBCREDENTIALS | Credential broker | WEBCREDENTIALS=MyPasswordName |
CERTIFICATE | Certificate store | CERTIFICATE=HashID:sha1_of_cert, CERTIFICATE=CertBlob:base64String |
A short ABNF-defined string that names the set of principals permitted to remove protection from a DPAPI-NG-protected blob. The descriptor is self-describing -- it travels with the blob -- and is parsed at unwrap time to select a protection provider and resolve the wrapping key. Five keywords (SID, SDDL, LOCAL, WEBCREDENTIALS, CERTIFICATE) cover three authorisation classes (AD-forest principal, web credential, certificate store).
Provider selection: the descriptor is the contract; the provider is the implementation
The descriptor type drives provider selection. Microsoft's protection-providers reference is verbatim: "The Microsoft Key Protection provider is chosen for rule strings that begin with SID, SDDL, and LOCAL. The Microsoft Client Key Protection provider parses rule strings that begin with WEBCREDENTIALS."
So SID=, SDDL=, and LOCAL= route to the Microsoft Key Protection Provider, which talks to the local Microsoft Key Distribution Service (kdssvc.dll). This is the path we open in §7 because it is what made gMSAs, dMSAs, Hello for Business software-KSP, and Credential Guard's persistence story possible. CERTIFICATE= selects the certificate's Key Storage Provider, which can be TPM-backed if the certificate's private key lives in a TPM-bound KSP (the TPM in Windows article in this series covers the TPM-bound case in detail). WEBCREDENTIALS= selects the Microsoft Client Key Protection Provider, which resolves through the Windows credential broker.
Diagram source
flowchart LR
Caller["NCryptProtectSecret(
plaintext, descriptor)"] --> Parse["Parse descriptor"]
Parse --> Sid{"SID="}
Parse --> Cert{"CERTIFICATE="}
Parse --> Local{"LOCAL="}
Parse --> Web{"WEBCREDENTIALS="}
Sid --> KSP["Microsoft Key Protection Provider
+ kdssvc.dll [MS-GKDI]"]
Cert --> CertKSP["Cert's KSP
(TPM-backed possible)"]
Local --> LocalProv["Microsoft Key Protection Provider
(LOCAL=user / LOCAL=machine)"]
Web --> Broker["Microsoft Client Key Protection Provider
(credential broker)"]
KSP --> Wrap
CertKSP --> Wrap
LocalProv --> Wrap
Broker --> Wrap
Wrap["Derive wrapping key,
encrypt content"] --> Blob["Self-describing blob
(descriptor + provider info
+ key id + ciphertext + HMAC)"] The output blob is self-describing per the protected-data-format reference: descriptor, provider info, key identifier, ciphertext, HMAC. Any device with a CNG implementation that can satisfy the descriptor decrypts. There is no out-of-band key shipping. There is no "encrypt the blob and ship the key separately." The descriptor is the key-distribution policy.
The CNG DPAPI overview page lists only two principal classes -- AD group and web credentials -- whereas the protection-descriptors page enumerates three (adding the certificate-store class). Both are correct: the certificate descriptor maps to a different provider, hence the two-principal framing on the higher-level overview page and the three-keyword framing on the descriptors-grammar page.DPAPI-NG separates who can decrypt (the descriptor) from where the key material lives (the provider). The descriptor is the contract; the provider is the implementation. This is the structural innovation that lets a blob protected on one machine be decrypted on another without any application-layer key-management code.
The LOCAL=user and CERTIFICATE= cases are interesting but mostly variations on themes that classic DPAPI or the Windows certificate store could already do. The case that required Microsoft to ship a new DC-side daemon -- the case that turned DPAPI-NG into the substrate for gMSAs, dMSAs, Hello for Business, and Credential Guard's persistence layer -- is the SID= AD-group descriptor. The next section opens its substrate: the Microsoft Key Distribution Service and the [MS-GKDI] protocol.
7. The Microsoft Key Distribution Service: How [MS-GKDI] Computes the Same Group Key on Every DC Without Talking to Any of Them
Imagine a forest with seven writable domain controllers. A laptop in Singapore protects a DPAPI-NG blob with SID=S-1-5-21-...XYZ (some AD group). Three months later, a phone in Seoul -- a member of the same group, on a different DC -- needs to decrypt it. Neither DC has ever heard of the blob. Both DCs derive exactly the same group key and hand it to the requesting member. No inter-DC synchronisation. No key-distribution code in either application. The mechanism is one forest-wide root key plus a deterministic key-derivation function.
The DC-side daemon that ships in every writable domain controller from Windows Server 2012 onward. Implements the [MS-GKDI] Group Key Distribution Protocol (current revision class 10.0, April 2024) and is the substrate for every DPAPI-NG SID= blob, every gMSA password, every dMSA password, and the TPM-less software-KSP path of Windows Hello for Business. The service has one job: given a (group, time-period) request from a member computer, derive and return the per-(group, period) key from a single forest-wide root key.
Provisioning the root key
Every forest needs exactly one KDS root key before any DPAPI-NG SID= consumer can use it. The PowerShell cmdlet Add-KdsRootKey is the provisioning entry point. The Microsoft Learn page is verbatim about the constraints: "The Add-KdsRootKey cmdlet generates a new root key for the Microsoft Group Key Distribution Service (KdsSvc) within Active Directory... It is required to run this only once per forest." The default EffectiveTime is "10 days after the current date" to allow Active Directory replication to converge across all writable DCs before any consumer tries to derive against the new key.
Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10)) override is for single-DC test forests only. Production forests should let the 10-day default replicate; using the back-dated override means the first consumer to call into the KDS may target a DC that has not yet received the new root key from replication.
The root key lives at CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,<forest-DN> and carries four attributes that downstream offensive-research tools enumerate: msKds-RootKeyData (the actual key bytes), msKds-KDFAlgorithmID (the KDF identifier, currently SP800-108 HMAC-SHA512), msKds-KDFParam (the KDF parameter block), and msKds-PrivateKeyLength. These four attributes, together, are everything a deterministic-KDF derivation needs to recompute every group key the forest will ever produce.
The single forest-wide secret that anchors every per-(group, period) key the Microsoft Key Distribution Service will ever derive, for every DPAPI-NG SID= blob, every gMSA, every dMSA, every Hello-for-Business software-KSP container, and every Credential-Guard isolated-secret persistence wrap. Provisioned with Add-KdsRootKey, exactly once per forest. Stored as four attributes (msKds-RootKeyData, msKds-KDFAlgorithmID, msKds-KDFParam, msKds-PrivateKeyLength) under CN=Master Root Keys,CN=Group Key Distribution Service,CN=Services,CN=Configuration,<forest-DN>. Currently, Microsoft documents no rotation procedure; see §9.3.
The L0 / L1 / L2 derivation chain
The [MS-GKDI] specification (current revision class 10.0, April 23 2024) describes the protocol. Internally, the KDS computes a three-level derivation:
- L0 seed key: derived from the root key and a label that includes the period-in-hours-thousands tier and the group-related input, via a NIST SP 800-108 KDF in counter mode using HMAC-SHA-512 (see NIST SP 800-108r1, August 2022).
- L1 seed key: derived from L0 with a second-tier label.
- L2 seed key: derived from L1 with the third-tier label. This is the actual symmetric key the DPAPI-NG blob's content key wraps under (or the seed for a per-period group ECDH key pair, in the public-key DPAPI-NG mode).
The first level of the [MS-GKDI] three-tier derivation chain. Computed deterministically from the KDS root key and a label that combines the time-period tier (in units of period-in-hours / 1000) and a group-related input. The whole point of the chain is that any DC that holds the same root key, given the same label, derives the same L0 seed key without coordination with any other DC -- which is also the whole reason a single root-key compromise compromises every key the forest will ever derive.
Diagram source
flowchart TD
Root["KDS root key
(forest-wide secret)"] --> L0["L0 seed key
(period_hours / 1000 + group input)"]
L0 --> L1["L1 seed key
(second-tier label)"]
L1 --> L2["L2 seed key
(third-tier label)"]
L2 --> Symmetric["Per-(group, period)
symmetric key OR
ECDH key-pair seed"]
KDF["SP800-108 counter-mode
KDF, HMAC-SHA-512"] -.derives.-> L0
KDF -.derives.-> L1
KDF -.derives.-> L2 The GetKey round trip
A member computer's local KDS-NG provider calls the GetKey RPC (the primary opnum of [MS-GKDI]) against any reachable writable DC. The DC computes the L0/L1/L2 chain on demand and returns the per-(group, period) key material. No inter-DC synchronisation is needed because the KDF is deterministic.
Diagram source
sequenceDiagram
participant Member as "Member computer (Microsoft Key Protection Provider)"
participant DC as "Nearest writable DC (kdssvc.dll)"
participant Root as Root key (AD-replicated)
Member->>DC: GetKey(group_sid, period_id)
DC->>Root: Read msKds-RootKeyData + KDF params
Root-->>DC: Root-key material
DC->>DC: Compute L0(period, group) -> L1 -> L2
DC-->>Member: Per-(group, period) key material
Member->>Member: Wrap (or unwrap) DPAPI-NG blob Every DC in the forest runs the same kdssvc.dll over the same root key with the same KDF; every authorised member of the named group can ask any DC for the per-(group, period) key and receive the same answer. The architecture is elegant. It is also, by structural necessity, the architecture that makes a one-shot read of the root key into a one-shot compromise of every key the forest will ever derive. Hold that thought; it is what §10 is built on. First we look at what actually rides on this substrate today.
8. The Four Things That Ride DPAPI-NG in 2026
One protocol, four production consumers. The same KDS root key that protects a SID= DPAPI-NG blob is also the root from which every gMSA password, every dMSA password, every TPM-less Windows Hello private key, and every Credential Guard isolated NT-hash is derived.
A Windows-Server-2012-introduced Active Directory account class whose password is computed automatically by the Microsoft Key Distribution Service on the DC, rotated every 30 days, 256 bytes long, and shared across multiple service hosts via the msDS-GroupMSAMembership SDDL gate. From the Microsoft Learn overview verbatim: "For a gMSA, the domain controller computes the password on the key that the Key Distribution Services provides, along with other attributes of the gMSA." Install-ADServiceAccount on each member computer caches the derivation locally so the service can boot under the account.
Diagram source
flowchart TD
Root["KDS root key"] --> Chain["[MS-GKDI] L0/L1/L2 chain"]
Chain --> GMSA["gMSA password
(30-day rotation,
256 bytes)"]
Chain --> DMSA["dMSA password
(machine-bound,
Server 2025)"]
Chain --> WHfB["WHfB software-KSP
(TPM-less devices,
SID + device descriptor)"]
Chain --> CG["Credential Guard
LsaIso-isolated secret
(trustlet identity descriptor)"] 8.1 Group Managed Service Accounts
gMSAs shipped in Windows Server 2012 (October 2012). The model: one AD account, multiple service hosts, no admin-managed password rotation, no per-service password file. The Microsoft Learn overview is verbatim about the chain: "The Microsoft Key Distribution Service (kdssvc.dll) lets you securely obtain the latest key or a specific key with a key identifier for an Active Directory account... For a gMSA, the domain controller computes the password on the key that the Key Distribution Services provides, along with other attributes of the gMSA."
The constructed attribute that surfaces the password to authorised member computers is msDS-ManagedPassword; it is computed on demand by the DC from the KDS chain when an authorised member queries it. The authorisation gate is the msDS-GroupMSAMembership security descriptor on the gMSA object: only principals whose SIDs satisfy that SDDL get the password back. Rotation is every 30 days. The password length is 256 bytes -- "a randomly generated password of 256 bytes, making it infeasible to crack" per the Semperis Golden gMSA write-up, which is true if and only if the KDS root key is intact. We come back to that if and only if in §10.
8.2 Delegated Managed Service Accounts
dMSAs shipped in Windows Server 2025 (GA November 1, 2024). The same KDS chain; a different authorisation gate. Rather than binding to AD group membership, dMSA authentication binds to machine identity: the Microsoft Learn overview describes dMSAs as "a machine account with managed and fully randomized keys, while disabling original service account passwords. Authentication for dMSA is linked to the device identity, which means that only specified machine identities mapped in Active Directory (AD) can access the account." The msDS-ManagedPasswordId attribute carries a machine-binding component. Microsoft's marketing framing positions dMSA as the "Kerberoast-immune" replacement for static service accounts.
The Server 2025 dMSA design has its own §10 footnote: the July 2025 Semperis Golden dMSA disclosure found that the msDS-ManagedPasswordId time-component has predictable structure with only ~1 024 plausible values, making offline brute-forcing tractable once the KDS root key is in hand.
8.3 Windows Hello for Business software-KSP credentials
The Hello for Business how it works page describes the credential as a per-user per-device asymmetric key pair. On TPM-equipped devices the private key lives in the TPM and DPAPI-NG is not in the wrap path. On TPM-less devices (the structural worst case) the WHfB private key sits in a CNG software Key Storage Provider container persisted as a DPAPI-NG-protected file whose protection descriptor binds it to the user SID + device. The TPM in Windows article in this series covers the TPM-bound case; here we record only that the software-KSP fallback rides on DPAPI-NG and inherits the KDS root-key dependency for the SID-bound branch.
A non-hardware-bound CNG key-storage provider that persists key material in DPAPI-NG-protected files in the user profile. Used by Windows Hello for Business on TPM-less devices and as the fallback when no hardware-bound KSP (TPM, smart card, Secure Enclave equivalent) is available. The structural worst case for the WHfB credential, because the private key lives in a file the OS can read in any user-context process, wrapped under a DPAPI-NG blob whose protection descriptor reduces back to the KDS root key for SID-anchored bindings.
8.4 Credential Guard isolated-secret persistence
The companion Credential Guard article in this series covers LsaIso.exe and the LSAISO trustlet's isolation boundary in depth; what matters here is that the trustlet's persistence layer is DPAPI-NG. LsaIso.exe, running in VTL1 IUM, stores the isolated NT one-way function outputs and Kerberos session keys in DPAPI-NG blobs whose protection descriptor binds them to the trustlet's own identity. The VSM master key (TPM-bound on TPM-2.0 systems per Microsoft Learn's Credential Guard overview, which describes how Credential Guard "uses Virtualization-based security (VBS) to isolate secrets") is what the trustlet seals its DPAPI-NG protection state under across reboots. The end result is that even though the Credential Guard model puts the credential outside lsass.exe, the persistence of that isolated secret rides on the same KDS-rooted DPAPI-NG chain every other consumer in this section uses.
| Consumer | Rotation | Authorisation gate | On-disk artefact | Recovery story |
|---|---|---|---|---|
| gMSA | 30 days | msDS-GroupMSAMembership SDDL | Cached on member via Install-ADServiceAccount | Recompute via KDS at any time |
| dMSA | Managed by KDS | Machine identity in AD | Cached on member; msDS-ManagedPasswordId carries machine binding | Recompute via KDS |
| WHfB software-KSP | Per-credential lifetime | User SID + device | DPAPI-NG-wrapped key container in user profile | New enrollment if lost |
| LsaIso DPAPI-NG persistence | Boot-cycle bound | Trustlet identity | Trustlet-managed VTL1 store, sealed under VSM master key | Re-derive on next logon |
Every shipping Windows credential primitive that just works across multiple devices, multiple service hosts, or multiple cold-boot cycles -- without per-application key-management code -- is sitting on the same KDS root key. The architectural bet is that the root key never leaks. The next two sections are about what happens when it does.
9. The Five Structural Ceilings DPAPI Cannot Close
A reader who has followed the chain through §§3-8 has earned the right to a sharp question: where does this whole architecture fail? Not where does it have bugs (it has very few cryptographic ones); where does the design draw a line that no implementation patch can move? There are exactly five such lines.
9.1 The user-context ceiling
Any process running as the user can call CryptUnprotectData or NCryptUnprotectSecret against any blob the user could decrypt. The chain binds the secret to the user identity, not the consuming process identity. This is the structural reason every modern browser-cookie-stealer family (Lokibot, Vidar, RedLine, Lumma, StealC) works against DPAPI-protected Chrome state keys; it is also the reason Google introduced Chrome app-bound encryption in July 2024 outside DPAPI. Will Harris of the Chrome security team is verbatim about why the patch had to live outside DPAPI rather than inside it: "On Windows, Chrome uses the Data Protection API (...). However, the DPAPI does not protect against malicious applications able to execute code as the logged in user -- which info-stealers take advantage of."
"On Windows, Chrome uses the Data Protection API (DPAPI). However, the DPAPI does not protect against malicious applications able to execute code as the logged in user -- which info-stealers take advantage of." -- Will Harris, Chrome security team, July 2024
9.2 The single-password-derivation ceiling
The classic-DPAPI master-key wrap is PBKDF2-HMAC-SHA512(SHA1(NT-hash), SID-as-UTF16LE, ~8000 iterations) in the modern (Windows 10/11) era per the Passcape table. 8 000 iterations of HMAC-SHA-512 is not strong against a modern wordlist attack on a leaked NT hash. The structural limit is the user's password, not the cryptographic primitive. The KDF parameter is tunable; the single secret in the chain is not -- a strong password makes the chain strong, a weak password makes the chain weak, and there is no architectural way to recover from a weak password short of re-deriving every user's master key under a stronger one.
9.3 The KDS-root-key irrotability ceiling
Once a KDS root key is in production use, rotating it would invalidate every gMSA msDS-ManagedPassword, every dMSA password, every SID= blob, every Hello-for-Business software-KSP container, and every Credential-Guard isolated-secret blob ever produced under it. Microsoft's documented mitigation is preventative (a system access control list on msKds-RootKeyData), not recoverable. This is the same structural ceiling that the Microsoft Learn DPAPI-backup-keys page admits for the older [MS-BKRP] keys, in identical language: "There currently is no officially supported way of changing or rotating these DPAPI backup keys on the domain controllers." Burn-the-forest-and-rebuild for [MS-BKRP]; SACL-the-attribute-and-hope for KDS root keys.
9.4 The hibernation / S4 ceiling
CryptProtectMemory / CryptUnprotectMemory provide an in-memory scrub primitive that scopes a secret across same-process / cross-process / cross-session lifetimes. They cannot scrub RAM written to hiberfil.sys on suspend-to-disk. On resume the master key is in plaintext in the page cache. The structural defence is BitLocker on the system volume (the BitLocker on Windows article in this series covers full-volume encryption end-to-end); within DPAPI itself the ceiling cannot move because the OS has to be able to resume.
9.5 The domain-backup-key concentration ceiling
The [MS-BKRP] backup key is the recoverability story for the password-reset case -- and it is also the structural reason any DA who can dump LSASS on a writable DC has every user's master key in the forest. mimikatz "lsadump::backupkeys /system:dc.contoso.local /export" is the canonical primitive, documented in the Mimikatz DPAPI wiki. The architectural answer (HSM-backing the DC's RSA private key) is not in Microsoft's mainline guidance; the recommendation when these keys are compromised is the burn-the-forest-and-rebuild line we just quoted.
These five ceilings are not bugs. They are the design's price for the design's other guarantees. The user-context ceiling buys ubiquitous adoption. The single-password ceiling buys a usable recovery path. The KDS-root-key ceiling buys cross-DC determinism. The hibernation ceiling buys process performance and resumability. The domain-backup-key ceiling buys enterprise recovery. Every one of the next five years' DPAPI incidents will hit one of these five ceilings.
The 2022-2025 disclosures are not five surprises. They are five different attackers naming five different ceilings out loud. The next section walks the named incidents one ceiling at a time.
10. The 2022-2025 Residual Class: Golden gMSA, Golden dMSA, and Chromium App-Bound Encryption
In March 2022, Yuval Gordon at Semperis published Introducing the Golden GMSA Attack and the GoldenGMSA C# tool. In July 2024, Google announced Chrome's app-bound encryption. In July 2025, Adi Malyanker (also Semperis) published Golden dMSA: What Is dMSA Authentication Bypass? and the GoldenDMSA tool, mirrored on PR Newswire with a July 16, 2025 dateline. Three disclosures in three years, three different ceilings -- but the same underlying pattern: each one is an admission that the structural ceiling cannot be patched inside DPAPI.
Diagram source
flowchart LR
C1["§9.1 user-context"] --> Chromium["Chromium app-bound encryption
(July 2024, Will Harris)"]
C2["§9.3 KDS-root-key irrotability"] --> GGMSA["Golden gMSA
(March 2022, Y. Gordon)"]
C2 --> GDMSA["Golden dMSA
(July 2025, A. Malyanker)"]
C3["§9.5 domain-backup-key"] --> MimikatzBKK["mimikatz lsadump::backupkeys"]
C4["§9.4 hibernation / S4"] --> BL["BitLocker article cross-link"] 10.1 Golden gMSA (Yuval Gordon, Semperis, March 2022)
Targets the §9.3 KDS-root-key irrotability ceiling. Gordon is verbatim about the two-step nature of the attack: "An attacker with high privileges can obtain all the ingredients for generating the password of any gMSA in the domain at any time with two steps: Retrieve several attributes from the KDS root key in the domain... Use the GoldenGMSA tool to generate the password of any gMSA associated with the key, without a privileged account." Step 1 is a one-shot read of the KDS root key attributes. Step 2 is offline derivation. There is no further DC interaction, ever, because the [MS-GKDI] chain is deterministic by NIST SP 800-108r1 design.
The defensive answer is in the GoldenGMSA repository: "configure a SACL on the KDS root key objects for everyone reading the msKds-RootKeyData attribute. Once the system access control list (SACL) is configured, any attempt to dump the key data of a KDS root key will generate security event 4662 on the DC where the object type is msKds-ProvRootKey and the account name is not a DC." Plus the cross-trust SACL on msDS-ManagedPasswordId with property GUID {0e78295a-c6d3-0a40-b491-d62251ffa0a6}. This is a detective control, not a recovery control. Once the root key has been read, every gMSA password the forest has ever issued or will ever issue under that root key is offline-derivable.
The Golden gMSA SACL detects same-trust reads only. Cross-trust reads of"An attacker with high privileges can obtain all the ingredients for generating the password of any gMSA in the domain at any time with two steps." -- Yuval Gordon, Semperis, March 2022
msDS-ManagedPasswordId from a forest the auditing forest does not control are the documented detection blind-spot. If your gMSA-using forest has a one-way trust from a forest you do not own, you cannot see its reads.
10.2 Golden dMSA (Adi Malyanker, Semperis, July 2025)
Targets the §9.3 ceiling, plus a fresh dMSA-specific surface. Malyanker is verbatim about the structural flaw: "a critical design flaw: a structure that's used for the password-generation computation contains predictable time-based components with only 1,024 possible combinations, making brute-force password generation computationally trivial." The four-phase pipeline is enumerated in the GoldenDMSA repository: "Phase 1: Key Material Extraction (pre requirement of the attack) -- Dump the KDS Root Key from the DC. Phase 2: Enumerate dMSA accounts ... Phase 3: ManagedPasswordID guessing ... Phase 4: Password Generation." The tool exposes commands wordlist, info, kds, bruteforce, compute, and convert -- the operational vocabulary the four-phase pipeline needs.
Semperis' own rating is MODERATE with the explicit caveat "to exploit it, attackers must possess a KDS root key available only to only the most privileged accounts: root Domain Admins, Enterprise Admins, and SYSTEM." That is exactly the §9.3 ceiling re-stated. The novelty in dMSA is the 1 024-combination time-component flaw -- a design weakness on top of the structural ceiling, not a substitute for it.
10.3 Chromium app-bound encryption (Will Harris, Google Chrome, July 30, 2024)
Targets the §9.1 user-context ceiling. The Google Security Blog announcement describes a COM-elevation service that wraps the Chrome state key with both DPAPI and a per-binary identity check the COM service enforces. The verbatim quote, mirrored via The Hacker News: "Because the app-bound service is running with system privileges, attackers need to do more than just coax a user into running a malicious app. Now, the malware has to gain system privileges, or inject code into Chrome, something that legitimate software shouldn't be doing."
The architecturally important word is "app-bound." Chromium's response to the user-context ceiling lives outside DPAPI. DPAPI itself is unchanged. The 2024 patch is application-layer code-identity pinning -- exactly what Generation-1 Protected Storage's abandoned Authenticode-access-rule clause was supposed to be in 2000, exactly what Apple Keychain has shipped on macOS for over two decades, exactly what the §11 wishlist asks for.
10.4 The recurring pattern
Each disclosure does not break a cryptographic primitive. Each is a re-statement of "the design's ceiling is the design's ceiling." The defensive answers are detection (SACL audit; cross-trust read alerting; binary-identity check) and workaround at a higher layer (the COM-elevation service Chrome wraps DPAPI in), never cryptographic strengthening of DPAPI itself. The 2026 reader's job is to recognise which ceiling each new incident hits.
| Year | Disclosure | Ceiling hit | Tool reference | Defensive answer |
|---|---|---|---|---|
| 2022 | Golden gMSA (Y. Gordon, Semperis) | §9.3 KDS irrotability | GoldenGMSA | SACL on msKds-RootKeyData; Event 4662 |
| 2024 | Chromium app-bound encryption (W. Harris, Google) | §9.1 user-context | Chrome 127 release notes | COM-elevation per-binary identity check, outside DPAPI |
| 2025 | Golden dMSA (A. Malyanker, Semperis) | §9.3 + dMSA msDS-ManagedPasswordId predictability | GoldenDMSA | SACL plus monitoring msDS-ManagedPasswordId brute-force |
By 2026 the pattern is clear: DPAPI's structural ceilings produce a steady drip of disclosures, each defensively answerable but none cryptographically fixable inside DPAPI itself. The open question is what the successor would even look like -- and that is the next section.
11. Open Problems
A few sharp open problems remain at the design layer -- problems that a future Generation 4 of the credential-vault tradition would have to solve. None of them is in Microsoft's published roadmap as of 2026.
KDS root-key rotation
A hybrid wrap-then-re-wrap-on-first-decrypt-under-new-root scheme could in principle restore rotation without invalidating existing blobs: every consumer would carry a tag indicating which root-key generation last unwrapped it; on first unwrap under a generation-N+1 root the system would re-wrap the consumer-side cache. No standard or product implements this today; no public proposal revises [MS-GKDI] to add it.
Code-identity pinning
Apple Keychain enforces "keychain items can be shared only between apps from the same developer ... enforced through code signing, provisioning profiles, and the Apple Developer Program." DPAPI / DPAPI-NG have no equivalent. A CALLER_ATTRIBUTION=Publisher:<wdac-rule-hash> descriptor would, in principle, give Windows the same property -- the SHA-256 hash of the WDAC rule that authorises the calling binary, baked into the descriptor, checked at unwrap time. No one is shipping it. The 2024 Chromium app-bound encryption response is the application-layer workaround that proves the design gap is real.
Post-quantum migration
DPAPI-NG today uses RSA-OAEP (CERTIFICATE= with RSA private keys) or ECDH P-256 / P-384 (SID= group descriptors) per the protected-data-format reference. Both wrap algorithms are vulnerable to Shor's algorithm on a sufficiently large quantum computer, and the persistent on-disk blob format is the harvest-now-decrypt-later target -- a framing surfaced across NIST's broader post-quantum migration corpus, including the NIST PQC project page and the linked NIST IR 8547 migration-timeline document. The migration story for the symmetric chain is comparatively easy (the SP800-108 KDF is parameterised; HMAC-SHA-512 has no quantum-cliff weakness for the relevant key sizes). The migration story for the public-key wrap is the hard part. NIST published FIPS 203 (ML-KEM) and FIPS 204 (ML-DSA) in August 2024; OpenSSH 9.9 (September 2024) and TLS deployments shipped hybrid post-quantum key exchange. Windows added experimental post-quantum TLS support in 2024 but has not yet announced ML-KEM CNG-DPAPI providers; see the companion Post-Quantum Cryptography on Windows article in this series for the broader Windows migration story. The shape that fits DPAPI's ceiling-laden design is hybrid wrap-then-re-wrap-on-first-decrypt: a hybrid wrap (RSA-OAEP || ML-KEM or ECDH || ML-KEM) -- protect under (RSA+ML-KEM) or (ECDH+ML-KEM) today; let consumers re-wrap to the new combiner on first unwrap; phase out the classical half on a long horizon -- is the only forward-compatible answer; Microsoft's published DPAPI-NG protection-providers have not yet announced one.
Credential roaming for Entra-joined-only / unmanaged-device estates
Classic DPAPI's cross-device story has always been "use roaming profiles" (deprecated) or "use the [MS-BKRP] backup key" (admin-recovery, not user-driven). DPAPI-NG's SID= solves it cleanly for AD-joined estates but the modern Entra-only estate has no native equivalent -- the LOCAL=user descriptor is per-machine. An ENTRAGROUP=<object-id> descriptor that resolved through Entra ID Token Service the way SID= resolves through KDS would close the gap. No public roadmap announces this.
Hibernation / S4
BitLocker on the system volume defends the disk; suspending fully to ROM (or refusing S4 entirely) defends the in-RAM master key. Hardware-bound key derivation (TPM-released-only-while-PCRs-stable) would close more of the gap; the TPM in Windows article in this series covers TPM-bound primitives that approximate this property.
Every one of these is a design gap -- a property the architecture would need a new primitive to satisfy. None is on Microsoft's announced roadmap. The architecture we have is the architecture we will have for at least the next five years; the practitioner's job is to know which ceilings their estate is exposed to and how to detect each of them.
12. Practical Guide and Closing
The four-audience guide for the 2026 practitioner.
12.1 For a developer
Use CryptProtectData / CryptUnprotectData for per-user-on-this-device secrets. Pass pOptionalEntropy to bind the blob to a per-application secret -- but understand it is security-by-obscurity, not a code-identity check; any reader of the SharpDPAPI source who knows your constant entropy can reproduce the unprotect call as the user.
Use NCryptProtectSecret with the appropriate descriptor for cross-device or multi-principal cases. LOCAL=user mirrors classic DPAPI on a single machine. SID=<group-sid> reaches AD groups via KDS. CERTIFICATE=HashID:<sha1> reaches a named certificate (TPM-backed for the high-security case). Use the WebAuthn / FIDO2 path for authentication secrets; do not store passwords in DPAPI when WHfB / passkey paths are available.
// Returns the appropriate DPAPI-NG protection-descriptor string for a use case.
// Reference: learn.microsoft.com windows win32 seccng protection-descriptors
function descriptorFor(useCase, ctx) {
switch (useCase) {
case "single-device-single-user":
return "LOCAL=user";
case "ad-group":
// Multiple machines, AD-authorized group members can decrypt.
return "SID=" + ctx.groupSid;
case "tpm-backed-cert":
// Decrypter must hold the named certificate's private key (TPM-bound KSP).
return "CERTIFICATE=HashID:" + ctx.certThumbprintSha1;
case "web-credential":
// Resolves through the Windows credential broker.
return "WEBCREDENTIALS=" + ctx.credName;
default:
throw new Error("Unknown use case: " + useCase);
}
}
console.log(descriptorFor("single-device-single-user"));
console.log(descriptorFor("ad-group", { groupSid: "S-1-5-21-...-5101" })); Press Run to execute.
12.2 For a defender or DFIR analyst
Triage the credential-vault inventory in §5 first. The high-value paths are %APPDATA%\Microsoft\Protect\<SID>\, %APPDATA%\Microsoft\Protect\CREDHIST, %APPDATA%\Microsoft\Credentials\, %LOCALAPPDATA%\Microsoft\Vault\, the Chromium / Edge profile databases, and the AD CN=Master Root Keys,... container.
The SACL guidance from the GoldenGMSA repository is the only detective control today: "configure a SACL on the KDS root key objects for everyone reading the msKds-RootKeyData attribute. Once the system access control list (SACL) is configured, any attempt to dump the key data of a KDS root key will generate security event 4662 on the DC where the object type is msKds-ProvRootKey and the account name is not a DC." Plus the cross-trust SACL on msDS-ManagedPasswordId with property GUID {0e78295a-c6d3-0a40-b491-d62251ffa0a6}.
Tooling: Mimikatz dpapi::* modules, SharpDPAPI, DPAPIck (Bursztein-Picod 2010), GoldenGMSA, GoldenDMSA, and the Volatility lsadump / cachedump / hashdump plugins for live-memory extraction of DPAPI_SYSTEM and the other LSA secrets that seed SYSTEM-context master-key derivation.
# Walk a synthetic profile-directory layout and emit a DPAPI-relevant triage report.
import os
from collections import defaultdict
# Synthetic profile layout for demonstration only.
synthetic = {
"Users/alice/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234-1001/Preferred": "8KB",
"Users/alice/AppData/Roaming/Microsoft/Protect/S-1-5-21-1234-1001/0d4a...": "740B",
"Users/alice/AppData/Roaming/Microsoft/Protect/CREDHIST": "176B",
"Users/alice/AppData/Roaming/Microsoft/Credentials/abcdef...": "300B",
"Users/alice/AppData/Local/Microsoft/Vault/4BF4C442-9B8A-41A0-B380-DD4A704DDB28/Policy.vpol": "180B",
"Users/alice/AppData/Local/Google/Chrome/User Data/Default/Cookies": "4MB",
"Users/alice/AppData/Local/Google/Chrome/User Data/Local State": "12KB",
}
categories = {
"Master keys": "/Microsoft/Protect/S-1-",
"CREDHIST chain": "/Protect/CREDHIST",
"Credential Manager": "/Microsoft/Credentials/",
"Windows Vault": "/Microsoft/Vault/",
"Chrome state key": "/Google/Chrome/User Data/Local State",
"Chrome cookies": "/Google/Chrome/User Data/Default/Cookies",
}
report = defaultdict(list)
for path, size in synthetic.items():
for label, marker in categories.items():
if marker in path:
report[label].append((path, size))
for label, items in report.items():
print("==", label)
for path, size in items:
print(" ", path, "(" + size + ")") Press Run to execute.
12.3 For a red-team operator
The chain of primitives most-commonly used (verbatim from the harmj0y operational guide and the Mimikatz wiki):
Operator command sequence (defensive context only)
The operational vocabulary, in order of dependency:
mimikatz "sekurlsa::dpapi"-- enumerate cached master keys from a livelsass.exe.mimikatz "dpapi::masterkey /in:<MK> /sid:<SID> /password:<known>"-- unwrap a master-key file.mimikatz "dpapi::cred /in:<credfile>"-- decrypt a Credential Manager entry.mimikatz "lsadump::backupkeys /system:dc.contoso.local /export"-- export the[MS-BKRP]RSA private key from a writable DC.SharpDPAPI triage /pvk:key.pvk-- offline triage with the domain backup key.SharpChrome cookies /pvk:key.pvk-- decrypt Chrome / Edge cookies offline.GoldenGMSA gmsainfothenGoldenGMSA compute -k <root-key-guid> -s <gmsa-sid> -m <managed-password-id>-- offline gMSA password derivation per the March 2022 disclosure.GoldenDMSA wordlist/bruteforce/compute-- the four-phase Server 2025 dMSA pipeline per the July 2025 disclosure.
The post-Credential-Guard scope reality: LSASS-isolated NT-hashes are gone (the Credential Guard article in this series covers what LsaIso.exe actually computes); on-disk DPAPI master keys, Chrome cookies, and Vault credentials are still there. The credential-vault inventory in §5 is the operator's map; the §10 disclosure list is the operator's playbook.
12.4 For a platform or identity engineer
Provision the KDS root key carefully. Use the Add-KdsRootKey default 10-day EffectiveTime for production forests so AD replication converges before any consumer derives against the new key; the -EffectiveTime ((Get-Date).AddHours(-10)) override is for single-DC test forests only, never production.
For Server 2025 dMSA, monitor the msDS-ManagedPasswordId brute-force surface until Microsoft addresses the time-component predictability the Golden dMSA disclosure named.
For Hello for Business, prefer the TPM-bound KSP (MS_PLATFORM_CRYPTO_PROVIDER); the software-KSP DPAPI-NG fallback is the structural worst case (per §8.3) and inherits the KDS root-key dependency on every TPM-less device.
Cross-platform context: Apple Keychain reaches a stronger upper bound (Secure-Enclave-bound + code-identity-pinned via the Apple Developer Program); GNOME libsecret covers the analogous Linux primitive over the Secret Service D-Bus interface. Neither is a drop-in replacement; both have shapes worth borrowing if Microsoft ever publishes the Generation-4 design.
12.5 The closing reflection
The credential vault under everything has a single-sentence summary in 2026: classic DPAPI is as strong as the user's password; DPAPI-NG is as strong as the KDS root key's life-cycle SOP is; both architectures admit ceilings the cryptography cannot move. The literacy a practitioner needs is the ability to recognise which ceiling any new incident hits. Twelve sections later, you have it.
Frequently Asked Questions
Frequently asked questions
Does Credential Guard mean Mimikatz cannot dump my browser cookies?
No. Credential Guard protects LSA-isolated secrets only. Chrome cookies live in the user's profile under DPAPI / DPAPI-NG and are decryptable by any process running as the user, exactly as before. The Chromium 2024 app-bound encryption is a per-process workaround for the §9.1 user-context ceiling, not a fix inside DPAPI itself.
If I reset my Microsoft Account password from the web, why do my saved Windows credentials disappear?
A web reset of a consumer Microsoft Account password does not append to CREDHIST and does not benefit from [MS-BKRP] backup -- there is no domain in the consumer scenario. Master keys encrypted under the previous password become irrecoverable for consumer Microsoft Accounts, full stop. Domain-joined enterprise users escape this because the domain backup key still works.
Are Group Managed Service Account passwords unguessable?
True if the KDS root key is intact -- the Semperis write-up records the "randomly generated password of 256 bytes, making it infeasible to crack" claim. False under the Golden gMSA / Golden dMSA assumption that any DA / SYSTEM on a DC can read msKds-RootKeyData. A 256-byte random password is irrelevant if the attacker can derive it offline.
Is DPAPI-NG just DPAPI on a newer crypto suite?
No. DPAPI-NG is a redesign whose protection model is descriptor-based (multi-principal, multi-device) rather than user-and-machine-bound. The two APIs coexist; classic DPAPI is still the default for CryptProtectData callers, and DPAPI-NG is the path for NCryptProtectSecret callers.
Are Windows Hello for Business private keys always TPM-bound?
No. On TPM-less devices the WHfB private key sits in a CNG software-KSP container persisted as a DPAPI-NG blob whose protection descriptor binds it to user SID + device, per the Hello for Business architecture. The TPM-bound case is the preferred deployment; the software-KSP fallback is the structural worst case and inherits the §9.3 KDS root-key dependency.
Does CryptProtectMemory protect against hibernation?
No. CryptProtectMemory scrubs in-memory secrets between same-process / cross-process / cross-session lifetimes but cannot prevent the OS from writing the page-protected RAM into hiberfil.sys on suspend-to-disk. BitLocker on the system volume is the structural defence (the BitLocker on Windows article in this series covers full-volume encryption end-to-end).
Did the Bursztein/Picod 2010 paper break DPAPI?
No. It broke the secrecy of DPAPI's design. The 2010 disclosure made the master-key chain public and tractable for offline forensics; it did not weaken the cryptography. The "break" was always structural -- DPAPI is as strong as the user's password is. The two-author byline is Bursztein and Picod (Black Hat DC 2010, USENIX WOOT 10), not "Bursztein, Picod and Aussel."
Study guide
Key terms
- DPAPI
- The Data Protection API; the per-user / per-machine secret-storage primitive in every Windows release from Windows 2000 onward.
- Master key
- A 64-byte random secret per user, stored under %APPDATA%/Microsoft/Protect/<SID>/<GUID>, encrypted under a pre-key derived from the user's password and SID.
- [MS-BKRP] BackupKey Remote Protocol
- The LSASS-hosted RPC interface that lets a member computer dual-wrap its master key under both the user password pre-key and a DC RSA backup public key; the canonical universal-decryption primitive available to Domain Admins.
- CREDHIST
- The previous-password hash chain stored in %APPDATA%/Microsoft/Protect/CREDHIST; one entry per self-initiated password change; broken by administrative reset and consumer-Microsoft-Account web reset.
- Protection descriptor (DPAPI-NG)
- The DPAPI-NG self-describing string (SID, SDDL, LOCAL, WEBCREDENTIALS, CERTIFICATE) that names the set of principals permitted to remove protection from a blob.
- Microsoft Key Distribution Service (kdssvc.dll)
- The DC-side daemon that implements the [MS-GKDI] protocol and computes per-(group, period) keys deterministically from a single forest-wide root key.
- KDS root key
- The single forest-wide secret that anchors every per-(group, period) key the KDS will ever derive; provisioned exactly once per forest with Add-KdsRootKey; documented as having no rotation procedure.
- Group Managed Service Account (gMSA)
- A Server-2012-introduced AD account whose 256-byte password is derived from the KDS chain and rotated every 30 days, gated by the msDS-GroupMSAMembership SDDL.
- Software KSP
- The non-hardware-bound CNG Key Storage Provider that persists key material as DPAPI-NG-protected files; used as the WHfB fallback on TPM-less devices.
- Golden gMSA / Golden dMSA
- The 2022 / 2025 Semperis offline-derivation attacks that compute any gMSA / dMSA password the forest will ever issue, given a one-shot read of the four KDS root-key attributes.
References
- CNG DPAPI. https://learn.microsoft.com/en-us/windows/win32/seccng/cng-dpapi ↩
- Protection descriptors (CNG DPAPI). https://learn.microsoft.com/en-us/windows/win32/seccng/protection-descriptors ↩
- Protected data format (CNG DPAPI). https://learn.microsoft.com/en-us/windows/win32/seccng/protected-data-format ↩
- Protection providers (CNG DPAPI). https://learn.microsoft.com/en-us/windows/win32/seccng/protection-providers ↩
- CryptProtectData function (dpapi.h). https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectdata ↩
- NCryptProtectSecret function (ncryptprotect.h). https://learn.microsoft.com/en-us/windows/win32/api/ncryptprotect/nf-ncryptprotect-ncryptprotectsecret ↩
- CryptProtectMemory function (dpapi.h). https://learn.microsoft.com/en-us/windows/win32/api/dpapi/nf-dpapi-cryptprotectmemory ↩
- [MS-GKDI]: Group Key Distribution Protocol. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-gkdi/943dd4f6-6b80-4a66-8594-80df6d2aad0a ↩
- [MS-BKRP]: BackupKey Remote Protocol. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-bkrp/ ↩
- [MS-BKRP]: BackupKey Remote Protocol Specification (PDF, v27.0, April 2024). https://winprotocoldocs-bhdugrdyduf5h2e4.b02.azurefd.net/MS-BKRP/[MS-BKRP].pdf - Source for the BackupKey RPC interface UUID and named-pipe binding ↩
- Group Managed Service Accounts overview. https://learn.microsoft.com/en-us/windows-server/security/group-managed-service-accounts/group-managed-service-accounts-overview ↩
- Add-KdsRootKey (PowerShell). https://learn.microsoft.com/en-us/powershell/module/kds/add-kdsrootkey ↩
- Delegated Managed Service Accounts overview. https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/manage/delegated-managed-service-accounts/delegated-managed-service-accounts-overview ↩
- [MS-ADTS]: MSDS-MANAGEDPASSWORD_BLOB. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a9019740-3d73-46ef-a9ae-3ea8eb86ac2e ↩
- Pstore (Windows developer notes). https://learn.microsoft.com/en-us/windows/win32/devnotes/pstore ↩
- CNG DPAPI backup keys on AD domain controllers. https://learn.microsoft.com/en-us/windows/win32/seccng/cng-dpapi-backup-keys-on-ad-domain-controllers ↩
- Windows Hello for Business: how it works. https://learn.microsoft.com/en-us/windows/security/identity-protection/hello-for-business/how-it-works ↩
- (2001). Windows Data Protection (MSDN ms995355). https://learn.microsoft.com/en-us/previous-versions/ms995355(v=msdn.10) - Canonical first public Microsoft-blessed DPAPI design document ↩
- DPAPI Black Box (Passcape). https://www.passcape.com/index.php?section=docsys&cmd=details&id=28 ↩
- Windows Password Recovery: DPAPI master-key analysis (Passcape). https://www.passcape.com/windows_password_recovery_dpapi_master_key ↩
- (2010). Reversing DPAPI and Stealing Windows Secrets Offline (Black Hat DC 2010). https://elie.net/talk/reversing-dpapi-and-stealing-windows-secrets-offline ↩
- (2010). Decrypting DPAPI data (slide deck, Black Hat DC 2010, February 3 2010). https://elie.net/static/files/reversing-dpapi-and-stealing-windows-secrets-offline/reversing-dpapi-and-stealing-windows-secrets-offline-slides.pdf ↩
- (2010). Recovering Windows Secrets and EFS Certificates Offline (USENIX WOOT 10). https://elie.net/static/files/reversing-dpapi-and-stealing-windows-secrets-offline/reversing-dpapi-and-stealing-windows-secrets-offline-paper.pdf ↩
- Recovering Windows Secrets and EFS Certificates Offline -- BibTeX. https://elie.net/publication/recovering-windows-secrets-and-efs-certificates-offline/ ↩
- (2010). Recovering Windows Secrets and EFS Certificates Offline. https://www.usenix.org/conference/woot10/recovering-windows-secrets-and-efs-certificates-offline - USENIX WOOT 10 canonical record; two authors: Bursztein and Picod ↩
- gentilkiwi/mimikatz repository metadata (GitHub API). https://api.github.com/repos/gentilkiwi/mimikatz ↩
- (2014). Mimikatz initial commit (gentilkiwi/mimikatz, April 6 2014). https://github.com/gentilkiwi/mimikatz/commit/6419a5f37918 ↩
- Mimikatz dpapi module wiki. https://github.com/gentilkiwi/mimikatz/wiki/module-~-dpapi ↩
- GhostPack/SharpDPAPI README. https://github.com/GhostPack/SharpDPAPI ↩
- Operational Guidance for Offensive User DPAPI Abuse. https://blog.harmj0y.net/redteaming/operational-guidance-for-offensive-user-dpapi-abuse/ ↩
- (2022). Introducing the Golden GMSA Attack (Semperis). https://www.semperis.com/blog/golden-gmsa-attack/ ↩
- Semperis/GoldenGMSA repository. https://github.com/Semperis/GoldenGMSA ↩
- (2025). Golden dMSA: What Is dMSA Authentication Bypass? (Semperis). https://www.semperis.com/blog/golden-dmsa-what-is-dmsa-authentication-bypass/ ↩
- Semperis/GoldenDMSA repository. https://github.com/Semperis/GoldenDMSA ↩
- (2025). Semperis Research Uncovers Critical Flaw in Windows Server 2025 (PR Newswire, July 16 2025). https://www.prnewswire.com/news-releases/semperis-research-uncovers-critical-flaw-in-windows-server-2025-exposing-managed-service-accounts-to-golden-dmsa-attack-302506258.html ↩
- (2017). DPAPI exploitation during pentest and password cracking (UniverShell 2017, Synacktiv). https://www.synacktiv.com/ressources/univershell_2017_dpapi.pdf ↩
- OPCDE Dubai 2017 agenda. https://github.com/comaeio/OPCDE/tree/master/2017 ↩
- (2017). The Blackbox of DPAPI: the gift that keeps on giving (OPCDE Dubai 2017). https://raw.githubusercontent.com/comaeio/OPCDE/master/2017/The%20Blackbox%20of%20DPAPI%20the%20gift%20that%20keeps%20on%20giving%20-%20Bartosz%20Inglot/The%20Blackbox%20of%20DPAPI%20-%20Bart%20Inglot.pdf ↩
- (2024). Improving the Security of Chrome Cookies on Windows (Google Security Blog, July 30 2024). https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html ↩
- (2024). Google Chrome Adds App-Bound Encryption (The Hacker News, August 2024). https://thehackernews.com/2024/08/google-chrome-adds-app-bound-encryption.html ↩
- How Mimikatz Became the Hacker Tool That Defines an Era (Wired). https://www.wired.com/story/how-mimikatz-became-go-to-hacker-tool/ ↩
- Data Protection API (Wikipedia). https://en.wikipedia.org/wiki/Data_Protection_API ↩
- Niels Ferguson (Wikipedia). https://en.wikipedia.org/wiki/Niels_Ferguson ↩
- Windows 2000 (Wikipedia). https://en.wikipedia.org/wiki/Windows_2000 ↩
- Windows Server 2012 (Wikipedia). https://en.wikipedia.org/wiki/Windows_Server_2012 ↩
- Windows Server 2025 (Wikipedia). https://en.wikipedia.org/wiki/Windows_Server_2025 ↩
- (2022). NIST SP 800-108 Rev. 1: Recommendation for Key Derivation Using Pseudorandom Functions. https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-108r1.pdf ↩
- Apple Platform Security: Keychain data protection. https://support.apple.com/guide/security/keychain-data-protection-secb0694df1a/web ↩
- libsecret API reference (GNOME). https://gnome.pages.gitlab.gnome.org/libsecret/ ↩
- (2024). NIST FIPS 203: Module-Lattice-Based Key-Encapsulation Mechanism Standard (ML-KEM). https://csrc.nist.gov/pubs/fips/203/final - Published August 13, 2024 ↩
- (2024). NIST FIPS 204: Module-Lattice-Based Digital Signature Standard (ML-DSA). https://csrc.nist.gov/pubs/fips/204/final - Published August 13, 2024 ↩
- (2024). OpenSSH 9.9 release notes. https://www.openssh.com/releasenotes.html - OpenSSH 9.9 released September 19, 2024 with sntrup761x25519-sha512 hybrid PQ KEX as default ↩
- NIST Post-Quantum Cryptography project. https://csrc.nist.gov/projects/post-quantum-cryptography - Source for the harvest-now-decrypt-later framing ↩
- volatilityfoundation/volatility3 (memory-forensics framework with LSA-secrets plugins lsadump/cachedump/hashdump). https://github.com/volatilityfoundation/volatility3 ↩
- Credential Guard overview. https://learn.microsoft.com/en-us/windows/security/identity-protection/credential-guard/ ↩
- Virtualization-based security (VBS) for OEMs. https://learn.microsoft.com/en-us/windows-hardware/design/device-experiences/oem-vbs ↩
- Chrome Releases: Stable Channel Update for Desktop (Chrome 127, July 23, 2024). https://chromereleases.googleblog.com/2024/07/stable-channel-update-for-desktop_23.html ↩
- BitLocker (Wikipedia). https://en.wikipedia.org/wiki/BitLocker ↩