3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
10 DPAPI access library (http://msdn.microsoft.com/en-us/library/ms995355.aspx)
11 This file uses code originally created by Crusher Joe:
12 http://article.gmane.org/gmane.comp.python.ctypes/420
13 And modified by Wayne Koorts:
14 http://stackoverflow.com/questions/463832/using-dpapi-with-python
17 from ctypes import windll, byref, cdll, Structure, POINTER, c_char, c_buffer
18 from ctypes.wintypes import DWORD
19 from waflib.Configure import conf
21 LocalFree = windll.kernel32.LocalFree
22 memcpy = cdll.msvcrt.memcpy
23 CryptProtectData = windll.crypt32.CryptProtectData
24 CryptUnprotectData = windll.crypt32.CryptUnprotectData
25 CRYPTPROTECT_UI_FORBIDDEN = 0x01
27 extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'.encode('ascii')
28 except AttributeError:
29 extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'
31 class DATA_BLOB(Structure):
34 ('pbData', POINTER(c_char))
37 def get_data(blob_out):
38 cbData = int(blob_out.cbData)
39 pbData = blob_out.pbData
40 buffer = c_buffer(cbData)
41 memcpy(buffer, pbData, cbData)
46 def dpapi_encrypt_data(self, input_bytes, entropy = extra_entropy):
48 Encrypts data and returns byte string
50 :param input_bytes: The data to be encrypted
51 :type input_bytes: String or Bytes
52 :param entropy: Extra entropy to add to the encryption process (optional)
53 :type entropy: String or Bytes
55 if not isinstance(input_bytes, bytes) or not isinstance(entropy, bytes):
56 self.fatal('The inputs to dpapi must be bytes')
57 buffer_in = c_buffer(input_bytes, len(input_bytes))
58 buffer_entropy = c_buffer(entropy, len(entropy))
59 blob_in = DATA_BLOB(len(input_bytes), buffer_in)
60 blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
61 blob_out = DATA_BLOB()
63 if CryptProtectData(byref(blob_in), 'python_data', byref(blob_entropy),
64 None, None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
65 return get_data(blob_out)
67 self.fatal('Failed to decrypt data')
70 def dpapi_decrypt_data(self, encrypted_bytes, entropy = extra_entropy):
72 Decrypts data and returns byte string
74 :param encrypted_bytes: The encrypted data
75 :type encrypted_bytes: Bytes
76 :param entropy: Extra entropy to add to the encryption process (optional)
77 :type entropy: String or Bytes
79 if not isinstance(encrypted_bytes, bytes) or not isinstance(entropy, bytes):
80 self.fatal('The inputs to dpapi must be bytes')
81 buffer_in = c_buffer(encrypted_bytes, len(encrypted_bytes))
82 buffer_entropy = c_buffer(entropy, len(entropy))
83 blob_in = DATA_BLOB(len(encrypted_bytes), buffer_in)
84 blob_entropy = DATA_BLOB(len(entropy), buffer_entropy)
85 blob_out = DATA_BLOB()
86 if CryptUnprotectData(byref(blob_in), None, byref(blob_entropy), None,
87 None, CRYPTPROTECT_UI_FORBIDDEN, byref(blob_out)):
88 return get_data(blob_out)
90 self.fatal('Failed to decrypt data')