Add support for extracting/packing/otherwise handling dev WADs

This commit is contained in:
Campbell 2024-10-13 21:39:52 -04:00
parent c604c195d2
commit 9ae059b797
Signed by: NinjaCheetah
GPG Key ID: B547958AF96ED344
5 changed files with 33 additions and 10 deletions

View File

@ -1,6 +1,6 @@
[project] [project]
name = "libWiiPy" name = "libWiiPy"
version = "0.5.1" version = "0.5.2"
authors = [ authors = [
{ name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" }, { name="NinjaCheetah", email="ninjacheetah@ncxprogramming.com" },
{ name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" } { name="Lillian Skinner", email="lillian@randommeaninglesscharacters.com" }

View File

@ -7,11 +7,14 @@ common_key = 'ebe42a225e8593e448d9c5457381aaf7'
korean_key = '63b82bb4f4614e2e13f2fefbba4c9b7e' korean_key = '63b82bb4f4614e2e13f2fefbba4c9b7e'
vwii_key = '30bfc76e7c19afbb23163330ced7c28d' vwii_key = '30bfc76e7c19afbb23163330ced7c28d'
development_key = 'a1604a6a7123b529ae8bec32c816fcaa'
def get_common_key(common_key_index) -> bytes:
def get_common_key(common_key_index, dev=False) -> bytes:
""" """
Gets the specified Wii Common Key based on the index provided. If an invalid common key index is provided, this Gets the specified Wii Common Key based on the index provided. If an invalid common key index is provided, this
function falls back on always returning key 0 (the Common Key). function falls back on always returning key 0 (the Common Key). If the kwarg "dev" is specified, then key 0 will
point to the development common key rather than the retail one. Keys 1 and 2 are unaffected by this argument.
Possible values for common_key_index: 0: Common Key, 1: Korean Key, 2: vWii Key Possible values for common_key_index: 0: Common Key, 1: Korean Key, 2: vWii Key
@ -19,6 +22,8 @@ def get_common_key(common_key_index) -> bytes:
---------- ----------
common_key_index : int common_key_index : int
The index of the common key to be returned. The index of the common key to be returned.
dev : bool
If the dev keys should be used in place of the retail keys. Only affects key 0.
Returns Returns
------- -------
@ -27,6 +32,9 @@ def get_common_key(common_key_index) -> bytes:
""" """
match common_key_index: match common_key_index:
case 0: case 0:
if dev:
common_key_bin = binascii.unhexlify(development_key)
else:
common_key_bin = binascii.unhexlify(common_key) common_key_bin = binascii.unhexlify(common_key)
case 1: case 1:
common_key_bin = binascii.unhexlify(korean_key) common_key_bin = binascii.unhexlify(korean_key)

View File

@ -30,7 +30,7 @@ def _convert_tid_to_iv(title_id: str | bytes) -> bytes:
return title_key_iv return title_key_iv
def decrypt_title_key(title_key_enc: bytes, common_key_index: int, title_id: bytes | str) -> bytes: def decrypt_title_key(title_key_enc: bytes, common_key_index: int, title_id: bytes | str, dev=False) -> bytes:
""" """
Gets the decrypted version of the encrypted Title Key provided. Gets the decrypted version of the encrypted Title Key provided.
@ -44,6 +44,8 @@ def decrypt_title_key(title_key_enc: bytes, common_key_index: int, title_id: byt
The index of the common key used to encrypt the Title Key. The index of the common key used to encrypt the Title Key.
title_id : bytes, str title_id : bytes, str
The Title ID of the title that the key is for. The Title ID of the title that the key is for.
dev : bool
Whether the Title Key is encrypted with the development key or not.
Returns Returns
------- -------
@ -51,7 +53,7 @@ def decrypt_title_key(title_key_enc: bytes, common_key_index: int, title_id: byt
The decrypted Title Key. The decrypted Title Key.
""" """
# Load the correct common key for the title. # Load the correct common key for the title.
common_key = get_common_key(common_key_index) common_key = get_common_key(common_key_index, dev)
# Convert the IV into the correct format based on the type provided. # Convert the IV into the correct format based on the type provided.
title_key_iv = _convert_tid_to_iv(title_id) title_key_iv = _convert_tid_to_iv(title_id)
# The IV will always be in the same format by this point, so add the last 8 bytes. # The IV will always be in the same format by this point, so add the last 8 bytes.
@ -63,7 +65,7 @@ def decrypt_title_key(title_key_enc: bytes, common_key_index: int, title_id: byt
return title_key return title_key
def encrypt_title_key(title_key_dec: bytes, common_key_index: int, title_id: bytes | str) -> bytes: def encrypt_title_key(title_key_dec: bytes, common_key_index: int, title_id: bytes | str, dev=False) -> bytes:
""" """
Encrypts the provided Title Key with the selected common key. Encrypts the provided Title Key with the selected common key.
@ -77,6 +79,8 @@ def encrypt_title_key(title_key_dec: bytes, common_key_index: int, title_id: byt
The index of the common key used to encrypt the Title Key. The index of the common key used to encrypt the Title Key.
title_id : bytes, str title_id : bytes, str
The Title ID of the title that the key is for. The Title ID of the title that the key is for.
dev : bool
Whether the Title Key is encrypted with the development key or not.
Returns Returns
------- -------
@ -84,7 +88,7 @@ def encrypt_title_key(title_key_dec: bytes, common_key_index: int, title_id: byt
An encrypted Title Key. An encrypted Title Key.
""" """
# Load the correct common key for the title. # Load the correct common key for the title.
common_key = get_common_key(common_key_index) common_key = get_common_key(common_key_index, dev)
# Convert the IV into the correct format based on the type provided. # Convert the IV into the correct format based on the type provided.
title_key_iv = _convert_tid_to_iv(title_id) title_key_iv = _convert_tid_to_iv(title_id)
# The IV will always be in the same format by this point, so add the last 8 bytes. # The IV will always be in the same format by this point, so add the last 8 bytes.

View File

@ -40,6 +40,9 @@ class Ticket:
Attributes Attributes
---------- ----------
is_dev : bool
Whether this Ticket is signed for development or not, and whether the Title Key is encrypted for development
or not.
signature : bytes signature : bytes
The signature applied to the ticket. The signature applied to the ticket.
ticket_version : int ticket_version : int
@ -56,6 +59,8 @@ class Ticket:
The index of the common key required to decrypt this ticket's Title Key. The index of the common key required to decrypt this ticket's Title Key.
""" """
def __init__(self): def __init__(self):
# If this is a dev ticket
self.is_dev: bool = False # Defaults to false, set to true during load if this ticket is using dev certs.
# Signature blob header # Signature blob header
self.signature_type: bytes = b'' # Type of signature, always 0x10001 for RSA-2048 self.signature_type: bytes = b'' # Type of signature, always 0x10001 for RSA-2048
self.signature: bytes = b'' # Actual signature data self.signature: bytes = b'' # Actual signature data
@ -155,6 +160,11 @@ class Ticket:
limit_type = int.from_bytes(ticket_data.read(4)) limit_type = int.from_bytes(ticket_data.read(4))
limit_value = int.from_bytes(ticket_data.read(4)) limit_value = int.from_bytes(ticket_data.read(4))
self.title_limits_list.append(_TitleLimit(limit_type, limit_value)) self.title_limits_list.append(_TitleLimit(limit_type, limit_value))
# Check certs to see if this is a retail or dev ticket. Treats unknown certs as being retail for now.
if self.signature_issuer.find("Root-CA00000002-XS00000006") != -1:
self.is_dev = True
else:
self.is_dev = False
def dump(self) -> bytes: def dump(self) -> bytes:
""" """
@ -315,7 +325,7 @@ class Ticket:
bytes bytes
The decrypted title key. The decrypted title key.
""" """
title_key = decrypt_title_key(self.title_key_enc, self.common_key_index, self.title_id) title_key = decrypt_title_key(self.title_key_enc, self.common_key_index, self.title_id, self.is_dev)
return title_key return title_key
def set_title_id(self, title_id) -> None: def set_title_id(self, title_id) -> None:

View File

@ -137,7 +137,8 @@ class Title:
self.tmd.set_title_id(title_id) self.tmd.set_title_id(title_id)
title_key_decrypted = self.ticket.get_title_key() title_key_decrypted = self.ticket.get_title_key()
self.ticket.set_title_id(title_id) self.ticket.set_title_id(title_id)
title_key_encrypted = encrypt_title_key(title_key_decrypted, self.ticket.common_key_index, title_id) title_key_encrypted = encrypt_title_key(title_key_decrypted, self.ticket.common_key_index, title_id,
self.ticket.is_dev)
self.ticket.title_key_enc = title_key_encrypted self.ticket.title_key_enc = title_key_encrypted
def set_title_version(self, title_version: str | int) -> None: def set_title_version(self, title_version: str | int) -> None: