initial code for dumping imessages in a reasonable format
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
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()
|
||||
Reference in New Issue
Block a user