luigoalma be289b4c55 Just search both nands for certs on callee
Since in all cases that LoadCertFromCertDb is called
is always twice, one for sysnand and another for emunand
just make it a single call and quit early when cert found.
2021-05-22 14:12:19 +02:00

150 lines
5.3 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 cert from certs.db
if (LoadCertFromCertDb(&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
};
static const char* const retail_issuers[] = {"Root-CA00000003-CP0000000b", "Root-CA00000003"};
static const char* const dev_issuers[] = {"Root-CA00000004-CP0000000a", "Root-CA00000004"};
size_t size = TMD_CDNCERT_SIZE;
if (BuildRawCertBundleFromCertDb(tmdcert, &size, !IS_DEVKIT ? retail_issuers : dev_issuers, 2) ||
size != TMD_CDNCERT_SIZE) {
return 1;
}
// 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;
}