[nx] Add XciHeader

This commit is contained in:
jakcron 2018-04-21 17:37:30 +08:00
parent fac2753afd
commit 17c16cfa7a
5 changed files with 690 additions and 7 deletions

View file

@ -0,0 +1,134 @@
#pragma once
#include <nx/xci.h>
#include <fnd/MemoryBlob.h>
#include <fnd/List.h>
#include <fnd/ISerialiseableBinary.h>
namespace nx
{
class XciHeader :
public fnd::ISerialiseableBinary
{
public:
XciHeader();
XciHeader(const XciHeader& other);
XciHeader(const byte_t* bytes, size_t len);
bool operator==(const XciHeader& other) const;
bool operator!=(const XciHeader& other) const;
void operator=(const XciHeader& other);
// to be used after export
const byte_t* getBytes() const;
size_t getSize() const;
// export/import binary
void exportBinary();
void importBinary(const byte_t* bytes, size_t len);
// variables
void clear();
uint32_t getRomAreaStartPage() const;
void setRomAreaStartPage(uint32_t startPage);
uint32_t getBackupAreaStartPage() const;
void setBackupAreaStartPage(uint32_t startPage);
byte_t getKekIndex() const;
void setKekIndex(byte_t kekIndex);
byte_t getTitleKeyDecIndex() const;
void setTitleKeyDecIndex(byte_t index);
byte_t getRomSizeType() const;
void setRomSizeType(byte_t romSizeType);
byte_t getCardHeaderVersion() const;
void setCardHeaderVersion(byte_t version);
byte_t getFlags() const;
void setFlags(byte_t flags);
uint64_t getPackageId() const;
void setPackageId(uint64_t id);
uint32_t getValidDataEndPage() const;
void setValidDataEndPage(uint32_t page);
const crypto::aes::sAesIvCtr& getAesCbcIv() const;
void setAesCbcIv(const crypto::aes::sAesIvCtr& iv);
uint64_t getPartitionFsAddress() const;
void setPartitionFsAddress(uint64_t address);
uint64_t getPartitionFsSize() const;
void setPartitionFsSize(uint64_t size);
const crypto::sha::sSha256Hash& getPartitionFsHash() const;
void setPartitionFsHash(const crypto::sha::sSha256Hash& hash);
const crypto::sha::sSha256Hash& getInitialDataHash() const;
void setInitialDataHash(const crypto::sha::sSha256Hash& hash);
uint32_t getSelSec() const;
void setSelSec(uint32_t sel_sec);
uint32_t getSelT1Key() const;
void setSelT1Key(uint32_t sel_t1_key);
uint32_t getSelKey() const;
void setSelKey(uint32_t sel_key);
uint32_t getLimAreaPage() const;
void setLimAreaPage(uint32_t page);
uint32_t getFwVerMajor() const;
void setFwVerMajor(uint32_t ver);
uint32_t getFwVerMinor() const;
void setFwVerMinor(uint32_t ver);
uint32_t getAccCtrl1() const;
void setAccCtrl1(uint32_t acc_ctrl_1);
uint32_t getWait1TimeRead() const;
void setWait1TimeRead(uint32_t seconds);
uint32_t getWait2TimeRead() const;
void setWait2TimeRead(uint32_t seconds);
uint32_t getWait1TimeWrite() const;
void setWait1TimeWrite(uint32_t seconds);
uint32_t getWait2TimeWrite() const;
void setWait2TimeWrite(uint32_t seconds);
uint32_t getFwMode() const;
void setFwMode(uint32_t fw_mode);
uint32_t getUppVersion() const;
void setUppVersion(uint32_t version);
const byte_t* getUppHash() const;
void setUppHash(const byte_t* hash);
uint64_t getUppId() const;
void setUppId(uint64_t id);
private:
const std::string kModuleName = "XCI_HEADER";
// binary
fnd::MemoryBlob mBinaryBlob;
// data
uint32_t mRomAreaStartPage;
uint32_t mBackupAreaStartPage;
byte_t mKekIndex;
byte_t mTitleKeyDecIndex;
byte_t mRomSize;
byte_t mCardHeaderVersion;
byte_t mFlags;
uint64_t mPackageId;
uint32_t mValidDataEndPage;
crypto::aes::sAesIvCtr mAesCbcIv;
uint64_t mPartitionFsHeaderAddress;
uint64_t mPartitionFsHeaderSize;
crypto::sha::sSha256Hash mPartitionFsHeaderHash;
crypto::sha::sSha256Hash mInitialDataHash;
uint32_t mSelSec;
uint32_t mSelT1Key;
uint32_t mSelKey;
uint32_t mLimAreaPage;
// Encrypted Data
uint32_t mFwVersion[2];
uint32_t mAccCtrl1;
uint32_t mWait1TimeRead;
uint32_t mWait2TimeRead;
uint32_t mWait1TimeWrite;
uint32_t mWait2TimeWrite;
uint32_t mFwMode;
uint32_t mUppVersion;
byte_t mUppHash[8];
uint64_t mUppId;
// helpers
bool isEqual(const XciHeader& other) const;
void copyFrom(const XciHeader& other);
};
}

View file

@ -0,0 +1,13 @@
#pragma once
#include <nx/xci.h>
namespace nx
{
class XciUtils
{
public:
static inline uint64_t blockToAddr(uint32_t block) { return ((uint64_t)block) << 9; }
static void getXciHeaderAesIv(const nx::sXciHeader* hdr, byte_t* iv);
static void decryptXciHeader(const byte_t* src, byte_t* dst, const byte_t* key);
};
}

View file

@ -1,8 +1,10 @@
#pragma once
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/List.h> #include <fnd/List.h>
#include <crypto/aes.h> #include <crypto/aes.h>
#include <crypto/sha.h> #include <crypto/sha.h>
#include <crypto/rsa.h>
#include <fnd/ISerialiseableBinary.h> #include <fnd/ISerialiseableBinary.h>
namespace nx namespace nx
@ -13,6 +15,7 @@ namespace nx
static const uint32_t kHeaderEncOffset = 0x90; static const uint32_t kHeaderEncOffset = 0x90;
static const uint32_t kHeaderEncSize = 0x70; static const uint32_t kHeaderEncSize = 0x70;
static const uint32_t kPageSize = 0x200; static const uint32_t kPageSize = 0x200;
static const uint32_t kUppHashLen = 8;
static const uint32_t kCardKeyAreaPageCount = 8; static const uint32_t kCardKeyAreaPageCount = 8;
static const uint32_t kCardHeaderPageCount = 1; static const uint32_t kCardHeaderPageCount = 1;
static const uint32_t kReservedAreaPageCount = 55; static const uint32_t kReservedAreaPageCount = 55;
@ -21,6 +24,12 @@ namespace nx
static const uint32_t kNormalAreaStartPageAddress = kReservedAreaPageCount + kCertAreaPageCount + kCardHeaderPageCount + kCardKeyAreaPageCount; static const uint32_t kNormalAreaStartPageAddress = kReservedAreaPageCount + kCertAreaPageCount + kCardHeaderPageCount + kCardKeyAreaPageCount;
enum KekIndex
{
KEK_XCIE,
KEK_XCIR
};
enum RomSize enum RomSize
{ {
ROM_SIZE_1GB = 0xFA, ROM_SIZE_1GB = 0xFA,
@ -63,12 +72,12 @@ namespace nx
byte_t flags; byte_t flags;
le_uint64_t package_id; le_uint64_t package_id;
le_uint32_t valid_data_end_page; le_uint32_t valid_data_end_page;
byte_t reserved_01[4]; byte_t reserved_00[4];
byte_t encryption_iv[16]; crypto::aes::sAesIvCtr aescbc_iv;
le_uint64_t partition_fs_header_address; le_uint64_t partition_fs_header_address;
le_uint64_t partition_fs_header_size; le_uint64_t partition_fs_header_size;
byte_t partition_fs_header_hash[0x20]; crypto::sha::sSha256Hash partition_fs_header_hash;
byte_t initial_data_hash[0x20]; crypto::sha::sSha256Hash initial_data_hash;
le_uint32_t sel_sec; le_uint32_t sel_sec;
le_uint32_t sel_t1_key; le_uint32_t sel_t1_key;
le_uint32_t sel_key; le_uint32_t sel_key;
@ -82,13 +91,19 @@ namespace nx
le_uint32_t wait_2_time_write; le_uint32_t wait_2_time_write;
le_uint32_t fw_mode; le_uint32_t fw_mode;
le_uint32_t upp_version; le_uint32_t upp_version;
byte_t reserved_03[0x4]; byte_t reserved_01[0x4];
byte_t upp_hash[8]; byte_t upp_hash[xci::kUppHashLen];
le_uint64_t upp_id; le_uint64_t upp_id;
byte_t reserved_04[0x38]; byte_t reserved_02[0x38];
// END ENCRYPTION // END ENCRYPTION
}; };
struct sXciHeaderPage
{
byte_t signature[crypto::rsa::kRsa2048Size];
sXciHeader header;
}; // sizeof() = 512 (1 page)
struct sInitialData struct sInitialData
{ {
byte_t key_source[16]; // { package_id[8], zeros[8]} byte_t key_source[16]; // { package_id[8], zeros[8]}

View file

@ -0,0 +1,499 @@
#include <nx/XciHeader.h>
bool nx::XciHeader::isEqual(const XciHeader& other) const
{
return ( mRomAreaStartPage == other.mRomAreaStartPage \
& mBackupAreaStartPage == other.mBackupAreaStartPage \
& mKekIndex == other.mKekIndex \
& mTitleKeyDecIndex == other.mTitleKeyDecIndex \
& mRomSize == other.mRomSize \
& mCardHeaderVersion == other.mCardHeaderVersion \
& mFlags == other.mFlags \
& mPackageId == other.mPackageId \
& mValidDataEndPage == other.mValidDataEndPage \
& mAesCbcIv == other.mAesCbcIv \
& mPartitionFsHeaderAddress == other.mPartitionFsHeaderAddress \
& mPartitionFsHeaderSize == other.mPartitionFsHeaderSize \
& mPartitionFsHeaderHash == other.mPartitionFsHeaderHash \
& mInitialDataHash == other.mInitialDataHash \
& mSelSec == other.mSelSec \
& mSelT1Key == other.mSelT1Key \
& mSelKey == other.mSelKey \
& mLimAreaPage == other.mLimAreaPage \
& mFwVersion[0] == other.mFwVersion[0] \
& mFwVersion[1] == other.mFwVersion[1] \
& mAccCtrl1 == other.mAccCtrl1 \
& mWait1TimeRead == other.mWait1TimeRead \
& mWait2TimeRead == other.mWait2TimeRead \
& mWait1TimeWrite == other.mWait1TimeWrite \
& mWait2TimeWrite == other.mWait2TimeWrite \
& mFwMode == other.mFwMode \
& mUppVersion == other.mUppVersion \
& memcmp(mUppHash, other.mUppHash, xci::kUppHashLen) \
& mUppId == other.mUppId);
}
void nx::XciHeader::copyFrom(const XciHeader& other)
{
mRomAreaStartPage = other.mRomAreaStartPage;
mBackupAreaStartPage = other.mBackupAreaStartPage;
mKekIndex = other.mKekIndex;
mTitleKeyDecIndex = other.mTitleKeyDecIndex;
mRomSize = other.mRomSize;
mCardHeaderVersion = other.mCardHeaderVersion;
mFlags = other.mFlags;
mPackageId = other.mPackageId;
mValidDataEndPage = other.mValidDataEndPage;
mAesCbcIv = other.mAesCbcIv;
mPartitionFsHeaderAddress = other.mPartitionFsHeaderAddress;
mPartitionFsHeaderSize = other.mPartitionFsHeaderSize;
mPartitionFsHeaderHash = other.mPartitionFsHeaderHash;
mInitialDataHash = other.mInitialDataHash;
mSelSec = other.mSelSec;
mSelT1Key = other.mSelT1Key;
mSelKey = other.mSelKey;
mLimAreaPage = other.mLimAreaPage;
mFwVersion[0] = other.mFwVersion[0];
mFwVersion[1] = other.mFwVersion[1];
mAccCtrl1 = other.mAccCtrl1;
mWait1TimeRead = other.mWait1TimeRead;
mWait2TimeRead = other.mWait2TimeRead;
mWait1TimeWrite = other.mWait1TimeWrite;
mWait2TimeWrite = other.mWait2TimeWrite;
mFwMode = other.mFwMode;
mUppVersion = other.mUppVersion;
memcpy(mUppHash, other.mUppHash, xci::kUppHashLen);
mUppId = other.mUppId;
}
nx::XciHeader::XciHeader()
{
}
nx::XciHeader::XciHeader(const XciHeader& other)
{
importBinary(other.getBytes(), other.getSize());
}
nx::XciHeader::XciHeader(const byte_t* bytes, size_t len)
{
importBinary(bytes, len);
}
bool nx::XciHeader::operator==(const XciHeader& other) const
{
return isEqual(other);
}
bool nx::XciHeader::operator!=(const XciHeader& other) const
{
return isEqual(other) == false;
}
void nx::XciHeader::operator=(const XciHeader& other)
{
copyFrom(other);
}
// to be used after export
const byte_t* nx::XciHeader::getBytes() const
{
return mBinaryBlob.getBytes();
}
size_t nx::XciHeader::getSize() const
{
return mBinaryBlob.getSize();
}
// export/import binary
void nx::XciHeader::exportBinary()
{
fnd::Exception(kModuleName, "exportBinary() not implemented");
}
void nx::XciHeader::importBinary(const byte_t* bytes, size_t len)
{
// check input data size
if (len < sizeof(sXciHeader))
{
throw fnd::Exception(kModuleName, "XCI header size is too small");
}
// clear internal members
clear();
// allocate internal local binary copy
mBinaryBlob.alloc(sizeof(sXciHeader));
memcpy(mBinaryBlob.getBytes(), bytes, mBinaryBlob.getSize());
// get sXciHeader ptr
const nx::sXciHeader* hdr = (const nx::sXciHeader*)mBinaryBlob.getBytes();
// check XCI signature
if (std::string(hdr->signature, 4) != xci::kXciSig)
{
throw fnd::Exception(kModuleName, "XCI header corrupt");
}
mRomAreaStartPage = hdr->rom_area_start_page.get();
mBackupAreaStartPage = hdr->backup_area_start_page.get();
mKekIndex = hdr->key_flag & 7;
mTitleKeyDecIndex = (hdr->key_flag >> 4) & 7;
mRomSize = hdr->rom_size;
mCardHeaderVersion = hdr->card_header_version;
mFlags = hdr->flags;
mPackageId = hdr->package_id.get();
mValidDataEndPage = hdr->valid_data_end_page.get();
for (size_t i = 0; i < crypto::aes::kAesBlockSize; i++)
mAesCbcIv.iv[i] = hdr->aescbc_iv.iv[15-i];
mPartitionFsHeaderAddress = hdr->partition_fs_header_address.get();
mPartitionFsHeaderSize = hdr->partition_fs_header_size.get();
mPartitionFsHeaderHash = hdr->partition_fs_header_hash;
mInitialDataHash = hdr->initial_data_hash;
mSelSec = hdr->sel_sec.get();
mSelT1Key = hdr->sel_t1_key.get();
mSelKey = hdr->sel_key.get();
mLimAreaPage = hdr->lim_area.get();
// if decrypted
if (hdr->reserved_02[sizeof(hdr->reserved_02)-1] == 0x00 && hdr->reserved_02[sizeof(hdr->reserved_02)-2] == 0x00)
{
mFwVersion[xci::FWVER_MAJOR] = hdr->fw_version[xci::FWVER_MAJOR].get();
mFwVersion[xci::FWVER_MINOR] = hdr->fw_version[xci::FWVER_MINOR].get();
mAccCtrl1 = hdr->acc_ctrl_1.get();
mWait1TimeRead = hdr->wait_1_time_read.get();
mWait2TimeRead = hdr->wait_2_time_read.get();
mWait1TimeWrite = hdr->wait_1_time_write.get();
mWait2TimeWrite = hdr->wait_2_time_write.get();
mFwMode = hdr->fw_mode.get();
mUppVersion = hdr->upp_version.get();
memcpy(mUppHash, hdr->upp_hash, xci::kUppHashLen);
mUppId = hdr->upp_id.get();
}
}
// variables
void nx::XciHeader::clear()
{
mRomAreaStartPage = 0;
mBackupAreaStartPage = 0;
mKekIndex = 0;
mTitleKeyDecIndex = 0;
mRomSize = 0;
mCardHeaderVersion = 0;
mFlags = 0;
mPackageId = 0;
mValidDataEndPage = 0;
memset(mAesCbcIv.iv, 0, sizeof(mAesCbcIv));
mPartitionFsHeaderAddress = 0;
mPartitionFsHeaderSize = 0;
memset(mPartitionFsHeaderHash.bytes, 0, sizeof(mPartitionFsHeaderHash));
memset(mInitialDataHash.bytes, 0, sizeof(mInitialDataHash));
mSelSec = 0;
mSelT1Key = 0;
mSelKey = 0;
mLimAreaPage = 0;
mFwVersion[0] = 0;
mFwVersion[1] = 0;
mAccCtrl1 = 0;
mWait1TimeRead = 0;
mWait2TimeRead = 0;
mWait1TimeWrite = 0;
mWait2TimeWrite = 0;
mFwMode = 0;
mUppVersion = 0;
memset(mUppHash, 0, xci::kUppHashLen);
mUppId = 0;
}
uint32_t nx::XciHeader::getRomAreaStartPage() const
{
return mRomAreaStartPage;
}
void nx::XciHeader::setRomAreaStartPage(uint32_t startPage)
{
mRomAreaStartPage = startPage;
}
uint32_t nx::XciHeader::getBackupAreaStartPage() const
{
return mBackupAreaStartPage;
}
void nx::XciHeader::setBackupAreaStartPage(uint32_t startPage)
{
mBackupAreaStartPage = startPage;
}
byte_t nx::XciHeader::getKekIndex() const
{
return mKekIndex;
}
void nx::XciHeader::setKekIndex(byte_t kekIndex)
{
mKekIndex = kekIndex;
}
byte_t nx::XciHeader::getTitleKeyDecIndex() const
{
return mTitleKeyDecIndex;
}
void nx::XciHeader::setTitleKeyDecIndex(byte_t index)
{
mTitleKeyDecIndex = index;
}
byte_t nx::XciHeader::getRomSizeType() const
{
return mRomSize;
}
void nx::XciHeader::setRomSizeType(byte_t romSizeType)
{
mRomSize = romSizeType;
}
byte_t nx::XciHeader::getCardHeaderVersion() const
{
return mCardHeaderVersion;
}
void nx::XciHeader::setCardHeaderVersion(byte_t version)
{
mCardHeaderVersion = version;
}
byte_t nx::XciHeader::getFlags() const
{
return mFlags;
}
void nx::XciHeader::setFlags(byte_t flags)
{
mFlags = flags;
}
uint64_t nx::XciHeader::getPackageId() const
{
return mPackageId;
}
void nx::XciHeader::setPackageId(uint64_t id)
{
mPackageId = id;
}
uint32_t nx::XciHeader::getValidDataEndPage() const
{
return mValidDataEndPage;
}
void nx::XciHeader::setValidDataEndPage(uint32_t page)
{
mValidDataEndPage = page;
}
const crypto::aes::sAesIvCtr& nx::XciHeader::getAesCbcIv() const
{
return mAesCbcIv;
}
void nx::XciHeader::setAesCbcIv(const crypto::aes::sAesIvCtr& iv)
{
mAesCbcIv = iv;
}
uint64_t nx::XciHeader::getPartitionFsAddress() const
{
return mPartitionFsHeaderAddress;
}
void nx::XciHeader::setPartitionFsAddress(uint64_t address)
{
mPartitionFsHeaderAddress = address;
}
uint64_t nx::XciHeader::getPartitionFsSize() const
{
return mPartitionFsHeaderSize;
}
void nx::XciHeader::setPartitionFsSize(uint64_t size)
{
mPartitionFsHeaderSize = size;
}
const crypto::sha::sSha256Hash& nx::XciHeader::getPartitionFsHash() const
{
return mPartitionFsHeaderHash;
}
void nx::XciHeader::setPartitionFsHash(const crypto::sha::sSha256Hash& hash)
{
mPartitionFsHeaderHash = hash;
}
const crypto::sha::sSha256Hash& nx::XciHeader::getInitialDataHash() const
{
return mInitialDataHash;
}
void nx::XciHeader::setInitialDataHash(const crypto::sha::sSha256Hash& hash)
{
mInitialDataHash = hash;
}
uint32_t nx::XciHeader::getSelSec() const
{
return mSelSec;
}
void nx::XciHeader::setSelSec(uint32_t sel_sec)
{
mSelSec = sel_sec;
}
uint32_t nx::XciHeader::getSelT1Key() const
{
return mSelT1Key;
}
void nx::XciHeader::setSelT1Key(uint32_t sel_t1_key)
{
mSelT1Key = sel_t1_key;
}
uint32_t nx::XciHeader::getSelKey() const
{
return mSelKey;
}
void nx::XciHeader::setSelKey(uint32_t sel_key)
{
mSelKey = sel_key;
}
uint32_t nx::XciHeader::getLimAreaPage() const
{
return mLimAreaPage;
}
void nx::XciHeader::setLimAreaPage(uint32_t page)
{
mLimAreaPage = page;
}
uint32_t nx::XciHeader::getFwVerMajor() const
{
return mFwVersion[xci::FWVER_MAJOR];
}
void nx::XciHeader::setFwVerMajor(uint32_t ver)
{
mFwVersion[xci::FWVER_MAJOR] = ver;
}
uint32_t nx::XciHeader::getFwVerMinor() const
{
return mFwVersion[xci::FWVER_MINOR];
}
void nx::XciHeader::setFwVerMinor(uint32_t ver)
{
mFwVersion[xci::FWVER_MINOR] = ver;
}
uint32_t nx::XciHeader::getAccCtrl1() const
{
return mAccCtrl1;
}
void nx::XciHeader::setAccCtrl1(uint32_t acc_ctrl_1)
{
mAccCtrl1 = acc_ctrl_1;
}
uint32_t nx::XciHeader::getWait1TimeRead() const
{
return mWait1TimeRead;
}
void nx::XciHeader::setWait1TimeRead(uint32_t seconds)
{
mWait1TimeRead = seconds;
}
uint32_t nx::XciHeader::getWait2TimeRead() const
{
return mWait2TimeRead;
}
void nx::XciHeader::setWait2TimeRead(uint32_t seconds)
{
mWait2TimeRead = seconds;
}
uint32_t nx::XciHeader::getWait1TimeWrite() const
{
return mWait1TimeWrite;
}
void nx::XciHeader::setWait1TimeWrite(uint32_t seconds)
{
mWait1TimeWrite = seconds;
}
uint32_t nx::XciHeader::getWait2TimeWrite() const
{
return mWait2TimeWrite;
}
void nx::XciHeader::setWait2TimeWrite(uint32_t seconds)
{
mWait2TimeWrite = seconds;
}
uint32_t nx::XciHeader::getFwMode() const
{
return mFwMode;
}
void nx::XciHeader::setFwMode(uint32_t fw_mode)
{
mFwMode = fw_mode;
}
uint32_t nx::XciHeader::getUppVersion() const
{
return mUppVersion;
}
void nx::XciHeader::setUppVersion(uint32_t version)
{
mUppVersion = version;
}
const byte_t* nx::XciHeader::getUppHash() const
{
return mUppHash;
}
void nx::XciHeader::setUppHash(const byte_t* hash)
{
memcpy(mUppHash, hash, xci::kUppHashLen);
}
uint64_t nx::XciHeader::getUppId() const
{
return mUppId;
}
void nx::XciHeader::setUppId(uint64_t id)
{
mUppId = id;
}

View file

@ -0,0 +1,22 @@
#include <nx/XciUtils.h>
void nx::XciUtils::getXciHeaderAesIv(const nx::sXciHeader* hdr, byte_t* iv)
{
for (size_t i = 0; i < 16; i++)
{
iv[15-i] = hdr->aescbc_iv.iv[i];
}
}
void nx::XciUtils::decryptXciHeader(const byte_t* src, byte_t* dst, const byte_t* key)
{
byte_t iv[crypto::aes::kAesBlockSize];
getXciHeaderAesIv((const nx::sXciHeader*)src, iv);
// copy plain
memcpy(dst, src, nx::xci::kHeaderEncOffset);
// decrypt encrypted data
crypto::aes::AesCbcDecrypt(src + nx::xci::kHeaderEncOffset, nx::xci::kHeaderEncSize, key, iv, dst + nx::xci::kHeaderEncOffset);
}