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 "message.h"
|
||||
#include "maketmd.h"
|
||||
#include "nand/crypto.h"
|
||||
#include "nand/nandio.h"
|
||||
#include "nand/ticket0.h"
|
||||
#include "rom.h"
|
||||
#include "storage.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 result = false;
|
||||
@ -360,12 +428,21 @@ bool install(char* fpath, bool systemTitle)
|
||||
clearScreen(&bottomScreen);
|
||||
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
|
||||
iprintf("Install Size: ");
|
||||
swiWaitForVBlank();
|
||||
|
||||
unsigned long long fileSize = getRomSize(fpath);
|
||||
unsigned long long installSize = fileSize + _getSaveDataSize(h);
|
||||
if (tmdFound) installSize += 708;
|
||||
|
||||
printBytes(installSize);
|
||||
iprintf("\n");
|
||||
@ -404,9 +481,7 @@ bool install(char* fpath, bool systemTitle)
|
||||
}
|
||||
}
|
||||
|
||||
//check for saves or legit tmd
|
||||
int extensionPos = strrchr(fpath, '.') - fpath;
|
||||
|
||||
//check for saves
|
||||
char pubPath[PATH_MAX];
|
||||
strcpy(pubPath, fpath);
|
||||
strcpy(pubPath + extensionPos, ".pub");
|
||||
@ -443,11 +518,6 @@ bool install(char* fpath, bool systemTitle)
|
||||
goto error;
|
||||
}
|
||||
|
||||
char tmdPath[PATH_MAX];
|
||||
strcpy(tmdPath, fpath);
|
||||
strcpy(tmdPath + extensionPos, ".tmd");
|
||||
bool tmdFound = getFileSizePath(tmdPath) == 520;
|
||||
|
||||
if (_iqueHack(h))
|
||||
fixHeader = true;
|
||||
|
||||
@ -607,7 +677,7 @@ bool install(char* fpath, bool systemTitle)
|
||||
sprintf(newTmdPath, "%s/title.tmd", contentPath);
|
||||
if (tmdFound)
|
||||
{
|
||||
if (copyFile(tmdPath, newTmdPath) != 0)
|
||||
if (copyFilePart(tmdPath, 0, 520, newTmdPath) != 0)
|
||||
goto error;
|
||||
}
|
||||
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
|
||||
result = true;
|
||||
iprintf("\x1B[42m"); //green
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
static dsi_context nand_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 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);
|
||||
|
||||
uint8_t key[16];
|
||||
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
|
||||
dsi_set_key(&nand_ctx, key) ;
|
||||
generate_key(key, console_id, is3DS ? NAND_3DS : NAND);
|
||||
dsi_set_key(&nand_ctx, key) ;
|
||||
|
||||
generate_key(key, console_id, ES);
|
||||
dsi_set_key(&es_ctx, key) ;
|
||||
u32 normalkey[4];
|
||||
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);
|
||||
|
||||
@ -99,7 +103,17 @@ void dsi_nand_crypt(uint8_t* out, const uint8_t* in, uint32_t offset, unsigned c
|
||||
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)
|
||||
{
|
||||
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