Skip to content

Commit

Permalink
Use standard library plistlib over biplist
Browse files Browse the repository at this point in the history
Since Python 3.4, the standard library has supported the binary
plist format found in iPhone backups, and in 3.8 the support for
UID values was added:
https://docs.python.org/3.8/library/plistlib.html
This is all that is required by this library, so the additional
biplist dependency is now unnecesary.
  • Loading branch information
jsharkey13 committed Feb 10, 2024
1 parent d562ec4 commit f0a0ebc
Show file tree
Hide file tree
Showing 2 changed files with 5 additions and 7 deletions.
1 change: 0 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ classifiers = [
keywords = ["iPhone", "backup", "forensics", "iOS", "WhatsApp", "decryption", "iOS backup", "iTunes Backup"]

dependencies = [
"biplist>=1.0.3",
"pycryptodome>=3.20.0"
]

Expand Down
11 changes: 5 additions & 6 deletions src/iphone_backup_decrypt/iphone_backup.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import os.path
import plistlib
import shutil
import sqlite3
import struct
import tempfile

import biplist

from . import google_iphone_dataprotection

__all__ = ["EncryptedBackup", "RelativePath", "RelativePathsLike", "DomainLike", "MatchFiles"]
Expand Down Expand Up @@ -138,7 +137,7 @@ def _read_and_unlock_keybag(self):
return self._unlocked
# Open the Manifest.plist file to access the Keybag:
with open(self._manifest_plist_path, 'rb') as infile:
self._manifest_plist = biplist.readPlist(infile)
self._manifest_plist = plistlib.load(infile)
self._keybag = google_iphone_dataprotection.Keybag(self._manifest_plist['BackupKeyBag'])
# Attempt to unlock the Keybag:
self._unlocked = self._keybag.unlockWithPassphrase(self._passphrase)
Expand Down Expand Up @@ -188,8 +187,8 @@ def _decrypt_inner_file(self, *, file_id, file_bplist, if_modified_since=None):
# Ensure we've already unlocked the Keybag:
self._read_and_unlock_keybag()
# Read the plist data and extract file metadata:
plist = biplist.readPlistFromString(file_bplist)
file_data = plist['$objects'][plist['$top']['root'].integer]
plist = plistlib.loads(file_bplist)
file_data = plist['$objects'][plist['$top']['root'].data]
file_mtime = file_data.get("LastModified")
# Was the file modified since the time requested?
if if_modified_since and file_mtime <= if_modified_since:
Expand All @@ -199,7 +198,7 @@ def _decrypt_inner_file(self, *, file_id, file_bplist, if_modified_since=None):
protection_class = file_data['ProtectionClass']
if "EncryptionKey" not in file_data:
raise ValueError("Path is not an encrypted file.") # File is not encrypted; either a directory or empty.
encryption_key = plist['$objects'][file_data['EncryptionKey'].integer]['NS.data'][4:]
encryption_key = plist['$objects'][file_data['EncryptionKey'].data]['NS.data'][4:]
inner_key = self._keybag.unwrapKeyForClass(protection_class, encryption_key)
# Find the encrypted version of the file on disk and decrypt it:
filename_in_backup = os.path.join(self._backup_directory, file_id[:2], file_id)
Expand Down

0 comments on commit f0a0ebc

Please sign in to comment.