hacks/dump-imessages/iphone-dataprotection/python_scripts/keychain/keychain4.py

93 lines
3.3 KiB
Python

from crypto.aes import AESdecryptCBC
import struct
"""
iOS 4 keychain-2.db data column format
version 0x00000000
key class 0x00000008
kSecAttrAccessibleWhenUnlocked 6
kSecAttrAccessibleAfterFirstUnlock 7
kSecAttrAccessibleAlways 8
kSecAttrAccessibleWhenUnlockedThisDeviceOnly 9
kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly 10
kSecAttrAccessibleAlwaysThisDeviceOnly 11
wrapped AES256 key 0x28 bytes (passed to kAppleKeyStoreKeyUnwrap)
encrypted data (AES 256 CBC zero IV)
"""
from keychain import Keychain
from crypto.gcm import gcm_decrypt
from util.bplist import BPlistReader
KSECATTRACCESSIBLE = {
6: "kSecAttrAccessibleWhenUnlocked",
7: "kSecAttrAccessibleAfterFirstUnlock",
8: "kSecAttrAccessibleAlways",
9: "kSecAttrAccessibleWhenUnlockedThisDeviceOnly",
10: "kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly",
11: "kSecAttrAccessibleAlwaysThisDeviceOnly"
}
class Keychain4(Keychain):
def __init__(self, filename, keybag):
if not keybag.unlocked:
print "Keychain object created with locked keybag, some items won't be decrypted"
Keychain.__init__(self, filename)
self.keybag = keybag
def decrypt_item(self, row):
version, clas = struct.unpack("<LL", row["data"][0:8])
if self.keybag.isBackupKeybag():
if clas >= 9 and not self.keybag.deviceKey:
return {}
if version >= 2:
dict = self.decrypt_blob(row["data"])
if not dict:
return {"clas": clas, "rowid": row["rowid"]}
if dict.has_key("v_Data"):
dict["data"] = dict["v_Data"].data
else:
dict["data"] = ""
dict["rowid"] = row["rowid"]
dict["clas"] = clas
return dict
row["clas"] = clas
return Keychain.decrypt_item(self, row)
def decrypt_data(self, data):
data = self.decrypt_blob(data)
if type(data) == dict:
return data["v_Data"].data
return data
def decrypt_blob(self, blob):
if blob == None:
return ""
if len(blob) < 48:
print "keychain blob length must be >= 48"
return
version, clas = struct.unpack("<LL",blob[0:8])
self.clas=clas
if version == 0:
wrappedkey = blob[8:8+40]
encrypted_data = blob[48:]
elif version == 2:
l = struct.unpack("<L",blob[8:12])[0]
wrappedkey = blob[12:12+l]
encrypted_data = blob[12+l:-16]
else:
raise Exception("unknown keychain verson ", version)
return
unwrappedkey = self.keybag.unwrapKeyForClass(clas, wrappedkey, False)
if not unwrappedkey:
return
if version == 0:
return AESdecryptCBC(encrypted_data, unwrappedkey, padding=True)
elif version == 2:
binaryplist = gcm_decrypt(unwrappedkey, "", encrypted_data, "", blob[-16:])
return BPlistReader(binaryplist).parse()