diff --git a/arm9/source/game/cert.c b/arm9/source/game/cert.c index c1d07c8..d2e461f 100644 --- a/arm9/source/game/cert.c +++ b/arm9/source/game/cert.c @@ -13,6 +13,58 @@ static void GetCertDBPath(char* path, bool emunand) { strcpy(&path[1], ":/dbs/certs.db"); } +#define CERT_RETAIL_CA3_IDENT BIT(0) +#define CERT_RETAIL_XSc_IDENT BIT(1) +#define CERT_RETAIL_CPb_IDENT BIT(2) +#define CERT_DEV_CA4_IDENT BIT(3) +#define CERT_DEV_XS9_IDENT BIT(4) +#define CERT_DEV_CPa_IDENT BIT(5) +#define CERT_NO_STORE_SPACE (0xFF) + +static struct { + u32 loaded_certs_flg; + u8 retail_CA3_raw[CERT_RSA4096_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + u8 retail_XSc_raw[CERT_RSA2048_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + u8 retail_CPb_raw[CERT_RSA2048_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + u8 dev_CA4_raw[CERT_RSA4096_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + u8 dev_XS9_raw[CERT_RSA2048_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + u8 dev_CPa_raw[CERT_RSA2048_SIG_SIZE + CERT_RSA2048_BODY_SIZE]; + Certificate retail_CA3; + Certificate retail_XSc; + Certificate retail_CPb; + Certificate dev_CA4; + Certificate dev_XS9; + Certificate dev_CPa; +} _CommonCertsStorage = { + 0, // none loaded yet, ident defines used to say what's loaded + {0}, {0}, {0}, {0}, {0}, {0}, // no data yet + // cert structs pre-point already to raw certs + { + (CertificateSignature*)&_CommonCertsStorage.retail_CA3_raw[0], + (CertificateBody*)&_CommonCertsStorage.retail_CA3_raw[CERT_RSA4096_SIG_SIZE] + }, + { + (CertificateSignature*)&_CommonCertsStorage.retail_XSc_raw[0], + (CertificateBody*)&_CommonCertsStorage.retail_XSc_raw[CERT_RSA2048_SIG_SIZE] + }, + { + (CertificateSignature*)&_CommonCertsStorage.retail_CPb_raw[0], + (CertificateBody*)&_CommonCertsStorage.retail_CPb_raw[CERT_RSA2048_SIG_SIZE] + }, + { + (CertificateSignature*)&_CommonCertsStorage.dev_CA4_raw[0], + (CertificateBody*)&_CommonCertsStorage.dev_CA4_raw[CERT_RSA4096_SIG_SIZE] + }, + { + (CertificateSignature*)&_CommonCertsStorage.dev_XS9_raw[0], + (CertificateBody*)&_CommonCertsStorage.dev_XS9_raw[CERT_RSA2048_SIG_SIZE] + }, + { + (CertificateSignature*)&_CommonCertsStorage.dev_CPa_raw[0], + (CertificateBody*)&_CommonCertsStorage.dev_CPa_raw[CERT_RSA2048_SIG_SIZE] + } +}; + bool Certificate_IsValid(const Certificate* cert) { if (!cert || !cert->sig || !cert->data) return false; @@ -191,20 +243,176 @@ u32 Certificate_RawCopy(const Certificate* cert, void* raw) { return 0; } +// ptr free check, to not free if ptr is pointing to static storage!! +static inline void _Certificate_SafeFree(void* ptr) { + if ((u32)ptr >= (u32)&_CommonCertsStorage && (u32)ptr < (u32)&_CommonCertsStorage + sizeof(_CommonCertsStorage)) + return; + + free(ptr); +} + u32 Certificate_Cleanup(Certificate* cert) { if (!cert) return 1; - free(cert->sig); - free(cert->data); + _Certificate_SafeFree(cert->sig); + _Certificate_SafeFree(cert->data); cert->sig = NULL; cert->data = NULL; return 0; } +static u32 _Issuer_To_StorageIdent(const char* issuer) { + if (strncmp(issuer, "Root-CA0000000", 14) != 0) + return CERT_NO_STORE_SPACE; + + if (issuer[14] == '3') { // retail + if (issuer[15] == 0) + return CERT_RETAIL_CA3_IDENT; + if (issuer[15] != '-') + return CERT_NO_STORE_SPACE; + if (!strcmp(&issuer[16], "XS0000000c")) + return CERT_RETAIL_XSc_IDENT; + if (!strcmp(&issuer[16], "CP0000000b")) + return CERT_RETAIL_CPb_IDENT; + } + + if (issuer[14] == '4') { // dev + if (issuer[15] == 0) + return CERT_DEV_CA4_IDENT; + if (issuer[15] != '-') + return CERT_NO_STORE_SPACE; + if (!strcmp(&issuer[16], "XS00000009")) + return CERT_DEV_XS9_IDENT; + if (!strcmp(&issuer[16], "CP0000000a")) + return CERT_DEV_CPa_IDENT; + } + + return CERT_NO_STORE_SPACE; +} + +static bool _LoadFromCertStorage(Certificate* cert, u32 ident) { + if (ident == CERT_NO_STORE_SPACE) + return false; + + Certificate* _cert = NULL; + + switch (ident) { + case CERT_RETAIL_CA3_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_CA3_IDENT) + _cert = &_CommonCertsStorage.retail_CA3; + break; + case CERT_RETAIL_XSc_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_XSc_IDENT) + _cert = &_CommonCertsStorage.retail_XSc; + break; + case CERT_RETAIL_CPb_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_CPb_IDENT) + _cert = &_CommonCertsStorage.retail_CPb; + break; + case CERT_DEV_CA4_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_DEV_CA4_IDENT) + _cert = &_CommonCertsStorage.dev_CA4; + break; + case CERT_DEV_XS9_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_DEV_XS9_IDENT) + _cert = &_CommonCertsStorage.dev_XS9; + break; + case CERT_DEV_CPa_IDENT: + if (_CommonCertsStorage.loaded_certs_flg & CERT_DEV_CPa_IDENT) + _cert = &_CommonCertsStorage.dev_CPa; + break; + default: + break; + } + + if (!_cert) + return false; + + *cert = *_cert; + return true; +} + +static void _SaveToCertStorage(const Certificate* cert, u32 ident) { + if (ident == CERT_NO_STORE_SPACE) + return; + + Certificate* _cert = NULL; + u8* raw_space = NULL; + u32 raw_size = 0; + + switch (ident) { + case CERT_RETAIL_CA3_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_CA3_IDENT)) { + _cert = &_CommonCertsStorage.retail_CA3; + raw_space = &_CommonCertsStorage.retail_CA3_raw[0]; + raw_size = sizeof(_CommonCertsStorage.retail_CA3_raw); + } + break; + case CERT_RETAIL_XSc_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_XSc_IDENT)) { + _cert = &_CommonCertsStorage.retail_XSc; + raw_space = &_CommonCertsStorage.retail_XSc_raw[0]; + raw_size = sizeof(_CommonCertsStorage.retail_XSc_raw); + } + break; + case CERT_RETAIL_CPb_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_RETAIL_CPb_IDENT)) { + _cert = &_CommonCertsStorage.retail_CPb; + raw_space = &_CommonCertsStorage.retail_CPb_raw[0]; + raw_size = sizeof(_CommonCertsStorage.retail_CPb_raw); + } + break; + case CERT_DEV_CA4_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_DEV_CA4_IDENT)) { + _cert = &_CommonCertsStorage.dev_CA4; + raw_space = &_CommonCertsStorage.dev_CA4_raw[0]; + raw_size = sizeof(_CommonCertsStorage.dev_CA4_raw); + } + break; + case CERT_DEV_XS9_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_DEV_XS9_IDENT)) { + _cert = &_CommonCertsStorage.dev_XS9; + raw_space = &_CommonCertsStorage.dev_XS9_raw[0]; + raw_size = sizeof(_CommonCertsStorage.dev_XS9_raw); + } + break; + case CERT_DEV_CPa_IDENT: + if (!(_CommonCertsStorage.loaded_certs_flg & CERT_DEV_CPa_IDENT)) { + _cert = &_CommonCertsStorage.dev_CPa; + raw_space = &_CommonCertsStorage.dev_CPa_raw[0]; + raw_size = sizeof(_CommonCertsStorage.dev_CPa_raw); + } + break; + default: + break; + } + + if (!_cert || !raw_space || !raw_size) + return; + + 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; + + if (sig_size + data_size != raw_size) + return; + + if (!Certificate_RawCopy(cert, raw_space)) { + _CommonCertsStorage.loaded_certs_flg |= ident; + } +} + u32 LoadCertFromCertDb(bool emunand, Certificate* cert, const char* issuer) { if (!issuer || !cert) return 1; + u32 _ident = _Issuer_To_StorageIdent(issuer); + if (_LoadFromCertStorage(cert, _ident)) { + return 0; + } + Certificate cert_local = {NULL, NULL}; char path[16]; @@ -304,6 +512,8 @@ u32 LoadCertFromCertDb(bool emunand, Certificate* cert, const char* issuer) { if (ret) { Certificate_Cleanup(&cert_local); + } else { + _SaveToCertStorage(&cert_local, _ident); } *cert = cert_local;