Load from certs.db more accordingly

Also extra cert handling code
This commit is contained in:
luigoalma 2021-04-22 00:22:35 +01:00 committed by d0k3
parent 236d2dc09c
commit 61c17e491f
5 changed files with 479 additions and 81 deletions

View File

@ -1,21 +1,313 @@
#include "cert.h" #include "cert.h"
#include "ff.h" #include "disadiff.h"
u32 LoadCertFromCertDb(u64 offset, Certificate* cert, u32* mod, u32* exp) { typedef struct {
Certificate cert_local; char magic[4]; // "CERT"
FIL db; u8 unk[4]; // afaik, always 0
UINT bytes_read; u8 used_size[4]; // size used after this header
u8 garbage[4]; // literally garbage values
} PACKED_STRUCT CertsDbPartitionHeader;
// not much in terms of error checking here static void GetCertDBPath(char* path, bool emunand) {
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK) path[0] = emunand ? '4' : '1';
strcpy(&path[1], ":/dbs/certs.db");
}
bool Certificate_IsValid(const Certificate* cert) {
if (!cert || !cert->sig || !cert->data)
return false;
u32 sig_type = getbe32(cert->sig->sig_type);
if (sig_type < 0x10000 || sig_type > 0x10005)
return false;
u32 keytype = getbe32(cert->data->keytype);
if (keytype > 2)
return false;
size_t issuer_len = strnlen(cert->data->issuer, 0x40);
size_t name_len = strnlen(cert->data->name, 0x40);
// if >= 0x40, cert can't fit as issuer for other objects later
// since later objects using the certificate as their issuer will have them use it as certissuer-certname
if (!issuer_len || !name_len || (issuer_len + name_len + 1) >= 0x40)
return false;
return true;
}
bool Certificate_IsRSA(const Certificate* cert) {
if (!Certificate_IsValid(cert)) return false;
if (getbe32(cert->data->keytype) >= 2) return false;
return true;
}
bool Certificate_IsECC(const Certificate* cert) {
if (!Certificate_IsValid(cert)) return false;
if (getbe32(cert->data->keytype) != 2) return false;
return true;
}
u32 Certificate_GetSignatureSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsValid(cert)) return 1;
u32 sig_type = getbe32(cert->sig->sig_type);
if (sig_type == 0x10000 || sig_type == 0x10003)
*size = 0x200;
else if (sig_type == 0x10001 || sig_type == 0x10004)
*size = 0x100;
else if (sig_type == 0x10002 || sig_type == 0x10005)
*size = 0x3C;
else
return 1; return 1;
f_lseek(&db, offset);
if (!cert) cert = &cert_local;
f_read(&db, cert, CERT_SIZE, &bytes_read);
f_close(&db);
if (mod) memcpy(mod, cert->mod, 0x100);
if (exp) *exp = getle32(cert->exp);
return 0; return 0;
} }
u32 Certificate_GetModulusSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsRSA(cert)) return 1;
u32 keytype = getbe32(cert->data->keytype);
if (keytype == 0)
*size = 4096 / 8;
else if (keytype == 1)
*size = 2048 / 8;
else return 1;
return 0;
}
u32 Certificate_GetModulus(const Certificate* cert, void* mod) {
u32 size;
if (!mod || Certificate_GetModulusSize(cert, &size)) return 1;
memcpy(mod, cert->data->pub_key_data, size);
return 0;
}
u32 Certificate_GetExponent(const Certificate* cert, void* exp) {
u32 size;
if (!exp || Certificate_GetModulusSize(cert, &size)) return 1;
memcpy(exp, &cert->data->pub_key_data[size], 4);
return 0;
}
u32 Certificate_GetEccSingleCoordinateSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsECC(cert)) return 1;
u32 keytype = getbe32(cert->data->keytype);
if (keytype == 2)
*size = 0x3C / 2;
else return 1;
return 0;
}
u32 Certificate_GetEccXY(const Certificate* cert, void* X, void* Y) {
u32 size;
if (!X || !Y || Certificate_GetEccSingleCoordinateSize(cert, &size)) return 1;
memcpy(X, cert->data->pub_key_data, size);
memcpy(Y, &cert->data->pub_key_data[size], size);
return 0;
}
static inline u32 _Certificate_GetSignatureChunkSizeFromType(u32 sig_type) {
if (sig_type == 0x10000 || sig_type == 0x10003)
return CERT_RSA4096_SIG_SIZE;
else if (sig_type == 0x10001 || sig_type == 0x10004)
return CERT_RSA2048_SIG_SIZE;
else if (sig_type == 0x10002 || sig_type == 0x10005)
return CERT_ECC_SIG_SIZE;
return 0;
}
u32 Certificate_GetSignatureChunkSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsValid(cert)) return 1;
u32 _size = _Certificate_GetSignatureChunkSizeFromType(getbe32(cert->sig->sig_type));
if (_size == 0) return 1;
*size = _size;
return 0;
}
static inline u32 _Certificate_GetDataChunkSizeFromType(u32 keytype) {
if (keytype == 0)
return CERT_RSA4096_BODY_SIZE;
else if (keytype == 1)
return CERT_RSA2048_BODY_SIZE;
else if (keytype == 2)
return CERT_ECC_BODY_SIZE;
return 0;
}
u32 Certificate_GetDataChunkSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsValid(cert)) return 1;
u32 _size = _Certificate_GetDataChunkSizeFromType(getbe32(cert->data->keytype));
if (_size == 0) return 1;
*size = _size;
return 0;
}
u32 Certificate_GetFullSize(const Certificate* cert, u32* size) {
if (!size || !Certificate_IsValid(cert)) return 1;
u32 sig_size = _Certificate_GetSignatureChunkSizeFromType(getbe32(cert->sig->sig_type));
u32 data_size = _Certificate_GetDataChunkSizeFromType(getbe32(cert->data->keytype));
if (sig_size == 0 || data_size == 0)
return 1;
*size = sig_size + data_size;
return 0;
}
u32 Certificate_RawCopy(const Certificate* cert, void* raw) {
if (!raw || !Certificate_IsValid(cert)) return 1;
u32 sig_size = _Certificate_GetSignatureChunkSizeFromType(getbe32(cert->sig->sig_type));
u32 data_size = _Certificate_GetDataChunkSizeFromType(getbe32(cert->data->keytype));
if (sig_size == 0 || data_size == 0)
return 1;
memcpy(raw, cert->sig, sig_size);
memcpy(&((u8*)raw)[sig_size], cert->data, data_size);
return 0;
}
u32 Certificate_Cleanup(Certificate* cert) {
if (!cert) return 1;
free(cert->sig);
free(cert->data);
cert->sig = NULL;
cert->data = NULL;
return 0;
}
u32 LoadCertFromCertDb(bool emunand, Certificate* cert, const char* issuer) {
if (!issuer || !cert) return 1;
Certificate cert_local = {NULL, NULL};
char path[16];
GetCertDBPath(path, emunand);
DisaDiffRWInfo info;
u8* cache = NULL;
if (GetDisaDiffRWInfo(path, &info, false) != 0) return 1;
cache = malloc(info.size_dpfs_lvl2);
if (!cache) return 1;
if (BuildDisaDiffDpfsLvl2Cache(path, &info, cache, info.size_dpfs_lvl2) != 0) {
free(cache);
return 1;
}
CertsDbPartitionHeader header;
if (ReadDisaDiffIvfcLvl4(path, &info, 0, sizeof(CertsDbPartitionHeader), &header) != sizeof(CertsDbPartitionHeader)) {
free(cache);
return 1;
}
if (getbe32(header.magic) != 0x43455254 /* 'CERT' */ ||
getbe32(header.unk) != 0 ||
getle32(header.used_size) & 0xFF) {
free(cache);
return 1;
}
u32 offset = sizeof(CertsDbPartitionHeader);
u32 max_offset = getle32(header.used_size) + sizeof(CertsDbPartitionHeader);
u32 ret = 1;
// certs.db has no filesystem.. its pretty plain, certificates after another
// but also, certificates are not equally sized
// so most cases of bad data, leads to giving up
while (offset < max_offset) {
char full_issuer[0x41];
u8 sig_type_data[4];
u8 keytype_data[4];
if (offset + 4 > max_offset) break;
if (ReadDisaDiffIvfcLvl4(path, &info, offset, 4, sig_type_data) != 4)
break;
u32 sig_type = getbe32(sig_type_data);
if (sig_type == 0x10002 || sig_type == 0x10005) break; // ECC signs not allowed on db
u32 sig_size = _Certificate_GetSignatureChunkSizeFromType(sig_type);
if (sig_size == 0) break;
u32 keytype_off = offset + sig_size + offsetof(CertificateBody, keytype);
if (keytype_off + 4 > max_offset) break;
if (ReadDisaDiffIvfcLvl4(path, &info, keytype_off, 4, keytype_data) != 4)
break;
u32 keytype = getbe32(keytype_data);
if (keytype == 2) break; // ECC keys not allowed on db
u32 data_size = _Certificate_GetDataChunkSizeFromType(keytype);
if (data_size == 0) break;
u32 full_size = sig_size + data_size;
if (offset + full_size > max_offset) break;
cert_local.sig = (CertificateSignature*)malloc(sig_size);
cert_local.data = (CertificateBody*)malloc(data_size);
if (!cert_local.sig || !cert_local.data)
break;
if (ReadDisaDiffIvfcLvl4(path, &info, offset, sig_size, cert_local.sig) != sig_size)
break;
if (ReadDisaDiffIvfcLvl4(path, &info, offset + sig_size, data_size, cert_local.data) != data_size)
break;
if (!Certificate_IsValid(&cert_local))
break;
if (snprintf(full_issuer, 0x41, "%s-%s", cert_local.data->issuer, cert_local.data->name) > 0x40)
break;
if (!strcmp(full_issuer, issuer)) {
ret = 0;
break;
}
Certificate_Cleanup(&cert_local);
offset += full_size;
}
if (ret) {
Certificate_Cleanup(&cert_local);
}
*cert = cert_local;
free(cache);
return ret;
}

View File

@ -2,21 +2,48 @@
#include "common.h" #include "common.h"
#define CERT_SIZE sizeof(Certificate) #define CERT_MAX_SIZE (sizeof(CertificateSignature) + 0x23C + sizeof(CertificateBody) + 0x238)
#define CERT_RSA4096_SIG_SIZE (sizeof(CertificateSignature) + 0x23C)
#define CERT_RSA2048_SIG_SIZE (sizeof(CertificateSignature) + 0x13C)
#define CERT_ECC_SIG_SIZE (sizeof(CertificateSignature) + 0x7C)
#define CERT_RSA4096_BODY_SIZE (sizeof(CertificateBody) + 0x238)
#define CERT_RSA2048_BODY_SIZE (sizeof(CertificateBody) + 0x138)
#define CERT_ECC_BODY_SIZE (sizeof(CertificateBody) + 0x78)
// from: http://3dbrew.org/wiki/Certificates // from: http://3dbrew.org/wiki/Certificates
// all numbers in big endian // all numbers in big endian
typedef struct { typedef struct {
u8 sig_type[4]; // expected: 0x010004 / RSA_2048 SHA256 u8 sig_type[4];
u8 signature[0x100]; u8 signature[];
u8 padding0[0x3C]; } PACKED_ALIGN(1) CertificateSignature;
u8 issuer[0x40];
u8 keytype[4]; // expected: 0x01 / RSA_2048
u8 name[0x40];
u8 unknown[4];
u8 mod[0x100];
u8 exp[0x04];
u8 padding1[0x34];
} PACKED_STRUCT Certificate;
u32 LoadCertFromCertDb(u64 offset, Certificate* cert, u32* mod, u32* exp); typedef struct {
char issuer[0x40];
u8 keytype[4];
char name[0x40];
u8 expiration[4];
u8 pub_key_data[];
} PACKED_ALIGN(1) CertificateBody;
typedef struct {
CertificateSignature* sig;
CertificateBody* data;
} Certificate;
bool Certificate_IsValid(const Certificate* cert);
bool Certificate_IsRSA(const Certificate* cert);
bool Certificate_IsECC(const Certificate* cert);
u32 Certificate_GetSignatureSize(const Certificate* cert, u32* size);
u32 Certificate_GetModulusSize(const Certificate* cert, u32* size);
u32 Certificate_GetModulus(const Certificate* cert, void* mod);
u32 Certificate_GetExponent(const Certificate* cert, void* exp);
u32 Certificate_GetEccSingleCoordinateSize(const Certificate* cert, u32* size);
u32 Certificate_GetEccXY(const Certificate* cert, void* X, void* Y);
u32 Certificate_GetSignatureChunkSize(const Certificate* cert, u32* size);
u32 Certificate_GetDataChunkSize(const Certificate* cert, u32* size);
u32 Certificate_GetFullSize(const Certificate* cert, u32* size);
u32 Certificate_RawCopy(const Certificate* cert, void* raw);
u32 Certificate_Cleanup(Certificate* cert);
u32 LoadCertFromCertDb(bool emunand, Certificate* cert, const char* issuer);

View File

@ -4,6 +4,7 @@
#include "ff.h" #include "ff.h"
#include "aes.h" #include "aes.h"
#include "sha.h" #include "sha.h"
#include "cert.h"
u32 ValidateCiaHeader(CiaHeader* header) { u32 ValidateCiaHeader(CiaHeader* header) {
if ((header->size_header != CIA_HEADER_SIZE) || if ((header->size_header != CIA_HEADER_SIZE) ||
@ -59,21 +60,49 @@ u32 BuildCiaCert(u8* ciacert) {
0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3 0x18, 0x83, 0xAF, 0xE0, 0xF4, 0xE5, 0x62, 0xBA, 0x69, 0xEE, 0x72, 0x2A, 0xC2, 0x4E, 0x95, 0xB3
}; };
// open certs.db file on SysNAND const char* issuer_ca = !IS_DEVKIT ? "Root-CA00000003" : "Root-CA00000004";
FIL db; const char* issuer_xs = !IS_DEVKIT ? "Root-CA00000003-XS0000000c" : "Root-CA00000004-XS00000009";
UINT bytes_read; const char* issuer_cp = !IS_DEVKIT ? "Root-CA00000003-CP0000000b" : "Root-CA00000004-CP0000000a";
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK)
// open certs.db file on SysNAND or EmuNAND
Certificate cert_ca;
Certificate cert_xs;
Certificate cert_cp;
if (LoadCertFromCertDb(false, &cert_ca, issuer_ca) != 0 && LoadCertFromCertDb(true, &cert_ca, issuer_ca) != 0)
return 1; return 1;
// grab CIA cert from 4 offsets
f_lseek(&db, 0x0C10); if (LoadCertFromCertDb(false, &cert_xs, issuer_xs) != 0 && LoadCertFromCertDb(true, &cert_xs, issuer_xs) != 0) {
f_read(&db, ciacert + 0x000, 0x1F0, &bytes_read); Certificate_Cleanup(&cert_ca);
f_lseek(&db, 0x3A00); return 1;
f_read(&db, ciacert + 0x1F0, 0x210, &bytes_read); }
f_lseek(&db, 0x3F10);
f_read(&db, ciacert + 0x400, 0x300, &bytes_read); if (LoadCertFromCertDb(false, &cert_cp, issuer_cp) != 0 && LoadCertFromCertDb(true, &cert_cp, issuer_cp) != 0) {
f_lseek(&db, 0x3C10); Certificate_Cleanup(&cert_ca);
f_read(&db, ciacert + 0x700, 0x300, &bytes_read); Certificate_Cleanup(&cert_xs);
f_close(&db); return 1;
}
u32 cert_size_ca;
u32 cert_size_xs;
u32 cert_size_cp;
if (Certificate_GetFullSize(&cert_ca, &cert_size_ca) != 0 ||
cert_size_ca != 0x400 ||
Certificate_GetFullSize(&cert_xs, &cert_size_xs) != 0 ||
cert_size_xs != 0x300 ||
Certificate_GetFullSize(&cert_cp, &cert_size_cp) != 0 ||
cert_size_cp != 0x300 ||
Certificate_RawCopy(&cert_ca, ciacert) != 0 ||
Certificate_RawCopy(&cert_xs, &ciacert[0x400]) != 0 ||
Certificate_RawCopy(&cert_cp, &ciacert[0x700]) != 0) {
Certificate_Cleanup(&cert_ca);
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_cp);
return 1;
}
Certificate_Cleanup(&cert_ca);
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_cp);
// check the certificate hash // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];

View File

@ -28,17 +28,26 @@ u32 ValidateTwlTicket(Ticket* ticket) {
} }
u32 ValidateTicketSignature(Ticket* ticket) { u32 ValidateTicketSignature(Ticket* ticket) {
static bool got_modexp = false; Certificate cert;
static u32 mod[0x100 / 0x4] = { 0 }; u32 mod[2048/8];
static u32 exp = 0; u32 exp = 0;
if (!got_modexp) {
// grab mod/exp from cert from cert.db // grab mod/exp from cert from cert.db
if (LoadCertFromCertDb(0x3F10, NULL, mod, &exp) == 0) if (LoadCertFromCertDb(false, &cert, (char*)(ticket->issuer)) != 0 && LoadCertFromCertDb(true, &cert, (char*)(ticket->issuer)) != 0)
got_modexp = true; return 1;
else 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) || if (!RSA_setKey2048(3, mod, exp) ||
!RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140)) !RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140))
return 1; return 1;
@ -89,19 +98,35 @@ u32 BuildTicketCert(u8* tickcert) {
0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E 0xC6, 0x4B, 0xD4, 0x8F, 0xDF, 0x13, 0x21, 0x3D, 0xFC, 0x72, 0xFC, 0x8D, 0x9F, 0xDD, 0x01, 0x0E
}; };
// open certs.db file on SysNAND const char* issuer_xs = !IS_DEVKIT ? "Root-CA00000003-XS0000000c" : "Root-CA00000004-XS00000009";
FIL db; const char* issuer_ca = !IS_DEVKIT ? "Root-CA00000003" : "Root-CA00000004";
UINT bytes_read;
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK) // open certs.db file on SysNAND or EmuNAND
Certificate cert_xs;
Certificate cert_ca;
if (LoadCertFromCertDb(false, &cert_xs, issuer_xs) != 0 && LoadCertFromCertDb(true, &cert_xs, issuer_xs) != 0)
return 1; return 1;
// grab ticket cert from 3 offsets
f_lseek(&db, 0x3F10); if (LoadCertFromCertDb(false, &cert_ca, issuer_ca) != 0 && LoadCertFromCertDb(true, &cert_ca, issuer_ca) != 0) {
f_read(&db, tickcert + 0x000, 0x300, &bytes_read); Certificate_Cleanup(&cert_xs);
f_lseek(&db, 0x0C10); return 1;
f_read(&db, tickcert + 0x300, 0x1F0, &bytes_read); }
f_lseek(&db, 0x3A00);
f_read(&db, tickcert + 0x4F0, 0x210, &bytes_read); u32 cert_size_xs;
f_close(&db); u32 cert_size_ca;
if (Certificate_GetFullSize(&cert_xs, &cert_size_xs) != 0 ||
cert_size_xs != 0x300 ||
Certificate_GetFullSize(&cert_ca, &cert_size_ca) != 0 ||
cert_size_ca != 0x400 ||
Certificate_RawCopy(&cert_xs, tickcert) != 0 ||
Certificate_RawCopy(&cert_ca, &tickcert[0x300]) != 0) {
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_ca);
return 1;
}
Certificate_Cleanup(&cert_xs);
Certificate_Cleanup(&cert_ca);
// check the certificate hash // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];

View File

@ -24,17 +24,26 @@ u32 ValidateTwlTmd(TitleMetaData* tmd) {
} }
u32 ValidateTmdSignature(TitleMetaData* tmd) { u32 ValidateTmdSignature(TitleMetaData* tmd) {
static bool got_modexp = false; Certificate cert;
static u32 mod[0x100 / 4] = { 0 }; u32 mod[2048/8];
static u32 exp = 0; u32 exp = 0;
if (!got_modexp) {
// grab mod/exp from cert from cert.db // grab mod/exp from cert from cert.db
if (LoadCertFromCertDb(0x3C10, NULL, mod, &exp) == 0) if (LoadCertFromCertDb(false, &cert, (char*)(tmd->issuer)) != 0 && LoadCertFromCertDb(true, &cert, (char*)(tmd->issuer)) != 0)
got_modexp = true; return 1;
else 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) || if (!RSA_setKey2048(3, mod, exp) ||
!RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4)) !RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4))
return 1; return 1;
@ -121,19 +130,35 @@ u32 BuildTmdCert(u8* tmdcert) {
0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71 0xC2, 0xE9, 0xCA, 0x93, 0x94, 0xF4, 0x29, 0xA0, 0x38, 0x54, 0x75, 0xFF, 0xAB, 0x6E, 0x8E, 0x71
}; };
// open certs.db file on SysNAND const char* issuer_cp = !IS_DEVKIT ? "Root-CA00000003-CP0000000b" : "Root-CA00000004-CP0000000a";
FIL db; const char* issuer_ca = !IS_DEVKIT ? "Root-CA00000003" : "Root-CA00000004";
UINT bytes_read;
if (f_open(&db, "1:/dbs/certs.db", FA_READ | FA_OPEN_EXISTING) != FR_OK) // 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; return 1;
// grab TMD cert from 3 offsets
f_lseek(&db, 0x3C10); if (LoadCertFromCertDb(false, &cert_ca, issuer_ca) != 0 && LoadCertFromCertDb(true, &cert_ca, issuer_ca) != 0) {
f_read(&db, tmdcert + 0x000, 0x300, &bytes_read); Certificate_Cleanup(&cert_cp);
f_lseek(&db, 0x0C10); return 1;
f_read(&db, tmdcert + 0x300, 0x1F0, &bytes_read); }
f_lseek(&db, 0x3A00);
f_read(&db, tmdcert + 0x4F0, 0x210, &bytes_read); u32 cert_size_cp;
f_close(&db); 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 // check the certificate hash
u8 cert_hash[0x20]; u8 cert_hash[0x20];