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