Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cart dump fails verification if it contains a title key encrypted partition yet contains a tik file. #178

Open
TheExpertNoob opened this issue May 5, 2024 · 9 comments

Comments

@TheExpertNoob
Copy link

This cart dump does contain a titlekey for its update. Within the tik, is the title key.
Below is the log output from nsz:

could not find title key 010066E01A9E2800!
Error while verifying file: D:\path\Shadows Over Loathing [010066E01A9E2000][v196608].xci
 Traceback (most recent call last):
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\__init__.py", line 309, in main
     verify(filePath, args.fix_padding, True, True)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\__init__.py", line 82, in verify
     NszVerify(filePath, fixPadding, raiseVerificationException, raisePfs0Exception, originalFilePath, statusReportInfo, pleaseNoPrint)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\NszDecompressor.py", line 48, in verify
     __decompressXcz(filePath, None, fixPadding, False, raiseVerificationException, raisePfs0Exception, originalFilePath, statusReportInfo, pleaseNoPrint)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\NszDecompressor.py", line 272, in __decompressXcz
     container.open(str(filePath), 'rb')
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Xci.py", line 317, in open
     self.partition(self.hfs0Offset + self.headerOffset, None, self.hfs0, cryptoKey = None)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
     n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Hfs0.py", line 148, in open
     self.files.append(self.partition(offset + headerSize, f.size, f))
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
     n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Hfs0.py", line 148, in open
     self.files.append(self.partition(offset + headerSize, f.size, f))
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
     n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Nca.py", line 246, in open
     fs.open(None, 'rb')
   File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Pfs0.py", line 237, in open
     raise IOError('Not a valid PFS0 partition ' + str(self.magic))
 OSError: Not a valid PFS0 partition b'\x87\x9eG\xc9'

Summary of errors which occurred while processing files:
Error while processing D:\path\Shadows Over Loathing [010066E01A9E2000][v196608].xci
Traceback (most recent call last):
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\__init__.py", line 309, in main
    verify(filePath, args.fix_padding, True, True)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\__init__.py", line 82, in verify
    NszVerify(filePath, fixPadding, raiseVerificationException, raisePfs0Exception, originalFilePath, statusReportInfo, pleaseNoPrint)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\NszDecompressor.py", line 48, in verify
    __decompressXcz(filePath, None, fixPadding, False, raiseVerificationException, raisePfs0Exception, originalFilePath, statusReportInfo, pleaseNoPrint)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\NszDecompressor.py", line 272, in __decompressXcz
    container.open(str(filePath), 'rb')
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Xci.py", line 317, in open
    self.partition(self.hfs0Offset + self.headerOffset, None, self.hfs0, cryptoKey = None)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
    n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Hfs0.py", line 148, in open
    self.files.append(self.partition(offset + headerSize, f.size, f))
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
    n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Hfs0.py", line 148, in open
    self.files.append(self.partition(offset + headerSize, f.size, f))
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\File.py", line 67, in partition
    n.open(None, None, cryptoType, cryptoKey, cryptoCounter)
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Nca.py", line 246, in open
    fs.open(None, 'rb')
  File "D:\nsz_v4.6.1\lib\site-packages\nsz\Fs\Pfs0.py", line 237, in open
    raise IOError('Not a valid PFS0 partition ' + str(self.magic))
OSError: Not a valid PFS0 partition b'\x87\x9eG\xc9

I have also attached some screenshots of the same cart dump from NXFileViewer which also can't find the titlekey (AccessKey) in the tik file. Which according to switchbrew docs, is where it should be?
ValidXCI
BlankKey
Key
Adding this title key to the title key database manually allows NXFileViewer to decrypt the partition as intended, but also still does not parse the titlekey from the tik inside the dump.

@TheExpertNoob
Copy link
Author

TheExpertNoob commented May 8, 2024

Something else I tried was pulling the latest repo as of this issue and added titlekeys.txt inside the nsz folder (also tried the root folder ./titlekeys.txt). Added the RightsID|TitleKey|name to said txt file and tried reverification. Apparently this is not the intended use of ExtractTitlekeys.py as I still received the same error?

@nicoboss
Copy link
Owner

nicoboss commented May 9, 2024

nsz never reads titlekeys.txt and instead obtains the titlekey directly from the ticket. Here the code that does so:

def getTitleKeyBlock(self):
	self.seekStart(0x40)
	self.titleKeyBlock = self.readInt(0x10, 'big')
	return self.titleKeyBlock

def titleKey(self):
	return format(self.getTitleKeyBlock(), 'X').zfill(32)

The file format of the ticket file is documented on https://switchbrew.org/wiki/Ticket. The fact that booth NXFileViewer and NSZ fail to optain the titlekey from the ticket seems to indicate that something causes the titlekey extraction to fail. Are you sure you have dumped and copied the latest prod.keys to %userprofile/.switch? Other missing keys could cause titlekey decription to fail which would explain who two independently developed tools experience the same issue.

@nicoboss
Copy link
Owner

nicoboss commented May 9, 2024

It only failing verification seams interesting. Are you just using NSZ to verify your dump or are you compressing your dump which is sucessful but it then fails the post-compression verification? The ticket file is copied over during compression/decompression as is so this causing the ticket to soehow get corrupted seems quite strange.

@nicoboss
Copy link
Owner

nicoboss commented May 9, 2024

Are you experience this issue just for this specific game or are other affected as well? The file name kind of confuses me. As far I’m aware the game cartages usually only contain the base game but this one seems to be the base game but with update v196608 included. I need to check my dumps again but I thought they were all v0. Maybe there is an option in nxdumptool to include updates or maybe you used a tool to later merge the cartage dump with an update dump. In any case I have the feeling XCI with updates included might not well-tested.

@TheExpertNoob
Copy link
Author

I am going to try and answer these the best I can in order.
I do have the latest prod.keys in the proper folder. Decryption only fails as both programs fail to extract the key. (manually adding the key to titlekeys.txt does allow for proper decryption in NXFileViewer).
NXFileViewer decrypts all the rest of the file including the ticket as I can obtain the titlekey manually.
I was initially using NSZ to compress and it fails immediately saying it couldn't find the titlekey (just verifying also fails)
Shin chan Shiro and the Coal Town [010049901D704000][v65536].xci a cart dump that also contains an update fails.
I have a compressed cart dump of TerraTech that included DLC and an update as well, thoguh I believe I compressed it with NSCB and it seems to have done fine with that software, yet NXFileViewer and NSZ complain about not being able to find the key.
I just recently started using NSZ from NSCB.

Just now tried compressing Shadows Over Loating with NSCB and it started up just fine! This is weird.

-------------------------------------------------------------------------------------
                        NINTENDO SWITCH CLEANER AND BUILDER
                     (THE XCI MULTI CONTENT BUILDER AND MORE)
-------------------------------------------------------------------------------------
=============================     BY JULESONTHEROAD     =============================
-------------------------------------------------------------------------------------
"                                POWERED BY SQUIRREL                                "
"                    BASED ON THE WORK OF BLAWAR AND LUCA FRAGA                     "
                                   VERSION 1.01
-------------------------------------------------------------------------------------
Program's github: https://github.com/julesontheroad/NSC_BUILDER
Blawar's github:  https://github.com/blawar
Luca Fraga's github: https://github.com/LucaFraga
-------------------------------------------------------------------------------------
*******************************
COMPRESS A NSP\XCI
*******************************
- Titlerights: 010066e01a9e28000000000000000010
- Titlekey: 1186BA638A938CA1CA238C640E3ECA22
Compressing with level 22 and 0 threads

Also to note, this is a cart dump with nxdumptool with default settings or settings I have been using.

@TheExpertNoob
Copy link
Author

Also looking at tickets.py padding seems to be set at 0x40 and nothing for signature types of padding 0x3C.

self.signaturePadding = 0x40 - ((self.signatureSizes[self.signatureType] + 4) % 0x40)

per https://switchbrew.org/wiki/Ticket

Could that be something to do with it?

@nicoboss
Copy link
Owner

nicoboss commented May 15, 2024

Your signature is at 0x3C?

Also looking at tickets.py padding seems to be set at 0x40 and nothing for signature types of padding 0x3C.

self.signaturePadding = 0x40 - ((self.signatureSizes[self.signatureType] + 4) % 0x40)

per https://switchbrew.org/wiki/Ticket

Could that be something to do with it?

Yes that could be the issue if you have a signature that isn't of type ECDSA. RSA signatures have a padding of 0x3D but I don't remember ever seeing them in an official ticket. What's the ticket signature type of your dump? Try just hardcoding 0x3C instead of 0x40 and see if that fixes your issue. If it does, I will make sure to implement support for the other signature types as this would indicate that someone actually started using them.

@TheExpertNoob
Copy link
Author

Ticket is of type 010004, RSA-2048 PKCS#1 v1.5 with SHA-256
size, 0x100
padding, 0x3C
I was reading that line as padding mathematics the modulo threw me off. Math is correct.
0x 40-((100+4)/40) = 3C which is correct

@nicoboss
Copy link
Owner

If you don't care about a proper fix, you can likely just hardcode the result of the titleKey(self) function to immediately return the titlekey. I currently have no idea why the titlekey extraction fails for your dump but might investigate this further.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants