1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
//! DPAPI (Data Protection API) blob format knowledge — algorithm IDs, the
//! provider GUID, Chrome cookie prefixes, and the hash/cipher parameter tables.
//!
//! Windows DPAPI (`CryptProtectData` / `CryptUnprotectData`) wraps secrets in a
//! `DPAPI_BLOB` whose header names a hash algorithm (`algId`) and a cipher
//! algorithm (`algIdEncrypt`) by their Wincrypt `CALG_*` constants. The session
//! key is derived as `HMAC_H(SHA1(masterKey), salt[||entropy])` and the cipher
//! key by impacket's `deriveKey`; both depend on the algorithm-specific block
//! sizes captured here.
//!
//! This module is the fleet's single source of truth for those **constants** —
//! the magic numbers only. The parsing and the RustCrypto-backed key derivation
//! / decryption live in the consuming reader (`dpapi-core`), per
//! forensicnomicon's knowledge-only charter (no I/O, no crypto here).
//!
//! # Authoritative sources
//!
//! - impacket `DPAPI` structures and `ALGORITHMS_DATA` — the reverse-engineered
//! reference the DFIR community has settled on:
//! <https://github.com/fortra/impacket/blob/master/impacket/dpapi.py>
//! (`ALGORITHMS_DATA` maps each `CALG_*` to its digest / block / key / IV
//! lengths; `DPAPI_BLOB.decrypt` / `.deriveKey` consume them.)
//! - Wincrypt `CALG_*` algorithm identifiers (`wincrypt.h`, `[MS-…]`):
//! <https://learn.microsoft.com/windows/win32/seccrypto/alg-id>
//! - The DPAPI provider GUID `{df9d8cd0-1501-11d1-8c7a-00c04fc297eb}` — the
//! well-known Microsoft Base Cryptographic Provider GUID written into every
//! `DPAPI_BLOB` header.
//! - Chromium `os_crypt` — the `v10`/`v20` AES-256-GCM cookie/value prefixes:
//! <https://source.chromium.org/chromium/chromium/src/+/main:components/os_crypt/sync/os_crypt_win.cc>
// ---------------------------------------------------------------------------
// Hash algorithm IDs (the blob's `algId`)
// ---------------------------------------------------------------------------
/// `CALG_SHA1` — SHA-1 hash (20-byte digest, 64-byte block). Wincrypt `0x8004`.
pub const CALG_SHA1: u32 = 0x8004;
/// `CALG_HMAC` — keyed-hash MAC. In a DPAPI blob this selects the **SHA-512**
/// hash module with a **64-byte** `deriveKey` salt/block field (impacket
/// `ALGORITHMS_DATA[0x8009][4] == 64`). Wincrypt `0x8009`.
pub const CALG_HMAC: u32 = 0x8009;
/// `CALG_SHA_512` — SHA-512 hash (64-byte digest, 128-byte block) with a
/// **128-byte** `deriveKey` salt/block field. Wincrypt `0x800e`.
pub const CALG_SHA_512: u32 = 0x800e;
// ---------------------------------------------------------------------------
// Cipher algorithm IDs (the blob's `algIdEncrypt`)
// ---------------------------------------------------------------------------
/// `CALG_AES_256` — AES-256 (32-byte key, 16-byte IV/block). Wincrypt `0x6610`.
pub const CALG_AES_256: u32 = 0x6610;
/// `CALG_3DES` — triple DES (24-byte key, 8-byte IV/block). Wincrypt `0x6603`.
pub const CALG_3DES: u32 = 0x6603;
// ---------------------------------------------------------------------------
// Provider GUID
// ---------------------------------------------------------------------------
/// The DPAPI provider GUID `{df9d8cd0-1501-11d1-8c7a-00c04fc297eb}` as the 16
/// raw bytes that appear in a `DPAPI_BLOB` header.
///
/// A GUID is serialised mixed-endian: the first three components
/// (`Data1`/`Data2`/`Data3`) are little-endian, the last two (`Data4`) are
/// big-endian. So `{df9d8cd0-1501-11d1-8c7a-00c04fc297eb}` is written on the
/// wire as `d0 8c 9d df 01 15 d1 11 8c 7a 00 c0 4f c2 97 eb`. A reader compares
/// the 16 bytes following the 4-byte version field against this const.
pub const PROVIDER_GUID_BYTES: = ;
// ---------------------------------------------------------------------------
// Chrome / Chromium cookie & value prefixes
// ---------------------------------------------------------------------------
/// Chrome/Chromium `v10` AES-256-GCM prefix — classic App-Bound encryption.
pub const CHROME_COOKIE_V10: & = b"v10";
/// Chrome/Chromium `v20` AES-256-GCM prefix (Chrome 127+ App-Bound encryption);
/// same wire layout as `v10` (`prefix | 12-byte nonce | ciphertext | 16-byte tag`).
pub const CHROME_COOKIE_V20: & = b"v20";
// ---------------------------------------------------------------------------
// Hash algorithm descriptor
// ---------------------------------------------------------------------------
/// Parameters of a DPAPI hash algorithm, mirroring impacket's `ALGORITHMS_DATA`.
///
/// Two distinct block sizes are in play and must not be conflated:
/// * `derive_block_len` is the salt/block field used by `deriveKey` (impacket
/// index `[4]`): 64 for SHA-1 and `CALG_HMAC`, 128 for `CALG_SHA_512`.
/// * `hash_block_len` is the underlying hash module's block size used by the
/// integrity check (SHA-1 = 64, SHA-512 = 128).
///
/// For `CALG_HMAC` (`0x8009`) these differ (64 vs 128), so both are tracked.
/// Resolve a DPAPI hash `algId` to its parameters, or `None` if unrecognised.
///
/// Recognises [`CALG_SHA1`] (`0x8004` → SHA-1), [`CALG_HMAC`] (`0x8009` →
/// SHA-512 module, 64-byte derive block) and [`CALG_SHA_512`] (`0x800e` →
/// SHA-512, 128-byte derive block). A caller may treat `None` as the historical
/// SHA-1 default, but the unrecognised `algId` is surfaced rather than hidden.
pub const
// ---------------------------------------------------------------------------
// Cipher algorithm descriptor
// ---------------------------------------------------------------------------
/// Parameters of a DPAPI cipher algorithm, mirroring impacket's `ALGORITHMS_DATA`.
///
/// The DPAPI IV is always zero-filled to `iv_len` (impacket: `iv=b'\x00'*IVLen`).
/// Resolve a DPAPI cipher `algIdEncrypt` to its parameters, or `None` if
/// unrecognised.
///
/// Recognises [`CALG_AES_256`] (`0x6610`) and [`CALG_3DES`] (`0x6603`).
pub const