Handle cert chain data in a title as a CertificateChain instead of bytes

This commit is contained in:
Campbell 2024-12-16 22:14:07 -05:00
parent ece19177c4
commit 8a15b1e82e
Signed by: NinjaCheetah
GPG Key ID: 670C282B3291D63D
2 changed files with 42 additions and 22 deletions

View File

@ -40,10 +40,10 @@ def download_title(title_id: str, title_version: int = None, wiiu_endpoint: bool
"""
# First, create the new title.
title = Title()
# Download and load the TMD, Ticket, and certs.
# Download and load the certificate chain, TMD, and Ticket.
title.load_cert_chain(download_cert_chain(wiiu_endpoint, endpoint_override))
title.load_tmd(download_tmd(title_id, title_version, wiiu_endpoint, endpoint_override))
title.load_ticket(download_ticket(title_id, wiiu_endpoint, endpoint_override))
title.wad.set_cert_data(download_cert(wiiu_endpoint, endpoint_override))
# Download all contents
title.load_content_records()
title.content.content_list = download_contents(title_id, title.tmd, wiiu_endpoint, endpoint_override)
@ -146,9 +146,9 @@ def download_ticket(title_id: str, wiiu_endpoint: bool = False, endpoint_overrid
return ticket
def download_cert(wiiu_endpoint: bool = False, endpoint_override: str = None) -> bytes:
def download_cert_chain(wiiu_endpoint: bool = False, endpoint_override: str = None) -> bytes:
"""
Downloads the signing certificate used by all WADs. This uses System Menu 4.3U as the source.
Downloads the signing certificate chain used by all WADs. This uses System Menu 4.3U as the source.
Parameters
----------
@ -163,7 +163,7 @@ def download_cert(wiiu_endpoint: bool = False, endpoint_override: str = None) ->
bytes
The cert file.
"""
# Download the TMD and cetk for the System Menu 4.3U.
# Download the TMD and cetk for System Menu 4.3U (v513).
if endpoint_override is not None:
endpoint_url = _validate_endpoint(endpoint_override)
else:
@ -175,18 +175,18 @@ def download_cert(wiiu_endpoint: bool = False, endpoint_override: str = None) ->
cetk_url = endpoint_url + "0000000100000002/cetk"
tmd = requests.get(url=tmd_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
cetk = requests.get(url=cetk_url, headers={'User-Agent': 'wii libnup/1.0'}, stream=True).content
# Assemble the certificate.
cert = b''
# Assemble the certificate chain.
cert_chain = b''
# Certificate Authority data.
cert += cetk[0x2A4 + 768:]
# Certificate Policy data.
cert += tmd[0x328:0x328 + 768]
# XS data.
cert += cetk[0x2A4:0x2A4 + 768]
# Since the cert is always the same, check the hash to make sure nothing went wildly wrong.
if hashlib.sha1(cert).hexdigest() != "ace0f15d2a851c383fe4657afc3840d6ffe30ad0":
cert_chain += cetk[0x2A4 + 768:]
# Certificate Policy (TMD certificate) data.
cert_chain += tmd[0x328:0x328 + 768]
# XS (Ticket certificate) data.
cert_chain += cetk[0x2A4:0x2A4 + 768]
# Since the cert chain is always the same, check the hash to make sure nothing went wildly wrong.
if hashlib.sha1(cert_chain).hexdigest() != "ace0f15d2a851c383fe4657afc3840d6ffe30ad0":
raise Exception("An unknown error has occurred downloading and creating the certificate.")
return cert
return cert_chain
def download_content(title_id: str, content_id: int, wiiu_endpoint: bool = False,

View File

@ -4,6 +4,7 @@
# See https://wiibrew.org/wiki/Title for details about how titles are formatted
import math
from .cert import CertificateChain
from .content import ContentRegion
from .ticket import Ticket
from .tmd import TMD
@ -19,17 +20,20 @@ class Title:
Attributes
----------
wad : WAD
wad: WAD
A WAD object of a WAD containing the title's data.
tmd : TMD
cert_chain: CertificateChain
The chain of certificates used to verify the contents of a title.
tmd: TMD
A TMD object of the title's TMD.
ticket : Ticket
ticket: Ticket
A Ticket object of the title's Ticket.
content: ContentRegion
A ContentRegion object containing the title's contents.
"""
def __init__(self):
self.wad: WAD = WAD()
self.cert_chain: CertificateChain = CertificateChain()
self.tmd: TMD = TMD()
self.ticket: Ticket = Ticket()
self.content: ContentRegion = ContentRegion()
@ -47,6 +51,9 @@ class Title:
# Create a new WAD object based on the WAD data provided.
self.wad = WAD()
self.wad.load(wad)
# Load the certificate chain.
self.cert_chain = CertificateChain()
self.cert_chain.load(self.wad.get_cert_data())
# Load the TMD.
self.tmd = TMD()
self.tmd.load(self.wad.get_tmd_data())
@ -75,6 +82,8 @@ class Title:
# Set WAD type to ib if the title being packed is boot2.
if self.tmd.title_id == "0000000100000001":
self.wad.wad_type = "ib"
# Dump the certificate chain and set it in the WAD.
self.wad.set_cert_data(self.cert_chain.dump())
# Dump the TMD and set it in the WAD.
# This requires updating the content records and number of contents in the TMD first.
self.tmd.content_records = self.content.content_records # This may not be needed because it's a ref already
@ -87,6 +96,19 @@ class Title:
self.wad.set_content_data(content_data, content_size)
return self.wad.dump()
def load_cert_chain(self, cert_chain: bytes) -> None:
"""
Load an existing certificate chain into the title. Note that this will overwrite any existing certificate chain
data for this title.
Parameters
----------
cert_chain: bytes
The data for the certificate chain to load.
"""
self.cert_chain.load(cert_chain)
def load_tmd(self, tmd: bytes) -> None:
"""
Load existing TMD data into the title. Note that this will overwrite any existing TMD data for this title.
@ -94,9 +116,8 @@ class Title:
Parameters
----------
tmd : bytes
The data for the WAD you wish to load.
The data for the TMD to load.
"""
# Load TMD.
self.tmd.load(tmd)
def load_ticket(self, ticket: bytes) -> None:
@ -107,9 +128,8 @@ class Title:
Parameters
----------
ticket : bytes
The data for the WAD you wish to load.
The data for the Ticket to load.
"""
# Load Ticket.
self.ticket.load(ticket)
def load_content_records(self) -> None: