From 4b5ac1a8e04298d5ffc105d5b0f13aa77c1f45d0 Mon Sep 17 00:00:00 2001 From: luigoalma Date: Thu, 6 May 2021 16:58:43 +0100 Subject: [PATCH] Certificate provide signature verification call --- arm9/source/game/cert.c | 68 +++++++++++++++++++++++++++++++++++++++ arm9/source/game/cert.h | 1 + arm9/source/game/ticket.c | 18 ++--------- arm9/source/game/tmd.c | 18 ++--------- 4 files changed, 73 insertions(+), 32 deletions(-) diff --git a/arm9/source/game/cert.c b/arm9/source/game/cert.c index aecb3d3..bc57b69 100644 --- a/arm9/source/game/cert.c +++ b/arm9/source/game/cert.c @@ -1,5 +1,6 @@ #include "cert.h" #include "disadiff.h" +#include "rsa.h" typedef struct { char magic[4]; // "CERT" @@ -230,6 +231,73 @@ u32 Certificate_GetFullSize(const Certificate* cert, u32* size) { return 0; } +static inline u32 _Certificate_KeytypeSignatureSize(u32 keytype) { + if (keytype == 0) + return 0x200; + else if (keytype == 1) + return 0x100; + else if (keytype == 2) + return 0x3C; + return 0; +} + +static inline u32 _Certificate_VerifyRSA4096(const Certificate* cert, const void* sig, const void* data, u32 data_size, bool sha256) { + (void)cert; (void)sig; (void)data; (void)data_size; (void)sha256; + return 2; // not implemented +} + +// noipa, to avoid form of inlining, cloning, etc, to avoid the extra stack usage when unneeded +static __attribute__((noipa)) bool _Certificate_SetKey2048Misaligned(const Certificate* cert) { + u32 mod[2048/8]; + u32 exp; + + memcpy(mod, cert->data->pub_key_data, 2048/8); + exp = getle32(&cert->data->pub_key_data[2048/8]); + + return RSA_setKey2048(3, mod, exp); +} + +static inline u32 _Certificate_VerifyRSA2048(const Certificate* cert, const void* sig, const void* data, u32 data_size, bool sha256) { + if (!sha256) + return 2; // not implemented + + int ret; + + if (((u32)&cert->data->pub_key_data[0]) & 0x3) + ret = !_Certificate_SetKey2048Misaligned(cert); + else + ret = !RSA_setKey2048(3, (const u32*)(const void*)&cert->data->pub_key_data[0], getle32(&cert->data->pub_key_data[2048/8])); + + if (ret) + return ret; + + return !RSA_verify2048(sig, data, data_size); +} + +static inline u32 _Certificate_VerifyECC(const Certificate* cert, const void* sig, const void* data, u32 data_size, bool sha256) { + (void)cert; (void)sig; (void)data; (void)data_size; (void)sha256; + return 2; // not implemented +} + +u32 Certificate_VerifySignatureBlock(const Certificate* cert, const void* sig, u32 sig_size, const void* data, u32 data_size, bool sha256) { + if (!sig || !sig_size || (!data && data_size) || !Certificate_IsValid(cert)) + return 1; + + u32 keytype = getbe32(cert->data->keytype); + u32 _sig_size = _Certificate_KeytypeSignatureSize(keytype); + + if (sig_size != _sig_size) + return 1; + + if (keytype == 0) + return _Certificate_VerifyRSA4096(cert, sig, data, data_size, sha256); + if (keytype == 1) + return _Certificate_VerifyRSA2048(cert, sig, data, data_size, sha256); + if (keytype == 2) + return _Certificate_VerifyECC(cert, sig, data, data_size, sha256); + return 1; +} + static u32 _Certificate_AllocCopyOutImpl(const Certificate* cert, Certificate* out_cert) { u32 sig_size = _Certificate_GetSignatureChunkSizeFromType(getbe32(cert->sig->sig_type)); u32 data_size = _Certificate_GetDataChunkSizeFromType(getbe32(cert->data->keytype)); diff --git a/arm9/source/game/cert.h b/arm9/source/game/cert.h index f8ed6f7..03c213a 100644 --- a/arm9/source/game/cert.h +++ b/arm9/source/game/cert.h @@ -43,6 +43,7 @@ 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_VerifySignatureBlock(const Certificate* cert, const void* sig, u32 sig_size, const void* data, u32 data_size, bool sha256); u32 Certificate_AllocCopyOut(const Certificate* cert, Certificate* out_cert); u32 Certificate_RawCopy(const Certificate* cert, void* raw); u32 Certificate_Cleanup(Certificate* cert); diff --git a/arm9/source/game/ticket.c b/arm9/source/game/ticket.c index 0ace6f0..14e5a7e 100644 --- a/arm9/source/game/ticket.c +++ b/arm9/source/game/ticket.c @@ -29,30 +29,16 @@ u32 ValidateTwlTicket(Ticket* ticket) { u32 ValidateTicketSignature(Ticket* ticket) { Certificate cert; - u32 mod[2048/8]; - u32 exp = 0; // grab cert from certs.db if (LoadCertFromCertDb(&cert, (char*)(ticket->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; - } + int ret = Certificate_VerifySignatureBlock(&cert, &(ticket->signature), 0x100, (void*)&(ticket->issuer), GetTicketSize(ticket) - 0x140, true); Certificate_Cleanup(&cert); - if (!RSA_setKey2048(3, mod, exp) || - !RSA_verify2048((void*) &(ticket->signature), (void*) &(ticket->issuer), GetTicketSize(ticket) - 0x140)) - return 1; - - return 0; + return ret; } u32 BuildFakeTicket(Ticket* ticket, u8* title_id) { diff --git a/arm9/source/game/tmd.c b/arm9/source/game/tmd.c index 50c20f5..c1b08d7 100644 --- a/arm9/source/game/tmd.c +++ b/arm9/source/game/tmd.c @@ -25,30 +25,16 @@ u32 ValidateTwlTmd(TitleMetaData* tmd) { 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; - } + int ret = Certificate_VerifySignatureBlock(&cert, &(tmd->signature), 0x100, (void*)&(tmd->issuer), 0xC4, true); Certificate_Cleanup(&cert); - if (!RSA_setKey2048(3, mod, exp) || - !RSA_verify2048((void*) &(tmd->signature), (void*) &(tmd->issuer), 0xC4)) - return 1; - - return 0; + return ret; } u32 VerifyTmd(TitleMetaData* tmd) {