text: # SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
  #
  # This software is provided under under a slightly modified version
  # of the Apache Software License. See the accompanying LICENSE file
  # for more information.
  #
  # Author:
  #  Alberto Solino (@agsolino)
  #
  # Description:
  #       DPAPI and Windows Vault parsing structures and manipulation
  #
  # References: All of the work done by these guys. I just adapted their work to my needs.
  #       https://www.passcape.com/index.php?section=docsys&cmd=details&id=28
  #       https://github.com/jordanbtucker/dpapick
  #       https://github.com/gentilkiwi/mimikatz/wiki/howto-~-credential-manager-saved-credentials (and everything else Ben did )
  #       http://blog.digital-forensics.it/2016/01/windows-revaulting.html
  #       https://www.passcape.com/windows_password_recovery_vault_explorer
  #       https://www.passcape.com/windows_password_recovery_dpapi_master_key
  #
  from __future__ import division
  from __future__ import print_function
  import sys
  
  from struct import unpack
  from datetime import datetime
  from binascii import unhexlify, hexlify
  from struct import pack
  from Cryptodome.Hash import HMAC, SHA512, SHA1
  from Cryptodome.Cipher import AES, DES3
  from Cryptodome.Util.Padding import unpad
  from Cryptodome.PublicKey import RSA
  from Cryptodome.Util.number import bytes_to_long
  from six import PY3
  
  from impacket.ese import getUnixTime
  from impacket.structure import Structure, hexdump
  from impacket.uuid import bin_to_string
  from impacket.dcerpc.v5.enum import Enum
  from impacket.dcerpc.v5.dtypes import RPC_SID
  
  # Algorithm classes
  ALG_CLASS_ANY                   = (0)
  ALG_CLASS_SIGNATURE             = (1 << 13)
  ALG_CLASS_MSG_ENCRYPT           = (2 << 13)
  ALG_CLASS_DATA_ENCRYPT          = (3 << 13)
  ALG_CLASS_HASH                  = (4 << 13)
  ALG_CLASS_KEY_EXCHANGE          = (5 << 13)
  ALG_CLASS_ALL                   = (7 << 13)
  
  # Algorithm types
  ALG_TYPE_ANY                    = (0)
  ALG_TYPE_DSS                    = (1 << 9)
  ALG_TYPE_RSA                    = (2 << 9)
  ALG_TYPE_BLOCK                  = (3 << 9)
  ALG_TYPE_STREAM                 = (4 << 9)
  ALG_TYPE_DH                     = (5 << 9)
  ALG_TYPE_SECURECHANNEL          = (6 << 9)
  ALG_SID_ANY                     = (0)
  ALG_SID_RSA_ANY                 = 0
  ALG_SID_RSA_PKCS                = 1
  ALG_SID_RSA_MSATWORK            = 2
  ALG_SID_RSA_ENTRUST             = 3
  ALG_SID_RSA_PGP                 = 4
  ALG_SID_DSS_ANY                 = 0
  ALG_SID_DSS_PKCS                = 1
  ALG_SID_DSS_DMS                 = 2
  ALG_SID_ECDSA                   = 3
  
  # Block cipher sub ids
  ALG_SID_DES                     = 1
  ALG_SID_3DES                    = 3
  ALG_SID_DESX                    = 4
  ALG_SID_IDEA                    = 5
  ALG_SID_CAST                    = 6
  ALG_SID_SAFERSK64               = 7
  ALG_SID_SAFERSK128              = 8
  ALG_SID_3DES_112                = 9
  ALG_SID_CYLINK_MEK              = 12
  ALG_SID_RC5                     = 13
  ALG_SID_AES_128                 = 14
  ALG_SID_AES_192                 = 15
  ALG_SID_AES_256                 = 16
  ALG_SID_AES                     = 17
  ALG_SID_SKIPJACK                = 10
  ALG_SID_TEK                     = 11
  
  CRYPT_MODE_CBCI                 = 6       # ANSI CBC Interleaved
  CRYPT_MODE_CFBP                 = 7       # ANSI CFB Pipelined
  CRYPT_MODE_OFBP                 = 8       # ANSI OFB Pipelined
  CRYPT_MODE_CBCOFM               = 9       # ANSI CBC + OF Masking
  CRYPT_MODE_CBCOFMI              = 10      # ANSI CBC + OFM Interleaved
  
  ALG_SID_RC2                     = 2
  ALG_SID_RC4                     = 1
  ALG_SID_SEAL                    = 2
  
  # Diffie - Hellman sub - ids
  ALG_SID_DH_SANDF                = 1
  ALG_SID_DH_EPHEM                = 2
  ALG_SID_AGREED_KEY_ANY          = 3
  ALG_SID_KEA                     = 4
  ALG_SID_ECDH                    = 5
  
  # Hash sub ids
  ALG_SID_MD2                     = 1
  ALG_SID_MD4                     = 2
  ALG_SID_MD5                     = 3
  ALG_SID_SHA                     = 4
  ALG_SID_SHA1                    = 4
  ALG_SID_MAC                     = 5
  ALG_SID_RIPEMD                  = 6
  ALG_SID_RIPEMD160               = 7
  ALG_SID_SSL3SHAMD5              = 8
  ALG_SID_HMAC                    = 9
  ALG_SID_TLS1PRF                 = 10
  ALG_SID_HASH_REPLACE_OWF        = 11
  ALG_SID_SHA_256                 = 12
  ALG_SID_SHA_384                 = 13
  ALG_SID_SHA_512                 = 14
  
  # secure channel sub ids
  ALG_SID_SSL3_MASTER             = 1
  ALG_SID_SCHANNEL_MASTER_HASH    = 2
  ALG_SID_SCHANNEL_MAC_KEY        = 3
  ALG_SID_PCT1_MASTER             = 4
  ALG_SID_SSL2_MASTER             = 5
  ALG_SID_TLS1_MASTER             = 6
  ALG_SID_SCHANNEL_ENC_KEY        = 7
  ALG_SID_ECMQV                   = 1
  
  def getFlags(myenum, flags):
      return '|'.join([name for name, member in myenum.__members__.items() if member.value & flags])
  
  class FLAGS(Enum):
      CRYPTPROTECT_UI_FORBIDDEN = 0x1
      CRYPTPROTECT_LOCAL_MACHINE = 0x4
      CRYPTPROTECT_CRED_SYNC = 0x8
      CRYPTPROTECT_AUDIT = 0x10
      CRYPTPROTECT_VERIFY_PROTECTION = 0x40
      CRYPTPROTECT_CRED_REGENERATE = 0x80
      CRYPTPROTECT_SYSTEM = 0x20000000
  
  # algorithm identifier definitions
  class ALGORITHMS(Enum):
      CALG_MD2                = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD2)
      CALG_MD4                = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD4)
      CALG_MD5                = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_MD5)
      CALG_SHA                = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA)
      CALG_SHA1               = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA1)
      CALG_RSA_SIGN           = (ALG_CLASS_SIGNATURE | ALG_TYPE_RSA | ALG_SID_RSA_ANY)
      CALG_DSS_SIGN           = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_DSS_ANY)
      CALG_NO_SIGN            = (ALG_CLASS_SIGNATURE | ALG_TYPE_ANY | ALG_SID_ANY)
      CALG_RSA_KEYX           = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_RSA|ALG_SID_RSA_ANY)
      CALG_DES                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DES)
      CALG_3DES_112           = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES_112)
      CALG_3DES               = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_3DES)
      CALG_DESX               = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_DESX)
      CALG_RC2                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC2)
      CALG_RC4                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_RC4)
      CALG_SEAL               = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_STREAM|ALG_SID_SEAL)
      CALG_DH_SF              = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_SANDF)
      CALG_DH_EPHEM           = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_DH_EPHEM)
      CALG_AGREEDKEY_ANY      = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_AGREED_KEY_ANY)
      CALG_KEA_KEYX           = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_DH|ALG_SID_KEA)
      CALG_HUGHES_MD5         = (ALG_CLASS_KEY_EXCHANGE|ALG_TYPE_ANY|ALG_SID_MD5)
      CALG_SKIPJACK           = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_SKIPJACK)
      CALG_TEK                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_TEK)
      CALG_SSL3_SHAMD5        = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5)
      CALG_SSL3_MASTER        = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL3_MASTER)
      CALG_SCHANNEL_MASTER_HASH   = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MASTER_HASH)
      CALG_SCHANNEL_MAC_KEY   = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_MAC_KEY)
      CALG_SCHANNEL_ENC_KEY   = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SCHANNEL_ENC_KEY)
      CALG_PCT1_MASTER        = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_PCT1_MASTER)
      CALG_SSL2_MASTER        = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_SSL2_MASTER)
      CALG_TLS1_MASTER        = (ALG_CLASS_MSG_ENCRYPT|ALG_TYPE_SECURECHANNEL|ALG_SID_TLS1_MASTER)
      CALG_RC5                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_RC5)
      CALG_HMAC               = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC)
      CALG_TLS1PRF            = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_TLS1PRF)
      CALG_HASH_REPLACE_OWF   = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HASH_REPLACE_OWF)
      CALG_AES_128            = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_128)
      CALG_AES_192            = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_192)
      CALG_AES_256            = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES_256)
      CALG_AES                = (ALG_CLASS_DATA_ENCRYPT|ALG_TYPE_BLOCK|ALG_SID_AES)
      CALG_SHA_256            = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_256)
      CALG_SHA_384            = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_384)
      CALG_SHA_512            = (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SHA_512)
      CALG_ECDH               = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_DH | ALG_SID_ECDH)
      CALG_ECMQV              = (ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_ANY | ALG_SID_ECMQV)
      CALG_ECDSA              = (ALG_CLASS_SIGNATURE | ALG_TYPE_DSS | ALG_SID_ECDSA)
  
  class CREDENTIAL_FLAGS(Enum):
      CRED_FLAGS_PASSWORD_FOR_CERT = 0x1
      CRED_FLAGS_PROMPT_NOW = 0x2
      CRED_FLAGS_USERNAME_TARGET = 0x4
      CRED_FLAGS_OWF_CRED_BLOB = 0x8
      CRED_FLAGS_REQUIRE_CONFIRMATION = 0x10
      CRED_FLAGS_WILDCARD_MATCH = 0x20
      CRED_FLAGS_VSM_PROTECTED = 0x40
      CRED_FLAGS_NGC_CERT = 0x80
  
  class CREDENTIAL_TYPE(Enum):
      CRED_TYPE_GENERIC = 0x1
      CRED_TYPE_DOMAIN_PASSWORD = 0x2
      CRED_TYPE_DOMAIN_CERTIFICATE = 0x3
      CRED_TYPE_DOMAIN_VISIBLE_PASSWORD = 0x4
      CRED_TYPE_GENERIC_CERTIFICATE = 0x5
      CRED_TYPE_DOMAIN_EXTENDED = 0x6
      CRED_TYPE_MAXIMUM = 0x7
      CRED_TYPE_MAXIMUM_EX = 0x8
  
  class CREDENTIAL_PERSIST(Enum):
      CRED_PERSIST_NONE = 0x0
      CRED_PERSIST_SESSION = 0x1
      CRED_PERSIST_LOCAL_MACHINE = 0x2
      CRED_PERSIST_ENTERPRISE = 0x3
  
  ALGORITHMS_DATA = {
      # Algorithm: key/SaltLen, CryptHashModule, Mode, IVLen, BlockSize
      ALGORITHMS.CALG_SHA.value: (160//8, SHA1, None, None, 512//8),
      ALGORITHMS.CALG_HMAC.value: (160//8, SHA512, None, None, 512//8),
      ALGORITHMS.CALG_3DES.value: (192//8, DES3, DES3.MODE_CBC, 64//8),
      ALGORITHMS.CALG_SHA_512.value: (128//8, SHA512, None, None, 1024//8),
      ALGORITHMS.CALG_AES_256.value: (256//8, AES, AES.MODE_CBC, 128//8),
  }
  
  class MasterKeyFile(Structure):
      structure = (
          ('Version', '<L=0'),
          ('unk1', '<L=0'),
          ('unk2', '<L=0'),
          ('Guid', "72s=b''"),
          ('Unkown', '<L=0'),
          ('Policy', '<L=0'),
          ('Flags', '<L=0'),
          ('MasterKeyLen', '<Q=0'),
          ('BackupKeyLen', '<Q=0'),
          ('CredHistLen', '<Q=0'),
          ('DomainKeyLen', '<Q=0'),
      )
  
      def dump(self):
          print("[MASTERKEYFILE]")
          print("Version     : %8x (%d)" % (self['Version'], self['Version']))
          print("Guid        : %s" % self['Guid'].decode('utf-16le'))
          print("Flags       : %8x (%d)" % (self['Flags'], self['Flags']))
          print("Policy      : %8x (%d)" % (self['Policy'], self['Policy']))
          print("MasterKeyLen: %.8x (%d)" % (self['MasterKeyLen'], self['MasterKeyLen']))
          print("BackupKeyLen: %.8x (%d)" % (self['BackupKeyLen'], self['BackupKeyLen']))
          print("CredHistLen : %.8x (%d)" % (self['CredHistLen'], self['CredHistLen']))
          print("DomainKeyLen: %.8x (%d)" % (self['DomainKeyLen'], self['DomainKeyLen']))
          print()
  
  class MasterKey(Structure):
      structure = (
          ('Version', '<L=0'),
          ('Salt', '16s=b""'),
          ('MasterKeyIterationCount', '<L=0'),
          ('HashAlgo', "<L=0"),
          ('CryptAlgo', '<L=0'),
          ('data', ':'),
      )
  
      def __init__(self, data = None, alignment = 0):
          Structure.__init__(self, data, alignment)
          self.decryptedKey = None
  
      def dump(self):
          print("[MASTERKEY]")
          print("Version     : %8x (%d)" % (self['Version'], self['Version']))
          print("Salt        : %s" % hexlify(self['Salt']))
          print("Rounds      : %8x (%d)" % (self['MasterKeyIterationCount'], self['MasterKeyIterationCount']))
          print("HashAlgo    : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name))
          print("CryptAlgo   : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
          print("data        : %s" % (hexlify(self['data'])))
          print()
  
      def deriveKey(self, passphrase, salt, keylen, count, hashFunction):
          keyMaterial = b""
          i = 1
          while len(keyMaterial) < keylen:
              U = salt + pack("!L", i)
              i += 1
              derived = bytearray(hashFunction(passphrase, U))
              for r in range(count - 1):
                  actual = bytearray(hashFunction(passphrase, derived))
                  if PY3:
                      derived = (int.from_bytes(derived, sys.byteorder) ^ int.from_bytes(actual, sys.byteorder)).to_bytes(len(actual), sys.byteorder)
                  else:
                      derived = bytearray([ chr((a) ^ (b)) for (a,b) in zip(derived, actual) ])
              keyMaterial += derived
  
          return keyMaterial[:keylen]
  
      def decrypt(self, key):
          if self['HashAlgo'] == ALGORITHMS.CALG_HMAC.value:
              hashModule = SHA1
          else:
              hashModule = ALGORITHMS_DATA[self['HashAlgo']][1]
  
          prf = lambda p, s: HMAC.new(p, s, hashModule).digest()
          derivedBlob = self.deriveKey(key, self['Salt'],
                                      ALGORITHMS_DATA[self['CryptAlgo']][0] + ALGORITHMS_DATA[self['CryptAlgo']][3],
                                      count=self['MasterKeyIterationCount'], hashFunction=prf)
  
          cryptKey = derivedBlob[:ALGORITHMS_DATA[self['CryptAlgo']][0]]
          iv = derivedBlob[ALGORITHMS_DATA[self['CryptAlgo']][0]:][:ALGORITHMS_DATA[self['CryptAlgo']][3]]
  
          cipher = ALGORITHMS_DATA[self['CryptAlgo']][1].new(cryptKey, mode = ALGORITHMS_DATA[self['CryptAlgo']][2], iv = iv)
          cleartext = cipher.decrypt(self['data'])
  
          decryptedKey = cleartext[-64:]
          hmacSalt = cleartext[:16]
          hmac = cleartext[16:][:ALGORITHMS_DATA[self['HashAlgo']][0]]
  
          hmacKey = HMAC.new(key, hmacSalt, hashModule).digest()
  
          hmacCalculated = HMAC.new(hmacKey, decryptedKey, hashModule ).digest()
  
          if hmacCalculated[:ALGORITHMS_DATA[self['HashAlgo']][0]] == hmac:
              self.decryptedKey = decryptedKey
              return decryptedKey
          else:
              return None
  
  class CredHist(Structure):
      structure = (
          ('Version', '<L=0'),
          ('Guid', "16s=b''"),
      )
      def dump(self):
          print("[CREDHIST]")
          print("Version       : %8x (%d)" % (self['Version'], self['Version']))
          print("Guid          : %s" % bin_to_string(self['Guid']))
          print()
  
  class DomainKey(Structure):
      structure = (
          ('Version', '<L=0'),
          ('SecretLen', '<L=0'),
          ('AccessCheckLen', '<L=0'),
          ('Guid', "16s=b"""),
          ('_SecretData', '_-SecretData', 'self["SecretLen"]'),
          ('SecretData', ':'),
          ('_AccessCheck', '_-AccessCheck', 'self["AccessCheckLen"]'),
          ('AccessCheck', ':'),
      )
      def dump(self):
          print("[DOMAINKEY]")
          print("Version       : %8x (%d)" % (self['Version'], self['Version']))
          print("Guid          : %s" % bin_to_string(self['Guid']))
          print("SecretLen     : %8x (%d)" % (self['SecretLen'], self['SecretLen']))
          print("AccessCheckLen: %.8x (%d)" % (self['AccessCheckLen'], self['AccessCheckLen']))
          print("SecretData    : %s" % (hexlify(self['SecretData'])))
          print("AccessCheck   : %s" % (hexlify(self['AccessCheck'])))
          print()
  
  class DPAPI_SYSTEM(Structure):
      structure = (
          ('Version', '<L=0'),
          ('MachineKey', '20s=b""'),
          ('UserKey', '20s=b""'),
      )
  
      def dump(self):
          print("[DPAPI_SYSTEM]")
          print("Version    : %8x (%d)" % (self['Version'], self['Version']))
          print("MachineKey : 0x%s" % hexlify(self['MachineKey']).decode('latin-1'))
          print("UserKey    : 0x%s" % hexlify(self['UserKey']).decode('latin-1'))
          print()
  
  class CredentialFile(Structure):
      structure = (
          ('Version', '<L=0'),
          ('Size', '<L=0'),
          ('Unknown', '<L=0'),
          ('_Data', '_-Data', 'self["Size"]'),
          ('Data', ':'),
      )
      #def dump(self):
      #    print("[CREDENTIAL FILE]")
      #    print("Version    : %8x (%d)" % (self['Version'], self['Version']))
      #    print("MachineKey : %s" % hexlify(self['MachineKey']))
      #    print("UserKey    : %s" % hexlify(self['UserKey']))
      #    print("CryptAlgo   : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
      #    print()
  
  
  class DPAPI_BLOB(Structure):
      structure = (
          ('Version', '<L=0'),
          ('GuidCredential', "16s=b"""),
          ('MasterKeyVersion', '<L=0'),
          ('GuidMasterKey', "16s=b"""),
          ('Flags', '<L=0'),
  
          ('DescriptionLen', '<L=0'),
          ('_Description', '_-Description', 'self["DescriptionLen"]'),
          ('Description', ':'),
  
          ('CryptAlgo', '<L=0'),
          ('CryptAlgoLen', '<L=0'),
  
          ('SaltLen', '<L=0'),
          ('_Salt', '_-Salt', 'self["SaltLen"]'),
          ('Salt', ':'),
  
          ('HMacKeyLen', '<L=0'),
          ('_HMacKey', '_-HMacKey', 'self["HMacKeyLen"]'),
          ('HMacKey', ':'),
  
          ('HashAlgo', '<L=0'),
          ('HashAlgoLen', '<L=0'),
  
          ('HMac', '<L=0'),
          ('_HMac', '_-HMac', 'self["HMac"]'),
          ('HMac', ':'),
  
          ('DataLen', '<L=0'),
          ('_Data', '_-Data', 'self["DataLen"]'),
          ('Data', ':'),
  
          ('SignLen', '<L=0'),
          ('_Sign', '_-Sign', 'self["SignLen"]'),
          ('Sign', ':'),
  
      )
  
      def dump(self):
          print("[BLOB]")
          print("Version          : %8x (%d)" % (self['Version'], self['Version']))
          print("Guid Credential  : %s" % bin_to_string(self['GuidCredential']))
          print("MasterKeyVersion : %8x (%d)" % (self['MasterKeyVersion'], self['MasterKeyVersion']))
          print("Guid MasterKey   : %s" % bin_to_string(self['GuidMasterKey']))
          print("Flags            : %8x (%s)" % (self['Flags'], getFlags(FLAGS, self['Flags'])))
          print("Description      : %s" % (self['Description'].decode('utf-16le')))
          print("CryptAlgo        : %.8x (%d) (%s)" % (self['CryptAlgo'], self['CryptAlgo'], ALGORITHMS(self['CryptAlgo']).name))
          print("Salt             : %s" % (hexlify(self['Salt'])))
          print("HMacKey          : %s" % (hexlify(self['HMacKey'])))
          print("HashAlgo         : %.8x (%d) (%s)" % (self['HashAlgo'], self['HashAlgo'], ALGORITHMS(self['HashAlgo']).name))
          print("HMac             : %s" % (hexlify(self['HMac'])))
          print("Data             : %s" % (hexlify(self['Data'])))
          print("Sign             : %s" % (hexlify(self['Sign'])))
          print()
  
  
      def deriveKey(self, sessionKey):
          def fixparity(deskey):
              from six import indexbytes, b
              temp = b''
              for i in range(len(deskey)):
                  t = (bin(indexbytes(deskey,i))[2:]).rjust(8,'0')
                  if t[:7].count('1') %2 == 0:
                      temp+= b(chr(int(t[:7]+'1',2)))
                  else:
                      temp+= b(chr(int(t[:7]+'0',2)))
              return temp
  
          if len(sessionKey) > ALGORITHMS_DATA[self['HashAlgo']][4]:
              derivedKey = HMAC.new(sessionKey,  digestmod = ALGORITHMS_DATA[self['HashAlgo']][1]).digest()
          else:
              derivedKey = sessionKey
  
  
          if len(derivedKey) < ALGORITHMS_DATA[self['CryptAlgo']][0]:
              # Extend the key
              derivedKey += b'\x00'*ALGORITHMS_DATA[self['HashAlgo']][4]
              ipad = bytearray([ i ^ 0x36 for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]])
              opad = bytearray([ i ^ 0x5c for i in bytearray(derivedKey)][:ALGORITHMS_DATA[self['HashAlgo']][4]])
              derivedKey = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad).digest() + \
                  ALGORITHMS_DATA[self['HashAlgo']][1].new(opad).digest()
              derivedKey = fixparity(derivedKey)
  
          return derivedKey
  
      def decrypt(self, key, entropy = None):
          keyHash = SHA1.new(key).digest()
          sessionKey = HMAC.new(keyHash, self['Salt'], ALGORITHMS_DATA[self['HashAlgo']][1])
          if entropy is not None:
              sessionKey.update(entropy)
  
          sessionKey = sessionKey.digest()
  
          # Derive the key
          derivedKey = self.deriveKey(sessionKey)
  
          cipher = ALGORITHMS_DATA[self['CryptAlgo']][1].new(derivedKey[:ALGORITHMS_DATA[self['CryptAlgo']][0]],
                                  mode=ALGORITHMS_DATA[self['CryptAlgo']][2], iv=b'\x00'*ALGORITHMS_DATA[self['CryptAlgo']][3])
          cleartext = unpad(cipher.decrypt(self['Data']), ALGORITHMS_DATA[self['CryptAlgo']][1].block_size)
  
          # Now check the signature
  
          # ToDo Fix this, it's just ugly, more testing so we can remove one
          toSign = (self.rawData[20:][:len(self.rawData)-20-len(self['Sign'])-4])
  
          # Calculate the different HMACKeys
          keyHash2 = keyHash + b"\x00"*ALGORITHMS_DATA[self['HashAlgo']][1].block_size
          ipad = bytearray([i ^ 0x36 for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size])
          opad = bytearray([i ^ 0x5c for i in bytearray(keyHash2)][:ALGORITHMS_DATA[self['HashAlgo']][1].block_size])
          a = ALGORITHMS_DATA[self['HashAlgo']][1].new(ipad)
          a.update(self['HMac'])
  
          hmacCalculated1 = ALGORITHMS_DATA[self['HashAlgo']][1].new(opad)
          hmacCalculated1.update(a.digest())
  
          if entropy is not None:
              hmacCalculated1.update(entropy)
  
          hmacCalculated1.update(toSign)
  
          hmacCalculated3 = HMAC.new(keyHash, self['HMac'], ALGORITHMS_DATA[self['HashAlgo']][1])
          if entropy is not None:
              hmacCalculated3.update(entropy)
  
          hmacCalculated3.update(toSign)
  
          if hmacCalculated1.digest() == self['Sign'] or hmacCalculated3.digest() == self['Sign']:
              return cleartext
          else:
              return None
  
  class VAULT_ATTRIBUTE(Structure):
      structure = (
          ('Id', '<L=0'),
          ('Unknown1', '<L=0'),
          ('Unknown2', '<L=0'),
          ('Unknown3', '<L=0'),
      )
  
      padding = (
          ('Pad', '6s=b""'),
      )
  
      id100 = (
          ('Unknown5', '<L=0'),
      )
  
      extended = (
          ('Size','<L=0'),
          ('IVPresent', '<B=?&IVSize'),
          ('IVSize', '<L=0'),
          ('_IV', '_-IV', 'self["IVSize"] if self["IVSize"] is not None else 0'),
          ('IV', ':'),
          ('_Data','_-Data', 'self["Size"]-self["IVSize"]-5 if self["IVPresent"] else self["Size"]-1'),
          ('Data',':'),
      )
      def __init__(self, data = None, alignment = 0):
          if len(data) > 20:
              if data[16:][:6] == b'\x00'*6:
                  self.structure += self.padding
              if unpack('<L',data[:4])[0] >= 100:
                  self.structure += self.id100
              if len(data[16:]) >= 9:
                  self.structure += self.extended
          Structure.__init__(self, data, alignment)
  
  
      def dump(self):
          print("[ATTRIBUTE %d]" % self['Id'])
          if len(self.rawData) > 28:
              print("Size   : 0x%x" % self['Size'])
              if self['IVPresent'] > 0:
                  print("IVSize : 0x%x" % self['IVSize'])
                  print("IV     : %s" % hexlify(self['IV']))
              print("Data   : %s" % hexlify(self['Data']))
  
  class VAULT_ATTRIBUTE_MAP_ENTRY(Structure):
      structure = (
          ('Id', '<L=0'),
          ('Offset', '<L=0'),
          ('Unknown1', '<L=0'),
      )
      def dump(self):
          print("[MAP ENTRY %d @ 0x%.8x]" % (self['Id'], self['Offset']))
  
  class VAULT_VCRD(Structure):
      structure = (
          ('SchemaGuid', "16s=b"""),
          ('Unknown0', '<L=0'),
          ('LastWritten', '<Q=0'),
          ('Unknown1', '<L=0'),
          ('Unknown2', '<L=0'),
          ('FriendlyNameLen', '<L=0'),
          ('FriendlyNameL', '_-FriendlyName', 'self["FriendlyNameLen"]'),
          ('FriendlyName', ':'),
          ('AttributesMapsSize', '<L=0'),
          ('AttributeL', '_-AttributeMaps', 'self["AttributesMapsSize"]'),
          ('AttributeMaps', ':'),
          ('Data', ':'),
      )
  
      def __init__(self, data = None, alignment = 0):
          Structure.__init__(self, data, alignment)
          if data is not None:
              # Process the MAP entries
              self.mapEntries = list()
              data = self['AttributeMaps']
              for i in range(self['AttributesMapsSize']//len(VAULT_ATTRIBUTE_MAP_ENTRY())):
                  entry = VAULT_ATTRIBUTE_MAP_ENTRY(data)
                  self.mapEntries.append(entry)
                  data = data[len(VAULT_ATTRIBUTE_MAP_ENTRY()):]
  
              self.attributesLen = list()
  
              for i in range(len(self.mapEntries)):
                  if i > 0:
                      self.attributesLen.append(self.mapEntries[i]['Offset']-self.mapEntries[i-1]['Offset'])
  
              self.attributesLen.append(len(self.rawData) - self.mapEntries[i]['Offset'] )
  
              self.attributes = list()
              for i, entry in enumerate(self.mapEntries):
                  attribute = VAULT_ATTRIBUTE(self.rawData[entry['Offset']:][:self.attributesLen[i]])
                  self.attributes.append(attribute)
  
              # Do we have remaining data?
              self['Data'] = self.rawData[self.mapEntries[-1]['Offset']+len(self.attributes[-1].getData()):]
  
      def dump(self):
          print("[VCRD]")
          print("SchemaGuid  : %s" % bin_to_string(self['SchemaGuid']))
          print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten']))))
          print("FriendlyName: %s" % (self['FriendlyName'].decode('utf-16le')))
          print()
          for i,entry in enumerate(self.mapEntries):
              entry.dump()
              self.attributes[i].dump()
          print()
          print("Remaining   : %s" % (hexlify(self['Data'])))
          print()
  
  class VAULT_VPOL(Structure):
      structure = (
          ('Version', '<L=0'),
          ('Guid', "16s=b"""),
          ('DescriptionLen', '<L=0'),
          ('_Description', '_-Description', 'self["DescriptionLen"]'),
          ('Description', ':'),
          ('Unknown', '12s=b""'),
          ('Size', '<L=0'),
          ('Guid2', "16s=b"""),
          ('Guid3', "16s=b"""),
          ('KeySize','<L=0'),
          ('_Blob', '_-Blob', 'self["KeySize"]'),
          ('Blob', ':', DPAPI_BLOB),
      )
  
      def dump(self):
          print("[VAULT_VPOL]")
          print("Version      : %8x (%d)" % (self['Version'], self['Version']))
          print("Guid         : %s" % bin_to_string(self['Guid']))
          print("Description  : %s" % (self['Description'].decode('utf-16le')))
          print("Size         : 0x%.8x (%d)" % (self['Size'], self['Size']))
          print("Guid2        : %s" % bin_to_string(self['Guid2']))
          print("Guid3        : %s" % bin_to_string(self['Guid3']))
          print("KeySize      : 0x%.8x (%d)" % (self['KeySize'], self['KeySize']))
          self['Blob'].dump()
          print()
  
  # from bcrypt.h
  class BCRYPT_KEY_DATA_BLOB_HEADER(Structure):
      structure = (
          ('dwMagic','<L=0'),
          ('dwVersion','<L=0'),
          ('cbKeyData','<L=0'),
          ('_bKey','_-bKey', 'self["cbKeyData"]'),
          ('bKey',':'),
      )
  
  # from https://media.defcon.org/DEF%20CON%2024/DEF%20CON%2024%20presentations/DEFCON-24-Jkambic-Cunning-With-Cng-Soliciting-Secrets-From-Schannel-WP.pdf
  class BCRYPT_KSSM_DATA_BLOB_HEADER(Structure):
      structure = (
          ('cbLength','<L=0'),
          ('dwKeyMagic','<L=0'),
          ('dwUnknown2','<L=0'),
          ('dwUnknown3','<L=0'),
          ('dwKeyBitLen','<L=0'),
          ('cbKeyLength','<L=0'),
          #('_bKey','_-bKey', 'self["cbKeyData"]'),
          #('AesKey','32s=""'),
          #('dwUnknown4','<L=0'),
          #('KeySchedule','448s=""'),
          #('dwUnknown5','<L=0'),
          #('cbScheduleLen','<L=0'),
          #('Unknown6','16s=""'),
      )
  
  class BCRYPT_KEY_WRAP(Structure):
      structureKDBM = (
          ('Size','<L=0'),
          ('Version','<L=0'),
          ('Unknown2','<L=0'),
          ('_bKeyBlob','_-bKeyBlob', 'self["Size"]'),
          ('bKeyBlob',':', BCRYPT_KEY_DATA_BLOB_HEADER),
      )
      structureKSSM = (
          ('Size','<L=0'),
          ('Version','<L=0'),
          ('Unknown2','<L=0'),
          ('_bKeyBlob','_-bKeyBlob', 'self["Size"]-8'),
          ('bKeyBlob',':'),
      )
      def __init__(self, data = None, alignment = 0):
          if len(data) >=16:
              if data[0:1] == b'\x24' or data[0:1] == b'\x34':
                  self.structure = self.structureKDBM
              else:
                  self.structure = self.structureKSSM
          Structure.__init__(self, data, alignment)
  
  class VAULT_VPOL_KEYS(Structure):
      structure = (
          ('Key1',':', BCRYPT_KEY_WRAP),
          ('Key2',':', BCRYPT_KEY_WRAP),
      )
      def dump(self):
          print("[VAULT_VPOL_KEYS]")
          if self['Key1']['Size'] > 0x24:
              print('Key1:')
              hexdump(self['Key1']['bKeyBlob'])
              print('Key2:')
              hexdump(self['Key2']['bKeyBlob'])
          else:
              print('Key1: 0x%s' % hexlify(self['Key1']['bKeyBlob']['bKey']).decode('latin-1'))
              print('Key2: 0x%s' % hexlify(self['Key2']['bKeyBlob']['bKey']).decode('latin-1'))
              print()
  
  class VAULT_INTERNET_EXPLORER(Structure):
      structure = (
          ('Version','<L=0'),
          ('Count','<L=0'),
          ('Unknown','<L=0'),
          ('Id1', '<L=0'),
          ('UsernameLen', '<L=0'),
          ('_Username', '_-Username','self["UsernameLen"]'),
          ('Username', ':'),
  
          ('Id2', '<L=0'),
          ('ResourceLen', '<L=0'),
          ('_Resource', '_-Resource', 'self["ResourceLen"]'),
          ('Resource', ':'),
  
          ('Id3', '<L=0'),
          ('PasswordLen', '<L=0'),
          ('_Password', '_-Password', 'self["PasswordLen"]'),
          ('Password', ':'),
      )
      def fromString(self, data):
          Structure.fromString(self, data)
  
      def dump(self):
          print("[Internet Explorer]")
          print('Username        : %s' % self['Username'].decode('utf-16le'))
          print('Resource        : %s' % self['Resource'].decode('utf-16le'))
          print('Password        : %s' % (hexlify(self['Password'])))
          print()
  
  class VAULT_WIN_BIO_KEY(Structure):
      structure = (
          ('Version','<L=0'),
          ('Count','<L=0'),
          ('Unknown','<L=0'),
          ('Id1', '<L=0'),
          ('SidLen', '<L=0'),
          ('_Sid', '_-Sid','self["SidLen"]'),
          ('Sid', ':'),
  
          ('Id2', '<L=0'),
          ('NameLen', '<L=0'),
          ('_Name', '_-Name', 'self["NameLen"]'),
          ('Name', ':'),
  
          ('Id3', '<L=0'),
          ('BioKeyLen', '<L=0'),
          ('_BioKey', '_-BioKey', 'self["BioKeyLen"]'),
          ('BioKey', ':'),
      )
      def fromString(self, data):
          Structure.fromString(self, data)
          if data is not None:
              bioKey = BCRYPT_KEY_DATA_BLOB_HEADER(unhexlify(self['BioKey'].decode('utf-16le')[:-1]))
              self['BioKey'] = bioKey
  
      def dump(self):
          print("[WINDOWS BIOMETRIC KEY]")
          print('Sid          : %s' % RPC_SID(b'\x05\x00\x00\x00'+self['Sid']).formatCanonical())
          print('Friendly Name: %s' % self['Name'].decode('utf-16le'))
          print('Biometric Key: 0x%s' % (hexlify(self['BioKey']['bKey'])).decode('latin-1'))
          print()
  
  class NGC_LOCAL_ACCOOUNT(Structure):
      structure = (
          ('Version','<L=0'),
          ('UnlockKeySize','<L=0'),
          ('IVSize','<L=0'),
          ('CipherTextSize','<L=0'),
          ('MustBeZeroTest','<L=0'),
          ('_UnlockKey', '_-UnlockKey', 'self["UnlockKeySize"]'),
          ('UnlockKey', ':'),
          ('_IV', '_-IV', 'self["IVSize"]'),
          ('IV', ':'),
          ('_CipherText', '_-CipherText', 'self["CipherTextSize"]'),
          ('CipherText', ':'),
      )
  #    def __init__(self, data=None, alignment = 0):
  #        hexdump(data)
      def dump(self):
          print("[NGC LOCAL ACCOOUNT]")
          print('UnlockKey    : %s' % hexlify(self["UnlockKey"]))
          print('IV           : %s' % hexlify(self["IV"]))
          print('CipherText   : %s' % hexlify(self["CipherText"]))
  
  class VAULT_NGC_ACCOOUNT(Structure):
      structure = (
          ('Version','<L=0'),
          ('Count','<L=0'),
          ('Unknown','<L=0'),
          ('Id1', '<L=0'),
          ('SidLen', '<L=0'),
          ('_Sid', '_-Sid','self["SidLen"]'),
          ('Sid', ':'),
  
          ('Id2', '<L=0'),
          ('NameLen', '<L=0'),
          ('_Name', '_-Name', 'self["NameLen"]'),
          ('Name', ':'),
  
          ('Id3', '<L=0'),
          ('BlobLen', '<L=0'),
          ('Blob', '_-Blob', 'self["BlobLen"]'),
          ('Blob', ':', NGC_LOCAL_ACCOOUNT),
      )
      def dump(self):
          print("[NGC VAULT]")
          print('Sid          : %s' % RPC_SID(b'\x05\x00\x00\x00'+self['Sid']).formatCanonical())
          print('Friendly Name: %s' % self['Name'].decode('utf-16le'))
          self['Blob'].dump()
          print()
  
  VAULT_KNOWN_SCHEMAS = {
      'WinBio Key': VAULT_WIN_BIO_KEY,
      'NGC Local Accoount Logon Vault Credential': VAULT_NGC_ACCOOUNT,
      'Internet Explorer': VAULT_INTERNET_EXPLORER,
  }
  
  class CREDENTIAL_ATTRIBUTE(Structure):
      # some info here https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credential_attributea
      structure = (
          ('Flags','<L=0'),
  
          ('KeyWordSize', '<L=0'),
          ('_KeyWord', '_-KeyWord', 'self["KeyWordSize"]'),
          ('KeyWord', ':'),
  
          ('DataSize', '<L=0'),
          ('_Data', '_-Data', 'self["DataSize"]'),
          ('Data', ':'),
      )
  
      def dump(self):
          print("KeyWord : %s" % (self['KeyWord'].decode('utf-16le')))
          #print("Flags   : %8x (%s)" % (self['Flags'], getFlags(CREDENTIAL_FLAGS, self['Flags'])))
          print("Data    : ")
          hexdump(self['Data'])
  
  class CREDENTIAL_BLOB(Structure):
      # some info here https://docs.microsoft.com/en-us/windows/desktop/api/wincred/ns-wincred-_credentiala
      structure = (
          ('Flags','<L=0'),
          ('Size','<L=0'),
          ('Unknown0','<L=0'),
          ('Type','<L=0'),
          ('Flags2','<L=0'),
          ('LastWritten','<Q=0'),
          ('Unknown2','<L=0'),
          ('Persist','<L=0'),
          ('AttrCount','<L=0'),
          ('Unknown3','<Q=0'),
  
          ('TargetSize','<L=0'),
          ('_Target','_-Target','self["TargetSize"]'),
          ('Target',':'),
  
          ('TargetAliasSize', '<L=0'),
          ('_TargetAliast', '_-TargetAlias', 'self["TargetAliasSize"]'),
          ('TargetAlias', ':'),
  
          ('DescriptionSize', '<L=0'),
          ('_Description', '_-Description', 'self["DescriptionSize"]'),
          ('Description', ':'),
  
          ('UnknownSize', '<L=0'),
          ('_Unknown', '_-Unknown', 'self["UnknownSize"]'),
          ('Unknown', ':'),
  
          ('UsernameSize', '<L=0'),
          ('_Username', '_-Username', 'self["UsernameSize"]'),
          ('Username', ':'),
  
          ('Unknown3Size', '<L=0'),
          ('_Unknown3', '_-Unknown3', 'self["Unknown3Size"]'),
          ('Unknown3', ':'),
  
          ('Remaining', ':'),
      )
      def __init__(self, data = None, alignment = 0):
          Structure.__init__(self, data, alignment)
          self.attributes = 0
          if data is not None:
              # Unpack the attributes
              remaining = self['Remaining']
              self.attributes = list()
              for i in range(self['AttrCount']):
                  attr = CREDENTIAL_ATTRIBUTE(remaining)
                  self.attributes.append(attr)
                  remaining = remaining[len(attr):]
  
      def dump(self):
          print("[CREDENTIAL]")
          print("LastWritten : %s" % (datetime.utcfromtimestamp(getUnixTime(self['LastWritten']))))
          print("Flags       : 0x%.8x (%s)" % (self['Flags'], getFlags(CREDENTIAL_FLAGS, self['Flags'])))
          print("Persist     : 0x%.8x (%s)" % (self['Persist'], CREDENTIAL_PERSIST(self['Persist']).name))
          print("Type        : 0x%.8x (%s)" % (self['Type'], CREDENTIAL_PERSIST(self['Type']).name))
          print("Target      : %s" % (self['Target'].decode('utf-16le')))
          print("Description : %s" % (self['Description'].decode('utf-16le')))
          print("Unknown     : %s" % (self['Unknown'].decode('utf-16le')))
          print("Username    : %s" % (self['Username'].decode('utf-16le')))
          try:
              print("Unknown     : %s" % (self['Unknown3'].decode('utf-16le')))
          except UnicodeDecodeError:
              print("Unknown     : %s" % (self['Unknown3'].decode('latin-1')))
  
          print()
          for entry in self.attributes:
              entry.dump()
  
  ALG_ID = '<L=0'
  
  class P_BACKUP_KEY(Structure):
      structure = (
          ('Version', '<L=0'),
          ('Data', ':'),
      )
  
  class PREFERRED_BACKUP_KEY(Structure):
      structure = (
          ('Version', '<L=0'),
          ('KeyLength', '<L=0'),
          ('CertificateLength', '<L=0'),
          ('Data', ':'),
      )
  
  class PVK_FILE_HDR(Structure):
      structure = (
          ('dwMagic', '<L=0'),
          ('dwVersion', '<L=0'),
          ('dwKeySpec', '<L=0'),
          ('dwEncryptType', '<L=0'),
          ('cbEncryptData', '<L=0'),
          ('cbPvk', '<L=0'),
      )
  
  class PUBLICKEYSTRUC(Structure):
      structure = (
          ('bType', '<B=0'),
          ('bVersion', '<B=0'),
          ('reserved', '<H=0'),
          ('aiKeyAlg', ALG_ID),
      )
  
  class RSAPUBKEY(Structure):
      structure = (
          ('magic', '<L=0'),
          ('bitlen', '<L=0'),
          ('pubexp', '<L=0'),
      )
  
  class PUBLIC_KEY_BLOB(Structure):
      structure = (
          ('publickeystruc', ':', PUBLICKEYSTRUC),
          ('rsapubkey', ':', RSAPUBKEY),
          ('_modulus', '_-modulus', 'self["rsapubkey"]["bitlen"] // 8'),
      )
  
  class PRIVATE_KEY_BLOB(Structure):
      structure = (
          ('publickeystruc', ':', PUBLICKEYSTRUC),
          ('rsapubkey', ':', RSAPUBKEY),
          ('_modulus', '_-modulus', 'self["rsapubkey"]["bitlen"] // 8'),
          ('modulus', ':'),
          ('_prime1', '_-prime1', 'self["rsapubkey"]["bitlen"] // 16'),
          ('prime1', ':'),
          ('_prime2', '_-prime2', 'self["rsapubkey"]["bitlen"] // 16'),
          ('prime2', ':'),
          ('_exponent1', '_-exponent1', 'self["rsapubkey"]["bitlen"] // 16'),
          ('exponent1', ':'),
          ('_exponent2', '_-exponent2', 'self["rsapubkey"]["bitlen"] // 16'),
          ('exponent2', ':'),
          ('_coefficient', '_-coefficient', 'self["rsapubkey"]["bitlen"] // 16'),
          ('coefficient', ':'),
          ('_privateExponent', '_-privateExponent', 'self["rsapubkey"]["bitlen"] // 8'),
          ('privateExponent', ':'),
      )
  
  class SIMPLE_KEY_BLOB(Structure):
      structure = (
          ('publickeystruc', ':', PUBLICKEYSTRUC),
          ('algid', ALG_ID),
          ('encryptedkey', ':'),
      )
  
  class DPAPI_DOMAIN_RSA_MASTER_KEY(Structure):
      structure = (
          ('cbMasterKey', '<L=0'),
          ('cbSuppKey', '<L=0'),
          ('buffer', ':'),
      )
  
  def privatekeyblob_to_pkcs1(key):
      '''
      parse private key into pkcs#1 format
      :param key:
      :return:
      '''
      modulus = bytes_to_long(key['modulus'][::-1]) # n
      prime1 = bytes_to_long(key['prime1'][::-1]) # p
      prime2 = bytes_to_long(key['prime2'][::-1]) # q
      exp1 = bytes_to_long(key['exponent1'][::-1])
      exp2 = bytes_to_long(key['exponent2'][::-1])
      coefficient = bytes_to_long(key['coefficient'][::-1])
      privateExp = bytes_to_long(key['privateExponent'][::-1]) # d
      if PY3:
          long = int
      pubExp = long(key['rsapubkey']['pubexp']) # e
      # RSA.Integer(prime2).inverse(prime1) # u
  
      r = RSA.construct((modulus, pubExp, privateExp, prime1, prime2))
      return r
  