diff --git a/src/libWiiPy/title/nus.py b/src/libWiiPy/title/nus.py index 5e63704..366f3f1 100644 --- a/src/libWiiPy/title/nus.py +++ b/src/libWiiPy/title/nus.py @@ -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, diff --git a/src/libWiiPy/title/title.py b/src/libWiiPy/title/title.py index 1db4680..c93e510 100644 --- a/src/libWiiPy/title/title.py +++ b/src/libWiiPy/title/title.py @@ -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: