39 lines
1.9 KiB
Python
39 lines
1.9 KiB
Python
"""Current-user Windows capability protection; no browser profile crypto state."""
|
|
import base64
|
|
import ctypes
|
|
import sys
|
|
|
|
class Blob(ctypes.Structure):
|
|
_fields_=[('size',ctypes.c_uint32),('data',ctypes.POINTER(ctypes.c_ubyte))]
|
|
|
|
def convert(data,protect):
|
|
crypt=ctypes.WinDLL('crypt32',use_last_error=True)
|
|
kernel=ctypes.WinDLL('kernel32',use_last_error=True)
|
|
allocation=(ctypes.c_ubyte*len(data)).from_buffer_copy(data)
|
|
source=Blob(len(data),allocation);result=Blob()
|
|
function=crypt.CryptProtectData if protect else crypt.CryptUnprotectData
|
|
function.argtypes=[ctypes.POINTER(Blob),ctypes.c_void_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.c_void_p,ctypes.c_uint32,ctypes.POINTER(Blob)]
|
|
function.restype=ctypes.c_int
|
|
# UI_FORBIDDEN; omit LOCAL_MACHINE so only the signed-in Windows user can decrypt.
|
|
if not function(ctypes.byref(source),None,None,None,None,1,ctypes.byref(result)):
|
|
raise ValueError('DPAPI rejected capability')
|
|
kernel.LocalFree.argtypes=[ctypes.c_void_p];kernel.LocalFree.restype=ctypes.c_void_p
|
|
try:return ctypes.string_at(result.data,result.size)
|
|
finally:kernel.LocalFree(result.data)
|
|
|
|
if __name__=='__main__':
|
|
try:
|
|
if sys.platform!='win32' or len(sys.argv)!=2 or sys.argv[1] not in ['protect','unprotect']: raise ValueError()
|
|
limit=32768 if sys.argv[1]=='protect' else 65536
|
|
data=sys.stdin.buffer.read(limit+1)
|
|
if not data or len(data)>limit:raise ValueError()
|
|
if sys.argv[1]=='protect':output=base64.b64encode(convert(data,True))
|
|
else:
|
|
decoded=base64.b64decode(data,validate=True)
|
|
if base64.b64encode(decoded)!=data:raise ValueError()
|
|
output=convert(decoded,False)
|
|
if len(output)>(65536 if sys.argv[1]=='protect' else 32768):raise ValueError()
|
|
sys.stdout.buffer.write(output)
|
|
except Exception:
|
|
sys.stderr.write('Windows capability protection failed.\n');sys.exit(1)
|