mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-26 20:22:17 +00:00
Some small changes.
* tik: check for common certificate availability before attempting to convert a personalized ticket to a common one. The raw common certificate chain data for the ticket signature issuer is now returned. * usb: skip waitMulti call in the USB background thread if an invalid endpoint max packet size was received from the USB host in the previous while loop iteration.
This commit is contained in:
parent
971d7fd72c
commit
679aa170b5
4 changed files with 60 additions and 21 deletions
38
source/tik.c
38
source/tik.c
|
@ -121,7 +121,7 @@ bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gam
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
bool tikConvertPersonalizedTicketToCommonTicket(Ticket *tik, u8 **out_raw_cert_chain, u64 *out_raw_cert_chain_size)
|
||||||
{
|
{
|
||||||
TikCommonBlock *tik_common_block = NULL;
|
TikCommonBlock *tik_common_block = NULL;
|
||||||
|
|
||||||
|
@ -130,9 +130,34 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
||||||
u64 signature_size = 0;
|
u64 signature_size = 0;
|
||||||
|
|
||||||
bool dev_cert = false;
|
bool dev_cert = false;
|
||||||
|
char cert_chain_issuer[0x40] = {0};
|
||||||
|
static const char *common_cert_names[] = { "XS00000020", "XS00000022", NULL };
|
||||||
|
|
||||||
|
u8 *raw_cert_chain = NULL;
|
||||||
|
u64 raw_cert_chain_size = 0;
|
||||||
|
|
||||||
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < SIGNED_TIK_MIN_SIZE || tik->size > SIGNED_TIK_MAX_SIZE || \
|
if (!tik || tik->type == TikType_None || tik->type > TikType_SigHmac160 || tik->size < SIGNED_TIK_MIN_SIZE || tik->size > SIGNED_TIK_MAX_SIZE || \
|
||||||
!(tik_common_block = tikGetCommonBlock(tik->data)) || tik_common_block->titlekey_type != TikTitleKeyType_Personalized) return;
|
!(tik_common_block = tikGetCommonBlock(tik->data)) || tik_common_block->titlekey_type != TikTitleKeyType_Personalized || !out_raw_cert_chain || !out_raw_cert_chain_size)
|
||||||
|
{
|
||||||
|
LOGFILE("Invalid parameters!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Generate raw certificate chain for the new signature issuer (common). */
|
||||||
|
dev_cert = (strstr(tik_common_block->issuer, "CA00000004") != NULL);
|
||||||
|
|
||||||
|
for(u8 i = 0; common_cert_names[i] != NULL; i++)
|
||||||
|
{
|
||||||
|
sprintf(cert_chain_issuer, "Root-CA%08X-%s", dev_cert ? 4 : 3, common_cert_names[i]);
|
||||||
|
raw_cert_chain = certGenerateRawCertificateChainBySignatureIssuer(cert_chain_issuer, &raw_cert_chain_size);
|
||||||
|
if (raw_cert_chain) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!raw_cert_chain)
|
||||||
|
{
|
||||||
|
LOGFILE("Failed to generate raw certificate chain for common ticket signature issuer!");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/* Wipe signature. */
|
/* Wipe signature. */
|
||||||
sig_type = signatureGetSigType(tik->data, false);
|
sig_type = signatureGetSigType(tik->data, false);
|
||||||
|
@ -141,9 +166,8 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
||||||
memset(signature, 0xFF, signature_size);
|
memset(signature, 0xFF, signature_size);
|
||||||
|
|
||||||
/* Change signature issuer. */
|
/* Change signature issuer. */
|
||||||
dev_cert = (strstr(tik_common_block->issuer, "CA00000004") != NULL);
|
|
||||||
memset(tik_common_block->issuer, 0, sizeof(tik_common_block->issuer));
|
memset(tik_common_block->issuer, 0, sizeof(tik_common_block->issuer));
|
||||||
sprintf(tik_common_block->issuer, "Root-CA%08X-XS00000020", (dev_cert ? 4 : 3));
|
sprintf(tik_common_block->issuer, "%s", cert_chain_issuer);
|
||||||
|
|
||||||
/* Wipe the titlekey block and copy the titlekek-encrypted titlekey to it. */
|
/* Wipe the titlekey block and copy the titlekek-encrypted titlekey to it. */
|
||||||
memset(tik_common_block->titlekey_block, 0, sizeof(tik_common_block->titlekey_block));
|
memset(tik_common_block->titlekey_block, 0, sizeof(tik_common_block->titlekey_block));
|
||||||
|
@ -164,6 +188,12 @@ void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik)
|
||||||
tik_common_block->sect_hdr_entry_size = 0;
|
tik_common_block->sect_hdr_entry_size = 0;
|
||||||
|
|
||||||
memset(tik->data + tik->size, 0, SIGNED_TIK_MAX_SIZE - tik->size);
|
memset(tik->data + tik->size, 0, SIGNED_TIK_MAX_SIZE - tik->size);
|
||||||
|
|
||||||
|
/* Update output pointers. */
|
||||||
|
*out_raw_cert_chain = raw_cert_chain;
|
||||||
|
*out_raw_cert_chain_size = raw_cert_chain_size;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsId *id)
|
static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsId *id)
|
||||||
|
|
|
@ -173,9 +173,10 @@ typedef struct {
|
||||||
/// Titlekey is also RSA-OAEP unwrapped (if needed) and titlekek decrypted right away.
|
/// Titlekey is also RSA-OAEP unwrapped (if needed) and titlekek decrypted right away.
|
||||||
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gamecard);
|
bool tikRetrieveTicketByRightsId(Ticket *dst, const FsRightsId *id, bool use_gamecard);
|
||||||
|
|
||||||
/// This will convert a TikTitleKeyType_Personalized ticket into a TikTitleKeyType_Common ticket.
|
/// Converts a TikTitleKeyType_Personalized ticket into a TikTitleKeyType_Common ticket and generates a raw certificate chain for the new signature issuer.
|
||||||
/// Bear in mind the 'size' member from the Ticket parameter will be updated by this function to remove any possible references to ESV1/ESV2 records.
|
/// Bear in mind the 'size' member from the Ticket parameter will be updated by this function to remove any possible references to ESV1/ESV2 records.
|
||||||
void tikConvertPersonalizedTicketToCommonTicket(Ticket *tik);
|
/// Raw certificate chain data will be saved to the provided pointers. certGenerateRawCertificateChainBySignatureIssuer() is used internally, so the output buffer must be freed by the user.
|
||||||
|
bool tikConvertPersonalizedTicketToCommonTicket(Ticket *tik, u8 **out_raw_cert_chain, u64 *out_raw_cert_chain_size);
|
||||||
|
|
||||||
/// Helper inline functions.
|
/// Helper inline functions.
|
||||||
|
|
||||||
|
|
24
source/usb.c
24
source/usb.c
|
@ -431,16 +431,20 @@ static void usbDetectionThreadFunc(void *arg)
|
||||||
|
|
||||||
Result rc = 0;
|
Result rc = 0;
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
|
bool skip_wait = false;
|
||||||
|
|
||||||
Waiter usb_change_event_waiter = waiterForEvent(g_usbStateChangeEvent);
|
Waiter usb_change_event_waiter = waiterForEvent(g_usbStateChangeEvent);
|
||||||
Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent);
|
Waiter usb_timeout_event_waiter = waiterForUEvent(&g_usbTimeoutEvent);
|
||||||
Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
|
Waiter exit_event_waiter = waiterForUEvent(&g_usbDetectionThreadExitEvent);
|
||||||
|
|
||||||
while(true)
|
while(true)
|
||||||
|
{
|
||||||
|
if (!skip_wait)
|
||||||
{
|
{
|
||||||
/* Wait until an event is triggered. */
|
/* Wait until an event is triggered. */
|
||||||
rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter);
|
rc = waitMulti(&idx, -1, usb_change_event_waiter, usb_timeout_event_waiter, exit_event_waiter);
|
||||||
if (R_FAILED(rc)) continue;
|
if (R_FAILED(rc)) continue;
|
||||||
|
}
|
||||||
|
|
||||||
rwlockWriteLock(&g_usbDeviceLock);
|
rwlockWriteLock(&g_usbDeviceLock);
|
||||||
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
rwlockWriteLock(&(g_usbDeviceInterface.lock));
|
||||||
|
@ -451,7 +455,7 @@ static void usbDetectionThreadFunc(void *arg)
|
||||||
/* Retrieve current USB connection status. */
|
/* Retrieve current USB connection status. */
|
||||||
/* Only proceed if we're dealing with a status change. */
|
/* Only proceed if we're dealing with a status change. */
|
||||||
g_usbHostAvailable = usbIsHostAvailable();
|
g_usbHostAvailable = usbIsHostAvailable();
|
||||||
g_usbSessionStarted = false;
|
g_usbSessionStarted = skip_wait = false;
|
||||||
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
|
||||||
g_usbEndpointMaxPacketSize = 0;
|
g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
|
@ -463,13 +467,18 @@ static void usbDetectionThreadFunc(void *arg)
|
||||||
if (g_usbHostAvailable)
|
if (g_usbHostAvailable)
|
||||||
{
|
{
|
||||||
/* Wait until a session is established. */
|
/* Wait until a session is established. */
|
||||||
/* If the session is successfully established, then we'll get the endpoint max packet size from the data chunk sent by the USB host. */
|
g_usbSessionStarted = usbStartSession();
|
||||||
|
if (g_usbSessionStarted)
|
||||||
|
{
|
||||||
|
/* Get the endpoint max packet size from the response sent by the USB host. */
|
||||||
/* This is done to accurately know when and where to enable Zero Length Termination (ZLT) packets during bulk transfers. */
|
/* This is done to accurately know when and where to enable Zero Length Termination (ZLT) packets during bulk transfers. */
|
||||||
/* As much as I'd like to avoid this, usb:ds doesn't disclose information such as the exact device descriptor and/or speed used by the USB host. */
|
/* As much as I'd like to avoid this, usb:ds doesn't disclose information such as the exact device descriptor and/or speed used by the USB host. */
|
||||||
g_usbSessionStarted = (usbStartSession() && usbGetMaxPacketSizeFromHost());
|
/* If this step fails (e.g. unexpected max packet size), the write endpoint will be stalled and we'll wait until a new USB session is established. */
|
||||||
|
if (!(skip_wait = !usbGetMaxPacketSizeFromHost())) LOGFILE("USB session successfully established. Endpoint max packet size: 0x%04X.", g_usbEndpointMaxPacketSize);
|
||||||
|
} else {
|
||||||
/* Check if the exit event was triggered while waiting for a session to be established. */
|
/* Check if the exit event was triggered while waiting for a session to be established. */
|
||||||
if (!g_usbSessionStarted && g_usbDetectionThreadExitFlag) break;
|
if (g_usbDetectionThreadExitFlag) break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
|
||||||
|
@ -545,14 +554,13 @@ static bool usbGetMaxPacketSizeFromHost(void)
|
||||||
usbDsEndpoint_Stall(g_usbDeviceInterface.endpoint_in);
|
usbDsEndpoint_Stall(g_usbDeviceInterface.endpoint_in);
|
||||||
rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in));
|
rwlockWriteUnlock(&(g_usbDeviceInterface.lock_in));
|
||||||
|
|
||||||
/* Reset endpoint max packet size. */
|
/* Reset flags. */
|
||||||
|
g_usbSessionStarted = false;
|
||||||
g_usbEndpointMaxPacketSize = 0;
|
g_usbEndpointMaxPacketSize = 0;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGFILE("USB session successfully established. Endpoint max packet size: 0x%04X.", g_usbEndpointMaxPacketSize);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
2
todo.txt
2
todo.txt
|
@ -4,7 +4,7 @@ todo:
|
||||||
nca: function to write re-encrypted nca headers / nca fs headers (don't forget nca0 please)
|
nca: function to write re-encrypted nca headers / nca fs headers (don't forget nca0 please)
|
||||||
nca: function to patch the private npdm acid signature from a program nca + patch the acid signature from the nca header
|
nca: function to patch the private npdm acid signature from a program nca + patch the acid signature from the nca header
|
||||||
|
|
||||||
tik: make sure the common crypto cert (XS20, XS22) is available when fakesigning a ticket
|
tik: option to wipe elicense property mask
|
||||||
tik: automatically dump tickets to the SD card?
|
tik: automatically dump tickets to the SD card?
|
||||||
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
tik: use dumped tickets when the original ones can't be found in the ES savefile?
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue