mirror of
https://github.com/d0k3/GodMode9.git
synced 2025-06-26 13:42:47 +00:00
171 lines
6.1 KiB
C
171 lines
6.1 KiB
C
#include "tmd.h"
|
|
#include "unittype.h"
|
|
#include "cert.h"
|
|
#include "sha.h"
|
|
#include "rsa.h"
|
|
#include "ff.h"
|
|
|
|
u32 ValidateTmd(TitleMetaData* tmd) {
|
|
static const u8 magic[] = { TMD_SIG_TYPE };
|
|
if ((memcmp(tmd->sig_type, magic, sizeof(magic)) != 0) ||
|
|
((strncmp((char*) tmd->issuer, TMD_ISSUER, 0x40) != 0) &&
|
|
(strncmp((char*) tmd->issuer, TMD_ISSUER_DEV, 0x40) != 0)))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
u32 ValidateTwlTmd(TitleMetaData* tmd) {
|
|
static const u8 magic[] = { TMD_SIG_TYPE_TWL };
|
|
if ((memcmp(tmd->sig_type, magic, sizeof(magic)) != 0) ||
|
|
(strncmp((char*) tmd->issuer, TMD_ISSUER_TWL, 0x40) != 0) ||
|
|
(getbe16(tmd->content_count) != 1))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
u32 ValidateTmdSignature(TitleMetaData* tmd) {
|
|
Certificate cert;
|
|
u32 mod[2048/8];
|
|
u32 exp = 0;
|
|
|
|
// grab mod/exp from cert from cert.db
|
|
if (LoadCertFromCertDb(false, &cert, (char*)(tmd->issuer)) != 0 && LoadCertFromCertDb(true, &cert, (char*)(tmd->issuer)) != 0)
|
|
return 1;
|
|
|
|
// current code only expects RSA2048
|
|
u32 mod_size;
|
|
if (Certificate_GetModulusSize(&cert, &mod_size) != 0 ||
|
|
mod_size != 2048/8 ||
|
|
Certificate_GetModulus(&cert, &mod) != 0 ||
|
|
Certificate_GetExponent(&cert, &exp) != 0) {
|
|
Certificate_Cleanup(&cert);
|
|
return 1;
|
|
}
|
|
|
|
Certificate_Cleanup(&cert);
|
|
|
|
if (!RSA_setKey2048(3, mod, exp) ||
|
|
!RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 VerifyTmd(TitleMetaData* tmd) {
|
|
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
|
|
u32 content_count = getbe16(tmd->content_count);
|
|
|
|
// TMD validation
|
|
if (ValidateTmd(tmd) != 0) return 1;
|
|
|
|
// check content info hash
|
|
if (sha_cmp(tmd->contentinfo_hash, (u8*)tmd->contentinfo, 64 * sizeof(TmdContentInfo), SHA256_MODE) != 0)
|
|
return 1;
|
|
|
|
// check hashes in content info
|
|
for (u32 i = 0, kc = 0; i < 64 && kc < content_count; i++) {
|
|
TmdContentInfo* info = tmd->contentinfo + i;
|
|
u32 k = getbe16(info->cmd_count);
|
|
if (sha_cmp(info->hash, content_list + kc, k * sizeof(TmdContentChunk), SHA256_MODE) != 0)
|
|
return 1;
|
|
kc += k;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 GetTmdCtr(u8* ctr, TmdContentChunk* chunk) {
|
|
memset(ctr, 0, 16);
|
|
memcpy(ctr, chunk->index, 2);
|
|
return 0;
|
|
}
|
|
|
|
u32 FixTmdHashes(TitleMetaData* tmd) {
|
|
TmdContentChunk* content_list = (TmdContentChunk*) (tmd + 1);
|
|
u32 content_count = getbe16(tmd->content_count);
|
|
// recalculate content info hashes
|
|
for (u32 i = 0, kc = 0; i < 64 && kc < content_count; i++) {
|
|
TmdContentInfo* info = tmd->contentinfo + i;
|
|
u32 k = getbe16(info->cmd_count);
|
|
sha_quick(info->hash, content_list + kc, k * sizeof(TmdContentChunk), SHA256_MODE);
|
|
kc += k;
|
|
}
|
|
sha_quick(tmd->contentinfo_hash, (u8*)tmd->contentinfo, 64 * sizeof(TmdContentInfo), SHA256_MODE);
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildFakeTmd(TitleMetaData* tmd, u8* title_id, u32 n_contents, u32 save_size, u32 twl_privsave_size, u8 twl_flag) {
|
|
static const u8 sig_type[4] = { TMD_SIG_TYPE };
|
|
// safety check: number of contents
|
|
if (n_contents > TMD_MAX_CONTENTS) return 1; // potential incompatibility here (!)
|
|
// set TMD all zero for a clean start
|
|
memset(tmd, 0x00, TMD_SIZE_N(n_contents));
|
|
// file TMD values
|
|
memcpy(tmd->sig_type, sig_type, 4);
|
|
memset(tmd->signature, 0xFF, 0x100);
|
|
snprintf((char*) tmd->issuer, 0x40, IS_DEVKIT ? TMD_ISSUER_DEV : TMD_ISSUER);
|
|
tmd->version = 0x01;
|
|
memcpy(tmd->title_id, title_id, 8);
|
|
tmd->title_type[3] = 0x40; // whatever
|
|
for (u32 i = 0; i < 4; i++) tmd->save_size[i] = (save_size >> (i*8)) & 0xFF; // le save size
|
|
for (u32 i = 0; i < 4; i++) tmd->twl_privsave_size[i] = (twl_privsave_size >> (i*8)) & 0xFF; // le privsave size
|
|
tmd->twl_flag = twl_flag;
|
|
tmd->content_count[0] = (u8) ((n_contents >> 8) & 0xFF);
|
|
tmd->content_count[1] = (u8) (n_contents & 0xFF);
|
|
memset(tmd->contentinfo_hash, 0xFF, 0x20); // placeholder (hash)
|
|
memcpy(tmd->contentinfo[0].cmd_count, tmd->content_count, 2);
|
|
memset(tmd->contentinfo[0].hash, 0xFF, 0x20); // placeholder (hash)
|
|
// nothing to do for content list (yet)
|
|
|
|
return 0;
|
|
}
|
|
|
|
u32 BuildTmdCert(u8* tmdcert) {
|
|
static const u8 cert_hash_expected[0x20] = {
|
|
0x91, 0x5F, 0x77, 0x3A, 0x07, 0x82, 0xD4, 0x27, 0xC4, 0xCE, 0xF5, 0x49, 0x25, 0x33, 0xE8, 0xEC,
|
|
0xF6, 0xFE, 0xA1, 0xEB, 0x8C, 0xCF, 0x59, 0x6E, 0x69, 0xBA, 0x2A, 0x38, 0x8D, 0x73, 0x8A, 0xE1
|
|
};
|
|
static const u8 cert_hash_expected_dev[0x20] = {
|
|
0x49, 0xC9, 0x41, 0x56, 0xCA, 0x86, 0xBD, 0x1F, 0x36, 0x51, 0x51, 0x6A, 0x4A, 0x9F, 0x54, 0xA1,
|
|
0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71
|
|
};
|
|
|
|
const char* issuer_cp = !IS_DEVKIT ? "Root-CA00000003-CP0000000b" : "Root-CA00000004-CP0000000a";
|
|
const char* issuer_ca = !IS_DEVKIT ? "Root-CA00000003" : "Root-CA00000004";
|
|
|
|
// open certs.db file on SysNAND or EmuNAND
|
|
Certificate cert_cp;
|
|
Certificate cert_ca;
|
|
if (LoadCertFromCertDb(false, &cert_cp, issuer_cp) != 0 && LoadCertFromCertDb(true, &cert_cp, issuer_cp) != 0)
|
|
return 1;
|
|
|
|
if (LoadCertFromCertDb(false, &cert_ca, issuer_ca) != 0 && LoadCertFromCertDb(true, &cert_ca, issuer_ca) != 0) {
|
|
Certificate_Cleanup(&cert_cp);
|
|
return 1;
|
|
}
|
|
|
|
u32 cert_size_cp;
|
|
u32 cert_size_ca;
|
|
if (Certificate_GetFullSize(&cert_cp, &cert_size_cp) != 0 ||
|
|
cert_size_cp != 0x300 ||
|
|
Certificate_GetFullSize(&cert_ca, &cert_size_ca) != 0 ||
|
|
cert_size_ca != 0x400 ||
|
|
Certificate_RawCopy(&cert_cp, tmdcert) != 0 ||
|
|
Certificate_RawCopy(&cert_ca, &tmdcert[0x300]) != 0) {
|
|
Certificate_Cleanup(&cert_cp);
|
|
Certificate_Cleanup(&cert_ca);
|
|
return 1;
|
|
}
|
|
|
|
Certificate_Cleanup(&cert_cp);
|
|
Certificate_Cleanup(&cert_ca);
|
|
|
|
// check the certificate hash
|
|
u8 cert_hash[0x20];
|
|
sha_quick(cert_hash, tmdcert, TMD_CDNCERT_SIZE, SHA256_MODE);
|
|
if (memcmp(cert_hash, IS_DEVKIT ? cert_hash_expected_dev : cert_hash_expected, 0x20) != 0)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|