mirror of
https://github.com/rvtr/TDT.git
synced 2025-10-31 13:51:07 -04:00
Forge a ticket if a legit TMD was given
With this it's able to be launched from the DSi Menu without RSA patches
This commit is contained in:
parent
c4e202b366
commit
bbada58a7a
@ -3,7 +3,9 @@
|
|||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include "maketmd.h"
|
#include "maketmd.h"
|
||||||
|
#include "nand/crypto.h"
|
||||||
#include "nand/nandio.h"
|
#include "nand/nandio.h"
|
||||||
|
#include "nand/ticket0.h"
|
||||||
#include "rom.h"
|
#include "rom.h"
|
||||||
#include "storage.h"
|
#include "storage.h"
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
@ -282,6 +284,72 @@ static void _createBannerSav(tDSiHeader* h, char* dataPath)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void _createTicket(tDSiHeader *h, char* ticketPath)
|
||||||
|
{
|
||||||
|
if (!h) return;
|
||||||
|
|
||||||
|
iprintf("Forging ticket...");
|
||||||
|
swiWaitForVBlank();
|
||||||
|
|
||||||
|
if (!ticketPath)
|
||||||
|
{
|
||||||
|
iprintf("\x1B[31m"); //red
|
||||||
|
iprintf("Failed\n");
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const u32 encryptedSize = sizeof(ticket_v0_t) + 0x20;
|
||||||
|
u8 *buffer = (u8*)memalign(4, encryptedSize); //memalign might be needed for encryption, but not sure
|
||||||
|
memset(buffer, 0, encryptedSize);
|
||||||
|
ticket_v0_t *ticket = (ticket_v0_t*)buffer;
|
||||||
|
ticket->sig_type[0] = 0x00;
|
||||||
|
ticket->sig_type[1] = 0x01;
|
||||||
|
ticket->sig_type[2] = 0x00;
|
||||||
|
ticket->sig_type[3] = 0x01;
|
||||||
|
strcpy(ticket->issuer, "Root-CA00000001-XS00000006");
|
||||||
|
PUT_UINT32_BE(h->tid_high, ticket->title_id, 0);
|
||||||
|
PUT_UINT32_BE(h->tid_low, ticket->title_id, 4);
|
||||||
|
memset(ticket->content_access_permissions, 0xFF, 0x20);
|
||||||
|
|
||||||
|
// Encrypt
|
||||||
|
if (dsi_es_block_crypt(buffer, encryptedSize, ENCRYPT) != 0)
|
||||||
|
{
|
||||||
|
iprintf("\x1B[31m"); //red
|
||||||
|
iprintf("Failed\n");
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
free(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *file = fopen(ticketPath, "wb");
|
||||||
|
if (!file)
|
||||||
|
{
|
||||||
|
iprintf("\x1B[31m"); //red
|
||||||
|
iprintf("Failed\n");
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
free(buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fwrite(buffer, 1, encryptedSize, file) != encryptedSize)
|
||||||
|
{
|
||||||
|
iprintf("\x1B[31m"); //red
|
||||||
|
iprintf("Failed\n");
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
iprintf("\x1B[42m"); //green
|
||||||
|
iprintf("Done\n");
|
||||||
|
iprintf("\x1B[47m"); //white
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool install(char* fpath, bool systemTitle)
|
bool install(char* fpath, bool systemTitle)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
@ -360,12 +428,21 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
clearScreen(&bottomScreen);
|
clearScreen(&bottomScreen);
|
||||||
iprintf("Installing %s\n\n", fpath); swiWaitForVBlank();
|
iprintf("Installing %s\n\n", fpath); swiWaitForVBlank();
|
||||||
|
|
||||||
|
//check for legit TMD, if found we'll generate a ticket which increases the size
|
||||||
|
int extensionPos = strrchr(fpath, '.') - fpath;
|
||||||
|
char tmdPath[PATH_MAX];
|
||||||
|
strcpy(tmdPath, fpath);
|
||||||
|
strcpy(tmdPath + extensionPos, ".tmd");
|
||||||
|
//DSi TMDs are 520, TMDs from NUS are 2,312. If 2,312 we can simply trim it to 520
|
||||||
|
bool tmdFound = (getFileSizePath(tmdPath) >= 520) || (getFileSizePath(tmdPath) == 2312);
|
||||||
|
|
||||||
//get install size
|
//get install size
|
||||||
iprintf("Install Size: ");
|
iprintf("Install Size: ");
|
||||||
swiWaitForVBlank();
|
swiWaitForVBlank();
|
||||||
|
|
||||||
unsigned long long fileSize = getRomSize(fpath);
|
unsigned long long fileSize = getRomSize(fpath);
|
||||||
unsigned long long installSize = fileSize + _getSaveDataSize(h);
|
unsigned long long installSize = fileSize + _getSaveDataSize(h);
|
||||||
|
if (tmdFound) installSize += 708;
|
||||||
|
|
||||||
printBytes(installSize);
|
printBytes(installSize);
|
||||||
iprintf("\n");
|
iprintf("\n");
|
||||||
@ -404,9 +481,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//check for saves or legit tmd
|
//check for saves
|
||||||
int extensionPos = strrchr(fpath, '.') - fpath;
|
|
||||||
|
|
||||||
char pubPath[PATH_MAX];
|
char pubPath[PATH_MAX];
|
||||||
strcpy(pubPath, fpath);
|
strcpy(pubPath, fpath);
|
||||||
strcpy(pubPath + extensionPos, ".pub");
|
strcpy(pubPath + extensionPos, ".pub");
|
||||||
@ -443,11 +518,6 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
char tmdPath[PATH_MAX];
|
|
||||||
strcpy(tmdPath, fpath);
|
|
||||||
strcpy(tmdPath + extensionPos, ".tmd");
|
|
||||||
bool tmdFound = getFileSizePath(tmdPath) == 520;
|
|
||||||
|
|
||||||
if (_iqueHack(h))
|
if (_iqueHack(h))
|
||||||
fixHeader = true;
|
fixHeader = true;
|
||||||
|
|
||||||
@ -607,7 +677,7 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
sprintf(newTmdPath, "%s/title.tmd", contentPath);
|
sprintf(newTmdPath, "%s/title.tmd", contentPath);
|
||||||
if (tmdFound)
|
if (tmdFound)
|
||||||
{
|
{
|
||||||
if (copyFile(tmdPath, newTmdPath) != 0)
|
if (copyFilePart(tmdPath, 0, 520, newTmdPath) != 0)
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -659,6 +729,23 @@ bool install(char* fpath, bool systemTitle)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//ticket folder /ticket/XXXXXXXX
|
||||||
|
if (tmdFound)
|
||||||
|
{
|
||||||
|
//ensure folders exist
|
||||||
|
char ticketPath[32];
|
||||||
|
siprintf(ticketPath, "%s:/ticket", sdnandMode ? "sd" : "nand");
|
||||||
|
mkdir(ticketPath, 0777);
|
||||||
|
siprintf(ticketPath, "%s/%08lx", ticketPath, h->tid_high);
|
||||||
|
mkdir(ticketPath, 0777);
|
||||||
|
|
||||||
|
//actual tik path
|
||||||
|
siprintf(ticketPath, "%s/%08lx.tik", ticketPath, h->tid_low);
|
||||||
|
|
||||||
|
if (access(ticketPath, F_OK) != 0 || choicePrint("Ticket already exists.\nKeep it?") == NO)
|
||||||
|
_createTicket(h, ticketPath);
|
||||||
|
}
|
||||||
|
|
||||||
//end
|
//end
|
||||||
result = true;
|
result = true;
|
||||||
iprintf("\x1B[42m"); //green
|
iprintf("\x1B[42m"); //green
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
static dsi_context nand_ctx ;
|
static dsi_context nand_ctx ;
|
||||||
static dsi_context boot2_ctx ;
|
static dsi_context boot2_ctx ;
|
||||||
static dsi_context es_ctx ;
|
static dsi_es_context es_ctx ;
|
||||||
|
|
||||||
static uint8_t nand_ctr_iv[16];
|
static uint8_t nand_ctr_iv[16];
|
||||||
static uint8_t boot2_ctr[16];
|
static uint8_t boot2_ctr[16];
|
||||||
@ -62,13 +62,17 @@ void dsi_crypt_init(const uint8_t *console_id_be, const uint8_t *emmc_cid, int i
|
|||||||
GET_UINT32_BE(console_id[1], console_id_be, 0);
|
GET_UINT32_BE(console_id[1], console_id_be, 0);
|
||||||
|
|
||||||
uint8_t key[16];
|
uint8_t key[16];
|
||||||
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
|
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
|
||||||
dsi_set_key(&nand_ctx, key) ;
|
dsi_set_key(&nand_ctx, key) ;
|
||||||
|
|
||||||
generate_key(key, console_id, ES);
|
u32 normalkey[4];
|
||||||
dsi_set_key(&es_ctx, key) ;
|
u32 tadsrl_keyX[4] = {0x4E00004A, 0x4A00004E, 0, 0};
|
||||||
|
tadsrl_keyX[2] = console_id[1] ^ 0xC80C4B72;
|
||||||
|
tadsrl_keyX[3] = console_id[0];
|
||||||
|
F_XY((u8 *)normalkey, (u8 *)tadsrl_keyX, DSi_ES_KEY_Y);
|
||||||
|
dsi_es_init(&es_ctx, (u8*)normalkey);
|
||||||
|
|
||||||
dsi_set_key(&boot2_ctx, DSi_BOOT2_KEY) ;
|
dsi_set_key(&boot2_ctx, DSi_BOOT2_KEY) ;
|
||||||
|
|
||||||
swiSHA1Calc(nand_ctr_iv, emmc_cid, 16);
|
swiSHA1Calc(nand_ctr_iv, emmc_cid, 16);
|
||||||
|
|
||||||
@ -99,7 +103,17 @@ void dsi_nand_crypt(uint8_t* out, const uint8_t* in, uint32_t offset, unsigned c
|
|||||||
u128_add32(ctr, 1);
|
u128_add32(ctr, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int dsi_es_block_crypt(uint8_t *buf, unsigned buf_len, crypt_mode_t mode)
|
||||||
|
{
|
||||||
|
if(mode == DECRYPT)
|
||||||
|
return dsi_es_decrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
|
||||||
|
else
|
||||||
|
dsi_es_encrypt(&es_ctx, buf, buf + buf_len - 0x20, buf_len - 0x20);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void dsi_boot2_crypt_set_ctr(uint32_t size_r)
|
void dsi_boot2_crypt_set_ctr(uint32_t size_r)
|
||||||
{
|
{
|
||||||
for (int i=0;i<4;i++)
|
for (int i=0;i<4;i++)
|
||||||
|
|||||||
132
arm9/src/nand/ticket0.h
Normal file
132
arm9/src/nand/ticket0.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma pack(push, 1)
|
||||||
|
#define PACKED
|
||||||
|
#elif ! defined PACKED
|
||||||
|
#define PACKED __attribute__ ((__packed__))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define RSA_2048_LEN (2048/8)
|
||||||
|
|
||||||
|
// most, if not all, are big endian
|
||||||
|
|
||||||
|
// http://problemkaputt.de/gbatek.htm#dsisdmmcdsiwareticketsandtitlemetadata
|
||||||
|
// http://dsibrew.org/wiki/Ticket
|
||||||
|
// http://wiibrew.org/wiki/Ticket
|
||||||
|
typedef struct {
|
||||||
|
uint8_t sig_type[4];
|
||||||
|
uint8_t sig[RSA_2048_LEN];
|
||||||
|
uint8_t padding0[0x3c];
|
||||||
|
char issuer[0x40];
|
||||||
|
uint8_t ecdh[0x3c];
|
||||||
|
uint8_t padding1[3];
|
||||||
|
uint8_t encrypted_title_key[0x10];
|
||||||
|
uint8_t unknown0;
|
||||||
|
uint8_t ticket_id[8];
|
||||||
|
uint8_t console_id[4];
|
||||||
|
uint8_t title_id[8];
|
||||||
|
uint8_t unknown1[2];
|
||||||
|
uint8_t version[2];
|
||||||
|
uint8_t permitted_titles_mask[4];
|
||||||
|
uint8_t permit_mask[4];
|
||||||
|
uint8_t title_export_allowed;
|
||||||
|
uint8_t common_key_index;
|
||||||
|
uint8_t unknown[0x30];
|
||||||
|
uint8_t content_access_permissions[0x40];
|
||||||
|
uint8_t padding2[2];
|
||||||
|
uint8_t time_limits[2 * 8 * sizeof(uint32_t)];
|
||||||
|
} PACKED ticket_v0_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(ticket_v0_t) == 0x2a4, "invalid sizeof(ticket_v0_t)");
|
||||||
|
|
||||||
|
// http://dsibrew.org/wiki/Tmd
|
||||||
|
// http://wiibrew.org/wiki/Title_metadata
|
||||||
|
typedef struct {
|
||||||
|
uint8_t sig_type[4];
|
||||||
|
uint8_t sig[RSA_2048_LEN];
|
||||||
|
uint8_t padding0[0x3c];
|
||||||
|
char issuer[0x40];
|
||||||
|
uint8_t version;
|
||||||
|
uint8_t ca_crl_version;
|
||||||
|
uint8_t signer_crl_version;
|
||||||
|
uint8_t padding1;
|
||||||
|
uint8_t system_version[8];
|
||||||
|
uint8_t title_id[8];
|
||||||
|
uint8_t title_type[4];
|
||||||
|
uint8_t group_id[2];
|
||||||
|
uint8_t public_save_size[4];
|
||||||
|
uint8_t private_save_size[4];
|
||||||
|
uint8_t padding2[8];
|
||||||
|
uint8_t parent_control[0x10];
|
||||||
|
uint8_t padding3[0x1e];
|
||||||
|
uint8_t access_rights[4];
|
||||||
|
uint8_t title_version[2];
|
||||||
|
uint8_t num_content[2];
|
||||||
|
uint8_t boot_index[2];
|
||||||
|
uint8_t padding4[2];
|
||||||
|
} PACKED tmd_header_v0_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(tmd_header_v0_t) == 0x1e4, "invalid sizeof(tmd_header_v0_t)");
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t content_id[4];
|
||||||
|
uint8_t index[2];
|
||||||
|
uint8_t type[2];
|
||||||
|
uint8_t size[8];
|
||||||
|
uint8_t sha1[20];
|
||||||
|
} PACKED tmd_content_v0_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(tmd_content_v0_t) == 0x24, "invalid sizeof(tmd_contend_v0_t)");
|
||||||
|
|
||||||
|
// used in ticket encryption
|
||||||
|
// http://problemkaputt.de/gbatek.htm#dsiesblockencryption
|
||||||
|
|
||||||
|
#define AES_CCM_MAC_LEN 0x10
|
||||||
|
#define AES_CCM_NONCE_LEN 0x0c
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t ccm_mac[AES_CCM_MAC_LEN];
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
uint8_t fixed_3a;
|
||||||
|
uint8_t nonce[AES_CCM_NONCE_LEN];
|
||||||
|
uint8_t len24be[3];
|
||||||
|
};
|
||||||
|
struct {
|
||||||
|
uint8_t padding[AES_CCM_NONCE_LEN];
|
||||||
|
// defined for convenience, it's still big endian with only 24 effective bits
|
||||||
|
// read it as 32 bit big endian and discard the highest 8 bits
|
||||||
|
uint8_t len32be[4];
|
||||||
|
};
|
||||||
|
uint8_t encrypted[0x10];
|
||||||
|
};
|
||||||
|
} PACKED es_block_footer_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(es_block_footer_t) == 0x20, "invalid sizeof(es_block_footer_t)");
|
||||||
|
|
||||||
|
// used in cert.sys
|
||||||
|
// http://problemkaputt.de/gbatek.htm#dsisdmmcfirmwaredevkpandcertsyscertificatefiles
|
||||||
|
// "DSi SD/MMC Firmware dev.kp and cert.sys Certificate Files"
|
||||||
|
typedef struct {
|
||||||
|
uint32_t signature_type;
|
||||||
|
uint8_t signature[RSA_2048_LEN];
|
||||||
|
uint8_t padding0[0x3c];
|
||||||
|
char signature_name[0x40];
|
||||||
|
uint32_t key_type;
|
||||||
|
char key_name[0x40];
|
||||||
|
uint32_t key_flags;
|
||||||
|
uint8_t rsa_key[RSA_2048_LEN];
|
||||||
|
uint8_t rsa_exp[4];
|
||||||
|
uint8_t padding1[0x34];
|
||||||
|
} PACKED cert_t;
|
||||||
|
|
||||||
|
static_assert(sizeof(cert_t) == 0x300, "invalid sizeof(cert_t)");
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#pragma pack(pop)
|
||||||
|
#endif
|
||||||
|
#undef PACKED
|
||||||
Loading…
Reference in New Issue
Block a user