[libnx|nstool] Added HierarchicalSha256Header and HierarchicalIntegrityHeader

This commit is contained in:
jakcron 2018-05-20 21:57:38 +08:00
parent d93116863e
commit 446927b53e
10 changed files with 170 additions and 152 deletions

View file

@ -1,5 +1,5 @@
#pragma once
#include <nx/hierarchicalsha256.h>
#include <nx/hierarchicalintegrity.h>
#include <fnd/MemoryBlob.h>
#include <fnd/List.h>
#include <fnd/ISerialiseableBinary.h>
@ -14,21 +14,21 @@ namespace nx
{
size_t offset;
size_t size;
size_t hash_block_size;
size_t block_size;
void operator=(const sLayer& other)
{
offset = other.offset;
size = other.size;
hash_block_size = other.hash_block_size;
block_size = other.block_size;
}
bool operator==(const sLayer& other)
bool operator==(const sLayer& other) const
{
return (offset == other.offset && size == other.size && hash_block_size == other.hash_block_size);
return (offset == other.offset && size == other.size && block_size == other.block_size);
}
bool operator!=(const sLayer& other)
bool operator!=(const sLayer& other) const
{
return !(*this == other);
}

View file

@ -21,12 +21,12 @@ namespace nx
size = other.size;
}
bool operator==(const sLayer& other)
bool operator==(const sLayer& other) const
{
return (offset == other.offset && size == other.size);
}
bool operator!=(const sLayer& other)
bool operator!=(const sLayer& other) const
{
return !(*this == other);
}

View file

@ -10,9 +10,9 @@ namespace nx
namespace hierarchicalintegrity
{
const std::string kStructSig = "IVFC";
static const uint32_t kTypeId = 0x20000;
static const size_t kMaxLayerNum = 7;
static const size_t kMaxMasterHashNum = 3;
static const uint32_t kRomfsTypeId = 0x20000;
static const size_t kDefaultLayerNum = 6;
static const size_t kHeaderAlignLen = 0x20;
}
#pragma pack(push,1)
@ -22,15 +22,14 @@ namespace nx
le_uint32_t type_id;
le_uint32_t master_hash_size;
le_uint32_t layer_num;
struct sLayer
{
le_uint64_t offset;
le_uint64_t size;
le_uint32_t block_size;
byte_t reserved[4];
} layer[hierarchicalintegrity::kMaxLayerNum];
byte_t reserved_00[0x8];
crypto::sha::sSha256Hash master_hash[hierarchicalintegrity::kMaxMasterHashNum];
};
struct sHierarchicalIntegrityLayerInfo // sizeof(0x18)
{
le_uint64_t offset;
le_uint64_t size;
le_uint32_t block_size;
byte_t reserved[4];
};
#pragma pack(pop)
}

View file

@ -8,9 +8,8 @@ namespace nx
{
namespace hierarchicalsha256
{
static const size_t kDefaultLevelNum = 2;
static const size_t kMaxLayoutNum = 2;
static const size_t kDefaultLayerNum = 2;
static const size_t kMaxLayerNum = 2;
}
#pragma pack(push,1)
@ -23,7 +22,7 @@ namespace nx
{
le_uint64_t offset;
le_uint64_t size;
} layer[hierarchicalsha256::kMaxLayoutNum];
} layer[hierarchicalsha256::kMaxLayerNum];
};
#pragma pack(pop)
}

View file

@ -5,8 +5,6 @@
#include <crypto/sha.h>
#include <crypto/rsa.h>
#include <fnd/ISerialiseableBinary.h>
#include <nx/hierarchicalintegrity.h>
#include <nx/hierarchicalsha256.h>
namespace nx
{
@ -21,7 +19,7 @@ namespace nx
static const size_t kAesKeyNum = 16;
static const size_t kRightsIdLen = 0x10;
static const size_t kKeyAreaEncryptionKeyNum = 3;
static const size_t kFsHeaderHashSuperblockLen = 0x130;
static const size_t kFsHeaderHashSuperblockLen = 0x138;
static const uint16_t kDefaultFsHeaderVersion = 2;
enum ProgramPartitionId
@ -120,12 +118,8 @@ namespace nx
byte_t hash_type;
byte_t encryption_type;
byte_t reserved_0[3];
union {
byte_t hash_superblock[nca::kFsHeaderHashSuperblockLen];
nx::sHierarchicalSha256Header hierarchicalsha256_header;
nx::sHierarchicalIntegrityHeader hierarchicalintergrity_header;
};
crypto::aes::sAesIvCtr base_ctr;
byte_t hash_superblock[nca::kFsHeaderHashSuperblockLen];
byte_t aes_ctr_upper[8];
byte_t reserved_1[0xB8];
};

View file

@ -1,3 +1,4 @@
#include <sstream>
#include <nx/HierarchicalIntegrityHeader.h>
nx::HierarchicalIntegrityHeader::HierarchicalIntegrityHeader()
@ -47,7 +48,66 @@ void nx::HierarchicalIntegrityHeader::exportBinary()
void nx::HierarchicalIntegrityHeader::importBinary(const byte_t * bytes, size_t len)
{
throw fnd::Exception(kModuleName, "importBinary() not implemented");
std::stringstream error_str;
// validate size for at least header
if (len < sizeof(nx::sHierarchicalIntegrityHeader))
{
throw fnd::Exception(kModuleName, "Header too small");
}
const nx::sHierarchicalIntegrityHeader* hdr = (const nx::sHierarchicalIntegrityHeader*)bytes;
// Validate Header Sig "IVFC"
if (std::string(hdr->signature, 4) != hierarchicalintegrity::kStructSig)
{
throw fnd::Exception(kModuleName, "Invalid struct magic");
}
// Validate TypeId
if (hdr->type_id.get() != nx::hierarchicalintegrity::kRomfsTypeId)
{
error_str.clear();
error_str << "Unsupported type id (" << std::hex << hdr->type_id.get() << ")";
throw fnd::Exception(kModuleName, error_str.str());
}
// Validate Layer Num
if (hdr->layer_num.get() != hierarchicalintegrity::kDefaultLayerNum+1)
{
error_str.clear();
error_str << "Invalid layer count. ";
error_str << "(actual=" << std::dec << hdr->layer_num.get() << ", expected=" << nx::hierarchicalintegrity::kDefaultLayerNum+1 << ")";
throw fnd::Exception(kModuleName, error_str.str());
}
// Get Sizes/Offsets
size_t master_hash_offset = align((sizeof(nx::sHierarchicalIntegrityHeader) + sizeof(nx::sHierarchicalIntegrityLayerInfo) * hdr->layer_num.get()), nx::hierarchicalintegrity::kHeaderAlignLen);
size_t total_size = master_hash_offset + hdr->master_hash_size.get();
// Validate total size
if (len < total_size)
{
throw fnd::Exception(kModuleName, "Header too small");
}
// copy to internal storage
mBinaryBlob.alloc(total_size);
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
// save layer info
const nx::sHierarchicalIntegrityLayerInfo* layer_info = (const nx::sHierarchicalIntegrityLayerInfo*)(mBinaryBlob.getBytes() + sizeof(nx::sHierarchicalIntegrityHeader));
for (size_t i = 0; i < hierarchicalintegrity::kDefaultLayerNum; i++)
{
mLayerInfo.addElement({layer_info[i].offset.get(), layer_info[i].size.get(), layer_info[i].block_size.get()});
}
// save hash list
const crypto::sha::sSha256Hash* hash_list = (const crypto::sha::sSha256Hash*)(mBinaryBlob.getBytes() + master_hash_offset);
for (size_t i = 0; i < hdr->master_hash_size.get()/sizeof(crypto::sha::sSha256Hash); i++)
{
mMasterHashList.addElement(hash_list[i]);
}
}
void nx::HierarchicalIntegrityHeader::clear()

View file

@ -49,6 +49,8 @@ void nx::HierarchicalSha256Header::exportBinary()
void nx::HierarchicalSha256Header::importBinary(const byte_t * bytes, size_t len)
{
std::stringstream error_str;
if (len < sizeof(nx::sHierarchicalSha256Header))
{
throw fnd::Exception(kModuleName, "Header too small");
@ -56,13 +58,12 @@ void nx::HierarchicalSha256Header::importBinary(const byte_t * bytes, size_t len
const nx::sHierarchicalSha256Header* hdr = (const nx::sHierarchicalSha256Header*)bytes;
if (hdr->layer_num.get() != nx::hierarchicalsha256::kDefaultLevelNum)
if (hdr->layer_num.get() != nx::hierarchicalsha256::kDefaultLayerNum)
{
std::stringstream ss;
ss.clear();
ss << "Invalid layer count. ";
ss << "(actual=" << hdr->layer_num.get() << ", expected=" << nx::hierarchicalsha256::kDefaultLevelNum << ")";
throw fnd::Exception(kModuleName, ss.str());
error_str.clear();
error_str << "Invalid layer count. ";
error_str << "(actual=" << std::dec << hdr->layer_num.get() << ", expected=" << nx::hierarchicalsha256::kDefaultLayerNum << ")";
throw fnd::Exception(kModuleName, error_str.str());
}
mMasterHash = hdr->master_hash;

View file

@ -50,8 +50,9 @@ byte_t nx::NcaUtils::getMasterKeyRevisionFromKeyGeneration(byte_t key_generation
void nx::NcaUtils::getNcaPartitionAesCtr(const nx::sNcaFsHeader* hdr, byte_t* ctr)
{
for (size_t i = 0; i < 16; i++)
for (size_t i = 0; i < 8; i++)
{
ctr[15-i] = hdr->base_ctr.iv[i];
ctr[7-i] = hdr->aes_ctr_upper[i];
ctr[15-i] = 0;
}
}

View file

@ -120,7 +120,7 @@ void NcaProcess::generateNcaBodyEncryptionKeys()
// otherwise decrypt key area
else
{
// if the titlekey_kek is available
// if the key_area_key is available
if (mKeyset->nca.key_area_key[keak_index][masterkey_rev] != zero_aesctr_key)
{
nx::AesKeygen::generateKey(mBodyKeys.aes_ctr.var.key, mHdr.getEncAesKeys()[nx::nca::KEY_AESCTR].key, mKeyset->nca.key_area_key[keak_index][masterkey_rev].key);
@ -153,6 +153,9 @@ void NcaProcess::generatePartitionConfiguration()
const nx::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
nx::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index];
// output structure
sPartitionInfo& info = mPartitions[partition.index];
// validate header hash
crypto::sha::sSha256Hash calc_hash;
crypto::sha::Sha256((const byte_t*)&mHdrBlock.fs_header[partition.index], sizeof(nx::sNcaFsHeader), calc_hash.bytes);
@ -165,19 +168,19 @@ void NcaProcess::generatePartitionConfiguration()
// setup AES-CTR
crypto::aes::sAesIvCtr ctr;
nx::NcaUtils::getNcaPartitionAesCtr(&fs_header, ctr.iv);
nx::NcaUtils::getNcaPartitionAesCtr(&fs_header, info.aes_ctr.iv);
// save partition config
mPartitions[partition.index].reader = nullptr;
mPartitions[partition.index].offset = partition.offset;
mPartitions[partition.index].size = partition.size;
mPartitions[partition.index].format_type = (nx::nca::FormatType)fs_header.format_type;
mPartitions[partition.index].hash_type = (nx::nca::HashType)fs_header.hash_type;
memcpy(mPartitions[partition.index].hash_superblock, fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
info.reader = nullptr;
info.offset = partition.offset;
info.size = partition.size;
info.version = fs_header.version.get();
info.format_type = (nx::nca::FormatType)fs_header.format_type;
info.hash_type = (nx::nca::HashType)fs_header.hash_type;
info.enc_type = (nx::nca::EncryptionType)fs_header.encryption_type;
// filter out unrecognised format types
switch (mPartitions[partition.index].format_type)
switch (info.format_type)
{
case (nx::nca::FORMAT_PFS0):
case (nx::nca::FORMAT_ROMFS):
@ -186,71 +189,45 @@ void NcaProcess::generatePartitionConfiguration()
continue;
}
// filter out unrecognised hash types
switch (mPartitions[partition.index].hash_type)
// filter out unrecognised hash types, and get data offsets
switch (info.hash_type)
{
case (nx::nca::HASH_NONE):
info.data_offset = info.offset;
info.data_size = info.size;
break;
case (nx::nca::HASH_HIERARCHICAL_SHA256):
info.hierarchicalsha256_header.importBinary(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
info.data_offset = info.hierarchicalsha256_header.getLayerInfo().atBack().offset;
info.data_size = info.hierarchicalsha256_header.getLayerInfo().atBack().size;
break;
case (nx::nca::HASH_HIERARCHICAL_INTERGRITY):
info.hierarchicalintergrity_header.importBinary(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
info.data_offset = info.hierarchicalintergrity_header.getLayerInfo().atBack().offset;
info.data_size = info.hierarchicalintergrity_header.getLayerInfo().atBack().size;
break;
default:
continue;
}
// create reader
// create reader based on encryption type0
switch(fs_header.encryption_type)
{
case (nx::nca::CRYPT_AESXTS):
case (nx::nca::CRYPT_AESCTREX):
mPartitions[partition.index].reader = nullptr;
info.reader = nullptr;
break;
case (nx::nca::CRYPT_AESCTR):
mPartitions[partition.index].reader = mBodyKeys.aes_ctr.isSet? new AesCtrWrappedIFile(mReader, mBodyKeys.aes_ctr.var, ctr) : nullptr;
info.reader = mBodyKeys.aes_ctr.isSet? new AesCtrWrappedIFile(mReader, mBodyKeys.aes_ctr.var, info.aes_ctr) : nullptr;
break;
case (nx::nca::CRYPT_NONE):
mPartitions[partition.index].reader = new CopiedIFile(mReader);
info.reader = new CopiedIFile(mReader);
break;
default:
error.clear();
error << "NCA FS Header [" << partition.index << "] EncryptionType(" << fs_header.encryption_type << "): UNKNOWN \n";
throw fnd::Exception(kModuleName, error.str());
}
// determine the data offset & size
if (mPartitions[partition.index].hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
{
mPartitions[partition.index].data_offset = mPartitions[partition.index].hierarchicalsha256_header.layer[1].offset.get();
mPartitions[partition.index].data_size = mPartitions[partition.index].hierarchicalsha256_header.layer[1].size.get();
}
else if (mPartitions[partition.index].hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
{
mPartitions[partition.index].data_offset = mPartitions[partition.index].hierarchicalintergrity_header.layer[5].offset.get();
mPartitions[partition.index].data_size = mPartitions[partition.index].hierarchicalintergrity_header.layer[5].size.get();
/*
if (mPartitions[partition.index].hierarchicalintergrity_header.layer_num.get() > nx::hierarchicalintegrity::kMaxLayerNum)
{
error.clear();
error << "NCA FS Header [" << partition.index << "] HierarchicalIntergrity header has an unsupported layer num (" << mPartitions[partition.index].hierarchicalintergrity_header.layer_num.get() << ")\n";
throw fnd::Exception(kModuleName, error.str());
}
const nx::sHierarchicalIntegrityHeader& hdr = mPartitions[partition.index].hierarchicalintergrity_header;
for (size_t j = 0; j < nx::hierarchicalintegrity::kMaxLayerNum; j++)
{
size_t index = nx::hierarchicalintegrity::kMaxLayerNum - 1 - j;
if (hdr.layer[index].offset.get() != 0)
{
mPartitions[partition.index].data_offset = hdr.layer[index].offset.get();
mPartitions[partition.index].data_size = hdr.layer[index].size.get();
break;
}
}
*/
}
else if (mPartitions[partition.index].hash_type == nx::nca::HASH_NONE)
{
mPartitions[partition.index].data_offset = 0;
}
}
}
@ -368,81 +345,64 @@ void NcaProcess::displayHeader()
printf(" Partitions:\n");
for (size_t i = 0; i < mHdr.getPartitions().getSize(); i++)
{
const nx::NcaHeader::sPartition& partition = mHdr.getPartitions()[i];
nx::sNcaFsHeader& fs_header = mHdrBlock.fs_header[partition.index];
sPartitionInfo& info = mPartitions[i];
printf(" %lu:\n", i);
printf(" Index: %d\n", partition.index);
printf(" Offset: 0x%" PRIx64 "\n", partition.offset);
printf(" Size: 0x%" PRIx64 "\n", partition.size);
printf(" Offset: 0x%" PRIx64 "\n", info.offset);
printf(" Size: 0x%" PRIx64 "\n", info.size);
crypto::sha::sSha256Hash ncaFsHeaderHash;
crypto::sha::Sha256((byte_t*)&fs_header, sizeof(nx::sNcaFsHeader), ncaFsHeaderHash.bytes);
if (partition.hash.compare(ncaFsHeaderHash) == false)
{
throw fnd::Exception(kModuleName, "NcaFsHeader has bad sha256 hash");
}
//fnd::SimpleTextOutput::hxdStyleDump((byte_t*)&fs_header, sizeof(nx::sNcaFsHeader));
printf(" FsHeader:\n");
printf(" Version: 0x%d\n", fs_header.version.get());
printf(" Format Type: %s\n", kFormatTypeStr[fs_header.format_type].c_str());
printf(" Hash Type: %s\n", kHashTypeStr[fs_header.hash_type].c_str());
printf(" Enc. Type: %s\n", kEncryptionTypeStr[fs_header.encryption_type].c_str());
if (fs_header.encryption_type == nx::nca::CRYPT_AESCTR)
printf(" Version: 0x%d\n", info.version);
printf(" Format Type: %s\n", kFormatTypeStr[info.format_type].c_str());
printf(" Hash Type: %s\n", kHashTypeStr[info.hash_type].c_str());
printf(" Enc. Type: %s\n", kEncryptionTypeStr[info.enc_type].c_str());
if (info.enc_type == nx::nca::CRYPT_AESCTR)
{
printf(" CTR: ");
printf(" AES-CTR: ");
crypto::aes::sAesIvCtr ctr;
nx::NcaUtils::getNcaPartitionAesCtr(&fs_header, ctr.iv);
crypto::aes::AesIncrementCounter(ctr.iv, partition.offset>>4, ctr.iv);
crypto::aes::AesIncrementCounter(info.aes_ctr.iv, info.offset>>4, ctr.iv);
fnd::SimpleTextOutput::hexDump(ctr.iv, sizeof(crypto::aes::sAesIvCtr));
}
if (fs_header.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
if (info.hash_type == nx::nca::HASH_HIERARCHICAL_INTERGRITY)
{
nx::sHierarchicalIntegrityHeader& hash_hdr = fs_header.hierarchicalintergrity_header;
nx::HierarchicalIntegrityHeader& hash_hdr = info.hierarchicalintergrity_header;
printf(" HierarchicalIntegrity Header:\n");
printf(" TypeId: 0x%x\n", hash_hdr.type_id.get());
printf(" MasterHashSize: 0x%x\n", hash_hdr.master_hash_size.get());
printf(" LayerNum: %d\n", hash_hdr.layer_num.get());
for (size_t i = 0; i < hash_hdr.layer_num.get(); i++)
//printf(" TypeId: 0x%x\n", hash_hdr.type_id.get());
//printf(" MasterHashSize: 0x%x\n", hash_hdr.master_hash_size.get());
//printf(" LayerNum: %d\n", hash_hdr.getLayerInfo().getSize());
for (size_t j = 0; j < hash_hdr.getLayerInfo().getSize(); j++)
{
printf(" Layer %d:\n", i);
printf(" Offset: 0x%" PRIx64 "\n", hash_hdr.layer[i].offset.get());
printf(" Size: 0x%" PRIx64 "\n", hash_hdr.layer[i].size.get());
printf(" BlockSize: 0x%" PRIx32 "\n", hash_hdr.layer[i].block_size.get());
printf(" Layer %d:\n", j);
printf(" Offset: 0x%" PRIx64 "\n", hash_hdr.getLayerInfo()[j].offset);
printf(" Size: 0x%" PRIx64 "\n", hash_hdr.getLayerInfo()[j].size);
printf(" BlockSize: 0x%" PRIx32 "\n", hash_hdr.getLayerInfo()[j].block_size);
}
for (size_t j = 0; j < hash_hdr.master_hash_size.get() / sizeof(crypto::sha::sSha256Hash); j++)
for (size_t j = 0; j < hash_hdr.getMasterHashList().getSize(); j++)
{
printf(" Master Hash %d: ", j);
fnd::SimpleTextOutput::hexDump(hash_hdr.master_hash[j].bytes, sizeof(crypto::sha::sSha256Hash));
fnd::SimpleTextOutput::hexDump(hash_hdr.getMasterHashList()[j].bytes, sizeof(crypto::sha::sSha256Hash));
}
}
else if (fs_header.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
else if (info.hash_type == nx::nca::HASH_HIERARCHICAL_SHA256)
{
nx::sHierarchicalSha256Header& hash_hdr = fs_header.hierarchicalsha256_header;
nx::HierarchicalSha256Header& hash_hdr = info.hierarchicalsha256_header;
printf(" HierarchicalSha256 Header:\n");
printf(" Master Hash: ");
fnd::SimpleTextOutput::hexDump(hash_hdr.master_hash.bytes, sizeof(crypto::sha::sSha256Hash));
printf(" HashBlockSize: 0x%x\n", hash_hdr.hash_block_size.get());
printf(" LayerNum: %d\n", hash_hdr.layer_num.get());
for (size_t i = 0; i < hash_hdr.layer_num.get(); i++)
fnd::SimpleTextOutput::hexDump(hash_hdr.getMasterHash().bytes, sizeof(crypto::sha::sSha256Hash));
printf(" HashBlockSize: 0x%x\n", hash_hdr.getHashBlockSize());
//printf(" LayerNum: %d\n", hash_hdr.getLayerInfo().getSize());
for (size_t i = 0; i < hash_hdr.getLayerInfo().getSize(); i++)
{
printf(" Layer %d:\n", i);
printf(" Offset: 0x%" PRIx64 "\n", hash_hdr.layer[i].offset.get());
printf(" Size: 0x%" PRIx64 "\n", hash_hdr.layer[i].size.get());
printf(" Offset: 0x%" PRIx64 "\n", hash_hdr.getLayerInfo()[i].offset);
printf(" Size: 0x%" PRIx64 "\n", hash_hdr.getLayerInfo()[i].size);
}
}
else
{
printf(" Hash Superblock:\n");
fnd::SimpleTextOutput::hxdStyleDump(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
}
//else
//{
// printf(" Hash Superblock:\n");
// fnd::SimpleTextOutput::hxdStyleDump(fs_header.hash_superblock, nx::nca::kFsHeaderHashSuperblockLen);
//}
}
}

View file

@ -3,6 +3,9 @@
#include <fnd/types.h>
#include <fnd/SimpleFile.h>
#include <nx/NcaHeader.h>
#include <nx/HierarchicalSha256Header.h>
#include <nx/HierarchicalIntegrityHeader.h>
#include "nstool.h"
@ -64,14 +67,15 @@ private:
size_t size;
size_t data_offset;
size_t data_size;
// meta data
uint16_t version;
nx::nca::FormatType format_type;
nx::nca::HashType hash_type;
union {
byte_t hash_superblock[nx::nca::kFsHeaderHashSuperblockLen];
nx::sHierarchicalSha256Header hierarchicalsha256_header;
nx::sHierarchicalIntegrityHeader hierarchicalintergrity_header;
};
nx::nca::EncryptionType enc_type;
nx::HierarchicalSha256Header hierarchicalsha256_header;
nx::HierarchicalIntegrityHeader hierarchicalintergrity_header;
crypto::aes::sAesIvCtr aes_ctr;
} mPartitions[nx::nca::kPartitionNum];
void generateNcaBodyEncryptionKeys();