Coverage for /root/GitHubProjects/impacket/impacket/dpapi.py : 86%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
2#
3# This software is provided under under a slightly modified version
4# of the Apache Software License. See the accompanying LICENSE file
5# for more information.
6#
7# Author:
8# Alberto Solino (@agsolino)
9#
10# Description:
11# DPAPI and Windows Vault parsing structures and manipulation
12#
13# References: All of the work done by these guys. I just adapted their work to my needs.
14# https://www.passcape.com/index.php?section=docsys&cmd=details&id=28
15# https://github.com/jordanbtucker/dpapick
16# https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials (and everything else Ben did )
17# http://blog.digital-forensics.it/2016/01/windows-revaulting.html
18# https://www.passcape.com/windows_password_recovery_vault_explorer
19# https://www.passcape.com/windows_password_recovery_dpapi_master_key
20#
21from __future__ import division
22from __future__ import print_function
23import sys
25from struct import unpack
26from datetime import datetime
27from binascii import unhexlify, hexlify
28from struct import pack
29from Cryptodome.Hash import HMAC, SHA512, SHA1
30from Cryptodome.Cipher import AES, DES3
31from Cryptodome.Util.Padding import unpad
32from Cryptodome.PublicKey import RSA
33from Cryptodome.Util.number import bytes_to_long
34from six import PY3
36from impacket.ese import getUnixTime
37from impacket.structure import Structure, hexdump
38from impacket.uuid import bin_to_string
39from impacket.dcerpc.v5.enum import Enum
40from impacket.dcerpc.v5.dtypes import RPC_SID
42# Algorithm classes
43ALG_CLASS_ANY = (0)
44ALG_CLASS_SIGNATURE = (1 << 13)
45ALG_CLASS_MSG_ENCRYPT = (2 << 13)
46ALG_CLASS_DATA_ENCRYPT = (3 << 13)
47ALG_CLASS_HASH = (4 << 13)
48ALG_CLASS_KEY_EXCHANGE = (5 << 13)
49ALG_CLASS_ALL = (7 << 13)
51# Algorithm types
52ALG_TYPE_ANY = (0)
53ALG_TYPE_DSS = (1 << 9)
54ALG_TYPE_RSA = (2 << 9)
55ALG_TYPE_BLOCK = (3 << 9)
56ALG_TYPE_STREAM = (4 << 9)
57ALG_TYPE_DH = (5 << 9)
58ALG_TYPE_SECURECHANNEL = (6 << 9)
59ALG_SID_ANY = (0)
60ALG_SID_RSA_ANY = 0
61ALG_SID_RSA_PKCS = 1
62ALG_SID_RSA_MSATWORK = 2
63ALG_SID_RSA_ENTRUST = 3
64ALG_SID_RSA_PGP = 4
65ALG_SID_DSS_ANY = 0
66ALG_SID_DSS_PKCS = 1
67ALG_SID_DSS_DMS = 2
68ALG_SID_ECDSA = 3
70# Block cipher sub ids
71ALG_SID_DES = 1
72ALG_SID_3DES = 3
73ALG_SID_DESX = 4
74ALG_SID_IDEA = 5
75ALG_SID_CAST = 6
76ALG_SID_SAFERSK64 = 7
77ALG_SID_SAFERSK128 = 8
78ALG_SID_3DES_112 = 9
79ALG_SID_CYLINK_MEK = 12
80ALG_SID_RC5 = 13
81ALG_SID_AES_128 = 14
82ALG_SID_AES_192 = 15
83ALG_SID_AES_256 = 16
84ALG_SID_AES = 17
85ALG_SID_SKIPJACK = 10
86ALG_SID_TEK = 11
88CRYPT_MODE_CBCI = 6 # ANSI CBC Interleaved
89CRYPT_MODE_CFBP = 7 # ANSI CFB Pipelined
90CRYPT_MODE_OFBP = 8 # ANSI OFB Pipelined
91CRYPT_MODE_CBCOFM = 9 # ANSI CBC + OF Masking
92CRYPT_MODE_CBCOFMI = 10 # ANSI CBC + OFM Interleaved
94ALG_SID_RC2 = 2
95ALG_SID_RC4 = 1
96ALG_SID_SEAL = 2
98# Diffie - Hellman sub - ids
99ALG_SID_DH_SANDF = 1
100ALG_SID_DH_EPHEM = 2
101ALG_SID_AGREED_KEY_ANY = 3
102ALG_SID_KEA = 4
103ALG_SID_ECDH = 5
105# Hash sub ids
106ALG_SID_MD2 = 1
107ALG_SID_MD4 = 2
108ALG_SID_MD5 = 3
109ALG_SID_SHA = 4
110ALG_SID_SHA1 = 4
111ALG_SID_MAC = 5
112ALG_SID_RIPEMD = 6
113ALG_SID_RIPEMD160 = 7
114ALG_SID_SSL3SHAMD5 = 8
115ALG_SID_HMAC = 9
116ALG_SID_TLS1PRF = 10
117ALG_SID_HASH_REPLACE_OWF = 11
118ALG_SID_SHA_256 = 12
119ALG_SID_SHA_384 = 13
120ALG_SID_SHA_512 = 14
122# secure channel sub ids
123ALG_SID_SSL3_MASTER = 1
124ALG_SID_SCHANNEL_MASTER_HASH = 2
125ALG_SID_SCHANNEL_MAC_KEY = 3
126ALG_SID_PCT1_MASTER = 4
127ALG_SID_SSL2_MASTER = 5
128ALG_SID_TLS1_MASTER = 6
129ALG_SID_SCHANNEL_ENC_KEY = 7
130ALG_SID_ECMQV = 1
132def getFlags(myenum, flags):
133 return '|'.join([name for name, member in myenum.__members__.items() if member.value & flags])
135class FLAGS(Enum):
136 CRYPTPROTECT_UI_FORBIDDEN = 0x1
137 CRYPTPROTECT_LOCAL_MACHINE = 0x4
138 CRYPTPROTECT_CRED_SYNC = 0x8
139 CRYPTPROTECT_AUDIT = 0x10
140 CRYPTPROTECT_VERIFY_PROTECTION = 0x40
141 CRYPTPROTECT_CRED_REGENERATE = 0x80
142 CRYPTPROTECT_SYSTEM = 0x20000000
144# algorithm identifier definitions
145class ALGORITHMS(Enum):
146 CALG_MD2 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2)
147 CALG_MD4 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4)
148 CALG_MD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5)
149 CALG_SHA = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA)
150 CALG_SHA1 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1)
151 CALG_RSA_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY)
152 CALG_DSS_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY)
153 CALG_NO_SIGN = (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY)
154 CALG_RSA_KEYX = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_RSA|ALG_SID_RSA_ANY)
155 CALG_DES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DES)
156 CALG_3DES_112 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES_112)
157 CALG_3DES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES)
158 CALG_DESX = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DESX)
159 CALG_RC2 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC2)
160 CALG_RC4 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_RC4)
161 CALG_SEAL = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_SEAL)
162 CALG_DH_SF = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_SANDF)
163 CALG_DH_EPHEM = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_EPHEM)
164 CALG_AGREEDKEY_ANY = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_AGREED_KEY_ANY)
165 CALG_KEA_KEYX = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_KEA)
166 CALG_HUGHES_MD5 = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_ANY|ALG_SID_MD5)
167 CALG_SKIPJACK = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_SKIPJACK)
168 CALG_TEK = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_TEK)
169 CALG_SSL3_SHAMD5 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5)
170 CALG_SSL3_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL3_MASTER)
171 CALG_SCHANNEL_MASTER_HASH = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MASTER_HASH)
172 CALG_SCHANNEL_MAC_KEY = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MAC_KEY)
173 CALG_SCHANNEL_ENC_KEY = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_ENC_KEY)
174 CALG_PCT1_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_PCT1_MASTER)
175 CALG_SSL2_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL2_MASTER)
176 CALG_TLS1_MASTER = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_TLS1_MASTER)
177 CALG_RC5 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC5)
178 CALG_HMAC = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC)
179 CALG_TLS1PRF = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF)
180 CALG_HASH_REPLACE_OWF = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF)
181 CALG_AES_128 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_128)
182 CALG_AES_192 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_192)
183 CALG_AES_256 = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_256)
184 CALG_AES = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES)
185 CALG_SHA_256 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
186 CALG_SHA_384 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384)
187 CALG_SHA_512 = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512)
188 CALG_ECDH = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH)
189 CALG_ECMQV = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV)
190 CALG_ECDSA = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA)
192class CREDENTIAL_FLAGS(Enum):
193 CRED_FLAGS_PASSWORD_FOR_CERT = 0x1
194 CRED_FLAGS_PROMPT_NOW = 0x2
195 CRED_FLAGS_USERNAME_TARGET = 0x4
196 CRED_FLAGS_OWF_CRED_BLOB = 0x8
197 CRED_FLAGS_REQUIRE_CONFIRMATION = 0x10
198 CRED_FLAGS_WILDCARD_MATCH = 0x20
199 CRED_FLAGS_VSM_PROTECTED = 0x40
200 CRED_FLAGS_NGC_CERT = 0x80
202class CREDENTIAL_TYPE(Enum):
203 CRED_TYPE_GENERIC = 0x1
204 CRED_TYPE_DOMAIN_PASSWORD = 0x2
205 CRED_TYPE_DOMAIN_CERTIFICATE = 0x3
206 CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0x4
207 CRED_TYPE_GENERIC_CERTIFICATE = 0x5
208 CRED_TYPE_DOMAIN_EXTENDED = 0x6
209 CRED_TYPE_MAXIMUM = 0x7
210 CRED_TYPE_MAXIMUM_EX = 0x8
212class CREDENTIAL_PERSIST(Enum):
213 CRED_PERSIST_NONE = 0x0
214 CRED_PERSIST_SESSION = 0x1
215 CRED_PERSIST_LOCAL_MACHINE = 0x2
216 CRED_PERSIST_ENTERPRISE = 0x3
218ALGORITHMS_DATA = {
219 # Algorithm: key/SaltLen, CryptHashModule, Mode, IVLen, BlockSize
220 ALGORITHMS.CALG_SHA.value: (160//8, SHA1, None, None, 512//8),
221 ALGORITHMS.CALG_HMAC.value: (160//8, SHA512, None, None, 512//8),
222 ALGORITHMS.CALG_3DES.value: (192//8, DES3, DES3.MODE_CBC, 64//8),
223 ALGORITHMS.CALG_SHA_512.value: (128//8, SHA512, None, None, 1024//8),
224 ALGORITHMS.CALG_AES_256.value: (256//8, AES, AES.MODE_CBC, 128//8),
225}
227class MasterKeyFile(Structure):
228 structure = (
229 ('Version', '<L=0'),
230 ('unk1', '<L=0'),
231 ('unk2', '<L=0'),
232 ('Guid', "72s=b''"),
233 ('Unkown', '<L=0'),
234 ('Policy', '<L=0'),
235 ('Flags', '<L=0'),
236 ('MasterKeyLen', '<Q=0'),
237 ('BackupKeyLen', '<Q=0'),
238 ('CredHistLen', '<Q=0'),
239 ('DomainKeyLen', '<Q=0'),
240 )
242 def dump(self):
243 print("[MASTERKEYFILE]")
244 print("Version : %8x (%d)" % (self['Version'], self['Version']))
245 print("Guid : %s" % self['Guid'].decode('utf-16le'))
246 print("Flags : %8x (%d)" % (self['Flags'], self['Flags']))
247 print("Policy : %8x (%d)" % (self['Policy'], self['Policy']))
248 print("MasterKeyLen: %.8x (%d)" % (self['MasterKeyLen'], self['MasterKeyLen']))
249 print("BackupKeyLen: %.8x (%d)" % (self['BackupKeyLen'], self['BackupKeyLen']))
250 print("CredHistLen : %.8x (%d)" % (self['CredHistLen'], self['CredHistLen']))
251 print("DomainKeyLen: %.8x (%d)" % (self['DomainKeyLen'], self['DomainKeyLen']))
252 print()
254class MasterKey(Structure):
255 structure = (
256 ('Version', '<L=0'),
257 ('Salt', '16s=b""'),
258 ('MasterKeyIterationCount', '<L=0'),
259 ('HashAlgo', "<L=0"),
260 ('CryptAlgo', '<L=0'),
261 ('data', ':'),
262 )
264 def __init__(self, data = None, alignment = 0):
265 Structure.__init__(self, data, alignment)
266 self.decryptedKey = None
268 def dump(self):
269 print("[MASTERKEY]")
270 print("Version : %8x (%d)" % (self['Version'], self['Version']))
271 print("Salt : %s" % hexlify(self['Salt']))
272 print("Rounds : %8x (%d)" % (self['MasterKeyIterationCount'], self['MasterKeyIterationCount']))
273 print("HashAlgo : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name))
274 print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
275 print("data : %s" % (hexlify(self['data'])))
276 print()
278 def deriveKey(self, passphrase, salt, keylen, count, hashFunction):
279 keyMaterial = b""
280 i = 1
281 while len(keyMaterial) < keylen:
282 U = salt + pack("!L", i)
283 i += 1
284 derived = bytearray(hashFunction(passphrase, U))
285 for r in range(count - 1):
286 actual = bytearray(hashFunction(passphrase, derived))
287 if PY3: 287 ↛ 290line 287 didn't jump to line 290, because the condition on line 287 was never false
288 derived = (int.from_bytes(derived, sys.byteorder) ^ int.from_bytes(actual, sys.byteorder)).to_bytes(len(actual), sys.byteorder)
289 else:
290 derived = bytearray([ chr((a) ^ (b)) for (a,b) in zip(derived, actual) ])
291 keyMaterial += derived
293 return keyMaterial[:keylen]
295 def decrypt(self, key):
296 if self['HashAlgo'] == ALGORITHMS.CALG_HMAC.value: 296 ↛ 297line 296 didn't jump to line 297, because the condition on line 296 was never true
297 hashModule = SHA1
298 else:
299 hashModule = ALGORITHMS_DATA[self['HashAlgo']][1]
301 prf = lambda p, s: HMAC.new(p, s, hashModule).digest()
302 derivedBlob = self.deriveKey(key, self['Salt'],
303 ALGORITHMS_DATA[self['CryptAlgo']][0] + ALGORITHMS_DATA[self['CryptAlgo']][3],
304 count=self['MasterKeyIterationCount'], hashFunction=prf)
306 cryptKey = derivedBlob[:ALGORITHMS_DATA[self['CryptAlgo']][0]]
307 iv = derivedBlob[ALGORITHMS_DATA[self['CryptAlgo']][0]:][:ALGORITHMS_DATA[self['CryptAlgo']][3]]
309 cipher = ALGORITHMS_DATA[self['CryptAlgo']][1].new(cryptKey, mode = ALGORITHMS_DATA[self['CryptAlgo']][2], iv = iv)
310 cleartext = cipher.decrypt(self['data'])
312 decryptedKey = cleartext[-64:]
313 hmacSalt = cleartext[:16]
314 hmac = cleartext[16:][:ALGORITHMS_DATA[self['HashAlgo']][0]]
316 hmacKey = HMAC.new(key, hmacSalt, hashModule).digest()
318 hmacCalculated = HMAC.new(hmacKey, decryptedKey, hashModule ).digest()
320 if hmacCalculated[:ALGORITHMS_DATA[self['HashAlgo']][0]] == hmac: 320 ↛ 324line 320 didn't jump to line 324, because the condition on line 320 was never false
321 self.decryptedKey = decryptedKey
322 return decryptedKey
323 else:
324 return None
326class CredHist(Structure):
327 structure = (
328 ('Version', '<L=0'),
329 ('Guid', "16s=b''"),
330 )
331 def dump(self):
332 print("[CREDHIST]")
333 print("Version : %8x (%d)" % (self['Version'], self['Version']))
334 print("Guid : %s" % bin_to_string(self['Guid']))
335 print()
337class DomainKey(Structure):
338 structure = (
339 ('Version', '<L=0'),
340 ('SecretLen', '<L=0'),
341 ('AccessCheckLen', '<L=0'),
342 ('Guid', "16s=b"""),
343 ('_SecretData', '_-SecretData', 'self["SecretLen"]'),
344 ('SecretData', ':'),
345 ('_AccessCheck', '_-AccessCheck', 'self["AccessCheckLen"]'),
346 ('AccessCheck', ':'),
347 )
348 def dump(self):
349 print("[DOMAINKEY]")
350 print("Version : %8x (%d)" % (self['Version'], self['Version']))
351 print("Guid : %s" % bin_to_string(self['Guid']))
352 print("SecretLen : %8x (%d)" % (self['SecretLen'], self['SecretLen']))
353 print("AccessCheckLen: %.8x (%d)" % (self['AccessCheckLen'], self['AccessCheckLen']))
354 print("SecretData : %s" % (hexlify(self['SecretData'])))
355 print("AccessCheck : %s" % (hexlify(self['AccessCheck'])))
356 print()
358class DPAPI_SYSTEM(Structure):
359 structure = (
360 ('Version', '<L=0'),
361 ('MachineKey', '20s=b""'),
362 ('UserKey', '20s=b""'),
363 )
365 def dump(self):
366 print("[DPAPI_SYSTEM]")
367 print("Version : %8x (%d)" % (self['Version'], self['Version']))
368 print("MachineKey : 0x%s" % hexlify(self['MachineKey']).decode('latin-1'))
369 print("UserKey : 0x%s" % hexlify(self['UserKey']).decode('latin-1'))
370 print()
372class CredentialFile(Structure):
373 structure = (
374 ('Version', '<L=0'),
375 ('Size', '<L=0'),
376 ('Unknown', '<L=0'),
377 ('_Data', '_-Data', 'self["Size"]'),
378 ('Data', ':'),
379 )
380 #def dump(self):
381 # print("[CREDENTIAL FILE]")
382 # print("Version : %8x (%d)" % (self['Version'], self['Version']))
383 # print("MachineKey : %s" % hexlify(self['MachineKey']))
384 # print("UserKey : %s" % hexlify(self['UserKey']))
385 # print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
386 # print()
389class DPAPI_BLOB(Structure):
390 structure = (
391 ('Version', '<L=0'),
392 ('GuidCredential', "16s=b"""),
393 ('MasterKeyVersion', '<L=0'),
394 ('GuidMasterKey', "16s=b"""),
395 ('Flags', '<L=0'),
397 ('DescriptionLen', '<L=0'),
398 ('_Description', '_-Description', 'self["DescriptionLen"]'),
399 ('Description', ':'),
401 ('CryptAlgo', '<L=0'),
402 ('CryptAlgoLen', '<L=0'),
404 ('SaltLen', '<L=0'),
405 ('_Salt', '_-Salt', 'self["SaltLen"]'),
406 ('Salt', ':'),
408 ('HMacKeyLen', '<L=0'),
409 ('_HMacKey', '_-HMacKey', 'self["HMacKeyLen"]'),
410 ('HMacKey', ':'),
412 ('HashAlgo', '<L=0'),
413 ('HashAlgoLen', '<L=0'),
415 ('HMac', '<L=0'),
416 ('_HMac', '_-HMac', 'self["HMac"]'),
417 ('HMac', ':'),
419 ('DataLen', '<L=0'),
420 ('_Data', '_-Data', 'self["DataLen"]'),
421 ('Data', ':'),
423 ('SignLen', '<L=0'),
424 ('_Sign', '_-Sign', 'self["SignLen"]'),
425 ('Sign', ':'),
427 )
429 def dump(self):
430 print("[BLOB]")
431 print("Version : %8x (%d)" % (self['Version'], self['Version']))
432 print("Guid Credential : %s" % bin_to_string(self['GuidCredential']))
433 print("MasterKeyVersion : %8x (%d)" % (self['MasterKeyVersion'], self['MasterKeyVersion']))
434 print("Guid MasterKey : %s" % bin_to_string(self['GuidMasterKey']))
435 print("Flags : %8x (%s)" % (self['Flags'], getFlags(FLAGS, self['Flags'])))
436 print("Description : %s" % (self['Description'].decode('utf-16le')))
437 print("CryptAlgo : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
438 print("Salt : %s" % (hexlify(self['Salt'])))
439 print("HMacKey : %s" % (hexlify(self['HMacKey'])))
440 print("HashAlgo : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name))
441 print("HMac : %s" % (hexlify(self['HMac'])))
442 print("Data : %s" % (hexlify(self['Data'])))
443 print("Sign : %s" % (hexlify(self['Sign'])))
444 print()
447 def deriveKey(self, sessionKey):
448 def fixparity(deskey):
449 from six import indexbytes, b
450 temp = b''
451 for i in range(len(deskey)):
452 t = (bin(indexbytes(deskey,i))[2:]).rjust(8,'0')
453 if t[:7].count('1') %2 == 0:
454 temp+= b(chr(int(t[:7]+'1',2)))
455 else:
456 temp+= b(chr(int(t[:7]+'0',2)))
457 return temp
459 if len(sessionKey) > ALGORITHMS_DATA[self['HashAlgo']][4]: 459 ↛ 460line 459 didn't jump to line 460, because the condition on line 459 was never true
460 derivedKey = HMAC.new(sessionKey, digestmod = ALGORITHMS_DATA[self['HashAlgo']][1]).digest()
461 else:
462 derivedKey = sessionKey
465 if len(derivedKey) < ALGORITHMS_DATA[self['CryptAlgo']][0]:
466 # Extend the key
467 derivedKey += b'\x00'*ALGORITHMS_DATA[self['HashAlgo']][4]
468 ipad = bytearray([ i ^ 0x36 for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]])
469 opad = bytearray([ i ^ 0x5c for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]])
470 derivedKey = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad).digest() + \
471 ALGORITHMS_DATA[self['HashAlgo']][1].new(opad).digest()
472 derivedKey = fixparity(derivedKey)
474 return derivedKey
476 def decrypt(self, key, entropy = None):
477 keyHash = SHA1.new(key).digest()
478 sessionKey = HMAC.new(keyHash, self['Salt'], ALGORITHMS_DATA[self['HashAlgo']][1])
479 if entropy is not None: 479 ↛ 480line 479 didn't jump to line 480, because the condition on line 479 was never true
480 sessionKey.update(entropy)
482 sessionKey = sessionKey.digest()
484 # Derive the key
485 derivedKey = self.deriveKey(sessionKey)
487 cipher = ALGORITHMS_DATA[self['CryptAlgo']][1].new(derivedKey[:ALGORITHMS_DATA[self['CryptAlgo']][0]],
488 mode=ALGORITHMS_DATA[self['CryptAlgo']][2], iv=b'\x00'*ALGORITHMS_DATA[self['CryptAlgo']][3])
489 cleartext = unpad(cipher.decrypt(self['Data']), ALGORITHMS_DATA[self['CryptAlgo']][1].block_size)
491 # Now check the signature
493 # ToDo Fix this, it's just ugly, more testing so we can remove one
494 toSign = (self.rawData[20:][:len(self.rawData)-20-len(self['Sign'])-4])
496 # Calculate the different HMACKeys
497 keyHash2 = keyHash + b"\x00"*ALGORITHMS_DATA[self['HashAlgo']][1].block_size
498 ipad = bytearray([i ^ 0x36 for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size])
499 opad = bytearray([i ^ 0x5c for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size])
500 a = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad)
501 a.update(self['HMac'])
503 hmacCalculated1 = ALGORITHMS_DATA[self['HashAlgo']][1].new(opad)
504 hmacCalculated1.update(a.digest())
506 if entropy is not None: 506 ↛ 507line 506 didn't jump to line 507, because the condition on line 506 was never true
507 hmacCalculated1.update(entropy)
509 hmacCalculated1.update(toSign)
511 hmacCalculated3 = HMAC.new(keyHash, self['HMac'], ALGORITHMS_DATA[self['HashAlgo']][1])
512 if entropy is not None: 512 ↛ 513line 512 didn't jump to line 513, because the condition on line 512 was never true
513 hmacCalculated3.update(entropy)
515 hmacCalculated3.update(toSign)
517 if hmacCalculated1.digest() == self['Sign'] or hmacCalculated3.digest() == self['Sign']: 517 ↛ 520line 517 didn't jump to line 520, because the condition on line 517 was never false
518 return cleartext
519 else:
520 return None
522class VAULT_ATTRIBUTE(Structure):
523 structure = (
524 ('Id', '<L=0'),
525 ('Unknown1', '<L=0'),
526 ('Unknown2', '<L=0'),
527 ('Unknown3', '<L=0'),
528 )
530 padding = (
531 ('Pad', '6s=b""'),
532 )
534 id100 = (
535 ('Unknown5', '<L=0'),
536 )
538 extended = (
539 ('Size','<L=0'),
540 ('IVPresent', '<B=?&IVSize'),
541 ('IVSize', '<L=0'),
542 ('_IV', '_-IV', 'self["IVSize"] if self["IVSize"] is not None else 0'),
543 ('IV', ':'),
544 ('_Data','_-Data', 'self["Size"]-self["IVSize"]-5 if self["IVPresent"] else self["Size"]-1'),
545 ('Data',':'),
546 )
547 def __init__(self, data = None, alignment = 0):
548 if len(data) > 20: 548 ↛ 555line 548 didn't jump to line 555, because the condition on line 548 was never false
549 if data[16:][:6] == b'\x00'*6: 549 ↛ 550line 549 didn't jump to line 550, because the condition on line 549 was never true
550 self.structure += self.padding
551 if unpack('<L',data[:4])[0] >= 100:
552 self.structure += self.id100
553 if len(data[16:]) >= 9:
554 self.structure += self.extended
555 Structure.__init__(self, data, alignment)
558 def dump(self):
559 print("[ATTRIBUTE %d]" % self['Id'])
560 if len(self.rawData) > 28:
561 print("Size : 0x%x" % self['Size'])
562 if self['IVPresent'] > 0:
563 print("IVSize : 0x%x" % self['IVSize'])
564 print("IV : %s" % hexlify(self['IV']))
565 print("Data : %s" % hexlify(self['Data']))
567class VAULT_ATTRIBUTE_MAP_ENTRY(Structure):
568 structure = (
569 ('Id', '<L=0'),
570 ('Offset', '<L=0'),
571 ('Unknown1', '<L=0'),
572 )
573 def dump(self):
574 print("[MAP ENTRY %d @ 0x%.8x]" % (self['Id'], self['Offset']))
576class VAULT_VCRD(Structure):
577 structure = (
578 ('SchemaGuid', "16s=b"""),
579 ('Unknown0', '<L=0'),
580 ('LastWritten', '<Q=0'),
581 ('Unknown1', '<L=0'),
582 ('Unknown2', '<L=0'),
583 ('FriendlyNameLen', '<L=0'),
584 ('FriendlyNameL', '_-FriendlyName', 'self["FriendlyNameLen"]'),
585 ('FriendlyName', ':'),
586 ('AttributesMapsSize', '<L=0'),
587 ('AttributeL', '_-AttributeMaps', 'self["AttributesMapsSize"]'),
588 ('AttributeMaps', ':'),
589 ('Data', ':'),
590 )
592 def __init__(self, data = None, alignment = 0):
593 Structure.__init__(self, data, alignment)
594 if data is not None: 594 ↛ exitline 594 didn't return from function '__init__', because the condition on line 594 was never false
595 # Process the MAP entries
596 self.mapEntries = list()
597 data = self['AttributeMaps']
598 for i in range(self['AttributesMapsSize']//len(VAULT_ATTRIBUTE_MAP_ENTRY())):
599 entry = VAULT_ATTRIBUTE_MAP_ENTRY(data)
600 self.mapEntries.append(entry)
601 data = data[len(VAULT_ATTRIBUTE_MAP_ENTRY()):]
603 self.attributesLen = list()
605 for i in range(len(self.mapEntries)):
606 if i > 0:
607 self.attributesLen.append(self.mapEntries[i]['Offset']-self.mapEntries[i-1]['Offset'])
609 self.attributesLen.append(len(self.rawData) - self.mapEntries[i]['Offset'] )
611 self.attributes = list()
612 for i, entry in enumerate(self.mapEntries):
613 attribute = VAULT_ATTRIBUTE(self.rawData[entry['Offset']:][:self.attributesLen[i]])
614 self.attributes.append(attribute)
616 # Do we have remaining data?
617 self['Data'] = self.rawData[self.mapEntries[-1]['Offset']+len(self.attributes[-1].getData()):]
619 def dump(self):
620 print("[VCRD]")
621 print("SchemaGuid : %s" % bin_to_string(self['SchemaGuid']))
622 print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten']))))
623 print("FriendlyName: %s" % (self['FriendlyName'].decode('utf-16le')))
624 print()
625 for i,entry in enumerate(self.mapEntries):
626 entry.dump()
627 self.attributes[i].dump()
628 print()
629 print("Remaining : %s" % (hexlify(self['Data'])))
630 print()
632class VAULT_VPOL(Structure):
633 structure = (
634 ('Version', '<L=0'),
635 ('Guid', "16s=b"""),
636 ('DescriptionLen', '<L=0'),
637 ('_Description', '_-Description', 'self["DescriptionLen"]'),
638 ('Description', ':'),
639 ('Unknown', '12s=b""'),
640 ('Size', '<L=0'),
641 ('Guid2', "16s=b"""),
642 ('Guid3', "16s=b"""),
643 ('KeySize','<L=0'),
644 ('_Blob', '_-Blob', 'self["KeySize"]'),
645 ('Blob', ':', DPAPI_BLOB),
646 )
648 def dump(self):
649 print("[VAULT_VPOL]")
650 print("Version : %8x (%d)" % (self['Version'], self['Version']))
651 print("Guid : %s" % bin_to_string(self['Guid']))
652 print("Description : %s" % (self['Description'].decode('utf-16le')))
653 print("Size : 0x%.8x (%d)" % (self['Size'], self['Size']))
654 print("Guid2 : %s" % bin_to_string(self['Guid2']))
655 print("Guid3 : %s" % bin_to_string(self['Guid3']))
656 print("KeySize : 0x%.8x (%d)" % (self['KeySize'], self['KeySize']))
657 self['Blob'].dump()
658 print()
660# from bcrypt.h
661class BCRYPT_KEY_DATA_BLOB_HEADER(Structure):
662 structure = (
663 ('dwMagic','<L=0'),
664 ('dwVersion','<L=0'),
665 ('cbKeyData','<L=0'),
666 ('_bKey','_-bKey', 'self["cbKeyData"]'),
667 ('bKey',':'),
668 )
670# from https://media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEFCON-24-Jkambic-Cunning-With-Cng-Soliciting-Secrets-From-Schannel-WP.pdf
671class BCRYPT_KSSM_DATA_BLOB_HEADER(Structure):
672 structure = (
673 ('cbLength','<L=0'),
674 ('dwKeyMagic','<L=0'),
675 ('dwUnknown2','<L=0'),
676 ('dwUnknown3','<L=0'),
677 ('dwKeyBitLen','<L=0'),
678 ('cbKeyLength','<L=0'),
679 #('_bKey','_-bKey', 'self["cbKeyData"]'),
680 #('AesKey','32s=""'),
681 #('dwUnknown4','<L=0'),
682 #('KeySchedule','448s=""'),
683 #('dwUnknown5','<L=0'),
684 #('cbScheduleLen','<L=0'),
685 #('Unknown6','16s=""'),
686 )
688class BCRYPT_KEY_WRAP(Structure):
689 structureKDBM = (
690 ('Size','<L=0'),
691 ('Version','<L=0'),
692 ('Unknown2','<L=0'),
693 ('_bKeyBlob','_-bKeyBlob', 'self["Size"]'),
694 ('bKeyBlob',':', BCRYPT_KEY_DATA_BLOB_HEADER),
695 )
696 structureKSSM = (
697 ('Size','<L=0'),
698 ('Version','<L=0'),
699 ('Unknown2','<L=0'),
700 ('_bKeyBlob','_-bKeyBlob', 'self["Size"]-8'),
701 ('bKeyBlob',':'),
702 )
703 def __init__(self, data = None, alignment = 0):
704 if len(data) >=16: 704 ↛ 709line 704 didn't jump to line 709, because the condition on line 704 was never false
705 if data[0:1] == b'\x24' or data[0:1] == b'\x34': 705 ↛ 708line 705 didn't jump to line 708, because the condition on line 705 was never false
706 self.structure = self.structureKDBM
707 else:
708 self.structure = self.structureKSSM
709 Structure.__init__(self, data, alignment)
711class VAULT_VPOL_KEYS(Structure):
712 structure = (
713 ('Key1',':', BCRYPT_KEY_WRAP),
714 ('Key2',':', BCRYPT_KEY_WRAP),
715 )
716 def dump(self):
717 print("[VAULT_VPOL_KEYS]")
718 if self['Key1']['Size'] > 0x24: 718 ↛ 719line 718 didn't jump to line 719, because the condition on line 718 was never true
719 print('Key1:')
720 hexdump(self['Key1']['bKeyBlob'])
721 print('Key2:')
722 hexdump(self['Key2']['bKeyBlob'])
723 else:
724 print('Key1: 0x%s' % hexlify(self['Key1']['bKeyBlob']['bKey']).decode('latin-1'))
725 print('Key2: 0x%s' % hexlify(self['Key2']['bKeyBlob']['bKey']).decode('latin-1'))
726 print()
728class VAULT_INTERNET_EXPLORER(Structure):
729 structure = (
730 ('Version','<L=0'),
731 ('Count','<L=0'),
732 ('Unknown','<L=0'),
733 ('Id1', '<L=0'),
734 ('UsernameLen', '<L=0'),
735 ('_Username', '_-Username','self["UsernameLen"]'),
736 ('Username', ':'),
738 ('Id2', '<L=0'),
739 ('ResourceLen', '<L=0'),
740 ('_Resource', '_-Resource', 'self["ResourceLen"]'),
741 ('Resource', ':'),
743 ('Id3', '<L=0'),
744 ('PasswordLen', '<L=0'),
745 ('_Password', '_-Password', 'self["PasswordLen"]'),
746 ('Password', ':'),
747 )
748 def fromString(self, data):
749 Structure.fromString(self, data)
751 def dump(self):
752 print("[Internet Explorer]")
753 print('Username : %s' % self['Username'].decode('utf-16le'))
754 print('Resource : %s' % self['Resource'].decode('utf-16le'))
755 print('Password : %s' % (hexlify(self['Password'])))
756 print()
758class VAULT_WIN_BIO_KEY(Structure):
759 structure = (
760 ('Version','<L=0'),
761 ('Count','<L=0'),
762 ('Unknown','<L=0'),
763 ('Id1', '<L=0'),
764 ('SidLen', '<L=0'),
765 ('_Sid', '_-Sid','self["SidLen"]'),
766 ('Sid', ':'),
768 ('Id2', '<L=0'),
769 ('NameLen', '<L=0'),
770 ('_Name', '_-Name', 'self["NameLen"]'),
771 ('Name', ':'),
773 ('Id3', '<L=0'),
774 ('BioKeyLen', '<L=0'),
775 ('_BioKey', '_-BioKey', 'self["BioKeyLen"]'),
776 ('BioKey', ':'),
777 )
778 def fromString(self, data):
779 Structure.fromString(self, data)
780 if data is not None:
781 bioKey = BCRYPT_KEY_DATA_BLOB_HEADER(unhexlify(self['BioKey'].decode('utf-16le')[:-1]))
782 self['BioKey'] = bioKey
784 def dump(self):
785 print("[WINDOWS BIOMETRIC KEY]")
786 print('Sid : %s' % RPC_SID(b'\x05\x00\x00\x00'+self['Sid']).formatCanonical())
787 print('Friendly Name: %s' % self['Name'].decode('utf-16le'))
788 print('Biometric Key: 0x%s' % (hexlify(self['BioKey']['bKey'])).decode('latin-1'))
789 print()
791class NGC_LOCAL_ACCOOUNT(Structure):
792 structure = (
793 ('Version','<L=0'),
794 ('UnlockKeySize','<L=0'),
795 ('IVSize','<L=0'),
796 ('CipherTextSize','<L=0'),
797 ('MustBeZeroTest','<L=0'),
798 ('_UnlockKey', '_-UnlockKey', 'self["UnlockKeySize"]'),
799 ('UnlockKey', ':'),
800 ('_IV', '_-IV', 'self["IVSize"]'),
801 ('IV', ':'),
802 ('_CipherText', '_-CipherText', 'self["CipherTextSize"]'),
803 ('CipherText', ':'),
804 )
805# def __init__(self, data=None, alignment = 0):
806# hexdump(data)
807 def dump(self):
808 print("[NGC LOCAL ACCOOUNT]")
809 print('UnlockKey : %s' % hexlify(self["UnlockKey"]))
810 print('IV : %s' % hexlify(self["IV"]))
811 print('CipherText : %s' % hexlify(self["CipherText"]))
813class VAULT_NGC_ACCOOUNT(Structure):
814 structure = (
815 ('Version','<L=0'),
816 ('Count','<L=0'),
817 ('Unknown','<L=0'),
818 ('Id1', '<L=0'),
819 ('SidLen', '<L=0'),
820 ('_Sid', '_-Sid','self["SidLen"]'),
821 ('Sid', ':'),
823 ('Id2', '<L=0'),
824 ('NameLen', '<L=0'),
825 ('_Name', '_-Name', 'self["NameLen"]'),
826 ('Name', ':'),
828 ('Id3', '<L=0'),
829 ('BlobLen', '<L=0'),
830 ('Blob', '_-Blob', 'self["BlobLen"]'),
831 ('Blob', ':', NGC_LOCAL_ACCOOUNT),
832 )
833 def dump(self):
834 print("[NGC VAULT]")
835 print('Sid : %s' % RPC_SID(b'\x05\x00\x00\x00'+self['Sid']).formatCanonical())
836 print('Friendly Name: %s' % self['Name'].decode('utf-16le'))
837 self['Blob'].dump()
838 print()
840VAULT_KNOWN_SCHEMAS = {
841 'WinBio Key': VAULT_WIN_BIO_KEY,
842 'NGC Local Accoount Logon Vault Credential': VAULT_NGC_ACCOOUNT,
843 'Internet Explorer': VAULT_INTERNET_EXPLORER,
844}
846class CREDENTIAL_ATTRIBUTE(Structure):
847 # some info here https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributea
848 structure = (
849 ('Flags','<L=0'),
851 ('KeyWordSize', '<L=0'),
852 ('_KeyWord', '_-KeyWord', 'self["KeyWordSize"]'),
853 ('KeyWord', ':'),
855 ('DataSize', '<L=0'),
856 ('_Data', '_-Data', 'self["DataSize"]'),
857 ('Data', ':'),
858 )
860 def dump(self):
861 print("KeyWord : %s" % (self['KeyWord'].decode('utf-16le')))
862 #print("Flags : %8x (%s)" % (self['Flags'], getFlags(CREDENTIAL_FLAGS, self['Flags'])))
863 print("Data : ")
864 hexdump(self['Data'])
866class CREDENTIAL_BLOB(Structure):
867 # some info here https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentiala
868 structure = (
869 ('Flags','<L=0'),
870 ('Size','<L=0'),
871 ('Unknown0','<L=0'),
872 ('Type','<L=0'),
873 ('Flags2','<L=0'),
874 ('LastWritten','<Q=0'),
875 ('Unknown2','<L=0'),
876 ('Persist','<L=0'),
877 ('AttrCount','<L=0'),
878 ('Unknown3','<Q=0'),
880 ('TargetSize','<L=0'),
881 ('_Target','_-Target','self["TargetSize"]'),
882 ('Target',':'),
884 ('TargetAliasSize', '<L=0'),
885 ('_TargetAliast', '_-TargetAlias', 'self["TargetAliasSize"]'),
886 ('TargetAlias', ':'),
888 ('DescriptionSize', '<L=0'),
889 ('_Description', '_-Description', 'self["DescriptionSize"]'),
890 ('Description', ':'),
892 ('UnknownSize', '<L=0'),
893 ('_Unknown', '_-Unknown', 'self["UnknownSize"]'),
894 ('Unknown', ':'),
896 ('UsernameSize', '<L=0'),
897 ('_Username', '_-Username', 'self["UsernameSize"]'),
898 ('Username', ':'),
900 ('Unknown3Size', '<L=0'),
901 ('_Unknown3', '_-Unknown3', 'self["Unknown3Size"]'),
902 ('Unknown3', ':'),
904 ('Remaining', ':'),
905 )
906 def __init__(self, data = None, alignment = 0):
907 Structure.__init__(self, data, alignment)
908 self.attributes = 0
909 if data is not None: 909 ↛ exitline 909 didn't return from function '__init__', because the condition on line 909 was never false
910 # Unpack the attributes
911 remaining = self['Remaining']
912 self.attributes = list()
913 for i in range(self['AttrCount']): 913 ↛ 914line 913 didn't jump to line 914, because the loop on line 913 never started
914 attr = CREDENTIAL_ATTRIBUTE(remaining)
915 self.attributes.append(attr)
916 remaining = remaining[len(attr):]
918 def dump(self):
919 print("[CREDENTIAL]")
920 print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten']))))
921 print("Flags : 0x%.8x (%s)" % (self['Flags'], getFlags(CREDENTIAL_FLAGS, self['Flags'])))
922 print("Persist : 0x%.8x (%s)" % (self['Persist'], CREDENTIAL_PERSIST(self['Persist']).name))
923 print("Type : 0x%.8x (%s)" % (self['Type'], CREDENTIAL_PERSIST(self['Type']).name))
924 print("Target : %s" % (self['Target'].decode('utf-16le')))
925 print("Description : %s" % (self['Description'].decode('utf-16le')))
926 print("Unknown : %s" % (self['Unknown'].decode('utf-16le')))
927 print("Username : %s" % (self['Username'].decode('utf-16le')))
928 try:
929 print("Unknown : %s" % (self['Unknown3'].decode('utf-16le')))
930 except UnicodeDecodeError:
931 print("Unknown : %s" % (self['Unknown3'].decode('latin-1')))
933 print()
934 for entry in self.attributes: 934 ↛ 935line 934 didn't jump to line 935, because the loop on line 934 never started
935 entry.dump()
937ALG_ID = '<L=0'
939class P_BACKUP_KEY(Structure):
940 structure = (
941 ('Version', '<L=0'),
942 ('Data', ':'),
943 )
945class PREFERRED_BACKUP_KEY(Structure):
946 structure = (
947 ('Version', '<L=0'),
948 ('KeyLength', '<L=0'),
949 ('CertificateLength', '<L=0'),
950 ('Data', ':'),
951 )
953class PVK_FILE_HDR(Structure):
954 structure = (
955 ('dwMagic', '<L=0'),
956 ('dwVersion', '<L=0'),
957 ('dwKeySpec', '<L=0'),
958 ('dwEncryptType', '<L=0'),
959 ('cbEncryptData', '<L=0'),
960 ('cbPvk', '<L=0'),
961 )
963class PUBLICKEYSTRUC(Structure):
964 structure = (
965 ('bType', '<B=0'),
966 ('bVersion', '<B=0'),
967 ('reserved', '<H=0'),
968 ('aiKeyAlg', ALG_ID),
969 )
971class RSAPUBKEY(Structure):
972 structure = (
973 ('magic', '<L=0'),
974 ('bitlen', '<L=0'),
975 ('pubexp', '<L=0'),
976 )
978class PUBLIC_KEY_BLOB(Structure):
979 structure = (
980 ('publickeystruc', ':', PUBLICKEYSTRUC),
981 ('rsapubkey', ':', RSAPUBKEY),
982 ('_modulus', '_-modulus', 'self["rsapubkey"]["bitlen"] // 8'),
983 )
985class PRIVATE_KEY_BLOB(Structure):
986 structure = (
987 ('publickeystruc', ':', PUBLICKEYSTRUC),
988 ('rsapubkey', ':', RSAPUBKEY),
989 ('_modulus', '_-modulus', 'self["rsapubkey"]["bitlen"] // 8'),
990 ('modulus', ':'),
991 ('_prime1', '_-prime1', 'self["rsapubkey"]["bitlen"] // 16'),
992 ('prime1', ':'),
993 ('_prime2', '_-prime2', 'self["rsapubkey"]["bitlen"] // 16'),
994 ('prime2', ':'),
995 ('_exponent1', '_-exponent1', 'self["rsapubkey"]["bitlen"] // 16'),
996 ('exponent1', ':'),
997 ('_exponent2', '_-exponent2', 'self["rsapubkey"]["bitlen"] // 16'),
998 ('exponent2', ':'),
999 ('_coefficient', '_-coefficient', 'self["rsapubkey"]["bitlen"] // 16'),
1000 ('coefficient', ':'),
1001 ('_privateExponent', '_-privateExponent', 'self["rsapubkey"]["bitlen"] // 8'),
1002 ('privateExponent', ':'),
1003 )
1005class SIMPLE_KEY_BLOB(Structure):
1006 structure = (
1007 ('publickeystruc', ':', PUBLICKEYSTRUC),
1008 ('algid', ALG_ID),
1009 ('encryptedkey', ':'),
1010 )
1012class DPAPI_DOMAIN_RSA_MASTER_KEY(Structure):
1013 structure = (
1014 ('cbMasterKey', '<L=0'),
1015 ('cbSuppKey', '<L=0'),
1016 ('buffer', ':'),
1017 )
1019def privatekeyblob_to_pkcs1(key):
1020 '''
1021 parse private key into pkcs#1 format
1022 :param key:
1023 :return:
1024 '''
1025 modulus = bytes_to_long(key['modulus'][::-1]) # n
1026 prime1 = bytes_to_long(key['prime1'][::-1]) # p
1027 prime2 = bytes_to_long(key['prime2'][::-1]) # q
1028 exp1 = bytes_to_long(key['exponent1'][::-1])
1029 exp2 = bytes_to_long(key['exponent2'][::-1])
1030 coefficient = bytes_to_long(key['coefficient'][::-1])
1031 privateExp = bytes_to_long(key['privateExponent'][::-1]) # d
1032 if PY3:
1033 long = int
1034 pubExp = long(key['rsapubkey']['pubexp']) # e
1035 # RSA.Integer(prime2).inverse(prime1) # u
1037 r = RSA.construct((modulus, pubExp, privateExp, prime1, prime2))
1038 return r