1#! /usr/bin/env python 2# encoding: utf-8 3# Matt Clarkson, 2012 4 5''' 6DPAPI access library (http://msdn.microsoft.com/en-us/library/ms995355.aspx) 7This file uses code originally created by Crusher Joe: 8http://article.gmane.org/gmane.comp.python.ctypes/420 9And modified by Wayne Koorts: 10http://stackoverflow.com/questions/463832/using-dpapi-with-python 11''' 12 13from ctypes import windll, byref, cdll, Structure, POINTER, c_char, c_buffer 14from ctypes.wintypes import DWORD 15from waflib.Configure import conf 16 17LocalFree = windll.kernel32.LocalFree 18memcpy = cdll.msvcrt.memcpy 19CryptProtectData = windll.crypt32.CryptProtectData 20CryptUnprotectData = windll.crypt32.CryptUnprotectData 21CRYPTPROTECT_UI_FORBIDDEN = 0x01 22try: 23 extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd'.encode('ascii') 24except AttributeError: 25 extra_entropy = 'cl;ad13 \0al;323kjd #(adl;k$#ajsd' 26 27class DATA_BLOB(Structure): 28 _fields_ = [ 29 ('cbData', DWORD), 30 ('pbData', POINTER(c_char)) 31 ] 32 33def 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) 38 LocalFree(pbData) 39 return buffer.raw 40 41@conf 42def dpapi_encrypt_data(self, input_bytes, entropy = extra_entropy): 43 ''' 44 Encrypts data and returns byte string 45 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 50 ''' 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() 58 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) 62 else: 63 self.fatal('Failed to decrypt data') 64 65@conf 66def dpapi_decrypt_data(self, encrypted_bytes, entropy = extra_entropy): 67 ''' 68 Decrypts data and returns byte string 69 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 74 ''' 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) 85 else: 86 self.fatal('Failed to decrypt data') 87 88