Skip to content

Commit 54e929e

Browse files
committed
move key extraction routines to pddbcommon.py
1 parent 05068c5 commit 54e929e

File tree

2 files changed

+114
-99
lines changed

2 files changed

+114
-99
lines changed

tools/backalyzer.py

Lines changed: 2 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ def bytes_to_semverstr(b):
3030
else:
3131
return "v{}.{}.{}-{}".format(maj, min, rev, extra)
3232

33-
def get_key(index, keyrom, length):
34-
ret = []
35-
for offset in range(length // 4):
36-
word = int.from_bytes(keyrom[(index + offset) * 4: (index + offset) * 4 + 4], 'big')
37-
ret += list(word.to_bytes(4, 'little'))
38-
return ret
39-
4033
def main():
4134
global MBBB_PAGES
4235
FSCB_PAGES = 16
@@ -249,101 +242,11 @@ def main():
249242
exit(1)
250243

251244
keyrom = pt_data[i:i+1024]
252-
user_key_enc = get_key(40, keyrom, 32)
253-
pepper = get_key(248, keyrom, 16)
254-
pepper[0] = pepper[0] ^ 1 # encodes the "boot" password type into the pepper
255-
256-
# acquire and massage the password so that we can decrypt the encrypted user key
257-
boot_pw = args.pin
258-
boot_pw_array = [0] * 73
259-
pw_len = 0
260-
for b in bytes(boot_pw.encode('utf-8')):
261-
boot_pw_array[pw_len] = b
262-
pw_len += 1
263-
pw_len += 1 # null terminate, so even the null password is one character long
264-
bcrypter = bcrypt.BCrypt()
265-
# logging.debug("{}".format(boot_pw_array[:pw_len]))
266-
logging.debug("user_key_enc: {}".format(list(user_key_enc)))
267-
logging.debug("private_key_enc: {}".format(list(get_key(8, keyrom, 32))))
268-
logging.debug("salt: {}".format(list(pepper)))
269-
hashed_pw = bcrypter.crypt_raw(boot_pw_array[:pw_len], pepper, 7)
270-
logging.debug("hashed_pw: {}".format(list(hashed_pw)))
271-
hasher = SHA512.new(truncate="256")
272-
hasher.update(hashed_pw)
273-
user_pw = hasher.digest()
274-
275-
user_key = []
276-
for (a, b) in zip(user_key_enc, user_pw):
277-
user_key += [a ^ b]
278-
logging.debug("user_key: {}".format(user_key))
279-
280-
rollback_limit = 255 - int.from_bytes(keyrom[254 * 4 : 254 * 4 + 4], 'little')
281-
logging.info("rollback limit: {}".format(rollback_limit))
282-
for i in range(rollback_limit):
283-
hasher = SHA512.new(truncate="256")
284-
hasher.update(bytes(user_key))
285-
user_key = hasher.digest()
286-
287-
logging.debug("hashed_key: {}".format(list(user_key)))
288-
289-
pddb = backup[4096:]
290-
else:
291-
dna_int = 0
292-
user_key = bytes([0] * 32)
293-
pddb = backup
245+
keys = extract_keys(keyrom, backup[4096:], args.pin, basis_credentials=basis_credentials)
294246

247+
pddb = backup[4096:]
295248
pddb_len = len(pddb)
296249
pddb_size_pages = pddb_len // PAGE_SIZE
297-
logging.info("Database size: 0x{:x}".format(pddb_len))
298-
299-
pt_len = pddb_size_pages * 16
300-
static_crypto_data = pddb[pt_len:pt_len + 0x1000]
301-
scd_ver = int.from_bytes(static_crypto_data[:4], 'little')
302-
if scd_ver != 2:
303-
logging.error("Static crypto data has wrong version {}", 2)
304-
exit(1)
305-
306-
wrapped_key_pt = static_crypto_data[4:4+40]
307-
wrapped_key_data = static_crypto_data[4+40:4+40+40]
308-
pddb_salt = static_crypto_data[4+40+40:]
309-
310-
# extract the .System key
311-
key_pt = aes_key_unwrap_with_padding(bytes(user_key), bytes(wrapped_key_pt))
312-
key_data = aes_key_unwrap_with_padding(bytes(user_key), bytes(wrapped_key_data))
313-
314-
logging.debug("key_pt {}".format(key_pt))
315-
logging.debug("key_data {}".format(key_data))
316-
keys = {}
317-
keys[SYSTEM_BASIS] = [key_pt, key_data]
318-
319-
# extract the secret basis keys
320-
for name, pw in basis_credentials.items():
321-
bname_copy = [0]*64
322-
plaintext_pw = [0]*73
323-
i = 0
324-
for c in list(name.encode('utf-8')):
325-
bname_copy[i] = c
326-
i += 1
327-
pw_len = 0
328-
for c in list(pw.encode('utf-8')):
329-
plaintext_pw[pw_len] = c
330-
pw_len += 1
331-
pw_len += 1 # null terminate
332-
# print(bname_copy)
333-
# print(plaintext_pw)
334-
hasher = SHA512.new(truncate="256")
335-
hasher.update(pddb_salt[32:])
336-
hasher.update(bytes(bname_copy))
337-
hasher.update(bytes(plaintext_pw))
338-
derived_salt = hasher.digest()
339-
340-
bcrypter = bcrypt.BCrypt()
341-
hashed_pw = bcrypter.crypt_raw(plaintext_pw[:pw_len], derived_salt[:16], 7)
342-
hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=pddb_salt[:32], info=b"pddb page table key")
343-
pt_key = hkdf.derive(hashed_pw)
344-
hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=pddb_salt[:32], info=b"pddb data key")
345-
data_key = hkdf.derive(hashed_pw)
346-
keys[name] = [pt_key, data_key]
347250

348251
# data_aad = SYSTEM_BASIS + PDDB_VERSION + dna
349252

tools/pddbcommon.py

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@
33
from rfc8452 import AES_GCM_SIV
44
from Crypto.Hash import SHA512
55
import binascii
6+
import bcrypt
7+
from cryptography.hazmat.primitives.keywrap import aes_key_unwrap_with_padding
8+
from cryptography.hazmat.primitives import hashes
9+
from cryptography.hazmat.primitives.kdf.hkdf import HKDF
610

711
import logging
812

@@ -744,3 +748,111 @@ def decode_fscb(img, keys, FSCB_LEN_PAGES=2, dna=DNA):
744748
spaceupdate_count += 1
745749
logging.info("Total SpaceUpdate entries found: {}".format(spaceupdate_count))
746750
return fscb
751+
752+
753+
# # # code to extract system basis given a keybox
754+
def get_key(index, keyrom, length):
755+
ret = []
756+
for offset in range(length // 4):
757+
word = int.from_bytes(keyrom[(index + offset) * 4: (index + offset) * 4 + 4], 'big')
758+
ret += list(word.to_bytes(4, 'little'))
759+
return ret
760+
761+
# Arguments:
762+
# - keyrom is the binary image of the key box, in plaintext
763+
# - pddb is the PDDB binary image, starting from the beginning of the PDDB itself (no backup headers, etc.)
764+
# - boot_pw is the boot pin
765+
# - basis_credentials is a list of the secret Bases
766+
# Returns:
767+
# - A dictionary of keys, by Basis name
768+
def extract_keys(keyrom, pddb, boot_pw, basis_credentials=None):
769+
user_key_enc = get_key(40, keyrom, 32)
770+
pepper = get_key(248, keyrom, 16)
771+
pepper[0] = pepper[0] ^ 1 # encodes the "boot" password type into the pepper
772+
773+
# acquire and massage the password so that we can decrypt the encrypted user key
774+
boot_pw_array = [0] * 73
775+
pw_len = 0
776+
for b in bytes(boot_pw.encode('utf-8')):
777+
boot_pw_array[pw_len] = b
778+
pw_len += 1
779+
pw_len += 1 # null terminate, so even the null password is one character long
780+
bcrypter = bcrypt.BCrypt()
781+
# logging.debug("{}".format(boot_pw_array[:pw_len]))
782+
logging.debug("user_key_enc: {}".format(list(user_key_enc)))
783+
logging.debug("private_key_enc: {}".format(list(get_key(8, keyrom, 32))))
784+
logging.debug("salt: {}".format(list(pepper)))
785+
hashed_pw = bcrypter.crypt_raw(boot_pw_array[:pw_len], pepper, 7)
786+
logging.debug("hashed_pw: {}".format(list(hashed_pw)))
787+
hasher = SHA512.new(truncate="256")
788+
hasher.update(hashed_pw)
789+
user_pw = hasher.digest()
790+
791+
user_key = []
792+
for (a, b) in zip(user_key_enc, user_pw):
793+
user_key += [a ^ b]
794+
logging.debug("user_key: {}".format(user_key))
795+
796+
rollback_limit = 255 - int.from_bytes(keyrom[254 * 4 : 254 * 4 + 4], 'little')
797+
logging.info("rollback limit: {}".format(rollback_limit))
798+
for i in range(rollback_limit):
799+
hasher = SHA512.new(truncate="256")
800+
hasher.update(bytes(user_key))
801+
user_key = hasher.digest()
802+
803+
logging.debug("hashed_key: {}".format(list(user_key)))
804+
805+
pddb_len = len(pddb)
806+
pddb_size_pages = pddb_len // PAGE_SIZE
807+
logging.info("Database size: 0x{:x}".format(pddb_len))
808+
809+
pt_len = pddb_size_pages * 16
810+
static_crypto_data = pddb[pt_len:pt_len + 0x1000]
811+
scd_ver = int.from_bytes(static_crypto_data[:4], 'little')
812+
if scd_ver != 2:
813+
logging.error("Static crypto data has wrong version {}", 2)
814+
exit(1)
815+
816+
wrapped_key_pt = static_crypto_data[4:4+40]
817+
wrapped_key_data = static_crypto_data[4+40:4+40+40]
818+
pddb_salt = static_crypto_data[4+40+40:]
819+
820+
# extract the .System key
821+
key_pt = aes_key_unwrap_with_padding(bytes(user_key), bytes(wrapped_key_pt))
822+
key_data = aes_key_unwrap_with_padding(bytes(user_key), bytes(wrapped_key_data))
823+
824+
logging.debug("key_pt {}".format(key_pt))
825+
logging.debug("key_data {}".format(key_data))
826+
keys = {}
827+
keys[SYSTEM_BASIS] = [key_pt, key_data]
828+
829+
# extract the secret basis keys
830+
for name, pw in basis_credentials.items():
831+
bname_copy = [0]*64
832+
plaintext_pw = [0]*73
833+
i = 0
834+
for c in list(name.encode('utf-8')):
835+
bname_copy[i] = c
836+
i += 1
837+
pw_len = 0
838+
for c in list(pw.encode('utf-8')):
839+
plaintext_pw[pw_len] = c
840+
pw_len += 1
841+
pw_len += 1 # null terminate
842+
# print(bname_copy)
843+
# print(plaintext_pw)
844+
hasher = SHA512.new(truncate="256")
845+
hasher.update(pddb_salt[32:])
846+
hasher.update(bytes(bname_copy))
847+
hasher.update(bytes(plaintext_pw))
848+
derived_salt = hasher.digest()
849+
850+
bcrypter = bcrypt.BCrypt()
851+
hashed_pw = bcrypter.crypt_raw(plaintext_pw[:pw_len], derived_salt[:16], 7)
852+
hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=pddb_salt[:32], info=b"pddb page table key")
853+
pt_key = hkdf.derive(hashed_pw)
854+
hkdf = HKDF(algorithm=hashes.SHA256(), length=32, salt=pddb_salt[:32], info=b"pddb data key")
855+
data_key = hkdf.derive(hashed_pw)
856+
keys[name] = [pt_key, data_key]
857+
858+
return keys

0 commit comments

Comments
 (0)