Replaced unnecessary BytesIO usages with standard variables

This commit is contained in:
Campbell 2024-05-06 19:34:18 -04:00
parent 98666285db
commit 045613216a
No known key found for this signature in database
GPG Key ID: E543751376940756
7 changed files with 184 additions and 204 deletions

View File

@ -79,21 +79,18 @@ class ContentRegion:
bytes
The full WAD file as bytes.
"""
# Open the stream and begin writing data to it.
with io.BytesIO() as content_region_data:
for content in self.content_list:
# Calculate padding after this content before the next one.
padding_bytes = 0
if (len(content) % 64) != 0:
padding_bytes = 64 - (len(content) % 64)
# Write content data, then the padding afterward if necessary.
content_region_data.write(content)
if padding_bytes > 0:
content_region_data.write(b'\x00' * padding_bytes)
content_region_data.seek(0x0)
content_region_raw = content_region_data.read()
content_region_data = b''
for content in self.content_list:
# Calculate padding after this content before the next one.
padding_bytes = 0
if (len(content) % 64) != 0:
padding_bytes = 64 - (len(content) % 64)
# Write content data, then the padding afterward if necessary.
content_region_data += content
if padding_bytes > 0:
content_region_data += b'\x00' * padding_bytes
# Return the raw ContentRegion for the data contained in the object.
return content_region_raw
return content_region_data
def get_enc_content_by_index(self, index: int) -> bytes:
"""

View File

@ -1,5 +1,6 @@
# "crypto.py" from libWiiPy by NinjaCheetah & Contributors
# https://github.com/NinjaCheetah/libWiiPy
import struct
from .commonkeys import get_common_key
from .shared import convert_tid_to_iv

View File

@ -3,7 +3,6 @@
#
# See https://wiibrew.org/wiki/NUS for details about the NUS
import io
import requests
import hashlib
from typing import List
@ -151,15 +150,13 @@ def download_cert(wiiu_endpoint: bool = False) -> bytes:
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.
with io.BytesIO() as cert_data:
# Certificate Authority data.
cert_data.write(cetk[0x2A4 + 768:])
# Certificate Policy data.
cert_data.write(tmd[0x328:0x328 + 768])
# XS data.
cert_data.write(cetk[0x2A4:0x2A4 + 768])
cert_data.seek(0x0)
cert = cert_data.read()
cert = 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":
raise Exception("An unknown error has occurred downloading and creating the certificate.")

View File

@ -6,6 +6,7 @@
import binascii
def align_value(value, alignment=64) -> int:
"""
Aligns the provided value to the set alignment (defaults to 64).
@ -28,24 +29,24 @@ def align_value(value, alignment=64) -> int:
return value
def pad_bytes_stream(data, alignment=64) -> bytes:
def pad_bytes(data, alignment=64) -> bytes:
"""
Pads the provided bytes stream to the provided alignment (defaults to 64).
Pads the provided bytes object to the provided alignment (defaults to 64).
Parameters
----------
data : BytesIO
data : bytes
The data to align.
alignment : int
The number to align to. Defaults to 64.
Returns
-------
BytesIO
bytes
The aligned data.
"""
while (data.getbuffer().nbytes % alignment) != 0:
data.write(b'\x00')
while (len(data) % alignment) != 0:
data += b'\x00'
return data

View File

@ -146,68 +146,62 @@ class Ticket:
bytes
The full Ticket file as bytes.
"""
# Open the stream and begin writing to it.
with io.BytesIO() as ticket_data:
# Signature type.
ticket_data.write(self.signature_type)
# Signature data.
ticket_data.write(self.signature)
# Padding to 64 bytes.
ticket_data.write(b'\x00' * 60)
# Signature issuer.
ticket_data.write(str.encode(self.signature_issuer))
# ECDH data.
ticket_data.write(self.ecdh_data)
# Ticket version.
ticket_data.write(int.to_bytes(self.ticket_version, 1))
# Reserved (all \0x00).
ticket_data.write(b'\x00\x00')
# Title Key.
ticket_data.write(self.title_key_enc)
# Unknown (write \0x00).
ticket_data.write(b'\x00')
# Ticket ID.
ticket_data.write(self.ticket_id)
# Console ID.
ticket_data.write(int.to_bytes(self.console_id, 4))
# Title ID.
ticket_data.write(binascii.unhexlify(self.title_id))
# Unknown data 1.
ticket_data.write(self.unknown1)
# Title version.
title_version_high = round(self.title_version / 256)
ticket_data.write(int.to_bytes(title_version_high, 1))
title_version_low = self.title_version % 256
ticket_data.write(int.to_bytes(title_version_low, 1))
# Permitted titles mask.
ticket_data.write(self.permitted_titles)
# Permit mask.
ticket_data.write(self.permit_mask)
# Title Export allowed.
ticket_data.write(int.to_bytes(self.title_export_allowed, 1))
# Common Key index.
ticket_data.write(int.to_bytes(self.common_key_index, 1))
# Unknown data 2.
ticket_data.write(self.unknown2)
# Content access permissions.
ticket_data.write(self.content_access_permissions)
# Padding (always \x00).
ticket_data.write(b'\x00\x00')
# Iterate over Title Limit objects, write them back into raw data, then add them to the Ticket.
for title_limit in range(len(self.title_limits_list)):
title_limit_data = io.BytesIO()
# Write all fields from the title limit entry.
title_limit_data.write(int.to_bytes(self.title_limits_list[title_limit].limit_type, 4))
title_limit_data.write(int.to_bytes(self.title_limits_list[title_limit].maximum_usage, 4))
# Seek to the start and write the entry to the Ticket.
title_limit_data.seek(0x0)
ticket_data.write(title_limit_data.read())
title_limit_data.close()
# Set the Ticket attribute of the object to the new raw Ticket.
ticket_data.seek(0x0)
ticket_data_raw = ticket_data.read()
ticket_data = b''
# Signature type.
ticket_data += self.signature_type
# Signature data.
ticket_data += self.signature
# Padding to 64 bytes.
ticket_data += b'\x00' * 60
# Signature issuer.
ticket_data += str.encode(self.signature_issuer)
# ECDH data.
ticket_data += self.ecdh_data
# Ticket version.
ticket_data += int.to_bytes(self.ticket_version, 1)
# Reserved (all \0x00).
ticket_data += b'\x00\x00'
# Title Key.
ticket_data += self.title_key_enc
# Unknown (write \0x00).
ticket_data += b'\x00'
# Ticket ID.
ticket_data += self.ticket_id
# Console ID.
ticket_data += int.to_bytes(self.console_id, 4)
# Title ID.
ticket_data += binascii.unhexlify(self.title_id)
# Unknown data 1.
ticket_data += self.unknown1
# Title version.
title_version_high = round(self.title_version / 256)
ticket_data += int.to_bytes(title_version_high, 1)
title_version_low = self.title_version % 256
ticket_data += int.to_bytes(title_version_low, 1)
# Permitted titles mask.
ticket_data += self.permitted_titles
# Permit mask.
ticket_data += self.permit_mask
# Title Export allowed.
ticket_data += int.to_bytes(self.title_export_allowed, 1)
# Common Key index.
ticket_data += int.to_bytes(self.common_key_index, 1)
# Unknown data 2.
ticket_data += self.unknown2
# Content access permissions.
ticket_data += self.content_access_permissions
# Padding (always \x00).
ticket_data += b'\x00\x00'
# Iterate over Title Limit objects, write them back into raw data, then add them to the Ticket.
for title_limit in range(len(self.title_limits_list)):
title_limit_data = b''
# Write all fields from the title limit entry.
title_limit_data += int.to_bytes(self.title_limits_list[title_limit].limit_type, 4)
title_limit_data += int.to_bytes(self.title_limits_list[title_limit].maximum_usage, 4)
# Write the entry to the ticket.
ticket_data += title_limit_data
# Return the raw TMD for the data contained in the object.
return ticket_data_raw
return ticket_data
def get_title_id(self) -> str:
"""

View File

@ -148,78 +148,72 @@ class TMD:
bytes
The full TMD file as bytes.
"""
# Open the stream and begin writing to it.
with io.BytesIO() as tmd_data:
# Signed blob header.
tmd_data.write(self.blob_header)
# Signing certificate issuer.
tmd_data.write(self.issuer)
# TMD version.
tmd_data.write(int.to_bytes(self.tmd_version, 1))
# Root certificate crl version.
tmd_data.write(int.to_bytes(self.ca_crl_version, 1))
# Signer crl version.
tmd_data.write(int.to_bytes(self.signer_crl_version, 1))
# If this is a vWii title or not.
tmd_data.write(int.to_bytes(self.vwii, 1))
# IOS Title ID.
tmd_data.write(binascii.unhexlify(self.ios_tid))
# Title's Title ID.
tmd_data.write(binascii.unhexlify(self.title_id))
# Content type.
tmd_data.write(binascii.unhexlify(self.content_type))
# Group ID.
tmd_data.write(int.to_bytes(self.group_id, 2))
# 2 bytes of zero for reasons.
tmd_data.write(b'\x00\x00')
# Region.
tmd_data.write(int.to_bytes(self.region, 2))
# Ratings.
tmd_data.write(self.ratings)
# Reserved (all \x00).
tmd_data.write(b'\x00' * 12)
# IPC mask.
tmd_data.write(self.ipc_mask)
# Reserved (all \x00).
tmd_data.write(b'\x00' * 18)
# Access rights.
tmd_data.write(self.access_rights)
# Title version.
title_version_high = round(self.title_version / 256)
tmd_data.write(int.to_bytes(title_version_high, 1))
title_version_low = self.title_version % 256
tmd_data.write(int.to_bytes(title_version_low, 1))
# Number of contents.
tmd_data.write(int.to_bytes(self.num_contents, 2))
# Boot index.
tmd_data.write(int.to_bytes(self.boot_index, 2))
# Minor version. Unused so write \x00.
tmd_data.write(b'\x00\x00')
# Iterate over content records, write them back into raw data, then add them to the TMD.
for content_record in range(self.num_contents):
content_data = io.BytesIO()
# Write all fields from the content record.
content_data.write(int.to_bytes(self.content_records[content_record].content_id, 4))
content_data.write(int.to_bytes(self.content_records[content_record].index, 2))
content_data.write(int.to_bytes(self.content_records[content_record].content_type, 2))
content_data.write(int.to_bytes(self.content_records[content_record].content_size, 8))
content_data.write(binascii.unhexlify(self.content_records[content_record].content_hash))
# Seek to the start and write the record to the TMD.
content_data.seek(0x0)
tmd_data.write(content_data.read())
content_data.close()
# Set the TMD attribute of the object to the new raw TMD.
tmd_data.seek(0x0)
tmd_data_raw = tmd_data.read()
tmd_data = b''
# Signed blob header.
tmd_data += self.blob_header
# Signing certificate issuer.
tmd_data += self.issuer
# TMD version.
tmd_data += int.to_bytes(self.tmd_version, 1)
# Root certificate crl version.
tmd_data += int.to_bytes(self.ca_crl_version, 1)
# Signer crl version.
tmd_data += int.to_bytes(self.signer_crl_version, 1)
# If this is a vWii title or not.
tmd_data += int.to_bytes(self.vwii, 1)
# IOS Title ID.
tmd_data += binascii.unhexlify(self.ios_tid)
# Title's Title ID.
tmd_data += binascii.unhexlify(self.title_id)
# Content type.
tmd_data += binascii.unhexlify(self.content_type)
# Group ID.
tmd_data += int.to_bytes(self.group_id, 2)
# 2 bytes of zero for reasons.
tmd_data += b'\x00\x00'
# Region.
tmd_data += int.to_bytes(self.region, 2)
# Ratings.
tmd_data += self.ratings
# Reserved (all \x00).
tmd_data += b'\x00' * 12
# IPC mask.
tmd_data += self.ipc_mask
# Reserved (all \x00).
tmd_data += b'\x00' * 18
# Access rights.
tmd_data += self.access_rights
# Title version.
title_version_high = round(self.title_version / 256)
tmd_data += int.to_bytes(title_version_high, 1)
title_version_low = self.title_version % 256
tmd_data += int.to_bytes(title_version_low, 1)
# Number of contents.
tmd_data += int.to_bytes(self.num_contents, 2)
# Boot index.
tmd_data += int.to_bytes(self.boot_index, 2)
# Minor version. Unused so write \x00.
tmd_data += b'\x00\x00'
# Iterate over content records, write them back into raw data, then add them to the TMD.
for content_record in range(self.num_contents):
content_data = b''
# Write all fields from the content record.
content_data += int.to_bytes(self.content_records[content_record].content_id, 4)
content_data += int.to_bytes(self.content_records[content_record].index, 2)
content_data += int.to_bytes(self.content_records[content_record].content_type, 2)
content_data += int.to_bytes(self.content_records[content_record].content_size, 8)
content_data += binascii.unhexlify(self.content_records[content_record].content_hash)
# Write the record to the TMD.
tmd_data += content_data
# Return the raw TMD for the data contained in the object.
return tmd_data_raw
return tmd_data
def get_title_region(self) -> str:
"""
Gets the region of the TMD's associated title.
Can be one of several possible values:
'JAP', 'USA', 'EUR', 'NONE', or 'KOR'.
'JAP', 'USA', 'EUR', 'WORLD', or 'KOR'.
Returns
-------
@ -234,7 +228,7 @@ class TMD:
case 2:
return "EUR"
case 3:
return "NONE"
return "WORLD"
case 4:
return "KOR"

View File

@ -5,7 +5,7 @@
import io
import binascii
from .shared import align_value, pad_bytes_stream
from .shared import align_value, pad_bytes
class WAD:
@ -15,7 +15,7 @@ class WAD:
Attributes
----------
wad_type : str
The type of WAD, either ib for boot2 or Is for normal installable WADs. libWiiPy only supports Is currently.
The type of WAD, either ib for boot2 or Is for normal installable WADs.
wad_cert_size : int
The size of the WAD's certificate.
wad_crl_size : int
@ -60,8 +60,8 @@ class WAD:
The data for the WAD you wish to load.
"""
with io.BytesIO(wad_data) as wad_data:
# Read the first 8 bytes of the file to ensure that it's a WAD. This will currently reject boot2 WADs, but
# this tool cannot handle them correctly right now anyway.
# Read the first 8 bytes of the file to ensure that it's a WAD. Has two possible valid values for the two
# different types of WADs that might be encountered.
wad_data.seek(0x0)
wad_magic_bin = wad_data.read(8)
wad_magic_hex = binascii.hexlify(wad_magic_bin)
@ -140,50 +140,46 @@ class WAD:
bytes
The full WAD file as bytes.
"""
# Open the stream and begin writing data to it.
with io.BytesIO() as wad_data:
# Lead-in data.
wad_data.write(b'\x00\x00\x00\x20')
# WAD type.
wad_data.write(str.encode(self.wad_type))
# WAD version.
wad_data.write(self.wad_version)
# WAD cert size.
wad_data.write(int.to_bytes(self.wad_cert_size, 4))
# WAD crl size.
wad_data.write(int.to_bytes(self.wad_crl_size, 4))
# WAD ticket size.
wad_data.write(int.to_bytes(self.wad_tik_size, 4))
# WAD TMD size.
wad_data.write(int.to_bytes(self.wad_tmd_size, 4))
# WAD content size.
wad_data.write(int.to_bytes(self.wad_content_size, 4))
# WAD meta size.
wad_data.write(int.to_bytes(self.wad_meta_size, 4))
wad_data = pad_bytes_stream(wad_data)
# Retrieve the cert data and write it out.
wad_data.write(self.get_cert_data())
wad_data = pad_bytes_stream(wad_data)
# Retrieve the crl data and write it out.
wad_data.write(self.get_crl_data())
wad_data = pad_bytes_stream(wad_data)
# Retrieve the ticket data and write it out.
wad_data.write(self.get_ticket_data())
wad_data = pad_bytes_stream(wad_data)
# Retrieve the TMD data and write it out.
wad_data.write(self.get_tmd_data())
wad_data = pad_bytes_stream(wad_data)
# Retrieve the meta/footer data and write it out.
wad_data.write(self.get_meta_data())
wad_data = pad_bytes_stream(wad_data)
# Retrieve the content data and write it out.
wad_data.write(self.get_content_data())
wad_data = pad_bytes_stream(wad_data)
# Seek to the beginning and save this as the WAD data for the object.
wad_data.seek(0x0)
wad_data_raw = wad_data.read()
wad_data = b''
# Lead-in data.
wad_data += b'\x00\x00\x00\x20'
# WAD type.
wad_data += str.encode(self.wad_type)
# WAD version.
wad_data += self.wad_version
# WAD cert size.
wad_data += int.to_bytes(self.wad_cert_size, 4)
# WAD crl size.
wad_data += int.to_bytes(self.wad_crl_size, 4)
# WAD ticket size.
wad_data += int.to_bytes(self.wad_tik_size, 4)
# WAD TMD size.
wad_data += int.to_bytes(self.wad_tmd_size, 4)
# WAD content size.
wad_data += int.to_bytes(self.wad_content_size, 4)
# WAD meta size.
wad_data += int.to_bytes(self.wad_meta_size, 4)
wad_data = pad_bytes(wad_data)
# Retrieve the cert data and write it out.
wad_data += self.get_cert_data()
wad_data = pad_bytes(wad_data)
# Retrieve the crl data and write it out.
wad_data += self.get_crl_data()
wad_data = pad_bytes(wad_data)
# Retrieve the ticket data and write it out.
wad_data += self.get_ticket_data()
wad_data = pad_bytes(wad_data)
# Retrieve the TMD data and write it out.
wad_data += self.get_tmd_data()
wad_data = pad_bytes(wad_data)
# Retrieve the meta/footer data and write it out.
wad_data += self.get_meta_data()
wad_data = pad_bytes(wad_data)
# Retrieve the content data and write it out.
wad_data += self.get_content_data()
wad_data = pad_bytes(wad_data)
# Return the raw WAD file for the data contained in the object.
return wad_data_raw
return wad_data
def get_wad_type(self) -> str:
"""