From 9f010c41290729ec4dd67520a64ad8dad4941290 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sat, 10 Oct 2020 17:08:17 -0400 Subject: [PATCH] NPDM ACID public key replacement + NCA ACID signature recalculation. --- source/nca.c | 9 ++++----- source/nca.h | 17 +++++++++++++---- source/npdm.c | 24 ++++++++++++++++++++++++ source/npdm.h | 3 +++ source/nso.h | 32 ++++++++++++++++++++++++++++++++ source/rsa.h | 1 + todo.txt | 4 +--- 7 files changed, 78 insertions(+), 12 deletions(-) diff --git a/source/nca.c b/source/nca.c index cedcffc..879f620 100644 --- a/source/nca.c +++ b/source/nca.c @@ -143,6 +143,9 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type, return false; } + /* Calculate decrypted header hash. */ + sha256CalculateHash(out->header_hash, &(out->header), sizeof(NcaHeader)); + if (out->rights_id_available) { /* Retrieve ticket. */ @@ -363,7 +366,6 @@ void ncaRemoveTitlekeyCrypto(NcaContext *ctx) /* Update context flags. */ ctx->rights_id_available = false; - ctx->dirty_header = true; } bool ncaEncryptHeader(NcaContext *ctx) @@ -375,7 +377,7 @@ bool ncaEncryptHeader(NcaContext *ctx) } /* Safety check: don't encrypt the header if we don't need to. */ - if (!ctx->dirty_header) return true; + if (!ncaIsHeaderDirty(ctx)) return true; size_t crypt_res = 0; const u8 *header_key = keysGetNcaHeaderKey(); @@ -1068,9 +1070,6 @@ static bool ncaGenerateHashDataPatch(NcaFsSectionContext *ctx, const void *data, /* Recalculate FS header hash. */ sha256CalculateHash(nca_ctx->header.fs_header_hash[ctx->section_num].hash, &(ctx->header), sizeof(NcaFsHeader)); - /* Enable the 'dirty_header' flag. */ - nca_ctx->dirty_header = true; - /* Copy content ID. */ memcpy(!is_integrity_patch ? &(hierarchical_sha256_patch->content_id) : &(hierarchical_integrity_patch->content_id), &(nca_ctx->content_id), sizeof(NcmContentId)); diff --git a/source/nca.h b/source/nca.h index 1452c8c..ced39cb 100644 --- a/source/nca.h +++ b/source/nca.h @@ -48,6 +48,8 @@ #define NCA_AES_XTS_SECTOR_SIZE 0x200 +#define NCA_ACID_SIGNATURE_AREA_SIZE 0x100 /* Signature is calculated starting at the NCA header magic word. */ + typedef enum { NcaDistributionType_Download = 0, NcaDistributionType_GameCard = 1 @@ -312,8 +314,8 @@ typedef struct { bool rights_id_available; bool titlekey_retrieved; u8 titlekey[AES_128_KEY_SIZE]; ///< Decrypted titlekey from the ticket. - bool dirty_header; NcaHeader header; ///< NCA header. + u8 header_hash[SHA256_HASH_SIZE]; ///< NCA header hash. Used to determine if it's necessary to replace the NCA header while dumping this NCA. NcaFsSectionContext fs_contexts[NCA_FS_HEADER_COUNT]; NcaDecryptedKeyArea decrypted_key_area; } NcaContext; @@ -368,7 +370,7 @@ void *ncaGenerateEncryptedFsSectionBlock(NcaFsSectionContext *ctx, const void *d /// Generates HierarchicalSha256 FS section patch data, which can be used to seamlessly replace NCA data. /// Input offset must be relative to the start of the last HierarchicalSha256 hash region (actual underlying FS). -/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header, and enables the 'dirty_header' flag from the NCA context. +/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header. /// As such, this function is not designed to generate more than one patch per HierarchicalSha256 FS section. bool ncaGenerateHierarchicalSha256Patch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalSha256Patch *out); @@ -378,7 +380,7 @@ void ncaWriteHierarchicalSha256PatchToMemoryBuffer(NcaContext *ctx, NcaHierarchi /// Generates HierarchicalIntegrity FS section patch data, which can be used to seamlessly replace NCA data. /// Input offset must be relative to the start of the last HierarchicalIntegrity hash level (actual underlying FS). -/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header, and enables the 'dirty_header' flag from the NCA context. +/// Bear in mind that this function recalculates both the NcaHashData block master hash and the NCA FS header hash from the NCA header. /// As such, this function is not designed to generate more than one patch per HierarchicalIntegrity FS section. bool ncaGenerateHierarchicalIntegrityPatch(NcaFsSectionContext *ctx, const void *data, u64 data_size, u64 data_offset, NcaHierarchicalIntegrityPatch *out); @@ -425,7 +427,14 @@ NX_INLINE void ncaSetDownloadDistributionType(NcaContext *ctx) { if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return; ctx->header.distribution_type = NcaDistributionType_Download; - ctx->dirty_header = true; +} + +NX_INLINE bool ncaIsHeaderDirty(NcaContext *ctx) +{ + if (!ctx) return false; + u8 tmp_hash[SHA256_HASH_SIZE] = {0}; + sha256CalculateHash(tmp_hash, &(ctx->header), sizeof(NcaHeader)); + return (memcmp(tmp_hash, ctx->header_hash, SHA256_HASH_SIZE) != 0); } NX_INLINE bool ncaValidateHierarchicalSha256Offsets(NcaHierarchicalSha256Data *hierarchical_sha256_data, u64 section_size) diff --git a/source/npdm.c b/source/npdm.c index f1d4494..2827b12 100644 --- a/source/npdm.c +++ b/source/npdm.c @@ -20,6 +20,7 @@ #include "utils.h" #include "npdm.h" +#include "rsa.h" bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx) { @@ -259,3 +260,26 @@ end: return success; } + +bool npdmChangeAcidPublicKeyAndNcaSignature(NpdmContext *npdm_ctx) +{ + NcaContext *nca_ctx = NULL; + + if (!npdmIsValidContext(npdm_ctx) || !(nca_ctx = (NcaContext*)npdm_ctx->pfs_ctx->nca_fs_ctx->nca_ctx) || nca_ctx->content_type != NcmContentType_Program) + { + LOGFILE("Invalid parameters!"); + return false; + } + + /* Update NPDM ACID public key. */ + memcpy(npdm_ctx->acid_header->public_key, rsa2048GetCustomPublicKey(), RSA2048_PUBKEY_SIZE); + + /* Update NCA ACID signature. */ + if (!rsa2048GenerateSha256BasedPssSignature(nca_ctx->header.acid_signature, &(nca_ctx->header.magic), NCA_ACID_SIGNATURE_AREA_SIZE)) + { + LOGFILE("Failed to generate RSA-2048-PSS NCA ACID signature!"); + return false; + } + + return true; +} diff --git a/source/npdm.h b/source/npdm.h index 8549d88..c20f4ad 100644 --- a/source/npdm.h +++ b/source/npdm.h @@ -555,6 +555,9 @@ typedef struct { /// Initializes a NpdmContext using a previously initialized PartitionFileSystemContext (which must belong to the ExeFS from a Program NCA). bool npdmInitializeContext(NpdmContext *out, PartitionFileSystemContext *pfs_ctx); +/// Changes the ACID public key from the NPDM in the input NpdmContext and updates the ACID signature from the NCA header in the underlying NCA context. +bool npdmChangeAcidPublicKeyAndNcaSignature(NpdmContext *npdm_ctx); + /// Helper inline functions. NX_INLINE void npdmFreeContext(NpdmContext *npdm_ctx) diff --git a/source/nso.h b/source/nso.h index 342741d..f7c9fc3 100644 --- a/source/nso.h +++ b/source/nso.h @@ -107,4 +107,36 @@ typedef struct { char name[]; } NsoModuleInfo; + + + +/* + + +typedef struct { + PartitionFileSystemContext *pfs_ctx; ///< PartitionFileSystemContext for the Program NCA FS section #0, which is where this NSO is stored. + PartitionFileSystemEntry *pfs_entry; ///< PartitionFileSystemEntry for this NSO in the Program NCA FS section #0. Used to read NSO data. + NsoHeader nso_header; ///< Copy of the NSO header. + + + + + + +} NsoContext; + + + + + +*/ + + + + + + + + + #endif /* __NSO_H__ */ diff --git a/source/rsa.h b/source/rsa.h index 68bbc3a..48581ec 100644 --- a/source/rsa.h +++ b/source/rsa.h @@ -26,6 +26,7 @@ #define __RSA_H__ #define RSA2048_SIG_SIZE 0x100 +#define RSA2048_PUBKEY_SIZE RSA2048_SIG_SIZE /// Generates a RSA-2048-PSS with SHA-256 signature using a custom RSA-2048 private key. /// Suitable to replace the ACID signature in a Program NCA header. diff --git a/todo.txt b/todo.txt index 1142495..ef6d9b4 100644 --- a/todo.txt +++ b/todo.txt @@ -1,10 +1,8 @@ todo: nca: functions for fs section lookup? (could just let the user choose...) + nca: add encrypted headers to nca context, avoid re-encrypting plaintext headers in place nca: function to write re-encrypted nca headers / nca fs headers (don't forget nca0 please) - nca: function to patch the private npdm acid signature from a program nca + patch the acid signature from the nca header - nca: add encrypted header to nca context, avoid re-encrypting the plaintext header in place - nca: use hash instead of dirty_header flag? tik: option to wipe elicense property mask tik: automatically dump tickets to the SD card?