1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-08 11:51:48 +00:00

NPDM ACID public key replacement + NCA ACID signature recalculation.

This commit is contained in:
Pablo Curiel 2020-10-10 17:08:17 -04:00
parent 2066b11d5a
commit 9f010c4129
7 changed files with 78 additions and 12 deletions

View file

@ -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));

View file

@ -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)

View file

@ -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;
}

View file

@ -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)

View file

@ -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__ */

View file

@ -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.

View file

@ -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?