dpapi.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # Matt Clarkson, 2012
  4. '''
  5. DPAPI access library (http://msdn.microsoft.com/en-us/library/ms995355.aspx)
  6. This file uses code originally created by Crusher Joe:
  7. http://article.gmane.org/gmane.comp.python.ctypes/420
  8. And modified by Wayne Koorts:
  9. http://stackoverflow.com/questions/463832/using-dpapi-with-python
  10. '''
  11. from ctypes import windll, byref, cdll, Structure, POINTER, c_char, c_buffer
  12. from ctypes.wintypes import DWORD
  13. from waflib.Configure import conf
  14. LocalFree = windll.kernel32.LocalFree
  15. memcpy = cdll.msvcrt.memcpy
  16. CryptProtectData = windll.crypt32.CryptProtectData
  17. CryptUnprotectData = windll.crypt32.CryptUnprotectData
  18. CRYPTPROTECT_UI_FORBIDDEN = 0x01
  19. try:
  20. extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'.encode('ascii')
  21. except AttributeError:
  22. extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'
  23. class DATA_BLOB(Structure):
  24. _fields_ = [
  25. ('cbData', DWORD),
  26. ('pbData', POINTER(c_char))
  27. ]
  28. def get_data(blob_out):
  29. cbData = int(blob_out.cbData)
  30. pbData = blob_out.pbData
  31. buffer = c_buffer(cbData)
  32. memcpy(buffer, pbData, cbData)
  33. LocalFree(pbData)
  34. return buffer.raw
  35. @conf
  36. def dpapi_encrypt_data(self, input_bytes, entropy = extra_entropy):
  37. '''
  38. Encrypts data and returns byte string
  39. :param input_bytes: The data to be encrypted
  40. :type input_bytes: String or Bytes
  41. :param entropy: Extra entropy to add to the encryption process (optional)
  42. :type entropy: String or Bytes
  43. '''
  44. if not isinstance(input_bytes, bytes) or not isinstance(entropy, bytes):
  45. self.fatal('The inputs to dpapi must be bytes')
  46. buffer_in = c_buffer(input_bytes, len(input_bytes))
  47. buffer_entropy = c_buffer(entropy, len(entropy))
  48. blob_in = DATA_BLOB(len(input_bytes), buffer_in)
  49. blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
  50. blob_out = DATA_BLOB()
  51. if CryptProtectData(byref(blob_in), 'python_data', byref(blob_entropy),
  52. None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
  53. return get_data(blob_out)
  54. else:
  55. self.fatal('Failed to decrypt data')
  56. @conf
  57. def dpapi_decrypt_data(self, encrypted_bytes, entropy = extra_entropy):
  58. '''
  59. Decrypts data and returns byte string
  60. :param encrypted_bytes: The encrypted data
  61. :type encrypted_bytes: Bytes
  62. :param entropy: Extra entropy to add to the encryption process (optional)
  63. :type entropy: String or Bytes
  64. '''
  65. if not isinstance(encrypted_bytes, bytes) or not isinstance(entropy, bytes):
  66. self.fatal('The inputs to dpapi must be bytes')
  67. buffer_in = c_buffer(encrypted_bytes, len(encrypted_bytes))
  68. buffer_entropy = c_buffer(entropy, len(entropy))
  69. blob_in = DATA_BLOB(len(encrypted_bytes), buffer_in)
  70. blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
  71. blob_out = DATA_BLOB()
  72. if CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None,
  73. None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
  74. return get_data(blob_out)
  75. else:
  76. self.fatal('Failed to decrypt data')