mirror of
https://github.com/mvt-project/mvt.git
synced 2026-09-30 21:19:41 +02:00
Replaces pyaes with cryptography and reorganize backup parser code
This commit is contained in:
1 parent
6cc67f3c1d
commit
a4d08f8f35
2 files changed
+34
-47
No files matched your search
@@ -7,9 +7,11 @@ import io
|
|||||||
import zlib
|
import zlib
|
||||||
import json
|
import json
|
||||||
import tarfile
|
import tarfile
|
||||||
import hashlib
|
|
||||||
import datetime
|
import datetime
|
||||||
import pyaes
|
from cryptography.hazmat.primitives import hashes
|
||||||
|
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
|
||||||
|
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||||
|
from cryptography.hazmat.primitives import padding
|
||||||
from mvt.common.utils import check_for_links, convert_timestamp_to_iso
|
from mvt.common.utils import check_for_links, convert_timestamp_to_iso
|
||||||
|
|
||||||
|
|
||||||
@@ -28,37 +30,7 @@ class InvalidBackupPassword(AndroidBackupParsingError):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def decrypt_master_key_blob(key, aes_iv, cipher_text):
|
|
||||||
"""
|
|
||||||
Decrypt the master key blob with AES
|
|
||||||
From : https://github.com/FloatingOctothorpe/dump_android_backup
|
|
||||||
"""
|
|
||||||
|
|
||||||
aes = pyaes.AESModeOfOperationCBC(key, aes_iv)
|
|
||||||
|
|
||||||
plain_text = b''
|
|
||||||
while len(plain_text) < len(cipher_text):
|
|
||||||
offset = len(plain_text)
|
|
||||||
plain_text += aes.decrypt(cipher_text[offset:(offset + 16)])
|
|
||||||
|
|
||||||
blob = io.BytesIO(plain_text)
|
|
||||||
master_iv_length = ord(blob.read(1))
|
|
||||||
master_iv = blob.read(master_iv_length)
|
|
||||||
master_key_length = ord(blob.read(1))
|
|
||||||
master_key = blob.read(master_key_length)
|
|
||||||
master_key_checksum_length = ord(blob.read(1))
|
|
||||||
master_key_checksum = blob.read(master_key_checksum_length)
|
|
||||||
|
|
||||||
return master_iv, master_key, master_key_checksum
|
|
||||||
|
|
||||||
|
|
||||||
def to_utf8_bytes(input_bytes):
|
def to_utf8_bytes(input_bytes):
|
||||||
"""Emulate bytes being converted into a "UTF8 byte array"
|
|
||||||
For more info see the Bouncy Castle Crypto package Strings.toUTF8ByteArray
|
|
||||||
method:
|
|
||||||
https://github.com/bcgit/bc-java/blob/master/core/src/main/java/org/bouncycastle/util/Strings.java#L142
|
|
||||||
From https://github.com/FloatingOctothorpe/dump_android_backup
|
|
||||||
"""
|
|
||||||
output = []
|
output = []
|
||||||
for byte in input_bytes:
|
for byte in input_bytes:
|
||||||
if byte < ord(b'\x80'):
|
if byte < ord(b'\x80'):
|
||||||
@@ -103,7 +75,6 @@ def parse_ab_header(data):
|
|||||||
def parse_backup_file(data, password=None):
|
def parse_backup_file(data, password=None):
|
||||||
"""
|
"""
|
||||||
Parse an ab file, returns a tar file
|
Parse an ab file, returns a tar file
|
||||||
Inspired by https://github.com/FloatingOctothorpe/dump_android_backup
|
|
||||||
"""
|
"""
|
||||||
if not data.startswith(b"ANDROID BACKUP"):
|
if not data.startswith(b"ANDROID BACKUP"):
|
||||||
raise AndroidBackupParsingError("Invalid file header")
|
raise AndroidBackupParsingError("Invalid file header")
|
||||||
@@ -124,13 +95,25 @@ def parse_backup_file(data, password=None):
|
|||||||
user_iv = bytes.fromhex(user_iv.decode("utf-8"))
|
user_iv = bytes.fromhex(user_iv.decode("utf-8"))
|
||||||
master_key_blob = bytes.fromhex(master_key_blob.decode("utf-8"))
|
master_key_blob = bytes.fromhex(master_key_blob.decode("utf-8"))
|
||||||
|
|
||||||
key = hashlib.pbkdf2_hmac('sha1',
|
kdf = PBKDF2HMAC(
|
||||||
password.encode('utf-8'),
|
algorithm=hashes.SHA1(),
|
||||||
user_salt,
|
length=32,
|
||||||
pbkdf2_rounds,
|
salt=user_salt,
|
||||||
PBKDF2_KEY_SIZE)
|
iterations=pbkdf2_rounds)
|
||||||
|
key = kdf.derive(password.encode("utf-8"))
|
||||||
|
|
||||||
|
cipher = Cipher(algorithms.AES(key), modes.CBC(user_iv))
|
||||||
|
decryptor = cipher.decryptor()
|
||||||
try:
|
try:
|
||||||
[master_iv, master_key, master_key_checksum] = decrypt_master_key_blob(key, user_iv, master_key_blob)
|
plain_text = decryptor.update(master_key_blob) + decryptor.finalize()
|
||||||
|
|
||||||
|
blob = io.BytesIO(plain_text)
|
||||||
|
master_iv_length = ord(blob.read(1))
|
||||||
|
master_iv = blob.read(master_iv_length)
|
||||||
|
master_key_length = ord(blob.read(1))
|
||||||
|
master_key = blob.read(master_key_length)
|
||||||
|
master_key_checksum_length = ord(blob.read(1))
|
||||||
|
master_key_checksum = blob.read(master_key_checksum_length)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
raise InvalidBackupPassword()
|
raise InvalidBackupPassword()
|
||||||
|
|
||||||
@@ -139,17 +122,21 @@ def parse_backup_file(data, password=None):
|
|||||||
else:
|
else:
|
||||||
hmac_mk = master_key
|
hmac_mk = master_key
|
||||||
|
|
||||||
calculated_checksum = hashlib.pbkdf2_hmac('sha1',
|
kdf = PBKDF2HMAC(
|
||||||
hmac_mk,
|
algorithm=hashes.SHA1(),
|
||||||
checksum_salt,
|
length=32,
|
||||||
pbkdf2_rounds,
|
salt=checksum_salt,
|
||||||
PBKDF2_KEY_SIZE)
|
iterations=pbkdf2_rounds)
|
||||||
|
calculated_checksum = kdf.derive(hmac_mk)
|
||||||
|
|
||||||
if master_key_checksum != calculated_checksum:
|
if master_key_checksum != calculated_checksum:
|
||||||
raise InvalidBackupPassword()
|
raise InvalidBackupPassword()
|
||||||
|
|
||||||
decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(master_key, master_iv))
|
cipher = Cipher(algorithms.AES(master_key), modes.CBC(master_iv))
|
||||||
tar_data = decrypter.feed(encrypted_data)
|
decryptor = cipher.decryptor()
|
||||||
|
tar_data = decryptor.update(encrypted_data) + decryptor.finalize()
|
||||||
|
unpadder = padding.PKCS7(128).unpadder()
|
||||||
|
tar_data = data = unpadder.update(tar_data)
|
||||||
|
|
||||||
if is_compressed == 1:
|
if is_compressed == 1:
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in new issue
Block a user