forked from Mirror/GodMode9
Except for cia building or loading cia just yet. Added more checks on ticket content index, mainly due to having effects in the ticket format itself, and are unknown still. Ability to determine ticket size. Verify signature with ticket's proper size. Changes to use the new Ticket struct with the flexible array member.
107 lines
4.0 KiB
C
107 lines
4.0 KiB
C
#include "ticket.h"
|
|
#include "unittype.h"
|
|
#include "cert.h"
|
|
#include "sha.h"
|
|
#include "rsa.h"
|
|
#include "ff.h"
|
|
|
|
u32 ValidateTicket(Ticket* ticket) {
|
|
const u8 magic[] = { TICKET_SIG_TYPE };
|
|
if ((memcmp(ticket->sig_type, magic, sizeof(magic)) != 0) ||
|
|
((strncmp((char*) ticket->issuer, TICKET_ISSUER, 0x40) != 0) &&
|
|
(strncmp((char*) ticket->issuer, TICKET_ISSUER_DEV, 0x40) != 0)) ||
|
|
(ticket->commonkey_idx >= 6) ||
|
|
(getbe32(&ticket->content_index[0]) != 0x10014) ||
|
|
(getbe32(&ticket->content_index[4]) < 0x14) ||
|
|
(getbe32(&ticket->content_index[12]) != 0x10014) ||
|
|
(getbe32(&ticket->content_index[16]) != 0))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
u32 ValidateTicketSignature(Ticket* ticket) {
|
|
static bool got_modexp = false;
|
|
static u32 mod[0x100 / 0x4] = { 0 };
|
|
static u32 exp = 0;
|
|
|
|
if (!got_modexp) {
|
|
// grab mod/exp from cert from cert.db
|
|
if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0)
|
|
got_modexp = true;
|
|
else return 1;
|
|
}
|
|
|
|
if (!RSA_setKey2048(3, mod, exp) ||
|
|
!RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildFakeTicket(Ticket* ticket, u8* title_id) {
|
|
const u8 sig_type[4] = { TICKET_SIG_TYPE }; // RSA_2048 SHA256
|
|
const u8 ticket_cnt_index[] = { // whatever this is
|
|
0x00, 0x01, 0x00, 0x14, 0x00, 0x00, 0x00, 0xAC, 0x00, 0x00, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x84,
|
|
0x00, 0x00, 0x00, 0x84, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
|
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
|
|
};
|
|
// set ticket all zero for a clean start
|
|
memset(ticket, 0x00, TICKET_COMMON_SIZE); // 0xAC being size of this fake ticket's content index
|
|
// fill ticket values
|
|
memcpy(ticket->sig_type, sig_type, 4);
|
|
memset(ticket->signature, 0xFF, 0x100);
|
|
snprintf((char*) ticket->issuer, 0x40, IS_DEVKIT ? TICKET_ISSUER_DEV : TICKET_ISSUER);
|
|
memset(ticket->ecdsa, 0xFF, 0x3C);
|
|
ticket->version = 0x01;
|
|
memset(ticket->titlekey, 0xFF, 16);
|
|
memcpy(ticket->title_id, title_id, 8);
|
|
ticket->commonkey_idx = 0x00; // eshop
|
|
ticket->audit = 0x01; // whatever
|
|
memcpy(ticket->content_index, ticket_cnt_index, sizeof(ticket_cnt_index));
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 GetTicketContentIndexSize(const Ticket* ticket) {
|
|
return getbe32(&ticket->content_index[4]);
|
|
}
|
|
|
|
u32 GetTicketSize(const Ticket* ticket) {
|
|
return sizeof(Ticket) + GetTicketContentIndexSize(ticket);
|
|
}
|
|
|
|
u32 BuildTicketCert(u8* tickcert) {
|
|
const u8 cert_hash_expected[0x20] = {
|
|
0xDC, 0x15, 0x3C, 0x2B, 0x8A, 0x0A, 0xC8, 0x74, 0xA9, 0xDC, 0x78, 0x61, 0x0E, 0x6A, 0x8F, 0xE3,
|
|
0xE6, 0xB1, 0x34, 0xD5, 0x52, 0x88, 0x73, 0xC9, 0x61, 0xFB, 0xC7, 0x95, 0xCB, 0x47, 0xE6, 0x97
|
|
};
|
|
const u8 cert_hash_expected_dev[0x20] = {
|
|
0x97, 0x2A, 0x32, 0xFF, 0x9D, 0x4B, 0xAA, 0x2F, 0x1A, 0x24, 0xCF, 0x21, 0x13, 0x87, 0xF5, 0x38,
|
|
0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E
|
|
};
|
|
|
|
// open certs.db file on SysNAND
|
|
FIL db;
|
|
UINT bytes_read;
|
|
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK)
|
|
return 1;
|
|
// grab ticket cert from 3 offsets
|
|
f_lseek(&db, 0x3F10);
|
|
f_read(&db, tickcert + 0x000, 0x300, &bytes_read);
|
|
f_lseek(&db, 0x0C10);
|
|
f_read(&db, tickcert + 0x300, 0x1F0, &bytes_read);
|
|
f_lseek(&db, 0x3A00);
|
|
f_read(&db, tickcert + 0x4F0, 0x210, &bytes_read);
|
|
f_close(&db);
|
|
|
|
// check the certificate hash
|
|
u8 cert_hash[0x20];
|
|
sha_quick(cert_hash, tickcert, TICKET_CDNCERT_SIZE, SHA256_MODE);
|
|
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|