Partial support for macOS Sequoia (15.x)

This commit is contained in:
Dhiru Kholia 2024-09-24 10:16:13 +05:30 committed by Dhiru Kholia
parent 182e2dd071
commit ad213b6c80
3 changed files with 54 additions and 44 deletions

View File

@ -136,8 +136,9 @@ processors work just fine (even for macOS Sonoma).
5. Monterey (12.6) 5. Monterey (12.6)
6. Ventura (13) - RECOMMENDED 6. Ventura (13) - RECOMMENDED
7. Sonoma (14) 7. Sonoma (14)
8. Sequoia (15)
Choose a product to download (1-7): 6 Choose a product to download (1-8): 6
``` ```
Note: Modern NVIDIA GPUs are supported on HighSierra but not on later Note: Modern NVIDIA GPUs are supported on HighSierra but not on later

View File

@ -16,28 +16,29 @@ pylint -> Your code has been rated at -0.08/10 ;(
""" """
import argparse import argparse
import binascii
import hashlib import hashlib
import json import json
import linecache import linecache
import os import os
import random import random
import struct import struct
import string
import sys import sys
try: try:
from urllib.request import Request, HTTPError, urlopen from urllib.request import Request, HTTPError, urlopen
from urllib.parse import urlparse from urllib.parse import urlparse
except ImportError: except ImportError:
from urllib2 import Request, HTTPError, urlopen print('ERROR: Python 2 is not supported, please use Python 3')
from urlparse import urlparse sys.exit(1)
SELF_DIR = os.path.dirname(os.path.realpath(__file__)) SELF_DIR = os.path.dirname(os.path.realpath(__file__))
RECENT_MAC = 'Mac-7BA5B2D9E42DDD94' # MacPro7,1
RECENT_MAC = 'Mac-27AD2F918AE68F61'
MLB_ZERO = '00000000000000000' MLB_ZERO = '00000000000000000'
MLB_VALID = 'C02749200YGJ803AX' MLB_VALID = 'F5K105303J9K3F71M'
MLB_PRODUCT = '00000000000J80300' MLB_PRODUCT = 'F5K00000000K3F700'
TYPE_SID = 16 TYPE_SID = 16
TYPE_K = 64 TYPE_K = 64
@ -52,12 +53,12 @@ INFO_SIGN_HASH = 'CH'
INFO_SIGN_SESS = 'CT' INFO_SIGN_SESS = 'CT'
INFO_REQURED = [INFO_PRODUCT, INFO_IMAGE_LINK, INFO_IMAGE_HASH, INFO_IMAGE_SESS, INFO_SIGN_LINK, INFO_SIGN_HASH, INFO_SIGN_SESS] INFO_REQURED = [INFO_PRODUCT, INFO_IMAGE_LINK, INFO_IMAGE_HASH, INFO_IMAGE_SESS, INFO_SIGN_LINK, INFO_SIGN_HASH, INFO_SIGN_SESS]
# Use -2 for better resize stability on Windows
TERMINAL_MARGIN = 2
def run_query(url, headers, post=None, raw=False): def run_query(url, headers, post=None, raw=False):
if post is not None: if post is not None:
data = '\n'.join([entry + '=' + post[entry] for entry in post]) data = '\n'.join(entry + '=' + post[entry] for entry in post).encode()
if sys.version_info[0] >= 3:
data = data.encode('utf-8')
else: else:
data = None data = None
req = Request(url=url, headers=headers, data=data) req = Request(url=url, headers=headers, data=data)
@ -72,12 +73,11 @@ def run_query(url, headers, post=None, raw=False):
def generate_id(id_type, id_value=None): def generate_id(id_type, id_value=None):
valid_chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] return id_value or ''.join(random.choices(string.hexdigits[:16].upper(), k=id_type))
return ''.join(random.choice(valid_chars) for i in range(id_type)) if not id_value else id_value
def product_mlb(mlb): def product_mlb(mlb):
return '00000000000' + mlb[11] + mlb[12] + mlb[13] + mlb[14] + '00' return '00000000000' + mlb[11:15] + '00'
def mlb_from_eeee(eeee): def mlb_from_eeee(eeee):
@ -88,13 +88,6 @@ def mlb_from_eeee(eeee):
return f'00000000000{eeee}00' return f'00000000000{eeee}00'
def int_from_unsigned_bytes(byte_list, byteorder):
if byteorder == 'little':
byte_list = byte_list[::-1]
encoded = binascii.hexlify(byte_list)
return int(encoded, 16)
# zhangyoufu https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7#gistcomment-3061078 2021-10-08 # zhangyoufu https://gist.github.com/MCJack123/943eaca762730ca4b7ae460b731b68e7#gistcomment-3061078 2021-10-08
Apple_EFI_ROM_public_key_1 = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF Apple_EFI_ROM_public_key_1 = 0xC3E748CAD9CD384329E10E25A91E43E1A762FF529ADE578C935BDDF9B13F2179D4855E6FC89E9E29CA12517D17DFA1EDCE0BEBF0EA7B461FFE61D94E2BDF72C196F89ACD3536B644064014DAE25A15DB6BB0852ECBD120916318D1CCDEA3C84C92ED743FC176D0BACA920D3FCF3158AFF731F88CE0623182A8ED67E650515F75745909F07D415F55FC15A35654D118C55A462D37A3ACDA08612F3F3F6571761EFCCBCC299AEE99B3A4FD6212CCFFF5EF37A2C334E871191F7E1C31960E010A54E86FA3F62E6D6905E1CD57732410A3EB0C6B4DEFDABE9F59BF1618758C751CD56CEF851D1C0EAA1C558E37AC108DA9089863D20E2E7E4BF475EC66FE6B3EFDCF
@ -128,8 +121,8 @@ def verify_chunklist(cnkpath):
if signature_method == 1: if signature_method == 1:
data = f.read(256) data = f.read(256)
assert len(data) == 256 assert len(data) == 256
signature = int_from_unsigned_bytes(data, 'little') signature = int.from_bytes(data, 'little')
plaintext = 0x1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003031300d0609608648016503040201050004200000000000000000000000000000000000000000000000000000000000000000 | int_from_unsigned_bytes(digest, 'big') plaintext = int(f'0x1{"f"*404}003031300d060960864801650304020105000420{"0"*64}', 16) | int.from_bytes(digest, 'big')
assert pow(signature, 0x10001, Apple_EFI_ROM_public_key_1) == plaintext assert pow(signature, 0x10001, Apple_EFI_ROM_public_key_1) == plaintext
elif signature_method == 2: elif signature_method == 2:
data = f.read(32) data = f.read(32)
@ -194,7 +187,9 @@ def get_image_info(session, bid, mlb=MLB_ZERO, diag=False, os_type='default', ci
try: try:
key, value = line.split(': ') key, value = line.split(': ')
info[key] = value info[key] = value
except Exception: except KeyError:
continue
except ValueError:
continue continue
for k in INFO_REQURED: for k in INFO_REQURED:
@ -214,34 +209,47 @@ def save_image(url, sess, filename='', directory=''):
} }
if not os.path.exists(directory): if not os.path.exists(directory):
os.mkdir(directory) os.makedirs(directory)
if filename == '': if filename == '':
filename = os.path.basename(purl.path) filename = os.path.basename(purl.path)
if filename.find('/') >= 0 or filename == '': if filename.find(os.sep) >= 0 or filename == '':
raise RuntimeError('Invalid save path ' + filename) raise RuntimeError('Invalid save path ' + filename)
print(f'Saving {url} to {directory}/{filename}...') print(f'Saving {url} to {directory}{os.sep}{filename}...')
with open(os.path.join(directory, filename), 'wb') as fh: with open(os.path.join(directory, filename), 'wb') as fh:
response = run_query(url, headers, raw=True) response = run_query(url, headers, raw=True)
total_size = int(response.headers['content-length']) / float(2 ** 20) headers = dict(response.headers)
# print(total_size) totalsize = -1
if total_size < 1: for header in headers:
total_size = response.headers['content-length'] if header.lower() == 'content-length':
print("Note: The total download size is %s bytes" % total_size) totalsize = int(headers[header])
else: break
print("Note: The total download size is %0.2f MB" % total_size)
size = 0 size = 0
oldterminalsize = 0
while True: while True:
chunk = response.read(2**20) chunk = response.read(2**20)
if not chunk: if not chunk:
break break
fh.write(chunk) fh.write(chunk)
size += len(chunk) size += len(chunk)
print(f'\r{size / (2**20)} MBs downloaded...', end='') terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
if oldterminalsize != terminalsize:
print(f'\r{"":<{terminalsize}}', end='')
oldterminalsize = terminalsize
if totalsize > 0:
progress = size / totalsize
barwidth = terminalsize // 3
print(f'\r{size / (2**20):.1f}/{totalsize / (2**20):.1f} MB ', end='')
if terminalsize > 55:
print(f'|{"=" * int(barwidth * progress):<{barwidth}}|', end='')
print(f' {progress*100:.1f}% downloaded', end='')
else:
# Fallback if Content-Length isn't available
print(f'\r{size / (2**20)} MB downloaded...', end='')
sys.stdout.flush() sys.stdout.flush()
print('\rDownload complete!\t\t\t\t\t') print('\nDownload complete!')
return os.path.join(directory, os.path.basename(filename)) return os.path.join(directory, os.path.basename(filename))
@ -250,10 +258,9 @@ def verify_image(dmgpath, cnkpath):
print('Verifying image with chunklist...') print('Verifying image with chunklist...')
with open(dmgpath, 'rb') as dmgf: with open(dmgpath, 'rb') as dmgf:
cnkcount = 0 for cnkcount, (cnksize, cnkhash) in enumerate(verify_chunklist(cnkpath), 1):
for cnksize, cnkhash in verify_chunklist(cnkpath): terminalsize = max(os.get_terminal_size().columns - TERMINAL_MARGIN, 0)
cnkcount += 1 print(f'\r{f"Chunk {cnkcount} ({cnksize} bytes)":<{terminalsize}}', end='')
print(f'\rChunk {cnkcount} ({cnksize} bytes)', end='')
sys.stdout.flush() sys.stdout.flush()
cnk = dmgf.read(cnksize) cnk = dmgf.read(cnksize)
if len(cnk) != cnksize: if len(cnk) != cnksize:
@ -262,7 +269,7 @@ def verify_image(dmgpath, cnkpath):
raise RuntimeError(f'Invalid chunk {cnkcount}: hash mismatch') raise RuntimeError(f'Invalid chunk {cnkcount}: hash mismatch')
if dmgf.read(1) != b'': if dmgf.read(1) != b'':
raise RuntimeError('Invalid image: larger than chunklist') raise RuntimeError('Invalid image: larger than chunklist')
print('\rImage verification complete!\t\t\t\t\t') print('\nImage verification complete!')
def action_download(args): def action_download(args):
@ -298,10 +305,10 @@ def action_download(args):
if args.verbose: if args.verbose:
print(info) print(info)
print(f'Downloading {info[INFO_PRODUCT]}...') print(f'Downloading {info[INFO_PRODUCT]}...')
dmgname = '' if args.basename == '' else args.basename + '.dmg'
dmgpath = save_image(info[INFO_IMAGE_LINK], info[INFO_IMAGE_SESS], dmgname, args.outdir)
cnkname = '' if args.basename == '' else args.basename + '.chunklist' cnkname = '' if args.basename == '' else args.basename + '.chunklist'
cnkpath = save_image(info[INFO_SIGN_LINK], info[INFO_SIGN_SESS], cnkname, args.outdir) cnkpath = save_image(info[INFO_SIGN_LINK], info[INFO_SIGN_SESS], cnkname, args.outdir)
dmgname = '' if args.basename == '' else args.basename + '.dmg'
dmgpath = save_image(info[INFO_IMAGE_LINK], info[INFO_IMAGE_SESS], dmgname, args.outdir)
try: try:
verify_image(dmgpath, cnkpath) verify_image(dmgpath, cnkpath)
return 0 return 0
@ -374,7 +381,7 @@ def action_selfcheck(args):
if product_default[INFO_PRODUCT] != valid_default[INFO_PRODUCT]: if product_default[INFO_PRODUCT] != valid_default[INFO_PRODUCT]:
# Product-only MLB can give the same value with valid default MLB. # Product-only MLB can give the same value with valid default MLB.
# This is not an error for all models, but for our chosen code it is. # This is not an error for all models, but for our chosen code it is.
print('ERROR: Valid and product MLB give mismatch, got {product_default[INFO_PRODUCT]} and {valid_default[INFO_PRODUCT]}') print(f'ERROR: Valid and product MLB give mismatch, got {product_default[INFO_PRODUCT]} and {valid_default[INFO_PRODUCT]}')
return 1 return 1
print('SUCCESS: Found no discrepancies with MLB validation algorithm!') print('SUCCESS: Found no discrepancies with MLB validation algorithm!')
@ -523,6 +530,7 @@ def main():
# No action specified, so present a download menu instead # No action specified, so present a download menu instead
# https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macrecovery/boards.json # https://github.com/acidanthera/OpenCorePkg/blob/master/Utilities/macrecovery/boards.json
# https://github.com/corpnewt/gibMacOS
products = [ products = [
{"name": "High Sierra (10.13)", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000J80300", "short": "high-sierra"}, {"name": "High Sierra (10.13)", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000J80300", "short": "high-sierra"},
{"name": "Mojave (10.14)", "b": "Mac-7BA5B2DFE22DDD8C", "m": "00000000000KXPG00", "short": "mojave"}, {"name": "Mojave (10.14)", "b": "Mac-7BA5B2DFE22DDD8C", "m": "00000000000KXPG00", "short": "mojave"},
@ -530,7 +538,8 @@ def main():
{"name": "Big Sur (11.7)", "b": "Mac-2BD1B31983FE1663", "m": "00000000000000000", "short": "big-sur"}, {"name": "Big Sur (11.7)", "b": "Mac-2BD1B31983FE1663", "m": "00000000000000000", "short": "big-sur"},
{"name": "Monterey (12.6)", "b": "Mac-B809C3757DA9BB8D", "m": "00000000000000000", "os_type": "latest", "short": "monterey"}, {"name": "Monterey (12.6)", "b": "Mac-B809C3757DA9BB8D", "m": "00000000000000000", "os_type": "latest", "short": "monterey"},
{"name": "Ventura (13) - RECOMMENDED", "b": "Mac-4B682C642B45593E", "m": "00000000000000000", "os_type": "latest", "short": "ventura"}, {"name": "Ventura (13) - RECOMMENDED", "b": "Mac-4B682C642B45593E", "m": "00000000000000000", "os_type": "latest", "short": "ventura"},
{"name": "Sonoma (14) ", "b": "Mac-A61BADE1FDAD7B05", "m": "00000000000000000", "short": "sonoma"} {"name": "Sonoma (14) ", "b": "Mac-827FAC58A8FDFA22", "m": "00000000000000000", "short": "sonoma"},
{"name": "Sequoia (15) ", "b": "Mac-7BA5B2D9E42DDD94", "m": "00000000000000000", "short": "Sequoia", "os_type": "latest"},
] ]
for index, product in enumerate(products): for index, product in enumerate(products):
name = product["name"] name = product["name"]

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB