6 DPAPI access library (http://msdn.microsoft.com/en-us/library/ms995355.aspx)
7 This file uses code originally created by Crusher Joe:
8 http://article.gmane.org/gmane.comp.python.ctypes/420
9 And modified by Wayne Koorts:
10 http://stackoverflow.com/questions/463832/using-dpapi-with-python
13 from ctypes
import windll
, byref
, cdll
, Structure
, POINTER
, c_char
, c_buffer
14 from ctypes
.wintypes
import DWORD
15 from waflib
.Configure
import conf
17 LocalFree
= windll
.kernel32
.LocalFree
18 memcpy
= cdll
.msvcrt
.memcpy
19 CryptProtectData
= windll
.crypt32
.CryptProtectData
20 CryptUnprotectData
= windll
.crypt32
.CryptUnprotectData
21 CRYPTPROTECT_UI_FORBIDDEN
= 0x01
23 extra_entropy
= 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'.encode('ascii')
24 except AttributeError:
25 extra_entropy
= 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'
27 class DATA_BLOB(Structure
):
30 ('pbData', POINTER(c_char
))
33 def get_data(blob_out
):
34 cbData
= int(blob_out
.cbData
)
35 pbData
= blob_out
.pbData
36 buffer = c_buffer(cbData
)
37 memcpy(buffer, pbData
, cbData
)
42 def dpapi_encrypt_data(self
, input_bytes
, entropy
= extra_entropy
):
44 Encrypts data and returns byte string
46 :param input_bytes: The data to be encrypted
47 :type input_bytes: String or Bytes
48 :param entropy: Extra entropy to add to the encryption process (optional)
49 :type entropy: String or Bytes
51 if not isinstance(input_bytes
, bytes
) or not isinstance(entropy
, bytes
):
52 self
.fatal('The inputs to dpapi must be bytes')
53 buffer_in
= c_buffer(input_bytes
, len(input_bytes
))
54 buffer_entropy
= c_buffer(entropy
, len(entropy
))
55 blob_in
= DATA_BLOB(len(input_bytes
), buffer_in
)
56 blob_entropy
= DATA_BLOB(len(entropy
), buffer_entropy
)
57 blob_out
= DATA_BLOB()
59 if CryptProtectData(byref(blob_in
), 'python_data', byref(blob_entropy
),
60 None, None, CRYPTPROTECT_UI_FORBIDDEN
, byref(blob_out
)):
61 return get_data(blob_out
)
63 self
.fatal('Failed to decrypt data')
66 def dpapi_decrypt_data(self
, encrypted_bytes
, entropy
= extra_entropy
):
68 Decrypts data and returns byte string
70 :param encrypted_bytes: The encrypted data
71 :type encrypted_bytes: Bytes
72 :param entropy: Extra entropy to add to the encryption process (optional)
73 :type entropy: String or Bytes
75 if not isinstance(encrypted_bytes
, bytes
) or not isinstance(entropy
, bytes
):
76 self
.fatal('The inputs to dpapi must be bytes')
77 buffer_in
= c_buffer(encrypted_bytes
, len(encrypted_bytes
))
78 buffer_entropy
= c_buffer(entropy
, len(entropy
))
79 blob_in
= DATA_BLOB(len(encrypted_bytes
), buffer_in
)
80 blob_entropy
= DATA_BLOB(len(entropy
), buffer_entropy
)
81 blob_out
= DATA_BLOB()
82 if CryptUnprotectData(byref(blob_in
), None, byref(blob_entropy
), None,
83 None, CRYPTPROTECT_UI_FORBIDDEN
, byref(blob_out
)):
84 return get_data(blob_out
)
86 self
.fatal('Failed to decrypt data')