1use aes::Aes256;
2use cbc::Decryptor;
3use cipher::{block_padding::Pkcs7, BlockDecryptMut, KeyIvInit};
4use hmac::{Hmac, Mac};
5use sha1::{Digest, Sha1};
6use sha2::Sha512;
7
8use forensicnomicon::dpapi::{cipher_alg_info, CALG_AES_256};
9
10use crate::blob::{hash_alg, HashAlg};
11use crate::error::DpapiError;
12
13pub fn decrypt_aes256_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, DpapiError> {
15 let mut buf = ciphertext.to_vec();
16 let decryptor =
17 Decryptor::<Aes256>::new_from_slices(key, iv).map_err(|_| DpapiError::InvalidKeyLength)?;
18 let plaintext = decryptor
19 .decrypt_padded_mut::<Pkcs7>(&mut buf)
20 .map_err(|_| DpapiError::DecryptionFailed)?;
21 Ok(plaintext.to_vec())
22}
23
24pub fn verify_hmac_sha1(key: &[u8], data: &[u8], expected: &[u8]) -> Result<(), DpapiError> {
26 let mut mac = Hmac::<Sha1>::new_from_slice(key).map_err(|_| DpapiError::InvalidKeyLength)?;
27 mac.update(data);
28 mac.verify_slice(expected)
29 .map_err(|_| DpapiError::HmacMismatch)
30}
31
32fn hmac_hash(alg: HashAlg, key: &[u8], msg: &[u8]) -> Result<Vec<u8>, DpapiError> {
34 if alg.is_sha512 {
35 let mut mac =
36 Hmac::<Sha512>::new_from_slice(key).map_err(|_| DpapiError::InvalidKeyLength)?;
37 mac.update(msg);
38 Ok(mac.finalize().into_bytes().to_vec())
39 } else {
40 let mut mac =
41 Hmac::<Sha1>::new_from_slice(key).map_err(|_| DpapiError::InvalidKeyLength)?;
42 mac.update(msg);
43 Ok(mac.finalize().into_bytes().to_vec())
44 }
45}
46
47fn plain_hash(alg: HashAlg, msg: &[u8]) -> Vec<u8> {
49 if alg.is_sha512 {
50 Sha512::digest(msg).to_vec()
51 } else {
52 Sha1::digest(msg).to_vec()
53 }
54}
55
56fn derive_key(alg: HashAlg, session_key: &[u8], cipher_key_len: usize) -> Vec<u8> {
62 let mut derived = if session_key.len() > alg.derive_block_len {
63 hmac_hash(alg, session_key, &[]).unwrap_or_default()
65 } else {
66 session_key.to_vec()
67 };
68
69 if derived.len() < cipher_key_len {
70 let mut padded = derived.clone();
71 padded.resize(alg.derive_block_len, 0);
72 let ipad: Vec<u8> = padded
73 .iter()
74 .take(alg.derive_block_len)
75 .map(|b| b ^ 0x36)
76 .collect();
77 let opad: Vec<u8> = padded
78 .iter()
79 .take(alg.derive_block_len)
80 .map(|b| b ^ 0x5c)
81 .collect();
82 let mut out = plain_hash(alg, &ipad);
83 out.extend_from_slice(&plain_hash(alg, &opad));
84 fixparity(&mut out);
85 derived = out;
86 }
87
88 derived
89}
90
91fn fixparity(key: &mut [u8]) {
94 for b in key.iter_mut() {
95 let high7 = *b >> 1;
96 let ones = high7.count_ones();
97 *b = (high7 << 1) | u8::from(ones % 2 == 0);
98 }
99}
100
101pub fn decrypt_dpapi_blob(
109 blob: &crate::blob::DpapiBlob,
110 master_key: &[u8],
111 entropy: Option<&[u8]>,
112) -> Result<Vec<u8>, DpapiError> {
113 let alg = hash_alg(blob.alg_id_hash);
114 let cipher = cipher_alg_info(blob.alg_id_encrypt)
118 .ok_or(DpapiError::UnsupportedAlgId(blob.alg_id_encrypt))?;
119 let is_aes256 = blob.alg_id_encrypt == CALG_AES_256;
120
121 let key_hash = Sha1::digest(master_key).to_vec();
123
124 let mut salt_msg = blob.salt.clone();
126 if let Some(e) = entropy {
127 salt_msg.extend_from_slice(e);
128 }
129 let session_key = hmac_hash(alg, &key_hash, &salt_msg)?;
130
131 let derived = derive_key(alg, &session_key, cipher.key_len);
132 if derived.len() < cipher.key_len {
133 return Err(DpapiError::InvalidKeyLength);
134 }
135 let iv = vec![0u8; cipher.iv_len];
136
137 let cleartext = if is_aes256 {
138 decrypt_aes256_cbc(&derived[..cipher.key_len], &iv, &blob.ciphertext)?
139 } else {
140 decrypt_3des_cbc(&derived[..cipher.key_len], &iv, &blob.ciphertext)?
141 };
142
143 verify_blob_signature(&alg, &key_hash, blob, entropy)?;
144 Ok(cleartext)
145}
146
147fn verify_blob_signature(
149 alg: &HashAlg,
150 key_hash: &[u8],
151 blob: &crate::blob::DpapiBlob,
152 entropy: Option<&[u8]>,
153) -> Result<(), DpapiError> {
154 let mut key_hash2 = key_hash.to_vec();
156 key_hash2.resize(key_hash.len() + alg.hash_block_len, 0);
157 let ipad: Vec<u8> = key_hash2
158 .iter()
159 .take(alg.hash_block_len)
160 .map(|b| b ^ 0x36)
161 .collect();
162 let opad: Vec<u8> = key_hash2
163 .iter()
164 .take(alg.hash_block_len)
165 .map(|b| b ^ 0x5c)
166 .collect();
167
168 let mut inner = ipad;
169 inner.extend_from_slice(&blob.hmac);
170 let inner_digest = plain_hash(*alg, &inner);
171
172 let mut outer = opad;
173 outer.extend_from_slice(&inner_digest);
174 if let Some(e) = entropy {
175 outer.extend_from_slice(e);
176 }
177 outer.extend_from_slice(&blob.to_sign);
178 let calc1 = plain_hash(*alg, &outer);
179
180 let mut msg2 = blob.hmac.clone();
182 if let Some(e) = entropy {
183 msg2.extend_from_slice(e);
184 }
185 msg2.extend_from_slice(&blob.to_sign);
186 let calc2 = hmac_hash(*alg, key_hash, &msg2)?;
187
188 if calc1 == blob.sign || calc2 == blob.sign {
189 Ok(())
190 } else {
191 Err(DpapiError::HmacMismatch)
192 }
193}
194
195fn decrypt_3des_cbc(key: &[u8], iv: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, DpapiError> {
196 use cbc::Decryptor as CbcDec;
197 use cipher::block_padding::NoPadding;
198 use des::TdesEde3;
199
200 let mut buf = ciphertext.to_vec();
201 let dec =
202 CbcDec::<TdesEde3>::new_from_slices(key, iv).map_err(|_| DpapiError::InvalidKeyLength)?;
203 let out = dec
204 .decrypt_padded_mut::<NoPadding>(&mut buf)
205 .map_err(|_| DpapiError::DecryptionFailed)?;
206
207 let unpadded = pkcs_unpad(out, 8)?;
209 Ok(unpadded)
210}
211
212fn pkcs_unpad(data: &[u8], block_len: usize) -> Result<Vec<u8>, DpapiError> {
214 let pad = *data.last().ok_or(DpapiError::DecryptionFailed)? as usize;
215 if pad == 0 || pad > block_len || pad > data.len() {
216 return Err(DpapiError::DecryptionFailed);
217 }
218 if data[data.len() - pad..].iter().any(|&b| b as usize != pad) {
219 return Err(DpapiError::DecryptionFailed);
220 }
221 Ok(data[..data.len() - pad].to_vec())
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227 use aes::Aes256;
228 use cbc::Encryptor;
229 use cipher::{block_padding::Pkcs7, BlockEncryptMut, KeyIvInit};
230
231 fn aes256_cbc_encrypt(key: &[u8; 32], iv: &[u8; 16], plaintext: &[u8]) -> Vec<u8> {
232 let enc = Encryptor::<Aes256>::new_from_slices(key, iv).unwrap();
233 let mut buf = plaintext.to_vec();
234 let pad_len = 16 - (buf.len() % 16);
236 buf.extend(std::iter::repeat_n(pad_len as u8, pad_len));
237 enc.encrypt_padded_mut::<Pkcs7>(&mut buf, plaintext.len())
238 .unwrap()
239 .to_vec()
240 }
241
242 #[test]
243 fn decrypt_aes256_cbc_roundtrip() {
244 let key = [0x42u8; 32];
245 let iv = [0x11u8; 16];
246 let plaintext = b"hello DPAPI world!";
247 let ciphertext = aes256_cbc_encrypt(&key, &iv, plaintext);
248 let recovered = decrypt_aes256_cbc(&key, &iv, &ciphertext).expect("decrypt ok");
249 assert_eq!(&recovered[..plaintext.len()], plaintext);
250 }
251
252 #[test]
253 fn verify_hmac_sha1_correct_passes() {
254 use hmac::{Hmac, Mac};
255 use sha1::Sha1;
256 let key = b"secretkey";
257 let data = b"some data to mac";
258 let mut mac = Hmac::<Sha1>::new_from_slice(key).unwrap();
259 mac.update(data);
260 let expected = mac.finalize().into_bytes();
261 assert!(verify_hmac_sha1(key, data, &expected).is_ok());
262 }
263
264 #[test]
265 fn verify_hmac_sha1_wrong_key_fails() {
266 use hmac::{Hmac, Mac};
267 use sha1::Sha1;
268 let data = b"data";
269 let mut mac = Hmac::<Sha1>::new_from_slice(b"key1").unwrap();
270 mac.update(data);
271 let expected = mac.finalize().into_bytes();
272 assert!(verify_hmac_sha1(b"key2", data, &expected).is_err());
273 }
274
275 fn hex(s: &str) -> Vec<u8> {
276 (0..s.len())
277 .step_by(2)
278 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
279 .collect()
280 }
281
282 const MASTER_KEY_HEX: &str = "9828d9873735439e823dbd216205ff88266d28ad685a413970c640d5ee943154bbade31fada673d542c72d707a163bb3d1bceb0c50465b359ae06998481b0ce3";
286 const VECTOR1_BLOB_HEX: &str = "01000000d08c9ddf0115d1118c7a00c04fc297eb0100000033f19f5ee340be4a8a2e2b4e62bd0cc6000000000200000000001066000000010000200000000d1af96e5e102266fd36d96ac7d1595552e5a4e972463f77e6e227f22d5fc8df000000000e8000000002000020000000834f3c5710c8a7474f7dbcea8ba28ab8e4d4443f50a0c63ff4eba1cce485295f20000000b61d7576c0c6caf3690edb247bde3f7edaa59580e3b4be1265ea78e8c1b8a61d400000001c03ab807147742649b6bdfd1c1344d178bb163842d70abacfd51233af909cb81a677ec05d8db996f587ef5ac410dc189beda756eb0d1b6ee376823e80968538";
287
288 const VECTOR2_BLOB_HEX: &str = "01000000d08c9ddf0115d1118c7a00c04fc297eb0100000033f19f5ee340be4a8a2e2b4e62bd0cc600000000020000000000106600000001000020000000f239c0018e71b33bef9a6299675c7e209eef1f6447bd578d19c7973548737545000000000e80000000020000200000009d9ef33e15ffb1b310a13ecec39b1c02adc39e8d40a7162f9f9bb3170c699a812000000040e820259332c47af42e5f9de629e109d1504641aad853f3818c40ac311cf24a4000000010f01a84a5cc0393d3ea44cc3a8ff00ca4d02fcabc7c353a6823c53e4e719c9b398282a06b8878250205160ed79fef8b026093ad5a467594953d6de28d71f8c9";
290
291 #[test]
292 fn decrypt_sha512_aes256_blob_no_entropy() {
293 let blob = crate::blob::parse_dpapi_blob(&hex(VECTOR1_BLOB_HEX)).expect("parse");
294 let mk = hex(MASTER_KEY_HEX);
295 let pt = decrypt_dpapi_blob(&blob, &mk, None).expect("decrypt");
296 assert_eq!(pt, b"Some test string");
297 }
298
299 #[test]
300 fn decrypt_sha512_aes256_blob_with_entropy() {
301 let blob = crate::blob::parse_dpapi_blob(&hex(VECTOR2_BLOB_HEX)).expect("parse");
302 let mk = hex(MASTER_KEY_HEX);
303 let pt = decrypt_dpapi_blob(&blob, &mk, Some(b"Some entropy")).expect("decrypt");
304 assert_eq!(pt, b"Some test string");
305 }
306
307 #[test]
308 fn decrypt_sha512_blob_wrong_entropy_fails_integrity() {
309 let blob = crate::blob::parse_dpapi_blob(&hex(VECTOR2_BLOB_HEX)).expect("parse");
310 let mk = hex(MASTER_KEY_HEX);
311 assert!(decrypt_dpapi_blob(&blob, &mk, None).is_err());
314 }
315}