mirror of
https://github.com/xprism1/ntool.git
synced 2025-06-18 16:25:33 -04:00
utils: add cia2cdn
This commit is contained in:
parent
a4e07b796a
commit
2cc05ae420
@ -40,6 +40,14 @@ python3 ntool.py cci2cia <path_to_cci> (--out <path_to_output_file>) (--cci_dev)
|
||||
python3 ntool.py cdn2cia <path_to_cdn_folder> (--out <path_to_output_file>) (--title-ver <ver>) (--cdn-dev) (--cia-dev)
|
||||
```
|
||||
|
||||
### Convert CIA to CDN contents
|
||||
- Pass `--titlekey` to use a custom titlekey to encrypt the content files (this field will be ignored if the ticket in the CIA is signed)
|
||||
- Pass `--cia-dev` if the CIA is dev-signed
|
||||
- Note that clean CDN contents are not guaranteed as the CIA may have improper contents (e.g. due to being decrypted)
|
||||
```py
|
||||
python3 ntool.py cia2cdn <path_to_cia> (--out <path_to_output_folder>) (--titlekey <titlekey>) (--cia-dev)
|
||||
```
|
||||
|
||||
### Full extraction and rebuild of NCCH/CIA/CCI:
|
||||
- First, use `ncch_extractall`/`cia_extractall`/`cci_extractall` to extract the NCCH/CIA/CCI to a folder
|
||||
- Pass the `--dev` flag to use dev crypto
|
||||
|
@ -111,14 +111,15 @@ class CDNReader:
|
||||
)
|
||||
|
||||
class CDNBuilder:
|
||||
def __init__(self, content_files=[], tik='', tmd='', dev=0, out_tik='tik_new'):
|
||||
def __init__(self, content_files=[], tik='', tmd='', titlekey='', dev=0, out='new'):
|
||||
'''
|
||||
content_files: list containing filenames of content files, which must each be named '[content index in hex, 4 chars].[contentID in hex, 8 chars].[ncch/nds]'
|
||||
Certificate chain will be appended at the end of the following files:
|
||||
- tik: path to ticket (optional)
|
||||
- tmd: path to tmd
|
||||
titlekey: decrypted title key in hex, will be used if provided and ticket is not provided (if neither ticket nor titlekey is provided, use titlekey generation algorithm)
|
||||
dev: 0 or 1 (if 1, use dev-crypto for ticket titlekey)
|
||||
out_tik: path to output ticket with cert chain appended
|
||||
out: path to output folder
|
||||
'''
|
||||
|
||||
content_files.sort(key=lambda h: int(h.split('.')[0], 16))
|
||||
@ -131,25 +132,35 @@ class CDNBuilder:
|
||||
if tik != '': # If ticket is present, parse ticket to get titlekey
|
||||
self.tik_read = tikReader(tik, dev)
|
||||
self.titlekey = self.tik_read.titlekey
|
||||
else: # Use titlekey generation algorithm
|
||||
self.titlekey = hextobytes(CTR.titlekey_gen(self.tmd_read.titleID, 'mypass'))
|
||||
else:
|
||||
if titlekey != '':
|
||||
self.titlekey = hextobytes(titlekey)
|
||||
else: # Use titlekey generation algorithm
|
||||
self.titlekey = hextobytes(CTR.titlekey_gen(self.tmd_read.titleID, 'mypass'))
|
||||
|
||||
if not os.path.isdir(out):
|
||||
os.makedirs(out)
|
||||
|
||||
# Encrypt content files
|
||||
for i in self.content_files:
|
||||
info = self.tmd_read.files[i]
|
||||
name = i.split('.')[1] # CDN files are named as contentID
|
||||
if 'iv' in info:
|
||||
iv = info['iv']
|
||||
else:
|
||||
iv = int(i.split('.')[0], 16).to_bytes(2, 'big') + (b'\0' * 14)
|
||||
f = open(i, 'rb')
|
||||
g = open(name, 'wb')
|
||||
cipher = AES.new(self.titlekey, AES.MODE_CBC, iv=info['iv'])
|
||||
g = open(os.path.join(out, name), 'wb')
|
||||
cipher = AES.new(self.titlekey, AES.MODE_CBC, iv=iv)
|
||||
for data in read_chunks(f, info['size']):
|
||||
g.write(cipher.encrypt(data))
|
||||
f.close()
|
||||
g.close()
|
||||
print(f'Wrote to {name}')
|
||||
print(f'Wrote to {os.path.join(out, name)}')
|
||||
|
||||
# Append certificate chain to end of tmd (and tik)
|
||||
name = f'tmd.{self.tmd_read.hdr.title_ver}'
|
||||
with open(name, 'wb') as f:
|
||||
with open(os.path.join(out, name), 'wb') as f:
|
||||
with open(tmd, 'rb') as g:
|
||||
f.write(g.read())
|
||||
if dev == 0:
|
||||
@ -162,10 +173,14 @@ class CDNBuilder:
|
||||
f.write(g.read())
|
||||
with open(os.path.join(resources_dir, 'CA00000004.cert'), 'rb') as g:
|
||||
f.write(g.read())
|
||||
print(f'Wrote to {name}')
|
||||
print(f'Wrote to {os.path.join(out, name)}')
|
||||
|
||||
if self.tik != '':
|
||||
with open(f'{out_tik}', 'wb') as f:
|
||||
if self.tik_read.data.consoleID == 0:
|
||||
tik_name = 'cetk'
|
||||
else:
|
||||
tik_name = 'tik'
|
||||
with open(os.path.join(out, tik_name), 'wb') as f:
|
||||
with open(tik, 'rb') as g:
|
||||
f.write(g.read())
|
||||
if dev == 0:
|
||||
@ -178,4 +193,4 @@ class CDNBuilder:
|
||||
f.write(g.read())
|
||||
with open(os.path.join(resources_dir, 'CA00000004.cert'), 'rb') as g:
|
||||
f.write(g.read())
|
||||
print(f'Wrote to {out_tik}')
|
||||
print(f'Wrote to {os.path.join(out, tik_name)}')
|
||||
|
16
ntool.py
16
ntool.py
@ -45,4 +45,18 @@ elif sys.argv[1] == 'cdn2cia':
|
||||
cdn_dev = 1
|
||||
elif sys.argv[i] == '--cia-dev':
|
||||
cia_dev = 1
|
||||
cdn2cia(path, out, title_ver, cdn_dev, cia_dev)
|
||||
cdn2cia(path, out, title_ver, cdn_dev, cia_dev)
|
||||
|
||||
elif sys.argv[1] == 'cia2cdn':
|
||||
path = sys.argv[2]
|
||||
out = ''
|
||||
titlekey = ''
|
||||
cia_dev = 0
|
||||
for i in range(2, len(sys.argv)):
|
||||
if sys.argv[i] == '--out':
|
||||
out = sys.argv[i + 1]
|
||||
elif sys.argv[i] == '--titlekey':
|
||||
titlekey = sys.argv[i + 1]
|
||||
elif sys.argv[i] == '--cia-dev':
|
||||
cia_dev = 1
|
||||
cia2cdn(path, out, titlekey, cia_dev)
|
25
utils.py
25
utils.py
@ -7,7 +7,7 @@ from lib.ctr_romfs import RomFSReader, RomFSBuilder
|
||||
from lib.ctr_crr import crrReader
|
||||
from lib.ctr_tmd import TMDReader, TMDBuilder
|
||||
from lib.ctr_tik import tikReader, tikBuilder
|
||||
from lib.ctr_cdn import CDNReader
|
||||
from lib.ctr_cdn import CDNReader, CDNBuilder
|
||||
from lib.ctr_cnt import cntReader
|
||||
|
||||
def cia_dev2retail(path, out=''):
|
||||
@ -569,6 +569,29 @@ def cdn2cia(path, out='', title_ver='', cdn_dev=0, cia_dev=0):
|
||||
os.chdir('..')
|
||||
shutil.move('tmp.cia', out)
|
||||
|
||||
def cia2cdn(path, out='', titlekey='', cia_dev=0):
|
||||
name = os.path.splitext(os.path.basename(path))[0]
|
||||
if out == '':
|
||||
out = name
|
||||
|
||||
cia = CIAReader(path, cia_dev)
|
||||
cia.extract()
|
||||
for i in ['cia_header.bin', 'cert.bin', 'meta.bin']:
|
||||
if os.path.isfile(i):
|
||||
os.remove(i)
|
||||
|
||||
tik = 'tik'
|
||||
tik_read = tikReader(tik)
|
||||
if not tik_read.verify()[0][1]: # Ticket has invalid sig
|
||||
tik = ''
|
||||
|
||||
cf = [i for i in os.listdir('.') if i.endswith('.ncch') or i.endswith('.nds')]
|
||||
CDNBuilder(content_files=cf, tik=tik, tmd='tmd', titlekey=titlekey, out=out)
|
||||
|
||||
for i in ['tik', 'tmd'] + cf:
|
||||
if os.path.isfile(i):
|
||||
os.remove(i)
|
||||
|
||||
def csu2retailcias(path, out=''):
|
||||
if out == '':
|
||||
out = 'updates_retail/'
|
||||
|
Loading…
Reference in New Issue
Block a user