From 3a1ef48b64cf55b24ce475779d529ebae7ab4518 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 30 Jun 2018 00:14:19 +0800 Subject: [PATCH 01/34] [nstool] Add support for detecting and specifying ticket and cert files. --- programs/nstool/makefile | 2 +- programs/nstool/source/UserSettings.cpp | 57 ++++++++++++++++++++++++- programs/nstool/source/UserSettings.h | 2 + programs/nstool/source/nstool.h | 2 + 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/programs/nstool/makefile b/programs/nstool/makefile index 85aa304..517a561 100644 --- a/programs/nstool/makefile +++ b/programs/nstool/makefile @@ -3,7 +3,7 @@ SRC_DIR = source OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # External dependencies -DEPENDS = nx-hb nx crypto compress fnd +DEPENDS = nx-hb nx es crypto compress fnd LIB_DIR = ../../lib LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep)) INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index ce6975a..ca5a65a 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -22,6 +22,7 @@ #include #include #include +#include UserSettings::UserSettings() {} @@ -43,7 +44,7 @@ void UserSettings::showHelp() printf("\n General Options:\n"); printf(" -d, --dev Use devkit keyset\n"); printf(" -k, --keyset Specify keyset file\n"); - printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset]\n"); + printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset, cert, tik]\n"); printf(" -y, --verify Verify file\n"); printf("\n Output Options:\n"); printf(" --showkeys Show keys generated\n"); @@ -695,6 +696,10 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str) type = FILE_NRO; else if (str == "nacp") type = FILE_NACP; + else if (str == "cert") + type = FILE_ES_CERT; + else if (str == "tik") + type = FILE_ES_TIK; else if (str == "aset" || str == "asset") type = FILE_HB_ASSET; else @@ -753,6 +758,12 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) // test nso else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _TYPE_PTR(nx::sNroHeader)->st_magic.get() == nx::nro::kNroStructMagic) file_type = FILE_NRO; + // test es certificate + else if (determineValidEsCertFromSample(scratch)) + file_type = FILE_ES_CERT; + // test es ticket + else if (determineValidEsTikFromSample(scratch)) + file_type = FILE_ES_TIK; // test hb asset else if (_ASSERT_SIZE(sizeof(nx::sAssetHeader)) && _TYPE_PTR(nx::sAssetHeader)->st_magic.get() == nx::aset::kAssetStructMagic) file_type = FILE_HB_ASSET; @@ -855,6 +866,50 @@ bool UserSettings::determineValidNacpFromSample(const fnd::Vec& sample) return true; } +bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample) const +{ + es::SignatureBlock sign; + + try + { + sign.fromBytes(sample.data(), sample.size()); + } + catch (...) + { + return false; + } + + if (sign.isLittleEndian() == true) + return false; + + if (sign.getSignType() != es::sign::SIGN_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ECDSA240_SHA256) + return false; + + return true; +} + +bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) const +{ + es::SignatureBlock sign; + + try + { + sign.fromBytes(sample.data(), sample.size()); + } + catch (...) + { + return false; + } + + if (sign.isLittleEndian() == false) + return false; + + if (sign.getSignType() != es::sign::SIGN_RSA2048_SHA256) + return false; + + return true; +} + nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str) { std::string str = type_str; diff --git a/programs/nstool/source/UserSettings.h b/programs/nstool/source/UserSettings.h index d7c27b9..c851647 100644 --- a/programs/nstool/source/UserSettings.h +++ b/programs/nstool/source/UserSettings.h @@ -106,5 +106,7 @@ private: bool determineValidNcaFromSample(const fnd::Vec& sample) const; bool determineValidCnmtFromSample(const fnd::Vec& sample) const; bool determineValidNacpFromSample(const fnd::Vec& sample) const; + bool determineValidEsCertFromSample(const fnd::Vec& sample) const; + bool determineValidEsTikFromSample(const fnd::Vec& sample) const; nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str); }; \ No newline at end of file diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h index cf6505c..7752ffa 100644 --- a/programs/nstool/source/nstool.h +++ b/programs/nstool/source/nstool.h @@ -27,6 +27,8 @@ enum FileType FILE_NSO, FILE_NRO, FILE_NACP, + FILE_ES_CERT, + FILE_ES_TIK, FILE_HB_ASSET, FILE_INVALID = -1, }; From 7ce716180f5636956f9e447db517d6593c29f11b Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 4 Jul 2018 12:21:36 +0800 Subject: [PATCH 02/34] [es] Add defintions to sign.h --- lib/libes/include/es/sign.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/libes/include/es/sign.h b/lib/libes/include/es/sign.h index 61d352f..4e3f85c 100644 --- a/lib/libes/include/es/sign.h +++ b/lib/libes/include/es/sign.h @@ -20,6 +20,9 @@ namespace es }; static const size_t kEcdsaSigSize = 0x3C; + + static const std::string kRootIssuerStr = "Root"; + static const std::string kIdentDelimiter = "-"; } #pragma pack(push,1) struct sRsa4096SignBlock From 5b9b85a7c4169c26c72969e3c2f523690eb62718 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 4 Jul 2018 13:00:49 +0800 Subject: [PATCH 03/34] [fnd] Initialise memory in Vec. --- lib/libfnd/include/fnd/Vec.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/libfnd/include/fnd/Vec.h b/lib/libfnd/include/fnd/Vec.h index 6cc2ed3..667027a 100644 --- a/lib/libfnd/include/fnd/Vec.h +++ b/lib/libfnd/include/fnd/Vec.h @@ -146,6 +146,11 @@ namespace fnd { fnd::Exception("Vec", "Failed to allocate memory for vector"); } + for (size_t i = 0; i < new_size; i++) + { + m_Vec[i] = 0; + } + m_Size = new_size; } @@ -159,6 +164,10 @@ namespace fnd { fnd::Exception("Vec", "Failed to allocate memory for vector"); } + for (size_t i = 0; i < new_size; i++) + { + new_vec[i] = 0; + } for (size_t i = 0; i < _MIN(m_Size, new_size); i++) { From ab029cd936549fc181f0a3912c36ce0dea587468 Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 4 Jul 2018 13:01:12 +0800 Subject: [PATCH 04/34] [es] Fix typo in TicketBody. --- lib/libes/source/TicketBody_V2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/libes/source/TicketBody_V2.cpp b/lib/libes/source/TicketBody_V2.cpp index a9bd29c..8dba0a3 100644 --- a/lib/libes/source/TicketBody_V2.cpp +++ b/lib/libes/source/TicketBody_V2.cpp @@ -86,7 +86,7 @@ void es::TicketBody_V2::toBytes() memcpy(body->reserved_region, mReservedRegion, ticket::kReservedRegionSize); body->ticket_id = (mTicketId); body->device_id = (mDeviceId); - memcmp(body->rights_id, mRightsId, ticket::kRightsIdSize); + memcpy(body->rights_id, mRightsId, ticket::kRightsIdSize); body->account_id = (mAccountId); body->sect_total_size = (mSectTotalSize); body->sect_header_offset = (mSectHeaderOffset); From 16041c2d655ecd03acebf8ce28267745ad90412e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 4 Jul 2018 13:35:35 +0800 Subject: [PATCH 05/34] [es] Fix bugs where member variables were not applied when copied. --- lib/libes/source/TicketBody_V2.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/libes/source/TicketBody_V2.cpp b/lib/libes/source/TicketBody_V2.cpp index 8dba0a3..0ba5919 100644 --- a/lib/libes/source/TicketBody_V2.cpp +++ b/lib/libes/source/TicketBody_V2.cpp @@ -26,6 +26,7 @@ void es::TicketBody_V2::operator=(const TicketBody_V2 & other) mEncType = other.mEncType; mTicketVersion = other.mTicketVersion; mLicenseType = other.mLicenseType; + mCommonKeyId = other.mCommonKeyId; mPreInstall = other.mPreInstall; mSharedTitle = other.mSharedTitle; mAllowAllContent = other.mAllowAllContent; @@ -74,10 +75,12 @@ void es::TicketBody_V2::toBytes() body->format_version = (ticket::kFormatVersion); - strncmp(body->issuer, mIssuer.c_str(), ticket::kIssuerSize); + strncpy(body->issuer, mIssuer.c_str(), ticket::kIssuerSize); memcpy(body->enc_title_key, mEncTitleKey, ticket::kEncTitleKeySize); body->title_key_enc_type = (mEncType); body->ticket_version = (mTicketVersion); + body->license_type = mLicenseType; + body->common_key_id = mCommonKeyId; byte_t property_mask = 0; property_mask |= mPreInstall ? _BIT(ticket::FLAG_PRE_INSTALL) : 0; property_mask |= mSharedTitle ? _BIT(ticket::FLAG_SHARED_TITLE) : 0; @@ -117,6 +120,7 @@ void es::TicketBody_V2::fromBytes(const byte_t * bytes, size_t len) mEncType = (ticket::TitleKeyEncType)body->title_key_enc_type; mTicketVersion = body->ticket_version.get(); mLicenseType = (ticket::LicenseType)body->license_type; + mCommonKeyId = body->common_key_id; mPreInstall = _HAS_BIT(body->property_mask, ticket::FLAG_PRE_INSTALL); mSharedTitle = _HAS_BIT(body->property_mask, ticket::FLAG_SHARED_TITLE); mAllowAllContent = _HAS_BIT(body->property_mask, ticket::FLAG_ALLOW_ALL_CONTENT); @@ -144,6 +148,7 @@ void es::TicketBody_V2::clear() mEncType = ticket::AES128_CBC; mTicketVersion = 0; mLicenseType = ticket::LICENSE_PERMANENT; + mCommonKeyId = 0; mPreInstall = false; mSharedTitle = false; mAllowAllContent = false; From 38eb0391b3c1a652f8f8cf91d24edd7cee41c41e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 4 Jul 2018 14:00:45 +0800 Subject: [PATCH 06/34] [es] Change handling of ticket property flags. --- lib/libes/include/es/TicketBody_V2.h | 15 ++------ lib/libes/source/TicketBody_V2.cpp | 55 ++++++++-------------------- 2 files changed, 20 insertions(+), 50 deletions(-) diff --git a/lib/libes/include/es/TicketBody_V2.h b/lib/libes/include/es/TicketBody_V2.h index 6b0be01..4741cac 100644 --- a/lib/libes/include/es/TicketBody_V2.h +++ b/lib/libes/include/es/TicketBody_V2.h @@ -1,6 +1,7 @@ #pragma once #include #include +#include #include namespace es @@ -42,14 +43,8 @@ namespace es byte_t getCommonKeyId() const; void setCommonKeyId(byte_t id); - bool isPreInstall() const; - void setIsPreInstall(bool isPreInstall); - - bool isSharedTitle() const; - void setIsSharedTitle(bool isSharedTitle); - - bool allowAllContent() const; - void setAllowAllContent(bool allowAllContent); + const fnd::List& getPropertyFlags() const; + void setPropertyFlags(const fnd::List& flags); const byte_t* getReservedRegion() const; void setReservedRegion(const byte_t* data, size_t len); @@ -91,9 +86,7 @@ namespace es uint16_t mTicketVersion; ticket::LicenseType mLicenseType; byte_t mCommonKeyId; - bool mPreInstall; - bool mSharedTitle; - bool mAllowAllContent; + fnd::List mPropertyFlags; byte_t mReservedRegion[ticket::kReservedRegionSize]; // explicitly reserved uint64_t mTicketId; uint64_t mDeviceId; diff --git a/lib/libes/source/TicketBody_V2.cpp b/lib/libes/source/TicketBody_V2.cpp index 0ba5919..8e19726 100644 --- a/lib/libes/source/TicketBody_V2.cpp +++ b/lib/libes/source/TicketBody_V2.cpp @@ -27,9 +27,7 @@ void es::TicketBody_V2::operator=(const TicketBody_V2 & other) mTicketVersion = other.mTicketVersion; mLicenseType = other.mLicenseType; mCommonKeyId = other.mCommonKeyId; - mPreInstall = other.mPreInstall; - mSharedTitle = other.mSharedTitle; - mAllowAllContent = other.mAllowAllContent; + mPropertyFlags = other.mPropertyFlags; memcpy(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize); mTicketId = other.mTicketId; mDeviceId = other.mDeviceId; @@ -49,9 +47,7 @@ bool es::TicketBody_V2::operator==(const TicketBody_V2 & other) const && (mEncType == other.mEncType) \ && (mTicketVersion == other.mTicketVersion) \ && (mLicenseType == other.mLicenseType) \ - && (mPreInstall == other.mPreInstall) \ - && (mSharedTitle == other.mSharedTitle) \ - && (mAllowAllContent == other.mAllowAllContent) \ + && (mPropertyFlags == other.mPropertyFlags) \ && (memcmp(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize) == 0) \ && (mTicketId == other.mTicketId) \ && (mDeviceId == other.mDeviceId) \ @@ -82,9 +78,10 @@ void es::TicketBody_V2::toBytes() body->license_type = mLicenseType; body->common_key_id = mCommonKeyId; byte_t property_mask = 0; - property_mask |= mPreInstall ? _BIT(ticket::FLAG_PRE_INSTALL) : 0; - property_mask |= mSharedTitle ? _BIT(ticket::FLAG_SHARED_TITLE) : 0; - property_mask |= mAllowAllContent ? _BIT(ticket::FLAG_ALLOW_ALL_CONTENT) : 0; + for (size_t i = 0; i < mPropertyFlags.size(); i++) + { + property_mask |= _BIT(mPropertyFlags[i]); + } body->property_mask = (property_mask); memcpy(body->reserved_region, mReservedRegion, ticket::kReservedRegionSize); body->ticket_id = (mTicketId); @@ -121,9 +118,11 @@ void es::TicketBody_V2::fromBytes(const byte_t * bytes, size_t len) mTicketVersion = body->ticket_version.get(); mLicenseType = (ticket::LicenseType)body->license_type; mCommonKeyId = body->common_key_id; - mPreInstall = _HAS_BIT(body->property_mask, ticket::FLAG_PRE_INSTALL); - mSharedTitle = _HAS_BIT(body->property_mask, ticket::FLAG_SHARED_TITLE); - mAllowAllContent = _HAS_BIT(body->property_mask, ticket::FLAG_ALLOW_ALL_CONTENT); + for (size_t i = 0; i < mPropertyFlags.size(); i++) + { + if (_HAS_BIT(body->property_mask, i)) + mPropertyFlags.addElement((ticket::PropertyMaskFlags)i); + } memcpy(mReservedRegion, body->reserved_region, ticket::kReservedRegionSize); mTicketId = body->ticket_id.get(); mDeviceId = body->device_id.get(); @@ -149,9 +148,7 @@ void es::TicketBody_V2::clear() mTicketVersion = 0; mLicenseType = ticket::LICENSE_PERMANENT; mCommonKeyId = 0; - mPreInstall = false; - mSharedTitle = false; - mAllowAllContent = false; + mPropertyFlags.clear(); memset(mReservedRegion, 0, ticket::kReservedRegionSize); mTicketId = 0; mDeviceId = 0; @@ -229,34 +226,14 @@ void es::TicketBody_V2::setCommonKeyId(byte_t id) mCommonKeyId = id; } -bool es::TicketBody_V2::isPreInstall() const +const fnd::List& es::TicketBody_V2::getPropertyFlags() const { - return mPreInstall; + return mPropertyFlags; } -void es::TicketBody_V2::setIsPreInstall(bool isPreInstall) +void es::TicketBody_V2::setPropertyFlags(const fnd::List& flags) { - mPreInstall = isPreInstall; -} - -bool es::TicketBody_V2::isSharedTitle() const -{ - return mSharedTitle; -} - -void es::TicketBody_V2::setIsSharedTitle(bool isSharedTitle) -{ - mSharedTitle = isSharedTitle; -} - -bool es::TicketBody_V2::allowAllContent() const -{ - return mAllowAllContent; -} - -void es::TicketBody_V2::setAllowAllContent(bool allowAllContent) -{ - mAllowAllContent = allowAllContent; + mPropertyFlags = flags; } const byte_t * es::TicketBody_V2::getReservedRegion() const From 4cdb213f8ded8459a632fbf9a719a29650549e5f Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 10 Jul 2018 22:58:19 +0800 Subject: [PATCH 07/34] [crypto] Add base64 wrapper. --- lib/libcrypto/include/crypto/base64.h | 14 ++++++++++++ lib/libcrypto/source/base64_wrapper.cpp | 30 +++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 lib/libcrypto/include/crypto/base64.h create mode 100644 lib/libcrypto/source/base64_wrapper.cpp diff --git a/lib/libcrypto/include/crypto/base64.h b/lib/libcrypto/include/crypto/base64.h new file mode 100644 index 0000000..67c6ecd --- /dev/null +++ b/lib/libcrypto/include/crypto/base64.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace crypto +{ +namespace base64 +{ + size_t B64_GetEncodeLen(const uint8_t* src, size_t slen); + void B64_Encode(const uint8_t* src, size_t slen, uint8_t* dst, size_t dlen); + size_t B64_GetDecodeLen(const uint8_t* src, size_t slen); + void B64_Decode(const uint8_t* src, size_t slen, uint8_t* dst, size_t dlen); +} +} \ No newline at end of file diff --git a/lib/libcrypto/source/base64_wrapper.cpp b/lib/libcrypto/source/base64_wrapper.cpp new file mode 100644 index 0000000..1bc6f66 --- /dev/null +++ b/lib/libcrypto/source/base64_wrapper.cpp @@ -0,0 +1,30 @@ +#include +#include + +size_t crypto::base64::B64_GetEncodeLen(const uint8_t* src, size_t slen) +{ + size_t dlen = 0; + + base64_encode(nullptr, &dlen, src, slen); + + return dlen; +} + +void crypto::base64::B64_Encode(const uint8_t* src, size_t slen, uint8_t* dst, size_t dlen) +{ + base64_encode(dst, &dlen, src, slen); +} + +size_t crypto::base64::B64_GetDecodeLen(const uint8_t* src, size_t slen) +{ + size_t dlen = 0; + + base64_decode(nullptr, &dlen, src, slen); + + return dlen; +} + +void crypto::base64::B64_Decode(const uint8_t* src, size_t slen, uint8_t* dst, size_t dlen) +{ + base64_decode(dst, &dlen, src, slen); +} \ No newline at end of file From 24d2be385baba04721b3e0f1decadb9ce2496262 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 10 Jul 2018 23:00:29 +0800 Subject: [PATCH 08/34] Fix issue with project file. --- .vscode/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index d1f6ffb..13d2bfc 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -27,7 +27,7 @@ "memory": "cpp", "tuple": "cpp", "__locale": "cpp", - "cinttypes": "cpp" + "cinttypes": "cpp", "__bit_reference": "cpp", } } \ No newline at end of file From 9ae737cdc8e524418a0f9d89a4c0377a5832ad4c Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 10 Jul 2018 23:00:39 +0800 Subject: [PATCH 09/34] Update VSCode include paths. --- .vscode/c_cpp_properties.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 17a1a7e..174c73a 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,7 +9,8 @@ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", "/usr/include", "${workspaceRoot}", - "${workspaceRoot}/lib/libcrypto/include", + "${workspaceRoot}/lib/libcrypto/include", + "${workspaceRoot}/lib/libcrypto/source/polarssl/libinclude", "${workspaceRoot}/lib/libcompress/include", "${workspaceRoot}/lib/libes/include", "${workspaceRoot}/lib/libfnd/include", From e7148c39ea973f1e9e2e236f8ced6fe603620bbf Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 10 Jul 2018 23:01:34 +0800 Subject: [PATCH 10/34] [nxtool] Inital reading of ES tickets and certificates. --- programs/nstool/source/EsCertProcess.cpp | 249 +++++++++++++++++++++++ programs/nstool/source/EsCertProcess.h | 46 +++++ programs/nstool/source/EsTikProcess.cpp | 187 +++++++++++++++++ programs/nstool/source/EsTikProcess.h | 38 ++++ programs/nstool/source/main.cpp | 24 +++ 5 files changed, 544 insertions(+) create mode 100644 programs/nstool/source/EsCertProcess.cpp create mode 100644 programs/nstool/source/EsCertProcess.h create mode 100644 programs/nstool/source/EsTikProcess.cpp create mode 100644 programs/nstool/source/EsTikProcess.h diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp new file mode 100644 index 0000000..7bab43f --- /dev/null +++ b/programs/nstool/source/EsCertProcess.cpp @@ -0,0 +1,249 @@ +#include +#include "OffsetAdjustedIFile.h" +#include "EsCertProcess.h" + +EsCertProcess::EsCertProcess() : + mFile(nullptr), + mOwnIFile(false), + mCliOutputMode(_BIT(OUTPUT_BASIC)), + mVerify(false) +{ +} + +EsCertProcess::~EsCertProcess() +{ + if (mOwnIFile) + { + delete mFile; + } +} + +void EsCertProcess::process() +{ + if (mFile == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } + + importCerts(); + if (mVerify) + validateCerts(); + + if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + displayCerts(); +} + +void EsCertProcess::setInputFile(fnd::IFile* file, bool ownIFile) +{ + mFile = file; + mOwnIFile = ownIFile; +} + +void EsCertProcess::setKeyset(const sKeyset* keyset) +{ + mKeyset = keyset; +} + +void EsCertProcess::setCliOutputMode(CliOutputMode mode) +{ + mCliOutputMode = mode; +} + +void EsCertProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void EsCertProcess::importCerts() +{ + fnd::Vec scratch; + + scratch.alloc(mFile->size()); + mFile->read(scratch.data(), 0, scratch.size()); + + es::SignedData cert; + for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) + { + cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos); + mCert.addElement(cert); + } +} + +void EsCertProcess::validateCerts() +{ + for (size_t i = 0; i < mCert.size(); i++) + { + EsCertProcess::validateCert(mCert[i]); + } +} + +void EsCertProcess::validateCert(const es::SignedData& cert) +{ + std::string cert_ident = ""; + cert_ident += cert.getBody().getSubject(); + cert_ident += cert.getBody().getSubject(); + cert_ident += cert.getBody().getSubject(); + + try + { + if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) + { + throw fnd::Exception(kModuleName, "Signed by Root"); + } + + const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + + if (issuer.getPublicKeyType() == es::cert::RSA4096 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA256)) + { + throw fnd::Exception(kModuleName, "RSA4096 signatures are not supported"); + } + else if (issuer.getPublicKeyType() == es::cert::RSA2048 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA256)) + { + throw fnd::Exception(kModuleName, "RSA2048 signatures are not supported"); + } + else if (issuer.getPublicKeyType() == es::cert::ECDSA240 && (cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA256)) + { + throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + } + else + { + throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + } + } + catch (const fnd::Exception& e) + { + printf("[WARNING] Failed to validate %s (%s)\n", cert_ident.c_str(), e.error()); + return; + } + +} + +void EsCertProcess::displayCerts() +{ + for (size_t i = 0; i < mCert.size(); i++) + { + displayCert(mCert[i]); + } +} + +void EsCertProcess::displayCert(const es::SignedData& cert) +{ +#define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff) +#define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0) +#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) + + printf("[ES Certificate]\n"); + printf(" SignType: %s", getSignTypeStr(cert.getSignature().getSignType())); + if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + printf(" (0x%" PRIx32 ") (%s)", cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian())); + printf("\n"); + printf(" Issuer: %s\n", cert.getBody().getIssuer().c_str()); + printf(" Subject: %s\n", cert.getBody().getSubject().c_str()); + printf(" PublicKeyType: %s", getPublicKeyType(cert.getBody().getPublicKeyType())); + if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + printf(" (%d)", cert.getBody().getPublicKeyType()); + printf("\n"); + printf(" CertID: 0x%" PRIx32 " \n", cert.getBody().getCertId()); + + if (cert.getBody().getPublicKeyType() == es::cert::RSA4096) + { + printf(" PublicKey:\n"); + printf(" Modulus:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa4096Size : 0x10, 0x10, 6); + printf(" Public Exponent:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); + } + else if (cert.getBody().getPublicKeyType() == es::cert::RSA2048) + { + printf(" PublicKey:\n"); + printf(" Public Exponent:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa2048Size : 0x10, 0x10, 6); + printf(" Modulus:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); + } + else if (cert.getBody().getPublicKeyType() == es::cert::ECDSA240) + { + printf(" PublicKey:\n"); + printf(" R:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); + printf(" S:\n"); + fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); + } + + + +#undef _HEXDUMP_L +#undef _HEXDUMP_U +#undef _SPLIT_VER +} + +const es::SignedData& EsCertProcess::getIssuerCert(const std::string& issuer_name) const +{ + std::string full_cert_name; + for (size_t i = 0; i < mCert.size(); i++) + { + full_cert_name = mCert[i].getBody().getIssuer() + es::sign::kIdentDelimiter + mCert[i].getBody().getSubject(); + if (full_cert_name == issuer_name) + { + return mCert[i]; + } + } + + throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); +} + +const char* EsCertProcess::getSignTypeStr(es::sign::SignType type) const +{ + const char* str; + switch (type) + { + case (es::sign::SIGN_RSA4096_SHA1): + str = "RSA4096-SHA1"; + break; + case (es::sign::SIGN_RSA2048_SHA1): + str = "RSA2048-SHA1"; + break; + case (es::sign::SIGN_ECDSA240_SHA1): + str = "ECDSA240-SHA1"; + break; + case (es::sign::SIGN_RSA4096_SHA256): + str = "RSA4096-SHA256"; + break; + case (es::sign::SIGN_RSA2048_SHA256): + str = "RSA2048-SHA256"; + break; + case (es::sign::SIGN_ECDSA240_SHA256): + str = "ECDSA240-SHA256"; + break; + default: + str = "Unknown"; + break; + } + return str; +} + +const char* EsCertProcess::getEndiannessStr(bool isLittleEndian) const +{ + return isLittleEndian ? "LittleEndian" : "BigEndian"; +} + +const char* EsCertProcess::getPublicKeyType(es::cert::PublicKeyType type) const +{ + const char* str; + switch (type) + { + case (es::cert::RSA4096): + str = "RSA4096"; + break; + case (es::cert::RSA2048): + str = "RSA2048"; + break; + case (es::cert::ECDSA240): + str = "ECDSA240"; + break; + default: + str = "Unknown"; + break; + } + return str; +} \ No newline at end of file diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h new file mode 100644 index 0000000..11c2430 --- /dev/null +++ b/programs/nstool/source/EsCertProcess.h @@ -0,0 +1,46 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include "nstool.h" + +class EsCertProcess +{ +public: + EsCertProcess(); + ~EsCertProcess(); + + void process(); + + void setInputFile(fnd::IFile* file, bool ownIFile); + void setKeyset(const sKeyset* keyset); + void setCliOutputMode(CliOutputMode type); + void setVerifyMode(bool verify); + +private: + const std::string kModuleName = "EsCertProcess"; + + fnd::IFile* mFile; + bool mOwnIFile; + const sKeyset* mKeyset; + CliOutputMode mCliOutputMode; + bool mVerify; + + fnd::List> mCert; + + void importCerts(); + void validateCerts(); + void validateCert(const es::SignedData& cert); + void displayCerts(); + void displayCert(const es::SignedData& cert); + + const es::SignedData& getIssuerCert(const std::string& issuer_name) const; + + const char* getSignTypeStr(es::sign::SignType type) const; + const char* getEndiannessStr(bool isLittleEndian) const; + const char* getPublicKeyType(es::cert::PublicKeyType type) const; +}; \ No newline at end of file diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp new file mode 100644 index 0000000..909979a --- /dev/null +++ b/programs/nstool/source/EsTikProcess.cpp @@ -0,0 +1,187 @@ +#include +#include "OffsetAdjustedIFile.h" +#include "EsTikProcess.h" + +EsTikProcess::EsTikProcess() : + mFile(nullptr), + mOwnIFile(false), + mCliOutputMode(_BIT(OUTPUT_BASIC)), + mVerify(false) +{ +} + +EsTikProcess::~EsTikProcess() +{ + if (mOwnIFile) + { + delete mFile; + } +} + +void EsTikProcess::process() +{ + fnd::Vec scratch; + + if (mFile == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } + + scratch.alloc(mFile->size()); + mFile->read(scratch.data(), 0, scratch.size()); + + mTik.fromBytes(scratch.data(), scratch.size()); + + if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) + displayTicket(); +} + +void EsTikProcess::setInputFile(fnd::IFile* file, bool ownIFile) +{ + mFile = file; + mOwnIFile = ownIFile; +} + +void EsTikProcess::setKeyset(const sKeyset* keyset) +{ + mKeyset = keyset; +} + +void EsTikProcess::setCliOutputMode(CliOutputMode mode) +{ + mCliOutputMode = mode; +} + +void EsTikProcess::setVerifyMode(bool verify) +{ + mVerify = verify; +} + +void EsTikProcess::displayTicket() +{ +#define _SPLIT_VER(ver) ( (ver>>10) & 0x3f), ( (ver>>4) & 0x3f), ( (ver>>0) & 0xf) +#define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0) +#define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) + + const es::TicketBody_V2& body = mTik.getBody(); + + printf("[ES Ticket]\n"); + printf(" SignType: 0x%" PRIx32 " (%s)\n", mTik.getSignature().getSignType(), mTik.getSignature().isLittleEndian()? "LittleEndian" : "BigEndian"); + printf(" Issuer: %s\n", body.getIssuer().c_str()); + printf(" Title Key:\n"); + printf(" EncMode: %s\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType())); + printf(" CommonKeyId: %02X\n", body.getCommonKeyId()); + printf(" EncData:\n"); + size_t size = body.getTitleKeyEncType() == es::ticket::RSA2048 ? crypto::rsa::kRsa2048Size : crypto::aes::kAes128KeySize; + fnd::SimpleTextOutput::hexDump(body.getEncTitleKey(), size, 0x10, 6); + + /* + if (body.getTitleKeyEncType() == es::ticket::AES128_CBC && body.getCommonKeyId() == 0) + { + byte_t iv[crypto::aes::kAesBlockSize]; + byte_t key[crypto::aes::kAes128KeySize]; + memcpy(iv, body.getRightsId(), crypto::aes::kAesBlockSize); + crypto::aes::AesCbcDecrypt(body.getEncTitleKey(), crypto::aes::kAes128KeySize, eticket_common_key, iv, key); + size = crypto::aes::kAes128KeySize; + printf(" TitleKey:\n"); + fnd::SimpleTextOutput::hexDump(key, size, 0x10, 6); + } + */ + printf(" Version: v%d.%d.%d (%d)\n", _SPLIT_VER(body.getTicketVersion()), body.getTicketVersion()); + printf(" License Type: %s\n", getLicenseTypeStr(body.getLicenseType())); + + if (body.getPropertyFlags().size() > 0) + { + printf(" Flags:\n"); + for (size_t i = 0; i < body.getPropertyFlags().size(); i++) + { + printf(" %s\n", getPropertyFlagStr(body.getPropertyFlags()[i])); + } + } + + printf(" Reserved Region:\n"); + fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4); + printf(" TicketId: 0x%016" PRIx64 "\n", body.getTicketId()); + printf(" DeviceId: 0x%016" PRIx64 "\n", body.getDeviceId()); + printf(" RightsId: "); + fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16); + + printf(" SectionTotalSize: 0x%x\n", body.getSectionTotalSize()); + printf(" SectionHeaderOffset: 0x%x\n", body.getSectionHeaderOffset()); + printf(" SectionNum: 0x%x\n", body.getSectionNum()); + printf(" SectionEntrySize: 0x%x\n", body.getSectionEntrySize()); + + +#undef _HEXDUMP_L +#undef _HEXDUMP_U +#undef _SPLIT_VER +} + +const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const +{ + const char* str = nullptr; + switch(flag) + { + case (es::ticket::AES128_CBC): + str = "Generic (AESCBC)"; + break; + case (es::ticket::RSA2048): + str = "Personalised (RSA2048)"; + break; + default: + str = "Unknown"; + break; + } + return str; +} + +const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const +{ + const char* str = nullptr; + switch(flag) + { + case (es::ticket::LICENSE_PERMANENT): + str = "Permanent"; + break; + case (es::ticket::LICENSE_DEMO): + str = "Demo"; + break; + case (es::ticket::LICENSE_TRIAL): + str = "Trial"; + break; + case (es::ticket::LICENSE_RENTAL): + str = "Rental"; + break; + case (es::ticket::LICENSE_SUBSCRIPTION): + str = "Subscription"; + break; + case (es::ticket::LICENSE_SERVICE): + str = "Service"; + break; + default: + str = "Unknown"; + break; + } + return str; +} + +const char* EsTikProcess::getPropertyFlagStr(byte_t flag) const +{ + const char* str = nullptr; + switch(flag) + { + case (es::ticket::FLAG_PRE_INSTALL): + str = "PreInstall"; + break; + case (es::ticket::FLAG_SHARED_TITLE): + str = "SharedTitle"; + break; + case (es::ticket::FLAG_ALLOW_ALL_CONTENT): + str = "AllContent"; + break; + default: + str = "Unknown"; + break; + } + return str; +} \ No newline at end of file diff --git a/programs/nstool/source/EsTikProcess.h b/programs/nstool/source/EsTikProcess.h new file mode 100644 index 0000000..97fb2fb --- /dev/null +++ b/programs/nstool/source/EsTikProcess.h @@ -0,0 +1,38 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include "nstool.h" + +class EsTikProcess +{ +public: + EsTikProcess(); + ~EsTikProcess(); + + void process(); + + void setInputFile(fnd::IFile* file, bool ownIFile); + void setKeyset(const sKeyset* keyset); + void setCliOutputMode(CliOutputMode mode); + void setVerifyMode(bool verify); + +private: + const std::string kModuleName = "EsTikProcess"; + + fnd::IFile* mFile; + bool mOwnIFile; + const sKeyset* mKeyset; + CliOutputMode mCliOutputMode; + bool mVerify; + + es::SignedData mTik; + + void displayTicket(); + const char* getTitleKeyPersonalisationStr(byte_t flag) const; + const char* getLicenseTypeStr(byte_t flag) const; + const char* getPropertyFlagStr(byte_t flag) const; +}; \ No newline at end of file diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp index 9039e59..fac17b5 100644 --- a/programs/nstool/source/main.cpp +++ b/programs/nstool/source/main.cpp @@ -10,6 +10,8 @@ #include "NsoProcess.h" #include "NroProcess.h" #include "NacpProcess.h" +#include "EsCertProcess.h" +#include "EsTikProcess.h" #include "AssetProcess.h" int main(int argc, char** argv) @@ -158,6 +160,28 @@ int main(int argc, char** argv) nacp.process(); } + else if (user_set.getFileType() == FILE_ES_CERT) + { + EsCertProcess cert; + + cert.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); + cert.setKeyset(&user_set.getKeyset()); + cert.setCliOutputMode(user_set.getCliOutputMode()); + cert.setVerifyMode(user_set.isVerifyFile()); + + cert.process(); + } + else if (user_set.getFileType() == FILE_ES_TIK) + { + EsTikProcess tik; + + tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); + tik.setKeyset(&user_set.getKeyset()); + tik.setCliOutputMode(user_set.getCliOutputMode()); + tik.setVerifyMode(user_set.isVerifyFile()); + + tik.process(); + } else if (user_set.getFileType() == FILE_HB_ASSET) { AssetProcess obj; From 03141e3923f3eab68d59141725d7c2b07b3f581e Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 11 Jul 2018 22:29:08 +0800 Subject: [PATCH 11/34] Update VS Project Files. --- programs/nstool/nstool.vcxproj | 10 +- programs/nstool/nstool.vcxproj.filters | 156 +++++++++++++------------ 2 files changed, 91 insertions(+), 75 deletions(-) diff --git a/programs/nstool/nstool.vcxproj b/programs/nstool/nstool.vcxproj index 35b393f..185044d 100644 --- a/programs/nstool/nstool.vcxproj +++ b/programs/nstool/nstool.vcxproj @@ -167,11 +167,16 @@ {91ba9e79-8242-4f7d-b997-0dfec95ea22b} + + + + + @@ -194,6 +199,8 @@ + + @@ -210,9 +217,6 @@ - - - diff --git a/programs/nstool/nstool.vcxproj.filters b/programs/nstool/nstool.vcxproj.filters index 4d94f58..11e8b67 100644 --- a/programs/nstool/nstool.vcxproj.filters +++ b/programs/nstool/nstool.vcxproj.filters @@ -15,21 +15,66 @@ + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + Header Files Header Files + + Header Files + + + Header Files + Header Files + + Header Files + Header Files + + Header Files + Header Files + + Header Files + Header Files @@ -39,103 +84,70 @@ Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - Source Files - - Source Files - - + Source Files Source Files + + Source Files + + + Source Files + + + Source Files + Source Files Source Files - - Source Files - - - Source Files - - - Source Files - - + Source Files Source Files - + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + Source Files Source Files - - - + + Source Files + + + Source Files + + + Source Files + + + Source Files + \ No newline at end of file From 90d1b78f2b514fb33e4fb79e02a1f31fdcf8859d Mon Sep 17 00:00:00 2001 From: jakcron Date: Wed, 11 Jul 2018 22:38:10 +0800 Subject: [PATCH 12/34] Update Project Files. --- lib/libes/es.vcxproj | 12 ++++++------ lib/libes/es.vcxproj.filters | 28 ++++++++++++++-------------- programs/nstool/nstool.vcxproj | 11 +++++++---- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/lib/libes/es.vcxproj b/lib/libes/es.vcxproj index ac4d213..c32ab08 100644 --- a/lib/libes/es.vcxproj +++ b/lib/libes/es.vcxproj @@ -121,12 +121,6 @@ - - - - - - @@ -137,6 +131,12 @@ + + + + + + diff --git a/lib/libes/es.vcxproj.filters b/lib/libes/es.vcxproj.filters index 1f0bc28..653f976 100644 --- a/lib/libes/es.vcxproj.filters +++ b/lib/libes/es.vcxproj.filters @@ -17,20 +17,6 @@ - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - Header Files @@ -57,4 +43,18 @@ Header Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/programs/nstool/nstool.vcxproj b/programs/nstool/nstool.vcxproj index 185044d..791ca14 100644 --- a/programs/nstool/nstool.vcxproj +++ b/programs/nstool/nstool.vcxproj @@ -90,7 +90,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -105,7 +105,7 @@ true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -122,7 +122,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -141,7 +141,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -157,6 +157,9 @@ {6adbb60d-dba0-411d-bd2d-a355ef8e0fe1} + + {7be99936-0d40-410d-944b-4513c2eff8dc} + {4d27edb9-5110-44fe-8ce2-d46c5ad3c55b} From 3a4259d2cc259978b4e2f7a809c12246c84e06f8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 29 Jul 2018 20:51:15 +0900 Subject: [PATCH 13/34] [es] Fixed a bug where string fields were not imported correctly. --- lib/libes/source/CertificateBody.cpp | 4 ++-- lib/libes/source/TicketBody_V2.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/libes/source/CertificateBody.cpp b/lib/libes/source/CertificateBody.cpp index 11bec02..6219a21 100644 --- a/lib/libes/source/CertificateBody.cpp +++ b/lib/libes/source/CertificateBody.cpp @@ -129,10 +129,10 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size) hdr = (const sCertificateHeader*)mRawBinary.data(); if (hdr->issuer[0] != 0) - mIssuer = std::string(hdr->issuer, cert::kIssuerSize); + mIssuer = std::string(hdr->issuer, _MIN(strlen(hdr->issuer), cert::kIssuerSize)); mPublicKeyType = (cert::PublicKeyType)hdr->key_type.get(); if (hdr->subject[0] != 0) - mSubject = std::string(hdr->subject, cert::kSubjectSize); + mSubject = std::string(hdr->subject, _MIN(strlen(hdr->subject), cert::kSubjectSize)); mCertId = hdr->cert_id.get(); // save public key diff --git a/lib/libes/source/TicketBody_V2.cpp b/lib/libes/source/TicketBody_V2.cpp index 8e19726..93816c7 100644 --- a/lib/libes/source/TicketBody_V2.cpp +++ b/lib/libes/source/TicketBody_V2.cpp @@ -112,7 +112,7 @@ void es::TicketBody_V2::fromBytes(const byte_t * bytes, size_t len) throw fnd::Exception(kModuleName, "Unsupported format version"); } - mIssuer.append(body->issuer, ticket::kIssuerSize); + mIssuer = std::string(body->issuer, _MIN(strlen(body->issuer), ticket::kIssuerSize)); memcpy(mEncTitleKey, body->enc_title_key, ticket::kEncTitleKeySize); mEncType = (ticket::TitleKeyEncType)body->title_key_enc_type; mTicketVersion = body->ticket_version.get(); From 29ecd03471851268b5e7d1557214e38cda11daaf Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 29 Jul 2018 21:01:11 +0900 Subject: [PATCH 14/34] [nstool] Replaced printf with cout pipes. --- programs/nstool/source/EsTikProcess.cpp | 104 ++++++++++++++++-------- programs/nstool/source/EsTikProcess.h | 1 + 2 files changed, 73 insertions(+), 32 deletions(-) diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp index 909979a..301607f 100644 --- a/programs/nstool/source/EsTikProcess.cpp +++ b/programs/nstool/source/EsTikProcess.cpp @@ -1,7 +1,12 @@ +#include +#include + #include #include "OffsetAdjustedIFile.h" #include "EsTikProcess.h" + + EsTikProcess::EsTikProcess() : mFile(nullptr), mOwnIFile(false), @@ -65,51 +70,56 @@ void EsTikProcess::displayTicket() const es::TicketBody_V2& body = mTik.getBody(); - printf("[ES Ticket]\n"); - printf(" SignType: 0x%" PRIx32 " (%s)\n", mTik.getSignature().getSignType(), mTik.getSignature().isLittleEndian()? "LittleEndian" : "BigEndian"); - printf(" Issuer: %s\n", body.getIssuer().c_str()); - printf(" Title Key:\n"); - printf(" EncMode: %s\n", getTitleKeyPersonalisationStr(body.getTitleKeyEncType())); - printf(" CommonKeyId: %02X\n", body.getCommonKeyId()); - printf(" EncData:\n"); + std::cout << "[ES Ticket]" << std::endl; + + std::cout << " SignType: " << getSignTypeStr(mTik.getSignature().getSignType()); + if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + std::cout << " (0x" << std::hex << mTik.getSignature().getSignType() << ")"; + std::cout << std::endl; + + std::cout << " Issuer: " << body.getIssuer() << std::endl; + std::cout << " Title Key:" << std::endl; + std::cout << " EncMode: " << getTitleKeyPersonalisationStr(body.getTitleKeyEncType()) << std::endl; + std::cout << " KeyGeneration: " << std::dec << (uint32_t)body.getCommonKeyId() << std::endl; + std::cout << " Data:" << std::endl; size_t size = body.getTitleKeyEncType() == es::ticket::RSA2048 ? crypto::rsa::kRsa2048Size : crypto::aes::kAes128KeySize; fnd::SimpleTextOutput::hexDump(body.getEncTitleKey(), size, 0x10, 6); - /* - if (body.getTitleKeyEncType() == es::ticket::AES128_CBC && body.getCommonKeyId() == 0) - { - byte_t iv[crypto::aes::kAesBlockSize]; - byte_t key[crypto::aes::kAes128KeySize]; - memcpy(iv, body.getRightsId(), crypto::aes::kAesBlockSize); - crypto::aes::AesCbcDecrypt(body.getEncTitleKey(), crypto::aes::kAes128KeySize, eticket_common_key, iv, key); - size = crypto::aes::kAes128KeySize; - printf(" TitleKey:\n"); - fnd::SimpleTextOutput::hexDump(key, size, 0x10, 6); - } - */ - printf(" Version: v%d.%d.%d (%d)\n", _SPLIT_VER(body.getTicketVersion()), body.getTicketVersion()); - printf(" License Type: %s\n", getLicenseTypeStr(body.getLicenseType())); + printf(" Version: v%d.%d.%d", _SPLIT_VER(body.getTicketVersion())); + if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + printf(" (%d)", body.getTicketVersion()); + printf("\n"); + + std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl; if (body.getPropertyFlags().size() > 0) { - printf(" Flags:\n"); + std::cout << " Flags:" << std::endl; for (size_t i = 0; i < body.getPropertyFlags().size(); i++) { - printf(" %s\n", getPropertyFlagStr(body.getPropertyFlags()[i])); + std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl; } } - printf(" Reserved Region:\n"); - fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4); - printf(" TicketId: 0x%016" PRIx64 "\n", body.getTicketId()); - printf(" DeviceId: 0x%016" PRIx64 "\n", body.getDeviceId()); - printf(" RightsId: "); + if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + { + std::cout << " Reserved Region:" << std::endl; + fnd::SimpleTextOutput::hexDump(body.getReservedRegion(), 8, 0x10, 4); + } + + if (body.getTicketId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + std::cout << " TicketId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getTicketId() << std::endl; + + if (body.getDeviceId() != 0 || _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) + std::cout << " DeviceId: 0x" << std::hex << std::setw(16) << std::setfill('0') << body.getDeviceId() << std::endl; + + std::cout << " RightsId: " << std::endl << " "; fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16); - printf(" SectionTotalSize: 0x%x\n", body.getSectionTotalSize()); - printf(" SectionHeaderOffset: 0x%x\n", body.getSectionHeaderOffset()); - printf(" SectionNum: 0x%x\n", body.getSectionNum()); - printf(" SectionEntrySize: 0x%x\n", body.getSectionEntrySize()); + std::cout << " SectionTotalSize: 0x" << std::hex << body.getSectionTotalSize() << std::endl; + std::cout << " SectionHeaderOffset: 0x" << std::hex << body.getSectionHeaderOffset() << std::endl; + std::cout << " SectionNum: 0x" << std::hex << body.getSectionNum() << std::endl; + std::cout << " SectionEntrySize: 0x" << std::hex << body.getSectionEntrySize() << std::endl; #undef _HEXDUMP_L @@ -117,6 +127,36 @@ void EsTikProcess::displayTicket() #undef _SPLIT_VER } +const char* EsTikProcess::getSignTypeStr(uint32_t type) const +{ + const char* str = nullptr; + switch(type) + { + case (es::sign::SIGN_RSA4096_SHA1): + str = "RSA4096_SHA1"; + break; + case (es::sign::SIGN_RSA2048_SHA1): + str = "RSA2048_SHA1"; + break; + case (es::sign::SIGN_ECDSA240_SHA1): + str = "ECDSA240_SHA1"; + break; + case (es::sign::SIGN_RSA4096_SHA256): + str = "RSA4096_SHA256"; + break; + case (es::sign::SIGN_RSA2048_SHA256): + str = "RSA2048_SHA256"; + break; + case (es::sign::SIGN_ECDSA240_SHA256): + str = "ECDSA240_SHA256"; + break; + default: + str = "Unknown"; + break; + } + return str; +} + const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const { const char* str = nullptr; diff --git a/programs/nstool/source/EsTikProcess.h b/programs/nstool/source/EsTikProcess.h index 97fb2fb..edbe38d 100644 --- a/programs/nstool/source/EsTikProcess.h +++ b/programs/nstool/source/EsTikProcess.h @@ -32,6 +32,7 @@ private: es::SignedData mTik; void displayTicket(); + const char* getSignTypeStr(uint32_t type) const; const char* getTitleKeyPersonalisationStr(byte_t flag) const; const char* getLicenseTypeStr(byte_t flag) const; const char* getPropertyFlagStr(byte_t flag) const; From b2dd703ce1d92a63893e740d6d7fe9f2caa36dd3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 29 Jul 2018 21:27:08 +0900 Subject: [PATCH 15/34] [nstool] Replaced printf with cout pipes. --- programs/nstool/source/EsCertProcess.cpp | 48 ++++++++++++------------ 1 file changed, 25 insertions(+), 23 deletions(-) diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 7bab43f..73f44a3 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -1,3 +1,6 @@ +#include +#include + #include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" @@ -79,10 +82,7 @@ void EsCertProcess::validateCerts() void EsCertProcess::validateCert(const es::SignedData& cert) { - std::string cert_ident = ""; - cert_ident += cert.getBody().getSubject(); - cert_ident += cert.getBody().getSubject(); - cert_ident += cert.getBody().getSubject(); + std::string cert_ident = cert.getBody().getIssuer() + es::sign::kIdentDelimiter + cert.getBody().getSubject(); try { @@ -132,41 +132,43 @@ void EsCertProcess::displayCert(const es::SignedData& cert) #define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0) #define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) - printf("[ES Certificate]\n"); - printf(" SignType: %s", getSignTypeStr(cert.getSignature().getSignType())); + std::cout << "[ES Certificate]" << std::endl; + + std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType()); if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) - printf(" (0x%" PRIx32 ") (%s)", cert.getSignature().getSignType(), getEndiannessStr(cert.getSignature().isLittleEndian())); - printf("\n"); - printf(" Issuer: %s\n", cert.getBody().getIssuer().c_str()); - printf(" Subject: %s\n", cert.getBody().getSubject().c_str()); - printf(" PublicKeyType: %s", getPublicKeyType(cert.getBody().getPublicKeyType())); + std::cout << " (0x" << std::hex << cert.getSignature().getSignType() << ") (" << getEndiannessStr(cert.getSignature().isLittleEndian()); + std::cout << std::endl; + + std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl; + std::cout << " Subject: " << cert.getBody().getSubject() << std::endl; + std::cout << " PublicKeyType: " << getPublicKeyType(cert.getBody().getPublicKeyType()); if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) - printf(" (%d)", cert.getBody().getPublicKeyType()); - printf("\n"); - printf(" CertID: 0x%" PRIx32 " \n", cert.getBody().getCertId()); + std::cout << " (" << std::dec << cert.getBody().getPublicKeyType() << ")"; + std::cout << std::endl; + std::cout << " CertID: 0x" << std::hex << cert.getBody().getCertId() << std::endl; if (cert.getBody().getPublicKeyType() == es::cert::RSA4096) { - printf(" PublicKey:\n"); - printf(" Modulus:\n"); + std::cout << " PublicKey:" << std::endl; + std::cout << " Modulus:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa4096Size : 0x10, 0x10, 6); - printf(" Public Exponent:\n"); + std::cout << " Public Exponent:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } else if (cert.getBody().getPublicKeyType() == es::cert::RSA2048) { - printf(" PublicKey:\n"); - printf(" Public Exponent:\n"); + std::cout << " PublicKey:" << std::endl; + std::cout << " Public Exponent:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa2048Size : 0x10, 0x10, 6); - printf(" Modulus:\n"); + std::cout << " Modulus:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } else if (cert.getBody().getPublicKeyType() == es::cert::ECDSA240) { - printf(" PublicKey:\n"); - printf(" R:\n"); + std::cout << " PublicKey:" << std::endl; + std::cout << " R:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); - printf(" S:\n"); + std::cout << " S:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); } From 6f5c7fd353217a9fba8a7409b4febda67aedbd32 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 29 Jul 2018 21:28:01 +0900 Subject: [PATCH 16/34] [nstool] Fixed method name typo. --- programs/nstool/source/EsCertProcess.cpp | 4 ++-- programs/nstool/source/EsCertProcess.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 73f44a3..1299b6a 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -141,7 +141,7 @@ void EsCertProcess::displayCert(const es::SignedData& cert) std::cout << " Issuer: " << cert.getBody().getIssuer() << std::endl; std::cout << " Subject: " << cert.getBody().getSubject() << std::endl; - std::cout << " PublicKeyType: " << getPublicKeyType(cert.getBody().getPublicKeyType()); + std::cout << " PublicKeyType: " << getPublicKeyTypeStr(cert.getBody().getPublicKeyType()); if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) std::cout << " (" << std::dec << cert.getBody().getPublicKeyType() << ")"; std::cout << std::endl; @@ -229,7 +229,7 @@ const char* EsCertProcess::getEndiannessStr(bool isLittleEndian) const return isLittleEndian ? "LittleEndian" : "BigEndian"; } -const char* EsCertProcess::getPublicKeyType(es::cert::PublicKeyType type) const +const char* EsCertProcess::getPublicKeyTypeStr(es::cert::PublicKeyType type) const { const char* str; switch (type) diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index 11c2430..42db9e8 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -42,5 +42,5 @@ private: const char* getSignTypeStr(es::sign::SignType type) const; const char* getEndiannessStr(bool isLittleEndian) const; - const char* getPublicKeyType(es::cert::PublicKeyType type) const; + const char* getPublicKeyTypeStr(es::cert::PublicKeyType type) const; }; \ No newline at end of file From 0aebf43f4bb76904ba8b9c772c882185e2f27d60 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 30 Jul 2018 03:18:02 +0800 Subject: [PATCH 17/34] [nstool|es] Add cert validation. --- lib/libes/include/es/SignUtils.h | 14 +++++ lib/libes/include/es/SignatureBlock.h | 6 +- lib/libes/include/es/sign.h | 27 ++++++--- lib/libes/source/SignUtils.cpp | 45 ++++++++++++++ lib/libes/source/SignatureBlock.cpp | 44 +++++++------- programs/nstool/source/EsCertProcess.cpp | 77 +++++++++++++++++++----- programs/nstool/source/EsCertProcess.h | 4 +- programs/nstool/source/EsTikProcess.cpp | 24 ++++---- programs/nstool/source/UserSettings.cpp | 4 +- 9 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 lib/libes/include/es/SignUtils.h create mode 100644 lib/libes/source/SignUtils.cpp diff --git a/lib/libes/include/es/SignUtils.h b/lib/libes/include/es/SignUtils.h new file mode 100644 index 0000000..26996a2 --- /dev/null +++ b/lib/libes/include/es/SignUtils.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace es +{ + +namespace sign +{ + es::sign::SignatureAlgo getSignatureAlgo(es::sign::SignatureId sign_id); + es::sign::HashAlgo getHashAlgo(es::sign::SignatureId sign_id); +} + +} \ No newline at end of file diff --git a/lib/libes/include/es/SignatureBlock.h b/lib/libes/include/es/SignatureBlock.h index 46d5c98..111d5d7 100644 --- a/lib/libes/include/es/SignatureBlock.h +++ b/lib/libes/include/es/SignatureBlock.h @@ -24,8 +24,8 @@ namespace es // variables void clear(); - es::sign::SignType getSignType() const; - void setSignType(es::sign::SignType type); + es::sign::SignatureId getSignType() const; + void setSignType(es::sign::SignatureId type); bool isLittleEndian() const; void setLittleEndian(bool isLE); @@ -41,7 +41,7 @@ namespace es fnd::Vec mRawBinary; // variables - es::sign::SignType mSignType; + es::sign::SignatureId mSignType; bool mIsLittleEndian; fnd::Vec mSignature; }; diff --git a/lib/libes/include/es/sign.h b/lib/libes/include/es/sign.h index 4e3f85c..47b4d3d 100644 --- a/lib/libes/include/es/sign.h +++ b/lib/libes/include/es/sign.h @@ -9,14 +9,27 @@ namespace es { namespace sign { - enum SignType + enum SignatureId { - SIGN_RSA4096_SHA1 = 0x10000, - SIGN_RSA2048_SHA1, - SIGN_ECDSA240_SHA1, - SIGN_RSA4096_SHA256, - SIGN_RSA2048_SHA256, - SIGN_ECDSA240_SHA256, + SIGN_ID_RSA4096_SHA1 = 0x10000, + SIGN_ID_RSA2048_SHA1, + SIGN_ID_ECDSA240_SHA1, + SIGN_ID_RSA4096_SHA256, + SIGN_ID_RSA2048_SHA256, + SIGN_ID_ECDSA240_SHA256, + }; + + enum SignatureAlgo + { + SIGN_ALGO_RSA4096, + SIGN_ALGO_RSA2048, + SIGN_ALGO_ECDSA240 + }; + + enum HashAlgo + { + HASH_ALGO_SHA1, + HASH_ALGO_SHA256 }; static const size_t kEcdsaSigSize = 0x3C; diff --git a/lib/libes/source/SignUtils.cpp b/lib/libes/source/SignUtils.cpp new file mode 100644 index 0000000..29b594d --- /dev/null +++ b/lib/libes/source/SignUtils.cpp @@ -0,0 +1,45 @@ +#include + +es::sign::SignatureAlgo es::sign::getSignatureAlgo(es::sign::SignatureId sign_id) +{ + SignatureAlgo sign_algo = SIGN_ALGO_RSA4096; + + switch (sign_id) + { + case (es::sign::SIGN_ID_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA4096_SHA256): + sign_algo = SIGN_ALGO_RSA4096; + break; + case (es::sign::SIGN_ID_RSA2048_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA256): + sign_algo = SIGN_ALGO_RSA2048; + break; + case (es::sign::SIGN_ID_ECDSA240_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA256): + sign_algo = SIGN_ALGO_ECDSA240; + break; + }; + + return sign_algo; +} + +es::sign::HashAlgo es::sign::getHashAlgo(es::sign::SignatureId sign_id) +{ + HashAlgo hash_algo = HASH_ALGO_SHA1; + + switch (sign_id) + { + case (es::sign::SIGN_ID_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA1): + hash_algo = HASH_ALGO_SHA1; + break; + case (es::sign::SIGN_ID_RSA4096_SHA256): + case (es::sign::SIGN_ID_RSA2048_SHA256): + case (es::sign::SIGN_ID_ECDSA240_SHA256): + hash_algo = HASH_ALGO_SHA256; + break; + }; + + return hash_algo; +} \ No newline at end of file diff --git a/lib/libes/source/SignatureBlock.cpp b/lib/libes/source/SignatureBlock.cpp index 4aa591d..b568e7c 100644 --- a/lib/libes/source/SignatureBlock.cpp +++ b/lib/libes/source/SignatureBlock.cpp @@ -37,18 +37,18 @@ void es::SignatureBlock::toBytes() switch (mSignType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -80,18 +80,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) signType = ((be_uint32_t*)src)->get(); switch (signType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -103,18 +103,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) signType = ((le_uint32_t*)src)->get(); switch (signType) { - case (sign::SIGN_RSA4096_SHA1): - case (sign::SIGN_RSA4096_SHA256): + case (sign::SIGN_ID_RSA4096_SHA1): + case (sign::SIGN_ID_RSA4096_SHA256): totalSize = sizeof(sRsa4096SignBlock); sigSize = crypto::rsa::kRsa4096Size; break; - case (sign::SIGN_RSA2048_SHA1): - case (sign::SIGN_RSA2048_SHA256): + case (sign::SIGN_ID_RSA2048_SHA1): + case (sign::SIGN_ID_RSA2048_SHA256): totalSize = sizeof(sRsa2048SignBlock); sigSize = crypto::rsa::kRsa2048Size; break; - case (sign::SIGN_ECDSA240_SHA1): - case (sign::SIGN_ECDSA240_SHA256): + case (sign::SIGN_ID_ECDSA240_SHA1): + case (sign::SIGN_ID_ECDSA240_SHA256): totalSize = sizeof(sEcdsa240SignBlock); sigSize = sign::kEcdsaSigSize; break; @@ -133,7 +133,7 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) mRawBinary.alloc(totalSize); memcpy(mRawBinary.data(), src, totalSize); - mSignType = (sign::SignType)signType; + mSignType = (sign::SignatureId)signType; mSignature.alloc(sigSize); memcpy(mSignature.data(), mRawBinary.data() + 4, sigSize); } @@ -146,17 +146,17 @@ const fnd::Vec& es::SignatureBlock::getBytes() const void es::SignatureBlock::clear() { mRawBinary.clear(); - mSignType = sign::SIGN_RSA4096_SHA1; + mSignType = sign::SIGN_ID_RSA4096_SHA1; mIsLittleEndian = false; mSignature.clear(); } -es::sign::SignType es::SignatureBlock::getSignType() const +es::sign::SignatureId es::SignatureBlock::getSignType() const { return mSignType; } -void es::SignatureBlock::setSignType(es::sign::SignType type) +void es::SignatureBlock::setSignType(es::sign::SignatureId type) { mSignType = type; } diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 1299b6a..4e31423 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -2,6 +2,7 @@ #include #include +#include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" @@ -83,25 +84,50 @@ void EsCertProcess::validateCerts() void EsCertProcess::validateCert(const es::SignedData& cert) { std::string cert_ident = cert.getBody().getIssuer() + es::sign::kIdentDelimiter + cert.getBody().getSubject(); + + es::sign::SignatureAlgo cert_sign_algo = es::sign::getSignatureAlgo(cert.getSignature().getSignType()); + es::sign::HashAlgo cert_hash_algo = es::sign::getHashAlgo(cert.getSignature().getSignType()); + byte_t cert_hash[crypto::sha::kSha256HashLen]; + memset(cert_hash, 0, crypto::sha::kSha256HashLen); + try { + // special case if signed by Root if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) { throw fnd::Exception(kModuleName, "Signed by Root"); } - const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + // get cert hash + switch (cert_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + crypto::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); + break; + case (es::sign::HASH_ALGO_SHA256): + crypto::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); + break; + default: + throw fnd::Exception(kModuleName, "Unrecognised hash type"); + } - if (issuer.getPublicKeyType() == es::cert::RSA4096 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA4096_SHA256)) + // try to find issuer cert + const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); + + // validate signature + int sig_validate_res = -1; + + if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) { - throw fnd::Exception(kModuleName, "RSA4096 signatures are not supported"); + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } - else if (issuer.getPublicKeyType() == es::cert::RSA2048 && (cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_RSA2048_SHA256)) + else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) { - throw fnd::Exception(kModuleName, "RSA2048 signatures are not supported"); + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } - else if (issuer.getPublicKeyType() == es::cert::ECDSA240 && (cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA1 || cert.getSignature().getSignType() == es::sign::SIGN_ECDSA240_SHA256)) + else if (issuer_pubk_type == es::cert::ECDSA240 && cert_sign_algo == es::sign::SIGN_ALGO_ECDSA240) { throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); } @@ -109,10 +135,15 @@ void EsCertProcess::validateCert(const es::SignedData& cert { throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); } + + if (sig_validate_res != 0) + { + throw fnd::Exception(kModuleName, "Incorrect signature"); + } } catch (const fnd::Exception& e) { - printf("[WARNING] Failed to validate %s (%s)\n", cert_ident.c_str(), e.error()); + std::cout << "[WARNING] Failed to validate " << cert_ident << " (" << e.error() << ")" << std::endl; return; } @@ -194,27 +225,45 @@ const es::SignedData& EsCertProcess::getIssuerCert(const st throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); } -const char* EsCertProcess::getSignTypeStr(es::sign::SignType type) const +crypto::sha::HashType EsCertProcess::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo es_hash_algo) const +{ + crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; + + switch (es_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + hash_type = crypto::sha::HASH_SHA1; + break; + case (es::sign::HASH_ALGO_SHA256): + hash_type = crypto::sha::HASH_SHA256; + break; + }; + + return hash_type; +} + + +const char* EsCertProcess::getSignTypeStr(es::sign::SignatureId type) const { const char* str; switch (type) { - case (es::sign::SIGN_RSA4096_SHA1): + case (es::sign::SIGN_ID_RSA4096_SHA1): str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_RSA2048_SHA1): + case (es::sign::SIGN_ID_RSA2048_SHA1): str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ECDSA240_SHA1): + case (es::sign::SIGN_ID_ECDSA240_SHA1): str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_RSA4096_SHA256): + case (es::sign::SIGN_ID_RSA4096_SHA256): str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_RSA2048_SHA256): + case (es::sign::SIGN_ID_RSA2048_SHA256): str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ECDSA240_SHA256): + case (es::sign::SIGN_ID_ECDSA240_SHA256): str = "ECDSA240-SHA256"; break; default: diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index 42db9e8..8017a4f 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -40,7 +40,9 @@ private: const es::SignedData& getIssuerCert(const std::string& issuer_name) const; - const char* getSignTypeStr(es::sign::SignType type) const; + crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; + + const char* getSignTypeStr(es::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; const char* getPublicKeyTypeStr(es::cert::PublicKeyType type) const; }; \ No newline at end of file diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp index 301607f..4ef721e 100644 --- a/programs/nstool/source/EsTikProcess.cpp +++ b/programs/nstool/source/EsTikProcess.cpp @@ -132,23 +132,23 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const const char* str = nullptr; switch(type) { - case (es::sign::SIGN_RSA4096_SHA1): - str = "RSA4096_SHA1"; + case (es::sign::SIGN_ID_RSA4096_SHA1): + str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_RSA2048_SHA1): - str = "RSA2048_SHA1"; + case (es::sign::SIGN_ID_RSA2048_SHA1): + str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ECDSA240_SHA1): - str = "ECDSA240_SHA1"; + case (es::sign::SIGN_ID_ECDSA240_SHA1): + str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_RSA4096_SHA256): - str = "RSA4096_SHA256"; + case (es::sign::SIGN_ID_RSA4096_SHA256): + str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_RSA2048_SHA256): - str = "RSA2048_SHA256"; + case (es::sign::SIGN_ID_RSA2048_SHA256): + str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ECDSA240_SHA256): - str = "ECDSA240_SHA256"; + case (es::sign::SIGN_ID_ECDSA240_SHA256): + str = "ECDSA240-SHA256"; break; default: str = "Unknown"; diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index ca5a65a..aaf2477 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -882,7 +882,7 @@ bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample if (sign.isLittleEndian() == true) return false; - if (sign.getSignType() != es::sign::SIGN_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ECDSA240_SHA256) + if (sign.getSignType() != es::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ID_ECDSA240_SHA256) return false; return true; @@ -904,7 +904,7 @@ bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) if (sign.isLittleEndian() == false) return false; - if (sign.getSignType() != es::sign::SIGN_RSA2048_SHA256) + if (sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256) return false; return true; From a569ecc29dc7116fd077b9be3468a4fb89a47180 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 30 Jul 2018 04:21:19 +0800 Subject: [PATCH 18/34] [nstool] Root issuer validation, via key provided in key file. --- KEYS.md | 4 ++ programs/nstool/source/EsCertProcess.cpp | 48 +++++++++++++----------- programs/nstool/source/UserSettings.cpp | 21 +++++++---- programs/nstool/source/nstool.h | 4 +- 4 files changed, 47 insertions(+), 30 deletions(-) diff --git a/KEYS.md b/KEYS.md index bd1b706..0a3098b 100644 --- a/KEYS.md +++ b/KEYS.md @@ -25,6 +25,10 @@ package2_sign_key_private : RSA2048 Private Exponent (0x100 bytes) ; Ticket Keys ticket_commonkey_## : AES128 Key (0x10 bytes) +; PKI Root Signing Key +pki_root_sign_key_modulus : RSA4096 Modulus (0x200 bytes) +pki_root_sign_key_private : RSA4096 Private Exponent (0x200 bytes) + ; NCA Keys nca_header_key : AES128-XTS Key (0x20 bytes) nca_header_sign_key_modulus : RSA2048 Modulus (0x100 bytes) diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 4e31423..c7b143b 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -93,12 +93,6 @@ void EsCertProcess::validateCert(const es::SignedData& cert try { - // special case if signed by Root - if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) - { - throw fnd::Exception(kModuleName, "Signed by Root"); - } - // get cert hash switch (cert_hash_algo) { @@ -112,28 +106,40 @@ void EsCertProcess::validateCert(const es::SignedData& cert throw fnd::Exception(kModuleName, "Unrecognised hash type"); } - // try to find issuer cert - const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); - es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); - // validate signature int sig_validate_res = -1; - if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) + // special case if signed by Root + if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) - { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else if (issuer_pubk_type == es::cert::ECDSA240 && cert_sign_algo == es::sign::SIGN_ALGO_ECDSA240) - { - throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + if (cert_sign_algo != es::sign::SIGN_ALGO_RSA4096) + { + throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); + } + sig_validate_res = crypto::rsa::pkcs::rsaVerify(mKeyset->pki_root_sign_key, getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } else { - throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + // try to find issuer cert + const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); + es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); + + if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); + } + else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); + } + else if (issuer_pubk_type == es::cert::ECDSA240 && cert_sign_algo == es::sign::SIGN_ALGO_ECDSA240) + { + throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + } + else + { + throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + } } if (sig_validate_res != 0) diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index aaf2477..cf8e393 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -423,6 +423,7 @@ void UserSettings::populateKeyset(sCmdArgs& args) const std::string kKekGenSource = "aes_kek_generation"; const std::string kKeyGenSource = "aes_key_generation"; const std::string kAcidBase = "acid"; + const std::string kPkiRootBase = "pki_root"; const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" }; const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"}; const std::string kNcaBodyKeakIndexName[3] = {"application", "ocean", "system"}; @@ -492,17 +493,21 @@ void UserSettings::populateKeyset(sCmdArgs& args) _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kKeyStr), mKeyset.xci.header_key.key, 0x10); // store rsa keys - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, 0x100); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, 0x100); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, crypto::rsa::kRsa2048Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, crypto::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, 0x100); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, 0x100); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, crypto::rsa::kRsa2048Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, crypto::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, 0x100); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, 0x100); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, crypto::rsa::kRsa2048Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, crypto::rsa::kRsa2048Size); + + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, crypto::rsa::kRsa2048Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, crypto::rsa::kRsa2048Size); + + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki_root_sign_key.priv_exponent, crypto::rsa::kRsa4096Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki_root_sign_key.modulus, crypto::rsa::kRsa4096Size); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, 0x100); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, 0x100); // save keydata from input args if (args.nca_bodykey.isSet) diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h index 7752ffa..470a687 100644 --- a/programs/nstool/source/nstool.h +++ b/programs/nstool/source/nstool.h @@ -65,6 +65,8 @@ struct sKeyset { crypto::rsa::sRsa2048Key acid_sign_key; + crypto::rsa::sRsa4096Key pki_root_sign_key; + crypto::aes::sAes128Key package1_key[kMasterKeyNum]; crypto::rsa::sRsa2048Key package2_sign_key; crypto::aes::sAes128Key package2_key[kMasterKeyNum]; @@ -86,7 +88,7 @@ struct sKeyset crypto::rsa::sRsa2048Key header_sign_key; crypto::aes::sAes128Key header_key; } xci; - + struct sTicketData { crypto::rsa::sRsa2048Key sign_key; From c4b2d96bf1ce7f5bff55dc5beb10c399c2ecf2a0 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 30 Jul 2018 04:42:46 +0800 Subject: [PATCH 19/34] [nstool] Rename variable. --- programs/nstool/source/EsCertProcess.cpp | 2 +- programs/nstool/source/UserSettings.cpp | 4 ++-- programs/nstool/source/nstool.h | 7 ++++++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index c7b143b..8c28f1f 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -116,7 +116,7 @@ void EsCertProcess::validateCert(const es::SignedData& cert { throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); } - sig_validate_res = crypto::rsa::pkcs::rsaVerify(mKeyset->pki_root_sign_key, getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); + sig_validate_res = crypto::rsa::pkcs::rsaVerify(mKeyset->pki.root_sign_key, getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); } else { diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index cf8e393..9fc7c60 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -505,8 +505,8 @@ void UserSettings::populateKeyset(sCmdArgs& args) _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, crypto::rsa::kRsa2048Size); _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, crypto::rsa::kRsa2048Size); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki_root_sign_key.priv_exponent, crypto::rsa::kRsa4096Size); - _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki_root_sign_key.modulus, crypto::rsa::kRsa4096Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki.root_sign_key.priv_exponent, crypto::rsa::kRsa4096Size); + _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki.root_sign_key.modulus, crypto::rsa::kRsa4096Size); // save keydata from input args diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h index 470a687..9c5f6c9 100644 --- a/programs/nstool/source/nstool.h +++ b/programs/nstool/source/nstool.h @@ -65,7 +65,7 @@ struct sKeyset { crypto::rsa::sRsa2048Key acid_sign_key; - crypto::rsa::sRsa4096Key pki_root_sign_key; + crypto::aes::sAes128Key package1_key[kMasterKeyNum]; crypto::rsa::sRsa2048Key package2_sign_key; @@ -94,6 +94,11 @@ struct sKeyset crypto::rsa::sRsa2048Key sign_key; crypto::aes::sAes128Key titlekey_kek[kMasterKeyNum]; } ticket; + + struct sPkiData + { + crypto::rsa::sRsa4096Key root_sign_key; + } pki; }; inline byte_t charToByte(char chr) From 31b6ce0b58280a23976a9d9b31f8dea3377242eb Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 30 Jul 2018 04:43:17 +0800 Subject: [PATCH 20/34] [nstool] Remove unneeded white space. --- programs/nstool/source/nstool.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h index 9c5f6c9..b94521d 100644 --- a/programs/nstool/source/nstool.h +++ b/programs/nstool/source/nstool.h @@ -64,9 +64,6 @@ struct sOptional struct sKeyset { crypto::rsa::sRsa2048Key acid_sign_key; - - - crypto::aes::sAes128Key package1_key[kMasterKeyNum]; crypto::rsa::sRsa2048Key package2_sign_key; crypto::aes::sAes128Key package2_key[kMasterKeyNum]; From 25139e319da0c333781317a9edde730acb37711c Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 31 Jul 2018 12:44:25 +0800 Subject: [PATCH 21/34] [crypto] Update VS Project files. --- lib/libcrypto/crypto.vcxproj | 2 ++ lib/libcrypto/crypto.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/libcrypto/crypto.vcxproj b/lib/libcrypto/crypto.vcxproj index 72a1f38..06dfa98 100644 --- a/lib/libcrypto/crypto.vcxproj +++ b/lib/libcrypto/crypto.vcxproj @@ -121,6 +121,7 @@ + @@ -137,6 +138,7 @@ + diff --git a/lib/libcrypto/crypto.vcxproj.filters b/lib/libcrypto/crypto.vcxproj.filters index 09c9b93..0110282 100644 --- a/lib/libcrypto/crypto.vcxproj.filters +++ b/lib/libcrypto/crypto.vcxproj.filters @@ -66,6 +66,9 @@ Header Files\crypto + + Header Files\crypto + @@ -101,6 +104,9 @@ Source Files\polarssl + + Source Files + From 41a6e7e0ae1fd7bcd0656ac37a083da748c6af57 Mon Sep 17 00:00:00 2001 From: jakcron Date: Tue, 31 Jul 2018 12:55:15 +0800 Subject: [PATCH 22/34] [es] Update VS Project Files. --- lib/libes/es.vcxproj | 2 ++ lib/libes/es.vcxproj.filters | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/libes/es.vcxproj b/lib/libes/es.vcxproj index c32ab08..fa5b849 100644 --- a/lib/libes/es.vcxproj +++ b/lib/libes/es.vcxproj @@ -128,6 +128,7 @@ + @@ -135,6 +136,7 @@ + diff --git a/lib/libes/es.vcxproj.filters b/lib/libes/es.vcxproj.filters index 653f976..7f614e2 100644 --- a/lib/libes/es.vcxproj.filters +++ b/lib/libes/es.vcxproj.filters @@ -36,6 +36,9 @@ Header Files + + Header Files + Header Files @@ -53,6 +56,9 @@ Source Files + + Source Files + Source Files From 104fecde823a65b5b68a5fe861bbdbaadae2a799 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sat, 4 Aug 2018 10:05:47 +0800 Subject: [PATCH 23/34] [nstool] Remove unneeded comments. --- programs/nstool/source/NcaProcess.cpp | 30 +++++---------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/programs/nstool/source/NcaProcess.cpp b/programs/nstool/source/NcaProcess.cpp index 89f5d80..5080f06 100644 --- a/programs/nstool/source/NcaProcess.cpp +++ b/programs/nstool/source/NcaProcess.cpp @@ -286,27 +286,6 @@ void NcaProcess::process() // process partition processPartitions(); - - /* - NCA is a file container - A hashed and signed file container - - To verify a NCA: (R=regular step) - 1 - decrypt header (R) - 2 - verify signature[0] - 3 - validate hashes of fs_headers - 4 - determine how to read/decrypt the partitions (R) - 5 - validate the partitions depending on their hash method - 6 - if this NCA is a Program or Patch, open main.npdm from partition0 - 7 - validate ACID - 8 - use public key in ACID to verify NCA signature[1] - - Things to consider - * because of the manditory steps between verifcation steps - the NCA should be ready to be pulled to pieces before any printing is done - so the verification text can be presented without interuption - - */ } void NcaProcess::setInputFile(fnd::IFile* file, bool ownIFile) @@ -695,9 +674,10 @@ void NcaProcess::displayHeader() printf(" Partitions:\n"); for (size_t i = 0; i < mHdr.getPartitions().size(); i++) { - sPartitionInfo& info = mPartitions[i]; + size_t index = mHdr.getPartitions()[i].index; + sPartitionInfo& info = mPartitions[index]; - printf(" %d:\n", (int)i); + printf(" %d:\n", (int)index); printf(" Offset: 0x%" PRIx64 "\n", (uint64_t)info.offset); printf(" Size: 0x%" PRIx64 "\n", (uint64_t)info.size); printf(" Format Type: %s\n", getFormatTypeStr(info.format_type)); @@ -790,7 +770,7 @@ void NcaProcess::processPartitions() pfs.setListFs(mListFs); if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM) { - pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(i))); + pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(index))); } else { @@ -811,7 +791,7 @@ void NcaProcess::processPartitions() romfs.setListFs(mListFs); if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM) { - romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(i))); + romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(index))); } else { From 24fa6da66646eac114c896d387b53c4bf82a0d30 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 5 Aug 2018 23:09:07 +0800 Subject: [PATCH 24/34] [nstool] Encapsulate nnpki validation in PkiValidator. --- programs/nstool/source/EsCertProcess.cpp | 112 +------------ programs/nstool/source/EsCertProcess.h | 4 - programs/nstool/source/PkiValidator.cpp | 192 +++++++++++++++++++++++ programs/nstool/source/PkiValidator.h | 33 ++++ 4 files changed, 232 insertions(+), 109 deletions(-) create mode 100644 programs/nstool/source/PkiValidator.cpp create mode 100644 programs/nstool/source/PkiValidator.h diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 8c28f1f..9f65bf0 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -5,6 +5,7 @@ #include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" +#include "PkiValidator.h" EsCertProcess::EsCertProcess() : mFile(nullptr), @@ -75,84 +76,18 @@ void EsCertProcess::importCerts() void EsCertProcess::validateCerts() { - for (size_t i = 0; i < mCert.size(); i++) - { - EsCertProcess::validateCert(mCert[i]); - } -} - -void EsCertProcess::validateCert(const es::SignedData& cert) -{ - std::string cert_ident = cert.getBody().getIssuer() + es::sign::kIdentDelimiter + cert.getBody().getSubject(); + PkiValidator pki; - es::sign::SignatureAlgo cert_sign_algo = es::sign::getSignatureAlgo(cert.getSignature().getSignType()); - es::sign::HashAlgo cert_hash_algo = es::sign::getHashAlgo(cert.getSignature().getSignType()); - byte_t cert_hash[crypto::sha::kSha256HashLen]; - memset(cert_hash, 0, crypto::sha::kSha256HashLen); - - - try + try { - // get cert hash - switch (cert_hash_algo) - { - case (es::sign::HASH_ALGO_SHA1): - crypto::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); - break; - case (es::sign::HASH_ALGO_SHA256): - crypto::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash); - break; - default: - throw fnd::Exception(kModuleName, "Unrecognised hash type"); - } - - // validate signature - int sig_validate_res = -1; - - // special case if signed by Root - if (cert.getBody().getIssuer() == es::sign::kRootIssuerStr) - { - if (cert_sign_algo != es::sign::SIGN_ALGO_RSA4096) - { - throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); - } - sig_validate_res = crypto::rsa::pkcs::rsaVerify(mKeyset->pki.root_sign_key, getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else - { - // try to find issuer cert - const es::CertificateBody& issuer = getIssuerCert(cert.getBody().getIssuer()).getBody(); - es::cert::PublicKeyType issuer_pubk_type = issuer.getPublicKeyType(); - - if (issuer_pubk_type == es::cert::RSA4096 && cert_sign_algo == es::sign::SIGN_ALGO_RSA4096) - { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else if (issuer_pubk_type == es::cert::RSA2048 && cert_sign_algo == es::sign::SIGN_ALGO_RSA2048) - { - sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(cert_hash_algo), cert_hash, cert.getSignature().getSignature().data()); - } - else if (issuer_pubk_type == es::cert::ECDSA240 && cert_sign_algo == es::sign::SIGN_ALGO_ECDSA240) - { - throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); - } - else - { - throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); - } - } - - if (sig_validate_res != 0) - { - throw fnd::Exception(kModuleName, "Incorrect signature"); - } + pki.setRootKey(mKeyset->pki.root_sign_key); + pki.addCertificates(mCert); } - catch (const fnd::Exception& e) + catch (const fnd::Exception& e) { - std::cout << "[WARNING] Failed to validate " << cert_ident << " (" << e.error() << ")" << std::endl; + std::cout << "[WARNING] " << e.error() << std::endl; return; } - } void EsCertProcess::displayCerts() @@ -216,39 +151,6 @@ void EsCertProcess::displayCert(const es::SignedData& cert) #undef _SPLIT_VER } -const es::SignedData& EsCertProcess::getIssuerCert(const std::string& issuer_name) const -{ - std::string full_cert_name; - for (size_t i = 0; i < mCert.size(); i++) - { - full_cert_name = mCert[i].getBody().getIssuer() + es::sign::kIdentDelimiter + mCert[i].getBody().getSubject(); - if (full_cert_name == issuer_name) - { - return mCert[i]; - } - } - - throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); -} - -crypto::sha::HashType EsCertProcess::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo es_hash_algo) const -{ - crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; - - switch (es_hash_algo) - { - case (es::sign::HASH_ALGO_SHA1): - hash_type = crypto::sha::HASH_SHA1; - break; - case (es::sign::HASH_ALGO_SHA256): - hash_type = crypto::sha::HASH_SHA256; - break; - }; - - return hash_type; -} - - const char* EsCertProcess::getSignTypeStr(es::sign::SignatureId type) const { const char* str; diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index 8017a4f..ae88555 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -34,13 +34,9 @@ private: void importCerts(); void validateCerts(); - void validateCert(const es::SignedData& cert); void displayCerts(); void displayCert(const es::SignedData& cert); - const es::SignedData& getIssuerCert(const std::string& issuer_name) const; - - crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; const char* getSignTypeStr(es::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp new file mode 100644 index 0000000..46ee72f --- /dev/null +++ b/programs/nstool/source/PkiValidator.cpp @@ -0,0 +1,192 @@ +#include "PkiValidator.h" +#include +#include +#include +#include + +PkiValidator::PkiValidator() +{ + clearCertificates(); +} + +void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key) +{ + // save a copy of the certificate bank + fnd::List> old_certs = mCertificateBank; + + // clear the certificate bank + mCertificateBank.clear(); + + // overwrite the root key + mRootKey = root_key; + + // if there were certificates before, reimport them (so they are checked against the new root key) + if (old_certs.size() > 0) + { + addCertificates(old_certs); + } +} + +void PkiValidator::addCertificates(const fnd::List>& certs) +{ + std::string cert_ident; + es::sign::SignatureAlgo cert_sign_algo; + es::sign::HashAlgo cert_hash_algo; + fnd::Vec cert_sign, cert_hash; + + try + { + for (size_t i = 0; i < certs.size(); i++) + { + makeCertIdent(certs[i], cert_ident); + + if (doesCertExist(cert_ident) == true) + { + throw fnd::Exception(kModuleName, "Certificate already exists"); + } + + cert_sign_algo = es::sign::getSignatureAlgo(certs[i].getSignature().getSignType()); + cert_hash_algo = es::sign::getHashAlgo(certs[i].getSignature().getSignType()); + + // get cert hash + switch (cert_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + cert_hash.alloc(crypto::sha::kSha1HashLen); + crypto::sha::Sha1(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); + break; + case (es::sign::HASH_ALGO_SHA256): + cert_hash.alloc(crypto::sha::kSha256HashLen); + crypto::sha::Sha256(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); + break; + default: + throw fnd::Exception(kModuleName, "Unrecognised hash type"); + } + + validateSignature(certs[i].getBody().getIssuer(), certs[i].getSignature().getSignType(), certs[i].getSignature().getSignature(), cert_hash); + + mCertificateBank.addElement(certs[i]); + } + } + catch (const fnd::Exception& e) + { + std::stringstream ss; + ss << "Failed to add certificate " << cert_ident << " (" << e.error() << ")"; + throw fnd::Exception(kModuleName, ss.str()); + } +} + +void PkiValidator::clearCertificates() +{ + mCertificateBank.clear(); +} + +void PkiValidator::validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const +{ + es::sign::SignatureAlgo sign_algo = es::sign::getSignatureAlgo(signature_id); + es::sign::HashAlgo hash_algo = es::sign::getHashAlgo(signature_id); + + + // validate signature + int sig_validate_res = -1; + + // special case if signed by Root + if (issuer == es::sign::kRootIssuerStr) + { + if (sign_algo != es::sign::SIGN_ALGO_RSA4096) + { + throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); + } + sig_validate_res = crypto::rsa::pkcs::rsaVerify(mRootKey, getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else + { + // try to find issuer cert + const es::CertificateBody& issuer_cert = getCert(issuer).getBody(); + es::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType(); + + if (issuer_pubk_type == es::cert::RSA4096 && sign_algo == es::sign::SIGN_ALGO_RSA4096) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (issuer_pubk_type == es::cert::RSA2048 && sign_algo == es::sign::SIGN_ALGO_RSA2048) + { + sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); + } + else if (issuer_pubk_type == es::cert::ECDSA240 && sign_algo == es::sign::SIGN_ALGO_ECDSA240) + { + throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); + } + else + { + throw fnd::Exception(kModuleName, "Mismatch between issuer public key and signature type"); + } + } + + if (sig_validate_res != 0) + { + throw fnd::Exception(kModuleName, "Incorrect signature"); + } + + +} + +void PkiValidator::makeCertIdent(const es::SignedData& cert, std::string& ident) const +{ + makeCertIdent(cert.getBody().getIssuer(), cert.getBody().getSubject(), ident); +} + +void PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const +{ + ident = issuer + es::sign::kIdentDelimiter + subject; + ident = ident.substr(0, _MIN(ident.length(),64)); +} + +bool PkiValidator::doesCertExist(const std::string& ident) const +{ + bool exists = false; + std::string full_cert_name; + for (size_t i = 0; i < mCertificateBank.size(); i++) + { + makeCertIdent(mCertificateBank[i], full_cert_name); + if (full_cert_name == ident) + { + exists = true; + break; + } + } + + return exists; +} + +const es::SignedData& PkiValidator::getCert(const std::string& ident) const +{ + std::string full_cert_name; + for (size_t i = 0; i < mCertificateBank.size(); i++) + { + makeCertIdent(mCertificateBank[i], full_cert_name); + if (full_cert_name == ident) + { + return mCertificateBank[i]; + } + } + + throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); +} + +crypto::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const +{ + crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; + + switch (hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + hash_type = crypto::sha::HASH_SHA1; + break; + case (es::sign::HASH_ALGO_SHA256): + hash_type = crypto::sha::HASH_SHA256; + break; + }; + + return hash_type; +} \ No newline at end of file diff --git a/programs/nstool/source/PkiValidator.h b/programs/nstool/source/PkiValidator.h new file mode 100644 index 0000000..3ba54ec --- /dev/null +++ b/programs/nstool/source/PkiValidator.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +class PkiValidator +{ +public: + PkiValidator(); + + void setRootKey(const crypto::rsa::sRsa4096Key& root_key); + void addCertificates(const fnd::List>& certs); + void clearCertificates(); + + void validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; + +private: + const std::string kModuleName = "NNPkiValidator"; + + + crypto::rsa::sRsa4096Key mRootKey; + fnd::List> mCertificateBank; + + void makeCertIdent(const es::SignedData& cert, std::string& ident) const; + void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const; + bool doesCertExist(const std::string& ident) const; + const es::SignedData& getCert(const std::string& ident) const; + crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; +}; \ No newline at end of file From e9b3c7296a81a8f089bd886ebe25e6a02c3e0d18 Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 5 Aug 2018 23:11:34 +0800 Subject: [PATCH 25/34] [nstool] Remove unused variable. --- programs/nstool/source/PkiValidator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp index 46ee72f..91cdfda 100644 --- a/programs/nstool/source/PkiValidator.cpp +++ b/programs/nstool/source/PkiValidator.cpp @@ -32,7 +32,7 @@ void PkiValidator::addCertificates(const fnd::List cert_sign, cert_hash; + fnd::Vec cert_hash; try { From b911b5984b2288079d649bb6a5d1bd0c1694ba8d Mon Sep 17 00:00:00 2001 From: jakcron Date: Sun, 5 Aug 2018 23:15:36 +0800 Subject: [PATCH 26/34] [nstool] Added PkiValidator::addCertificate() --- programs/nstool/source/PkiValidator.cpp | 65 +++++++++++++------------ programs/nstool/source/PkiValidator.h | 1 + 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp index 91cdfda..623f938 100644 --- a/programs/nstool/source/PkiValidator.cpp +++ b/programs/nstool/source/PkiValidator.cpp @@ -28,6 +28,14 @@ void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key) } void PkiValidator::addCertificates(const fnd::List>& certs) +{ + for (size_t i = 0; i < certs.size(); i++) + { + addCertificate(certs[i]); + } +} + +void PkiValidator::addCertificate(const es::SignedData& cert) { std::string cert_ident; es::sign::SignatureAlgo cert_sign_algo; @@ -35,38 +43,35 @@ void PkiValidator::addCertificates(const fnd::List cert_hash; try - { - for (size_t i = 0; i < certs.size(); i++) + { + makeCertIdent(cert, cert_ident); + + if (doesCertExist(cert_ident) == true) { - makeCertIdent(certs[i], cert_ident); - - if (doesCertExist(cert_ident) == true) - { - throw fnd::Exception(kModuleName, "Certificate already exists"); - } - - cert_sign_algo = es::sign::getSignatureAlgo(certs[i].getSignature().getSignType()); - cert_hash_algo = es::sign::getHashAlgo(certs[i].getSignature().getSignType()); - - // get cert hash - switch (cert_hash_algo) - { - case (es::sign::HASH_ALGO_SHA1): - cert_hash.alloc(crypto::sha::kSha1HashLen); - crypto::sha::Sha1(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); - break; - case (es::sign::HASH_ALGO_SHA256): - cert_hash.alloc(crypto::sha::kSha256HashLen); - crypto::sha::Sha256(certs[i].getBody().getBytes().data(), certs[i].getBody().getBytes().size(), cert_hash.data()); - break; - default: - throw fnd::Exception(kModuleName, "Unrecognised hash type"); - } - - validateSignature(certs[i].getBody().getIssuer(), certs[i].getSignature().getSignType(), certs[i].getSignature().getSignature(), cert_hash); - - mCertificateBank.addElement(certs[i]); + throw fnd::Exception(kModuleName, "Certificate already exists"); } + + cert_sign_algo = es::sign::getSignatureAlgo(cert.getSignature().getSignType()); + cert_hash_algo = es::sign::getHashAlgo(cert.getSignature().getSignType()); + + // get cert hash + switch (cert_hash_algo) + { + case (es::sign::HASH_ALGO_SHA1): + cert_hash.alloc(crypto::sha::kSha1HashLen); + crypto::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data()); + break; + case (es::sign::HASH_ALGO_SHA256): + cert_hash.alloc(crypto::sha::kSha256HashLen); + crypto::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data()); + break; + default: + throw fnd::Exception(kModuleName, "Unrecognised hash type"); + } + + validateSignature(cert.getBody().getIssuer(), cert.getSignature().getSignType(), cert.getSignature().getSignature(), cert_hash); + + mCertificateBank.addElement(cert); } catch (const fnd::Exception& e) { diff --git a/programs/nstool/source/PkiValidator.h b/programs/nstool/source/PkiValidator.h index 3ba54ec..c585c41 100644 --- a/programs/nstool/source/PkiValidator.h +++ b/programs/nstool/source/PkiValidator.h @@ -14,6 +14,7 @@ public: void setRootKey(const crypto::rsa::sRsa4096Key& root_key); void addCertificates(const fnd::List>& certs); + void addCertificate(const es::SignedData& cert); void clearCertificates(); void validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; From 43270f2e80d88a40b5ba6ba71fb71e6cad00e9f3 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 14:59:09 +0800 Subject: [PATCH 27/34] Misc --- .vscode/settings.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.vscode/settings.json b/.vscode/settings.json index 13d2bfc..768b573 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -29,5 +29,12 @@ "__locale": "cpp", "cinttypes": "cpp", "__bit_reference": "cpp", + "algorithm": "cpp", + "__functional_base_03": "cpp", + "__tuple": "cpp", + "chrono": "cpp", + "functional": "cpp", + "limits": "cpp", + "ratio": "cpp" } } \ No newline at end of file From 900415f49d3468f848029274cfab42379c3c91ea Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 16:59:56 +0800 Subject: [PATCH 28/34] Take out non-ticket code from libes into new libpki. --- .vscode/c_cpp_properties.json | 5 +- README.md | 13 ++--- lib/libes/include/es/SignUtils.h | 14 ----- lib/libes/source/SignUtils.cpp | 45 ---------------- .../include/pki}/CertificateBody.h | 6 +-- lib/libpki/include/pki/SignUtils.h | 14 +++++ .../include/pki}/SignatureBlock.h | 10 ++-- .../es => libpki/include/pki}/SignedData.h | 8 +-- .../include/es => libpki/include/pki}/cert.h | 2 +- .../include/es => libpki/include/pki}/sign.h | 2 +- lib/libpki/makefile | 47 +++++++++++++++++ .../source/CertificateBody.cpp | 48 ++++++++--------- lib/libpki/source/SignUtils.cpp | 45 ++++++++++++++++ .../source/SignatureBlock.cpp | 32 ++++++------ lib/makefile | 2 +- programs/nstool/makefile | 2 +- programs/nstool/source/EsCertProcess.cpp | 34 ++++++------ programs/nstool/source/EsCertProcess.h | 12 ++--- programs/nstool/source/EsTikProcess.cpp | 12 ++--- programs/nstool/source/EsTikProcess.h | 4 +- programs/nstool/source/PkiValidator.cpp | 52 +++++++++---------- programs/nstool/source/PkiValidator.h | 18 +++---- programs/nstool/source/UserSettings.cpp | 14 ++--- 23 files changed, 245 insertions(+), 196 deletions(-) delete mode 100644 lib/libes/include/es/SignUtils.h delete mode 100644 lib/libes/source/SignUtils.cpp rename lib/{libes/include/es => libpki/include/pki}/CertificateBody.h (94%) create mode 100644 lib/libpki/include/pki/SignUtils.h rename lib/{libes/include/es => libpki/include/pki}/SignatureBlock.h (84%) rename lib/{libes/include/es => libpki/include/pki}/SignedData.h (94%) rename lib/{libes/include/es => libpki/include/pki}/cert.h (98%) rename lib/{libes/include/es => libpki/include/pki}/sign.h (98%) create mode 100644 lib/libpki/makefile rename lib/{libes => libpki}/source/CertificateBody.cpp (78%) create mode 100644 lib/libpki/source/SignUtils.cpp rename lib/{libes => libpki}/source/SignatureBlock.cpp (78%) diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 174c73a..e996876 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -9,10 +9,11 @@ "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include", "/usr/include", "${workspaceRoot}", - "${workspaceRoot}/lib/libcrypto/include", - "${workspaceRoot}/lib/libcrypto/source/polarssl/libinclude", + "${workspaceRoot}/lib/libcrypto/include", + "${workspaceRoot}/lib/libcrypto/source/polarssl/libinclude", "${workspaceRoot}/lib/libcompress/include", "${workspaceRoot}/lib/libes/include", + "${workspaceRoot}/lib/libpki/include", "${workspaceRoot}/lib/libfnd/include", "${workspaceRoot}/lib/libnx/include", "${workspaceRoot}/lib/libnx-hb/include" diff --git a/README.md b/README.md index 1bb4f5a..377b1a2 100644 --- a/README.md +++ b/README.md @@ -10,12 +10,13 @@ Tools & Libraries for NX (Nintendo Switch). # Libraries -* __libfnd__ - Foundation library. -* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls) -* __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4) -* __libes__ - Handling of (NX relevant) eShop file type processing. (eTickets, etc) -* __libnx__ - Handling of NX file types. -* __libnx-hb__ - Handling of NX (homebrew extensions) file types. +* __libfnd__ - Foundation library. +* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls) +* __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4) +* __libpki__ - Processes Nintendo's proprietary PKI. +* __libes__ - Processes Nintendo's eShop file types. +* __libnx__ - Processes NX file types. +* __libnx-hb__ - Processes NX file types (homebrew extensions). # Building diff --git a/lib/libes/include/es/SignUtils.h b/lib/libes/include/es/SignUtils.h deleted file mode 100644 index 26996a2..0000000 --- a/lib/libes/include/es/SignUtils.h +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once -#include -#include - -namespace es -{ - -namespace sign -{ - es::sign::SignatureAlgo getSignatureAlgo(es::sign::SignatureId sign_id); - es::sign::HashAlgo getHashAlgo(es::sign::SignatureId sign_id); -} - -} \ No newline at end of file diff --git a/lib/libes/source/SignUtils.cpp b/lib/libes/source/SignUtils.cpp deleted file mode 100644 index 29b594d..0000000 --- a/lib/libes/source/SignUtils.cpp +++ /dev/null @@ -1,45 +0,0 @@ -#include - -es::sign::SignatureAlgo es::sign::getSignatureAlgo(es::sign::SignatureId sign_id) -{ - SignatureAlgo sign_algo = SIGN_ALGO_RSA4096; - - switch (sign_id) - { - case (es::sign::SIGN_ID_RSA4096_SHA1): - case (es::sign::SIGN_ID_RSA4096_SHA256): - sign_algo = SIGN_ALGO_RSA4096; - break; - case (es::sign::SIGN_ID_RSA2048_SHA1): - case (es::sign::SIGN_ID_RSA2048_SHA256): - sign_algo = SIGN_ALGO_RSA2048; - break; - case (es::sign::SIGN_ID_ECDSA240_SHA1): - case (es::sign::SIGN_ID_ECDSA240_SHA256): - sign_algo = SIGN_ALGO_ECDSA240; - break; - }; - - return sign_algo; -} - -es::sign::HashAlgo es::sign::getHashAlgo(es::sign::SignatureId sign_id) -{ - HashAlgo hash_algo = HASH_ALGO_SHA1; - - switch (sign_id) - { - case (es::sign::SIGN_ID_RSA4096_SHA1): - case (es::sign::SIGN_ID_RSA2048_SHA1): - case (es::sign::SIGN_ID_ECDSA240_SHA1): - hash_algo = HASH_ALGO_SHA1; - break; - case (es::sign::SIGN_ID_RSA4096_SHA256): - case (es::sign::SIGN_ID_RSA2048_SHA256): - case (es::sign::SIGN_ID_ECDSA240_SHA256): - hash_algo = HASH_ALGO_SHA256; - break; - }; - - return hash_algo; -} \ No newline at end of file diff --git a/lib/libes/include/es/CertificateBody.h b/lib/libpki/include/pki/CertificateBody.h similarity index 94% rename from lib/libes/include/es/CertificateBody.h rename to lib/libpki/include/pki/CertificateBody.h index 20cbbe9..c02494c 100644 --- a/lib/libes/include/es/CertificateBody.h +++ b/lib/libpki/include/pki/CertificateBody.h @@ -1,9 +1,9 @@ #pragma once #include #include -#include +#include -namespace es +namespace pki { class CertificateBody : public fnd::ISerialisable @@ -27,7 +27,7 @@ namespace es const std::string& getIssuer() const; void setIssuer(const std::string& issuer); - es::cert::PublicKeyType getPublicKeyType() const; + pki::cert::PublicKeyType getPublicKeyType() const; void setPublicKeyType(cert::PublicKeyType type); const std::string& getSubject() const; diff --git a/lib/libpki/include/pki/SignUtils.h b/lib/libpki/include/pki/SignUtils.h new file mode 100644 index 0000000..dc71e2a --- /dev/null +++ b/lib/libpki/include/pki/SignUtils.h @@ -0,0 +1,14 @@ +#pragma once +#include +#include + +namespace pki +{ + +namespace sign +{ + pki::sign::SignatureAlgo getSignatureAlgo(pki::sign::SignatureId sign_id); + pki::sign::HashAlgo getHashAlgo(pki::sign::SignatureId sign_id); +} + +} \ No newline at end of file diff --git a/lib/libes/include/es/SignatureBlock.h b/lib/libpki/include/pki/SignatureBlock.h similarity index 84% rename from lib/libes/include/es/SignatureBlock.h rename to lib/libpki/include/pki/SignatureBlock.h index 111d5d7..1fcad69 100644 --- a/lib/libes/include/es/SignatureBlock.h +++ b/lib/libpki/include/pki/SignatureBlock.h @@ -1,9 +1,9 @@ #pragma once #include #include -#include +#include -namespace es +namespace pki { class SignatureBlock : public fnd::ISerialisable @@ -24,8 +24,8 @@ namespace es // variables void clear(); - es::sign::SignatureId getSignType() const; - void setSignType(es::sign::SignatureId type); + pki::sign::SignatureId getSignType() const; + void setSignType(pki::sign::SignatureId type); bool isLittleEndian() const; void setLittleEndian(bool isLE); @@ -41,7 +41,7 @@ namespace es fnd::Vec mRawBinary; // variables - es::sign::SignatureId mSignType; + pki::sign::SignatureId mSignType; bool mIsLittleEndian; fnd::Vec mSignature; }; diff --git a/lib/libes/include/es/SignedData.h b/lib/libpki/include/pki/SignedData.h similarity index 94% rename from lib/libes/include/es/SignedData.h rename to lib/libpki/include/pki/SignedData.h index 366b350..abe38d6 100644 --- a/lib/libes/include/es/SignedData.h +++ b/lib/libpki/include/pki/SignedData.h @@ -1,9 +1,9 @@ #pragma once #include #include -#include +#include -namespace es +namespace pki { template class SignedData @@ -25,7 +25,7 @@ namespace es // variables void clear(); - const es::SignatureBlock& getSignature() const; + const pki::SignatureBlock& getSignature() const; void setSignature(const SignatureBlock& signature); const T& getBody() const; @@ -111,7 +111,7 @@ namespace es } template - inline const es::SignatureBlock& SignedData::getSignature() const + inline const pki::SignatureBlock& SignedData::getSignature() const { return mSignature; } diff --git a/lib/libes/include/es/cert.h b/lib/libpki/include/pki/cert.h similarity index 98% rename from lib/libes/include/es/cert.h rename to lib/libpki/include/pki/cert.h index ab4ec0a..c7e3b52 100644 --- a/lib/libes/include/es/cert.h +++ b/lib/libpki/include/pki/cert.h @@ -5,7 +5,7 @@ #include #include -namespace es +namespace pki { namespace cert { diff --git a/lib/libes/include/es/sign.h b/lib/libpki/include/pki/sign.h similarity index 98% rename from lib/libes/include/es/sign.h rename to lib/libpki/include/pki/sign.h index 47b4d3d..27442cc 100644 --- a/lib/libes/include/es/sign.h +++ b/lib/libpki/include/pki/sign.h @@ -5,7 +5,7 @@ #include #include -namespace es +namespace pki { namespace sign { diff --git a/lib/libpki/makefile b/lib/libpki/makefile new file mode 100644 index 0000000..ba0b6e8 --- /dev/null +++ b/lib/libpki/makefile @@ -0,0 +1,47 @@ +# Sources +SRC_DIR = source +OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) + +# External dependencies +DEPENDS = fnd crypto +LIB_DIR = .. +INCS = -I"include" $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") + + +# Compiler Settings +CXXFLAGS = -std=c++11 $(INCS) -D__STDC_FORMAT_MACROS -Wall -Wno-unused-value +CFLAGS = -std=c11 $(INCS) -Wall -Wno-unused-value +ARFLAGS = cr -o +ifeq ($(OS),Windows_NT) + # Windows Only Flags/Libs + CC = x86_64-w64-mingw32-gcc + CXX = x86_64-w64-mingw32-g++ + CFLAGS += -Wno-unused-but-set-variable + CXXFLAGS += -Wno-unused-but-set-variable +else + UNAME = $(shell uname -s) + ifeq ($(UNAME), Darwin) + # MacOS Only Flags/Libs + CFLAGS += -Wno-unused-private-field + CXXFLAGS += -Wno-unused-private-field + ARFLAGS = rc + else + # *nix Only Flags/Libs + CFLAGS += -Wno-unused-but-set-variable + CXXFLAGS += -Wno-unused-but-set-variable + endif + +endif + +# Output +OUTPUT = $(shell basename $(CURDIR)).a + +main: build + +rebuild: clean build + +build: $(OBJS) + ar $(ARFLAGS) $(OUTPUT) $(OBJS) + +clean: + rm -rf $(OUTPUT) $(OBJS) \ No newline at end of file diff --git a/lib/libes/source/CertificateBody.cpp b/lib/libpki/source/CertificateBody.cpp similarity index 78% rename from lib/libes/source/CertificateBody.cpp rename to lib/libpki/source/CertificateBody.cpp index 6219a21..d844101 100644 --- a/lib/libes/source/CertificateBody.cpp +++ b/lib/libpki/source/CertificateBody.cpp @@ -1,16 +1,16 @@ -#include +#include -es::CertificateBody::CertificateBody() +pki::CertificateBody::CertificateBody() { clear(); } -es::CertificateBody::CertificateBody(const CertificateBody& other) +pki::CertificateBody::CertificateBody(const CertificateBody& other) { *this = other; } -void es::CertificateBody::operator=(const CertificateBody& other) +void pki::CertificateBody::operator=(const CertificateBody& other) { mRawBinary = other.mRawBinary; mIssuer = other.mIssuer; @@ -22,7 +22,7 @@ void es::CertificateBody::operator=(const CertificateBody& other) mEcdsa240PublicKey = other.mEcdsa240PublicKey; } -bool es::CertificateBody::operator==(const CertificateBody& other) const +bool pki::CertificateBody::operator==(const CertificateBody& other) const { return (mIssuer == other.mIssuer) \ && (mSubject == other.mSubject) \ @@ -33,12 +33,12 @@ bool es::CertificateBody::operator==(const CertificateBody& other) const && (mEcdsa240PublicKey == other.mEcdsa240PublicKey); } -bool es::CertificateBody::operator!=(const CertificateBody& other) const +bool pki::CertificateBody::operator!=(const CertificateBody& other) const { return !(*this == other); } -void es::CertificateBody::toBytes() +void pki::CertificateBody::toBytes() { // get public key size size_t pubkeySize = 0; @@ -86,7 +86,7 @@ void es::CertificateBody::toBytes() } } -void es::CertificateBody::fromBytes(const byte_t* src, size_t size) +void pki::CertificateBody::fromBytes(const byte_t* src, size_t size) { clear(); @@ -155,13 +155,13 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size) } } -const fnd::Vec& es::CertificateBody::getBytes() const +const fnd::Vec& pki::CertificateBody::getBytes() const { return mRawBinary; } -void es::CertificateBody::clear() +void pki::CertificateBody::clear() { mIssuer.clear(); mSubject.clear(); @@ -173,12 +173,12 @@ void es::CertificateBody::clear() memset(&mEcdsa240PublicKey, 0, sizeof(crypto::ecdsa::sEcdsa240Point)); } -const std::string& es::CertificateBody::getIssuer() const +const std::string& pki::CertificateBody::getIssuer() const { return mIssuer; } -void es::CertificateBody::setIssuer(const std::string& issuer) +void pki::CertificateBody::setIssuer(const std::string& issuer) { if (issuer.size() > cert::kIssuerSize) { @@ -188,22 +188,22 @@ void es::CertificateBody::setIssuer(const std::string& issuer) mIssuer = issuer; } -es::cert::PublicKeyType es::CertificateBody::getPublicKeyType() const +pki::cert::PublicKeyType pki::CertificateBody::getPublicKeyType() const { return mPublicKeyType; } -void es::CertificateBody::setPublicKeyType(cert::PublicKeyType type) +void pki::CertificateBody::setPublicKeyType(cert::PublicKeyType type) { mPublicKeyType = type; } -const std::string& es::CertificateBody::getSubject() const +const std::string& pki::CertificateBody::getSubject() const { return mSubject; } -void es::CertificateBody::setSubject(const std::string& subject) +void pki::CertificateBody::setSubject(const std::string& subject) { if (subject.size() > cert::kSubjectSize) { @@ -213,42 +213,42 @@ void es::CertificateBody::setSubject(const std::string& subject) mSubject = subject; } -uint32_t es::CertificateBody::getCertId() const +uint32_t pki::CertificateBody::getCertId() const { return mCertId; } -void es::CertificateBody::setCertId(uint32_t id) +void pki::CertificateBody::setCertId(uint32_t id) { mCertId = id; } -const crypto::rsa::sRsa4096Key& es::CertificateBody::getRsa4098PublicKey() const +const crypto::rsa::sRsa4096Key& pki::CertificateBody::getRsa4098PublicKey() const { return mRsa4096PublicKey; } -void es::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key) +void pki::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key) { mRsa4096PublicKey = key; } -const crypto::rsa::sRsa2048Key& es::CertificateBody::getRsa2048PublicKey() const +const crypto::rsa::sRsa2048Key& pki::CertificateBody::getRsa2048PublicKey() const { return mRsa2048PublicKey; } -void es::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key) +void pki::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key) { mRsa2048PublicKey = key; } -const crypto::ecdsa::sEcdsa240Point& es::CertificateBody::getEcdsa240PublicKey() const +const crypto::ecdsa::sEcdsa240Point& pki::CertificateBody::getEcdsa240PublicKey() const { return mEcdsa240PublicKey; } -void es::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key) +void pki::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key) { mEcdsa240PublicKey = key; } \ No newline at end of file diff --git a/lib/libpki/source/SignUtils.cpp b/lib/libpki/source/SignUtils.cpp new file mode 100644 index 0000000..43cfb1e --- /dev/null +++ b/lib/libpki/source/SignUtils.cpp @@ -0,0 +1,45 @@ +#include + +pki::sign::SignatureAlgo pki::sign::getSignatureAlgo(pki::sign::SignatureId sign_id) +{ + SignatureAlgo sign_algo = SIGN_ALGO_RSA4096; + + switch (sign_id) + { + case (pki::sign::SIGN_ID_RSA4096_SHA1): + case (pki::sign::SIGN_ID_RSA4096_SHA256): + sign_algo = SIGN_ALGO_RSA4096; + break; + case (pki::sign::SIGN_ID_RSA2048_SHA1): + case (pki::sign::SIGN_ID_RSA2048_SHA256): + sign_algo = SIGN_ALGO_RSA2048; + break; + case (pki::sign::SIGN_ID_ECDSA240_SHA1): + case (pki::sign::SIGN_ID_ECDSA240_SHA256): + sign_algo = SIGN_ALGO_ECDSA240; + break; + }; + + return sign_algo; +} + +pki::sign::HashAlgo pki::sign::getHashAlgo(pki::sign::SignatureId sign_id) +{ + HashAlgo hash_algo = HASH_ALGO_SHA1; + + switch (sign_id) + { + case (pki::sign::SIGN_ID_RSA4096_SHA1): + case (pki::sign::SIGN_ID_RSA2048_SHA1): + case (pki::sign::SIGN_ID_ECDSA240_SHA1): + hash_algo = HASH_ALGO_SHA1; + break; + case (pki::sign::SIGN_ID_RSA4096_SHA256): + case (pki::sign::SIGN_ID_RSA2048_SHA256): + case (pki::sign::SIGN_ID_ECDSA240_SHA256): + hash_algo = HASH_ALGO_SHA256; + break; + }; + + return hash_algo; +} \ No newline at end of file diff --git a/lib/libes/source/SignatureBlock.cpp b/lib/libpki/source/SignatureBlock.cpp similarity index 78% rename from lib/libes/source/SignatureBlock.cpp rename to lib/libpki/source/SignatureBlock.cpp index b568e7c..bf19b20 100644 --- a/lib/libes/source/SignatureBlock.cpp +++ b/lib/libpki/source/SignatureBlock.cpp @@ -1,16 +1,16 @@ -#include +#include -es::SignatureBlock::SignatureBlock() +pki::SignatureBlock::SignatureBlock() { clear(); } -es::SignatureBlock::SignatureBlock(const SignatureBlock& other) +pki::SignatureBlock::SignatureBlock(const SignatureBlock& other) { *this = other; } -void es::SignatureBlock::operator=(const SignatureBlock& other) +void pki::SignatureBlock::operator=(const SignatureBlock& other) { mRawBinary = other.mRawBinary; mSignType = other.mSignType; @@ -18,19 +18,19 @@ void es::SignatureBlock::operator=(const SignatureBlock& other) mSignature = other.mSignature; } -bool es::SignatureBlock::operator==(const SignatureBlock& other) const +bool pki::SignatureBlock::operator==(const SignatureBlock& other) const { return (mSignType == other.mSignType) \ && (mIsLittleEndian == other.mIsLittleEndian) \ && (mSignature == other.mSignature); } -bool es::SignatureBlock::operator!=(const SignatureBlock& other) const +bool pki::SignatureBlock::operator!=(const SignatureBlock& other) const { return !(*this == other); } -void es::SignatureBlock::toBytes() +void pki::SignatureBlock::toBytes() { size_t totalSize = 0; size_t sigSize = 0; @@ -68,7 +68,7 @@ void es::SignatureBlock::toBytes() memcpy(mRawBinary.data() + 4, mSignature.data(), sigSize); } -void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) +void pki::SignatureBlock::fromBytes(const byte_t* src, size_t size) { clear(); @@ -138,12 +138,12 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size) memcpy(mSignature.data(), mRawBinary.data() + 4, sigSize); } -const fnd::Vec& es::SignatureBlock::getBytes() const +const fnd::Vec& pki::SignatureBlock::getBytes() const { return mRawBinary; } -void es::SignatureBlock::clear() +void pki::SignatureBlock::clear() { mRawBinary.clear(); mSignType = sign::SIGN_ID_RSA4096_SHA1; @@ -151,32 +151,32 @@ void es::SignatureBlock::clear() mSignature.clear(); } -es::sign::SignatureId es::SignatureBlock::getSignType() const +pki::sign::SignatureId pki::SignatureBlock::getSignType() const { return mSignType; } -void es::SignatureBlock::setSignType(es::sign::SignatureId type) +void pki::SignatureBlock::setSignType(pki::sign::SignatureId type) { mSignType = type; } -bool es::SignatureBlock::isLittleEndian() const +bool pki::SignatureBlock::isLittleEndian() const { return mIsLittleEndian; } -void es::SignatureBlock::setLittleEndian(bool isLE) +void pki::SignatureBlock::setLittleEndian(bool isLE) { mIsLittleEndian = isLE; } -const fnd::Vec& es::SignatureBlock::getSignature() const +const fnd::Vec& pki::SignatureBlock::getSignature() const { return mSignature; } -void es::SignatureBlock::setSignature(const fnd::Vec& signature) +void pki::SignatureBlock::setSignature(const fnd::Vec& signature) { mSignature = signature; } diff --git a/lib/makefile b/lib/makefile index 89636d0..7928e45 100644 --- a/lib/makefile +++ b/lib/makefile @@ -1,4 +1,4 @@ -LIBS = libfnd libcrypto libcompress libes libnx libnx-hb +LIBS = libfnd libcrypto libcompress libes libpki libnx libnx-hb main: build rebuild: clean build diff --git a/programs/nstool/makefile b/programs/nstool/makefile index 517a561..ddbdcd3 100644 --- a/programs/nstool/makefile +++ b/programs/nstool/makefile @@ -3,7 +3,7 @@ SRC_DIR = source OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c))) # External dependencies -DEPENDS = nx-hb nx es crypto compress fnd +DEPENDS = nx-hb nx es pki crypto compress fnd LIB_DIR = ../../lib LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep)) INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/EsCertProcess.cpp index 9f65bf0..bd15514 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/EsCertProcess.cpp @@ -2,7 +2,7 @@ #include #include -#include +#include #include "OffsetAdjustedIFile.h" #include "EsCertProcess.h" #include "PkiValidator.h" @@ -66,7 +66,7 @@ void EsCertProcess::importCerts() scratch.alloc(mFile->size()); mFile->read(scratch.data(), 0, scratch.size()); - es::SignedData cert; + pki::SignedData cert; for (size_t f_pos = 0; f_pos < scratch.size(); f_pos += cert.getBytes().size()) { cert.fromBytes(scratch.data() + f_pos, scratch.size() - f_pos); @@ -98,7 +98,7 @@ void EsCertProcess::displayCerts() } } -void EsCertProcess::displayCert(const es::SignedData& cert) +void EsCertProcess::displayCert(const pki::SignedData& cert) { #define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff) #define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0) @@ -119,7 +119,7 @@ void EsCertProcess::displayCert(const es::SignedData& cert) std::cout << std::endl; std::cout << " CertID: 0x" << std::hex << cert.getBody().getCertId() << std::endl; - if (cert.getBody().getPublicKeyType() == es::cert::RSA4096) + if (cert.getBody().getPublicKeyType() == pki::cert::RSA4096) { std::cout << " PublicKey:" << std::endl; std::cout << " Modulus:" << std::endl; @@ -127,7 +127,7 @@ void EsCertProcess::displayCert(const es::SignedData& cert) std::cout << " Public Exponent:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } - else if (cert.getBody().getPublicKeyType() == es::cert::RSA2048) + else if (cert.getBody().getPublicKeyType() == pki::cert::RSA2048) { std::cout << " PublicKey:" << std::endl; std::cout << " Public Exponent:" << std::endl; @@ -135,7 +135,7 @@ void EsCertProcess::displayCert(const es::SignedData& cert) std::cout << " Modulus:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } - else if (cert.getBody().getPublicKeyType() == es::cert::ECDSA240) + else if (cert.getBody().getPublicKeyType() == pki::cert::ECDSA240) { std::cout << " PublicKey:" << std::endl; std::cout << " R:" << std::endl; @@ -151,27 +151,27 @@ void EsCertProcess::displayCert(const es::SignedData& cert) #undef _SPLIT_VER } -const char* EsCertProcess::getSignTypeStr(es::sign::SignatureId type) const +const char* EsCertProcess::getSignTypeStr(pki::sign::SignatureId type) const { const char* str; switch (type) { - case (es::sign::SIGN_ID_RSA4096_SHA1): + case (pki::sign::SIGN_ID_RSA4096_SHA1): str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_ID_RSA2048_SHA1): + case (pki::sign::SIGN_ID_RSA2048_SHA1): str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ID_ECDSA240_SHA1): + case (pki::sign::SIGN_ID_ECDSA240_SHA1): str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_ID_RSA4096_SHA256): + case (pki::sign::SIGN_ID_RSA4096_SHA256): str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_ID_RSA2048_SHA256): + case (pki::sign::SIGN_ID_RSA2048_SHA256): str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ID_ECDSA240_SHA256): + case (pki::sign::SIGN_ID_ECDSA240_SHA256): str = "ECDSA240-SHA256"; break; default: @@ -186,18 +186,18 @@ const char* EsCertProcess::getEndiannessStr(bool isLittleEndian) const return isLittleEndian ? "LittleEndian" : "BigEndian"; } -const char* EsCertProcess::getPublicKeyTypeStr(es::cert::PublicKeyType type) const +const char* EsCertProcess::getPublicKeyTypeStr(pki::cert::PublicKeyType type) const { const char* str; switch (type) { - case (es::cert::RSA4096): + case (pki::cert::RSA4096): str = "RSA4096"; break; - case (es::cert::RSA2048): + case (pki::cert::RSA2048): str = "RSA2048"; break; - case (es::cert::ECDSA240): + case (pki::cert::ECDSA240): str = "ECDSA240"; break; default: diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/EsCertProcess.h index ae88555..5ef7551 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/EsCertProcess.h @@ -4,8 +4,8 @@ #include #include #include -#include -#include +#include +#include #include "nstool.h" class EsCertProcess @@ -30,15 +30,15 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - fnd::List> mCert; + fnd::List> mCert; void importCerts(); void validateCerts(); void displayCerts(); - void displayCert(const es::SignedData& cert); + void displayCert(const pki::SignedData& cert); - const char* getSignTypeStr(es::sign::SignatureId type) const; + const char* getSignTypeStr(pki::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; - const char* getPublicKeyTypeStr(es::cert::PublicKeyType type) const; + const char* getPublicKeyTypeStr(pki::cert::PublicKeyType type) const; }; \ No newline at end of file diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp index 4ef721e..6476bd9 100644 --- a/programs/nstool/source/EsTikProcess.cpp +++ b/programs/nstool/source/EsTikProcess.cpp @@ -132,22 +132,22 @@ const char* EsTikProcess::getSignTypeStr(uint32_t type) const const char* str = nullptr; switch(type) { - case (es::sign::SIGN_ID_RSA4096_SHA1): + case (pki::sign::SIGN_ID_RSA4096_SHA1): str = "RSA4096-SHA1"; break; - case (es::sign::SIGN_ID_RSA2048_SHA1): + case (pki::sign::SIGN_ID_RSA2048_SHA1): str = "RSA2048-SHA1"; break; - case (es::sign::SIGN_ID_ECDSA240_SHA1): + case (pki::sign::SIGN_ID_ECDSA240_SHA1): str = "ECDSA240-SHA1"; break; - case (es::sign::SIGN_ID_RSA4096_SHA256): + case (pki::sign::SIGN_ID_RSA4096_SHA256): str = "RSA4096-SHA256"; break; - case (es::sign::SIGN_ID_RSA2048_SHA256): + case (pki::sign::SIGN_ID_RSA2048_SHA256): str = "RSA2048-SHA256"; break; - case (es::sign::SIGN_ID_ECDSA240_SHA256): + case (pki::sign::SIGN_ID_ECDSA240_SHA256): str = "ECDSA240-SHA256"; break; default: diff --git a/programs/nstool/source/EsTikProcess.h b/programs/nstool/source/EsTikProcess.h index edbe38d..f05e0b7 100644 --- a/programs/nstool/source/EsTikProcess.h +++ b/programs/nstool/source/EsTikProcess.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include "nstool.h" @@ -29,7 +29,7 @@ private: CliOutputMode mCliOutputMode; bool mVerify; - es::SignedData mTik; + pki::SignedData mTik; void displayTicket(); const char* getSignTypeStr(uint32_t type) const; diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp index 623f938..00b2088 100644 --- a/programs/nstool/source/PkiValidator.cpp +++ b/programs/nstool/source/PkiValidator.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include PkiValidator::PkiValidator() { @@ -12,7 +12,7 @@ PkiValidator::PkiValidator() void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key) { // save a copy of the certificate bank - fnd::List> old_certs = mCertificateBank; + fnd::List> old_certs = mCertificateBank; // clear the certificate bank mCertificateBank.clear(); @@ -27,7 +27,7 @@ void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key) } } -void PkiValidator::addCertificates(const fnd::List>& certs) +void PkiValidator::addCertificates(const fnd::List>& certs) { for (size_t i = 0; i < certs.size(); i++) { @@ -35,11 +35,11 @@ void PkiValidator::addCertificates(const fnd::List& cert) +void PkiValidator::addCertificate(const pki::SignedData& cert) { std::string cert_ident; - es::sign::SignatureAlgo cert_sign_algo; - es::sign::HashAlgo cert_hash_algo; + pki::sign::SignatureAlgo cert_sign_algo; + pki::sign::HashAlgo cert_hash_algo; fnd::Vec cert_hash; try @@ -51,17 +51,17 @@ void PkiValidator::addCertificate(const es::SignedData& cer throw fnd::Exception(kModuleName, "Certificate already exists"); } - cert_sign_algo = es::sign::getSignatureAlgo(cert.getSignature().getSignType()); - cert_hash_algo = es::sign::getHashAlgo(cert.getSignature().getSignType()); + cert_sign_algo = pki::sign::getSignatureAlgo(cert.getSignature().getSignType()); + cert_hash_algo = pki::sign::getHashAlgo(cert.getSignature().getSignType()); // get cert hash switch (cert_hash_algo) { - case (es::sign::HASH_ALGO_SHA1): + case (pki::sign::HASH_ALGO_SHA1): cert_hash.alloc(crypto::sha::kSha1HashLen); crypto::sha::Sha1(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data()); break; - case (es::sign::HASH_ALGO_SHA256): + case (pki::sign::HASH_ALGO_SHA256): cert_hash.alloc(crypto::sha::kSha256HashLen); crypto::sha::Sha256(cert.getBody().getBytes().data(), cert.getBody().getBytes().size(), cert_hash.data()); break; @@ -86,19 +86,19 @@ void PkiValidator::clearCertificates() mCertificateBank.clear(); } -void PkiValidator::validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const +void PkiValidator::validateSignature(const std::string& issuer, pki::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const { - es::sign::SignatureAlgo sign_algo = es::sign::getSignatureAlgo(signature_id); - es::sign::HashAlgo hash_algo = es::sign::getHashAlgo(signature_id); + pki::sign::SignatureAlgo sign_algo = pki::sign::getSignatureAlgo(signature_id); + pki::sign::HashAlgo hash_algo = pki::sign::getHashAlgo(signature_id); // validate signature int sig_validate_res = -1; // special case if signed by Root - if (issuer == es::sign::kRootIssuerStr) + if (issuer == pki::sign::kRootIssuerStr) { - if (sign_algo != es::sign::SIGN_ALGO_RSA4096) + if (sign_algo != pki::sign::SIGN_ALGO_RSA4096) { throw fnd::Exception(kModuleName, "Issued by Root, but does not have a RSA4096 signature"); } @@ -107,18 +107,18 @@ void PkiValidator::validateSignature(const std::string& issuer, es::sign::Signat else { // try to find issuer cert - const es::CertificateBody& issuer_cert = getCert(issuer).getBody(); - es::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType(); + const pki::CertificateBody& issuer_cert = getCert(issuer).getBody(); + pki::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType(); - if (issuer_pubk_type == es::cert::RSA4096 && sign_algo == es::sign::SIGN_ALGO_RSA4096) + if (issuer_pubk_type == pki::cert::RSA4096 && sign_algo == pki::sign::SIGN_ALGO_RSA4096) { sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa4098PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); } - else if (issuer_pubk_type == es::cert::RSA2048 && sign_algo == es::sign::SIGN_ALGO_RSA2048) + else if (issuer_pubk_type == pki::cert::RSA2048 && sign_algo == pki::sign::SIGN_ALGO_RSA2048) { sig_validate_res = crypto::rsa::pkcs::rsaVerify(issuer_cert.getRsa2048PublicKey(), getCryptoHashAlgoFromEsSignHashAlgo(hash_algo), hash.data(), signature.data()); } - else if (issuer_pubk_type == es::cert::ECDSA240 && sign_algo == es::sign::SIGN_ALGO_ECDSA240) + else if (issuer_pubk_type == pki::cert::ECDSA240 && sign_algo == pki::sign::SIGN_ALGO_ECDSA240) { throw fnd::Exception(kModuleName, "ECDSA signatures are not supported"); } @@ -136,14 +136,14 @@ void PkiValidator::validateSignature(const std::string& issuer, es::sign::Signat } -void PkiValidator::makeCertIdent(const es::SignedData& cert, std::string& ident) const +void PkiValidator::makeCertIdent(const pki::SignedData& cert, std::string& ident) const { makeCertIdent(cert.getBody().getIssuer(), cert.getBody().getSubject(), ident); } void PkiValidator::makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const { - ident = issuer + es::sign::kIdentDelimiter + subject; + ident = issuer + pki::sign::kIdentDelimiter + subject; ident = ident.substr(0, _MIN(ident.length(),64)); } @@ -164,7 +164,7 @@ bool PkiValidator::doesCertExist(const std::string& ident) const return exists; } -const es::SignedData& PkiValidator::getCert(const std::string& ident) const +const pki::SignedData& PkiValidator::getCert(const std::string& ident) const { std::string full_cert_name; for (size_t i = 0; i < mCertificateBank.size(); i++) @@ -179,16 +179,16 @@ const es::SignedData& PkiValidator::getCert(const std::stri throw fnd::Exception(kModuleName, "Issuer certificate does not exist"); } -crypto::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const +crypto::sha::HashType PkiValidator::getCryptoHashAlgoFromEsSignHashAlgo(pki::sign::HashAlgo hash_algo) const { crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1; switch (hash_algo) { - case (es::sign::HASH_ALGO_SHA1): + case (pki::sign::HASH_ALGO_SHA1): hash_type = crypto::sha::HASH_SHA1; break; - case (es::sign::HASH_ALGO_SHA256): + case (pki::sign::HASH_ALGO_SHA256): hash_type = crypto::sha::HASH_SHA256; break; }; diff --git a/programs/nstool/source/PkiValidator.h b/programs/nstool/source/PkiValidator.h index c585c41..217dfad 100644 --- a/programs/nstool/source/PkiValidator.h +++ b/programs/nstool/source/PkiValidator.h @@ -3,8 +3,8 @@ #include #include #include -#include -#include +#include +#include #include class PkiValidator @@ -13,22 +13,22 @@ public: PkiValidator(); void setRootKey(const crypto::rsa::sRsa4096Key& root_key); - void addCertificates(const fnd::List>& certs); - void addCertificate(const es::SignedData& cert); + void addCertificates(const fnd::List>& certs); + void addCertificate(const pki::SignedData& cert); void clearCertificates(); - void validateSignature(const std::string& issuer, es::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; + void validateSignature(const std::string& issuer, pki::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const; private: const std::string kModuleName = "NNPkiValidator"; crypto::rsa::sRsa4096Key mRootKey; - fnd::List> mCertificateBank; + fnd::List> mCertificateBank; - void makeCertIdent(const es::SignedData& cert, std::string& ident) const; + void makeCertIdent(const pki::SignedData& cert, std::string& ident) const; void makeCertIdent(const std::string& issuer, const std::string& subject, std::string& ident) const; bool doesCertExist(const std::string& ident) const; - const es::SignedData& getCert(const std::string& ident) const; - crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(es::sign::HashAlgo hash_algo) const; + const pki::SignedData& getCert(const std::string& ident) const; + crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(pki::sign::HashAlgo hash_algo) const; }; \ No newline at end of file diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 00e33cb..412fa70 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -22,7 +22,7 @@ #include #include #include -#include +#include UserSettings::UserSettings() {} @@ -756,10 +756,10 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) // test nso else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _TYPE_PTR(nx::sNroHeader)->st_magic.get() == nx::nro::kNroStructMagic) file_type = FILE_NRO; - // test es certificate + // test pki certificate else if (determineValidEsCertFromSample(scratch)) file_type = FILE_ES_CERT; - // test es ticket + // test ticket else if (determineValidEsTikFromSample(scratch)) file_type = FILE_ES_TIK; // test hb asset @@ -866,7 +866,7 @@ bool UserSettings::determineValidNacpFromSample(const fnd::Vec& sample) bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample) const { - es::SignatureBlock sign; + pki::SignatureBlock sign; try { @@ -880,7 +880,7 @@ bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample if (sign.isLittleEndian() == true) return false; - if (sign.getSignType() != es::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != es::sign::SIGN_ID_ECDSA240_SHA256) + if (sign.getSignType() != pki::sign::SIGN_ID_RSA4096_SHA256 && sign.getSignType() != pki::sign::SIGN_ID_RSA2048_SHA256 && sign.getSignType() != pki::sign::SIGN_ID_ECDSA240_SHA256) return false; return true; @@ -888,7 +888,7 @@ bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& sample bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) const { - es::SignatureBlock sign; + pki::SignatureBlock sign; try { @@ -902,7 +902,7 @@ bool UserSettings::determineValidEsTikFromSample(const fnd::Vec& sample) if (sign.isLittleEndian() == false) return false; - if (sign.getSignType() != es::sign::SIGN_ID_RSA2048_SHA256) + if (sign.getSignType() != pki::sign::SIGN_ID_RSA2048_SHA256) return false; return true; From a69f84a046b9a402d09c858f9bb52dbbe5e054a1 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 17:11:15 +0800 Subject: [PATCH 29/34] [nstool] Rename EsCertProcess to PkiCertProcess --- .../{EsCertProcess.cpp => PkiCertProcess.cpp} | 45 ++++++++++--------- .../{EsCertProcess.h => PkiCertProcess.h} | 11 ++--- programs/nstool/source/UserSettings.cpp | 4 +- programs/nstool/source/main.cpp | 6 +-- programs/nstool/source/nstool.h | 2 +- 5 files changed, 37 insertions(+), 31 deletions(-) rename programs/nstool/source/{EsCertProcess.cpp => PkiCertProcess.cpp} (78%) rename programs/nstool/source/{EsCertProcess.h => PkiCertProcess.h} (81%) diff --git a/programs/nstool/source/EsCertProcess.cpp b/programs/nstool/source/PkiCertProcess.cpp similarity index 78% rename from programs/nstool/source/EsCertProcess.cpp rename to programs/nstool/source/PkiCertProcess.cpp index bd15514..80f3b20 100644 --- a/programs/nstool/source/EsCertProcess.cpp +++ b/programs/nstool/source/PkiCertProcess.cpp @@ -4,10 +4,10 @@ #include #include #include "OffsetAdjustedIFile.h" -#include "EsCertProcess.h" +#include "PkiCertProcess.h" #include "PkiValidator.h" -EsCertProcess::EsCertProcess() : +PkiCertProcess::PkiCertProcess() : mFile(nullptr), mOwnIFile(false), mCliOutputMode(_BIT(OUTPUT_BASIC)), @@ -15,7 +15,7 @@ EsCertProcess::EsCertProcess() : { } -EsCertProcess::~EsCertProcess() +PkiCertProcess::~PkiCertProcess() { if (mOwnIFile) { @@ -23,7 +23,7 @@ EsCertProcess::~EsCertProcess() } } -void EsCertProcess::process() +void PkiCertProcess::process() { if (mFile == nullptr) { @@ -38,28 +38,28 @@ void EsCertProcess::process() displayCerts(); } -void EsCertProcess::setInputFile(fnd::IFile* file, bool ownIFile) +void PkiCertProcess::setInputFile(fnd::IFile* file, bool ownIFile) { mFile = file; mOwnIFile = ownIFile; } -void EsCertProcess::setKeyset(const sKeyset* keyset) +void PkiCertProcess::setKeyset(const sKeyset* keyset) { mKeyset = keyset; } -void EsCertProcess::setCliOutputMode(CliOutputMode mode) +void PkiCertProcess::setCliOutputMode(CliOutputMode mode) { mCliOutputMode = mode; } -void EsCertProcess::setVerifyMode(bool verify) +void PkiCertProcess::setVerifyMode(bool verify) { mVerify = verify; } -void EsCertProcess::importCerts() +void PkiCertProcess::importCerts() { fnd::Vec scratch; @@ -74,7 +74,7 @@ void EsCertProcess::importCerts() } } -void EsCertProcess::validateCerts() +void PkiCertProcess::validateCerts() { PkiValidator pki; @@ -90,7 +90,7 @@ void EsCertProcess::validateCerts() } } -void EsCertProcess::displayCerts() +void PkiCertProcess::displayCerts() { for (size_t i = 0; i < mCert.size(); i++) { @@ -98,13 +98,13 @@ void EsCertProcess::displayCerts() } } -void EsCertProcess::displayCert(const pki::SignedData& cert) +void PkiCertProcess::displayCert(const pki::SignedData& cert) { #define _SPLIT_VER(ver) ( (ver>>26) & 0x3f), ( (ver>>20) & 0x3f), ( (ver>>16) & 0xf), (ver & 0xffff) #define _HEXDUMP_U(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02X", var[a__a__A]); } while(0) #define _HEXDUMP_L(var, len) do { for (size_t a__a__A = 0; a__a__A < len; a__a__A++) printf("%02x", var[a__a__A]); } while(0) - std::cout << "[ES Certificate]" << std::endl; + std::cout << "[NNPKI Certificate]" << std::endl; std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType()); if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED)) @@ -123,7 +123,7 @@ void EsCertProcess::displayCert(const pki::SignedData& cer { std::cout << " PublicKey:" << std::endl; std::cout << " Modulus:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa4096Size : 0x10, 0x10, 6); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().modulus, getHexDumpLen(crypto::rsa::kRsa4096Size), 0x10, 6); std::cout << " Public Exponent:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa4098PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } @@ -131,7 +131,7 @@ void EsCertProcess::displayCert(const pki::SignedData& cer { std::cout << " PublicKey:" << std::endl; std::cout << " Public Exponent:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::rsa::kRsa2048Size : 0x10, 0x10, 6); + fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().modulus, getHexDumpLen(crypto::rsa::kRsa2048Size), 0x10, 6); std::cout << " Modulus:" << std::endl; fnd::SimpleTextOutput::hexDump(cert.getBody().getRsa2048PublicKey().public_exponent, crypto::rsa::kRsaPublicExponentSize, 0x10, 6); } @@ -139,9 +139,9 @@ void EsCertProcess::displayCert(const pki::SignedData& cer { std::cout << " PublicKey:" << std::endl; std::cout << " R:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); + fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().r, getHexDumpLen(crypto::ecdsa::kEcdsa240Size), 0x10, 6); std::cout << " S:" << std::endl; - fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? crypto::ecdsa::kEcdsa240Size : 0x10, 0x10, 6); + fnd::SimpleTextOutput::hexDump(cert.getBody().getEcdsa240PublicKey().s, getHexDumpLen(crypto::ecdsa::kEcdsa240Size), 0x10, 6); } @@ -151,7 +151,12 @@ void EsCertProcess::displayCert(const pki::SignedData& cer #undef _SPLIT_VER } -const char* EsCertProcess::getSignTypeStr(pki::sign::SignatureId type) const +size_t PkiCertProcess::getHexDumpLen(size_t max_size) const +{ + return _HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED) ? max_size : kSmallHexDumpLen; +} + +const char* PkiCertProcess::getSignTypeStr(pki::sign::SignatureId type) const { const char* str; switch (type) @@ -181,12 +186,12 @@ const char* EsCertProcess::getSignTypeStr(pki::sign::SignatureId type) const return str; } -const char* EsCertProcess::getEndiannessStr(bool isLittleEndian) const +const char* PkiCertProcess::getEndiannessStr(bool isLittleEndian) const { return isLittleEndian ? "LittleEndian" : "BigEndian"; } -const char* EsCertProcess::getPublicKeyTypeStr(pki::cert::PublicKeyType type) const +const char* PkiCertProcess::getPublicKeyTypeStr(pki::cert::PublicKeyType type) const { const char* str; switch (type) diff --git a/programs/nstool/source/EsCertProcess.h b/programs/nstool/source/PkiCertProcess.h similarity index 81% rename from programs/nstool/source/EsCertProcess.h rename to programs/nstool/source/PkiCertProcess.h index 5ef7551..31c63b2 100644 --- a/programs/nstool/source/EsCertProcess.h +++ b/programs/nstool/source/PkiCertProcess.h @@ -8,11 +8,11 @@ #include #include "nstool.h" -class EsCertProcess +class PkiCertProcess { public: - EsCertProcess(); - ~EsCertProcess(); + PkiCertProcess(); + ~PkiCertProcess(); void process(); @@ -22,7 +22,8 @@ public: void setVerifyMode(bool verify); private: - const std::string kModuleName = "EsCertProcess"; + const std::string kModuleName = "PkiCertProcess"; + static const size_t kSmallHexDumpLen = 0x10; fnd::IFile* mFile; bool mOwnIFile; @@ -37,7 +38,7 @@ private: void displayCerts(); void displayCert(const pki::SignedData& cert); - + size_t getHexDumpLen(size_t max_size) const; const char* getSignTypeStr(pki::sign::SignatureId type) const; const char* getEndiannessStr(bool isLittleEndian) const; const char* getPublicKeyTypeStr(pki::cert::PublicKeyType type) const; diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 412fa70..65d09a2 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -695,7 +695,7 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str) else if (str == "nacp") type = FILE_NACP; else if (str == "cert") - type = FILE_ES_CERT; + type = FILE_PKI_CERT; else if (str == "tik") type = FILE_ES_TIK; else if (str == "aset" || str == "asset") @@ -758,7 +758,7 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path) file_type = FILE_NRO; // test pki certificate else if (determineValidEsCertFromSample(scratch)) - file_type = FILE_ES_CERT; + file_type = FILE_PKI_CERT; // test ticket else if (determineValidEsTikFromSample(scratch)) file_type = FILE_ES_TIK; diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp index 7186282..0c8ef07 100644 --- a/programs/nstool/source/main.cpp +++ b/programs/nstool/source/main.cpp @@ -11,7 +11,7 @@ #include "NsoProcess.h" #include "NroProcess.h" #include "NacpProcess.h" -#include "EsCertProcess.h" +#include "PkiCertProcess.h" #include "EsTikProcess.h" #include "AssetProcess.h" @@ -175,9 +175,9 @@ int main(int argc, char** argv) nacp.process(); } - else if (user_set.getFileType() == FILE_ES_CERT) + else if (user_set.getFileType() == FILE_PKI_CERT) { - EsCertProcess cert; + PkiCertProcess cert; cert.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); cert.setKeyset(&user_set.getKeyset()); diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h index b94521d..a52c571 100644 --- a/programs/nstool/source/nstool.h +++ b/programs/nstool/source/nstool.h @@ -27,7 +27,7 @@ enum FileType FILE_NSO, FILE_NRO, FILE_NACP, - FILE_ES_CERT, + FILE_PKI_CERT, FILE_ES_TIK, FILE_HB_ASSET, FILE_INVALID = -1, From a757116502f2389e3cfabdd80596171f8ac251b4 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 17:56:23 +0800 Subject: [PATCH 30/34] [nstool] Add some punctuation to usage text. --- programs/nstool/source/UserSettings.cpp | 42 ++++++++++++------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 65d09a2..f50b9b1 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -42,34 +42,34 @@ void UserSettings::showHelp() printf("Usage: nstool [options... ] \n"); printf("\n General Options:\n"); - printf(" -d, --dev Use devkit keyset\n"); - printf(" -k, --keyset Specify keyset file\n"); - printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset, cert, tik]\n"); - printf(" -y, --verify Verify file\n"); + printf(" -d, --dev Use devkit keyset.\n"); + printf(" -k, --keyset Specify keyset file.\n"); + printf(" -t, --type Specify input file type. [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset, cert, tik]\n"); + printf(" -y, --verify Verify file.\n"); printf("\n Output Options:\n"); - printf(" --showkeys Show keys generated\n"); - printf(" --showlayout Show layout metadata\n"); - printf(" -v, --verbose Verbose output\n"); + printf(" --showkeys Show keys generated.\n"); + printf(" --showlayout Show layout metadata.\n"); + printf(" -v, --verbose Verbose output.\n"); printf("\n XCI (GameCard Image)\n"); printf(" nstool [--listfs] [--update --logo --normal --secure ] <.xci file>\n"); - printf(" --listfs Print file system in embedded partitions\n"); - printf(" --update Extract \"update\" partition to directory\n"); - printf(" --logo Extract \"logo\" partition to directory\n"); - printf(" --normal Extract \"normal\" partition to directory\n"); - printf(" --secure Extract \"secure\" partition to directory\n"); + printf(" --listfs Print file system in embedded partitions.\n"); + printf(" --update Extract \"update\" partition to directory.\n"); + printf(" --logo Extract \"logo\" partition to directory.\n"); + printf(" --normal Extract \"normal\" partition to directory.\n"); + printf(" --secure Extract \"secure\" partition to directory.\n"); printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); printf(" nstool [--listfs] [--fsdir ] \n"); - printf(" --listfs Print file system\n"); - printf(" --fsdir Extract file system to directory\n"); + printf(" --listfs Print file system.\n"); + printf(" --fsdir Extract file system to directory.\n"); printf("\n NCA (Nintendo Content Archive)\n"); printf(" nstool [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file>\n"); - printf(" --listfs Print file system in embedded partitions\n"); - printf(" --titlekey Specify title key extracted from ticket\n"); - printf(" --bodykey Specify body encryption key\n"); - printf(" --part0 Extract \"partition 0\" to directory \n"); - printf(" --part1 Extract \"partition 1\" to directory \n"); - printf(" --part2 Extract \"partition 2\" to directory \n"); - printf(" --part3 Extract \"partition 3\" to directory \n"); + printf(" --listfs Print file system in embedded partitions.\n"); + printf(" --titlekey Specify title key extracted from ticket.\n"); + printf(" --bodykey Specify body encryption key.\n"); + printf(" --part0 Extract \"partition 0\" to directory.\n"); + printf(" --part1 Extract \"partition 1\" to directory.\n"); + printf(" --part2 Extract \"partition 2\" to directory.\n"); + printf(" --part3 Extract \"partition 3\" to directory.\n"); printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n"); printf(" nstool [--listapi --listsym] [--insttype ] \n"); printf(" --listapi Print SDK API List.\n"); From 1266ba39763e7c4fb0adedcc48efc7456a8a6d4d Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 18:39:24 +0800 Subject: [PATCH 31/34] [nstool] NCA external content key can be sourced from a supplied ticket. --- programs/nstool/source/UserSettings.cpp | 32 ++++++++++++++++++++++++- programs/nstool/source/UserSettings.h | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index f50b9b1..58261fc 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include UserSettings::UserSettings() {} @@ -66,6 +67,7 @@ void UserSettings::showHelp() printf(" --listfs Print file system in embedded partitions.\n"); printf(" --titlekey Specify title key extracted from ticket.\n"); printf(" --bodykey Specify body encryption key.\n"); + printf(" --tik Specify ticket to source title key.\n"); printf(" --part0 Extract \"partition 0\" to directory.\n"); printf(" --part1 Extract \"partition 1\" to directory.\n"); printf(" --part2 Extract \"partition 2\" to directory.\n"); @@ -297,6 +299,12 @@ void UserSettings::populateCmdArgs(const std::vector& arg_list, sCm cmd_args.nca_bodykey = arg_list[i+1]; } + else if (arg_list[i] == "--tik") + { + if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); + cmd_args.ticket_path = arg_list[i+1]; + } + else if (arg_list[i] == "--part0") { if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); @@ -527,6 +535,28 @@ void UserSettings::populateKeyset(sCmdArgs& args) } } + // get titlekey from ticket + if (args.ticket_path.isSet) + { + fnd::SimpleFile tik_file; + fnd::Vec tik_raw; + pki::SignedData tik; + + tik_file.open(args.ticket_path.var, fnd::SimpleFile::Read); + tik_raw.alloc(tik_file.size()); + tik_file.read(tik_raw.data(), tik_raw.size()); + tik.fromBytes(tik_raw.data(), tik_raw.size()); + + if (tik.getBody().getTitleKeyEncType() == es::ticket::AES128_CBC) + { + memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), crypto::aes::kAes128KeySize); + } + else + { + std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl; + } + } + #undef _SAVE_KEYDATA #undef _CONCAT_3_STRINGS #undef _CONCAT_2_STRINGS diff --git a/programs/nstool/source/UserSettings.h b/programs/nstool/source/UserSettings.h index 505b586..43d773c 100644 --- a/programs/nstool/source/UserSettings.h +++ b/programs/nstool/source/UserSettings.h @@ -62,6 +62,7 @@ private: sOptional fs_path; sOptional nca_titlekey; sOptional nca_bodykey; + sOptional ticket_path; sOptional part0_path; sOptional part1_path; sOptional part2_path; From 9fb67312945bf1a39d203ac265dc5727f3f987b8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 19:36:42 +0800 Subject: [PATCH 32/34] [nstool] Add ticket validation via provided cert chain file. --- programs/nstool/source/EsTikProcess.cpp | 62 +++++++++++++++++++---- programs/nstool/source/EsTikProcess.h | 6 +++ programs/nstool/source/UserSettings.cpp | 65 +++++++++++++++++++++++++ programs/nstool/source/UserSettings.h | 7 +++ programs/nstool/source/main.cpp | 1 + 5 files changed, 131 insertions(+), 10 deletions(-) diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp index 6476bd9..22402a8 100644 --- a/programs/nstool/source/EsTikProcess.cpp +++ b/programs/nstool/source/EsTikProcess.cpp @@ -2,8 +2,10 @@ #include #include +#include #include "OffsetAdjustedIFile.h" #include "EsTikProcess.h" +#include "PkiValidator.h" @@ -25,17 +27,10 @@ EsTikProcess::~EsTikProcess() void EsTikProcess::process() { - fnd::Vec scratch; + importTicket(); - if (mFile == nullptr) - { - throw fnd::Exception(kModuleName, "No file reader set."); - } - - scratch.alloc(mFile->size()); - mFile->read(scratch.data(), 0, scratch.size()); - - mTik.fromBytes(scratch.data(), scratch.size()); + if (mVerify) + verifyTicket(); if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC)) displayTicket(); @@ -52,6 +47,11 @@ void EsTikProcess::setKeyset(const sKeyset* keyset) mKeyset = keyset; } +void EsTikProcess::setCertificateChain(const fnd::List>& certs) +{ + mCerts = certs; +} + void EsTikProcess::setCliOutputMode(CliOutputMode mode) { mCliOutputMode = mode; @@ -62,6 +62,48 @@ void EsTikProcess::setVerifyMode(bool verify) mVerify = verify; } +void EsTikProcess::importTicket() +{ + if (mFile == nullptr) + { + throw fnd::Exception(kModuleName, "No file reader set."); + } + + fnd::Vec scratch; + scratch.alloc(mFile->size()); + mFile->read(scratch.data(), 0, scratch.size()); + mTik.fromBytes(scratch.data(), scratch.size()); +} + +void EsTikProcess::verifyTicket() +{ + PkiValidator pki_validator; + fnd::Vec tik_hash; + + switch (pki::sign::getHashAlgo(mTik.getSignature().getSignType())) + { + case (pki::sign::HASH_ALGO_SHA1): + tik_hash.alloc(crypto::sha::kSha1HashLen); + crypto::sha::Sha1(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data()); + break; + case (pki::sign::HASH_ALGO_SHA256): + tik_hash.alloc(crypto::sha::kSha256HashLen); + crypto::sha::Sha256(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data()); + break; + } + + try + { + pki_validator.setRootKey(mKeyset->pki.root_sign_key); + pki_validator.addCertificates(mCerts); + pki_validator.validateSignature(mTik.getBody().getIssuer(), mTik.getSignature().getSignType(), mTik.getSignature().getSignature(), tik_hash); + } + catch (const fnd::Exception& e) + { + std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl; + } +} + void EsTikProcess::displayTicket() { #define _SPLIT_VER(ver) ( (ver>>10) & 0x3f), ( (ver>>4) & 0x3f), ( (ver>>0) & 0xf) diff --git a/programs/nstool/source/EsTikProcess.h b/programs/nstool/source/EsTikProcess.h index f05e0b7..2ada90c 100644 --- a/programs/nstool/source/EsTikProcess.h +++ b/programs/nstool/source/EsTikProcess.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include "nstool.h" @@ -17,6 +18,7 @@ public: void setInputFile(fnd::IFile* file, bool ownIFile); void setKeyset(const sKeyset* keyset); + void setCertificateChain(const fnd::List>& certs); void setCliOutputMode(CliOutputMode mode); void setVerifyMode(bool verify); @@ -28,9 +30,13 @@ private: const sKeyset* mKeyset; CliOutputMode mCliOutputMode; bool mVerify; + + fnd::List> mCerts; pki::SignedData mTik; + void importTicket(); + void verifyTicket(); void displayTicket(); const char* getSignTypeStr(uint32_t type) const; const char* getTitleKeyPersonalisationStr(byte_t flag) const; diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 58261fc..10aa2a9 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -1,5 +1,6 @@ #include "UserSettings.h" #include "version.h" +#include "PkiValidator.h" #include #include #include @@ -23,6 +24,8 @@ #include #include #include +#include +#include #include UserSettings::UserSettings() @@ -185,6 +188,11 @@ const sOptional& UserSettings::getAssetNacpPath() const return mAssetNacpPath; } +const fnd::List>& UserSettings::getCertificateChain() const +{ + return mCertChain; +} + void UserSettings::populateCmdArgs(const std::vector& arg_list, sCmdArgs& cmd_args) { // show help text @@ -305,6 +313,12 @@ void UserSettings::populateCmdArgs(const std::vector& arg_list, sCm cmd_args.ticket_path = arg_list[i+1]; } + else if (arg_list[i] == "--cert") + { + if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); + cmd_args.cert_path = arg_list[i+1]; + } + else if (arg_list[i] == "--part0") { if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); @@ -535,6 +549,24 @@ void UserSettings::populateKeyset(sCmdArgs& args) } } + // import certificate chain + if (args.cert_path.isSet) + { + fnd::SimpleFile cert_file; + fnd::Vec cert_raw; + pki::SignedData cert; + + cert_file.open(args.cert_path.var, fnd::SimpleFile::Read); + cert_raw.alloc(cert_file.size()); + cert_file.read(cert_raw.data(), cert_raw.size()); + + for (size_t i = 0; i < cert_raw.size(); i+= cert.getBytes().size()) + { + cert.fromBytes(cert_raw.data() + i, cert_raw.size() - i); + mCertChain.addElement(cert); + } + } + // get titlekey from ticket if (args.ticket_path.isSet) { @@ -542,11 +574,44 @@ void UserSettings::populateKeyset(sCmdArgs& args) fnd::Vec tik_raw; pki::SignedData tik; + // open and import ticket tik_file.open(args.ticket_path.var, fnd::SimpleFile::Read); tik_raw.alloc(tik_file.size()); tik_file.read(tik_raw.data(), tik_raw.size()); tik.fromBytes(tik_raw.data(), tik_raw.size()); + // validate ticket signature + if (mCertChain.size() > 0) + { + PkiValidator pki_validator; + fnd::Vec tik_hash; + + switch (pki::sign::getHashAlgo(tik.getSignature().getSignType())) + { + case (pki::sign::HASH_ALGO_SHA1): + tik_hash.alloc(crypto::sha::kSha1HashLen); + crypto::sha::Sha1(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data()); + break; + case (pki::sign::HASH_ALGO_SHA256): + tik_hash.alloc(crypto::sha::kSha256HashLen); + crypto::sha::Sha256(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data()); + break; + } + + try + { + pki_validator.setRootKey(mKeyset.pki.root_sign_key); + pki_validator.addCertificates(mCertChain); + pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash); + } + catch (const fnd::Exception& e) + { + std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl; + } + + } + + // extract title key if (tik.getBody().getTitleKeyEncType() == es::ticket::AES128_CBC) { memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), crypto::aes::kAes128KeySize); diff --git a/programs/nstool/source/UserSettings.h b/programs/nstool/source/UserSettings.h index 43d773c..fd53077 100644 --- a/programs/nstool/source/UserSettings.h +++ b/programs/nstool/source/UserSettings.h @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include "nstool.h" @@ -39,6 +42,7 @@ public: const sOptional& getNcaPart3Path() const; const sOptional& getAssetIconPath() const; const sOptional& getAssetNacpPath() const; + const fnd::List>& getCertificateChain() const; private: const std::string kModuleName = "UserSettings"; @@ -63,6 +67,7 @@ private: sOptional nca_titlekey; sOptional nca_bodykey; sOptional ticket_path; + sOptional cert_path; sOptional part0_path; sOptional part1_path; sOptional part2_path; @@ -95,6 +100,8 @@ private: sOptional mAssetIconPath; sOptional mAssetNacpPath; + fnd::List> mCertChain; + bool mListApi; bool mListSymbols; nx::npdm::InstructionType mInstructionType; diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp index 0c8ef07..7eefe54 100644 --- a/programs/nstool/source/main.cpp +++ b/programs/nstool/source/main.cpp @@ -192,6 +192,7 @@ int main(int argc, char** argv) tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); tik.setKeyset(&user_set.getKeyset()); + tik.setCertificateChain(user_set.getCertificateChain()); tik.setCliOutputMode(user_set.getCliOutputMode()); tik.setVerifyMode(user_set.isVerifyFile()); From 09209feb7d37f863b210cc9c8fdc90437cdf6168 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 20:20:06 +0800 Subject: [PATCH 33/34] [nstool] Add usage text about --cert --- programs/nstool/source/UserSettings.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp index 10aa2a9..f2c5297 100644 --- a/programs/nstool/source/UserSettings.cpp +++ b/programs/nstool/source/UserSettings.cpp @@ -71,6 +71,7 @@ void UserSettings::showHelp() printf(" --titlekey Specify title key extracted from ticket.\n"); printf(" --bodykey Specify body encryption key.\n"); printf(" --tik Specify ticket to source title key.\n"); + printf(" --cert Specify certificate chain to verify ticket.\n"); printf(" --part0 Extract \"partition 0\" to directory.\n"); printf(" --part1 Extract \"partition 1\" to directory.\n"); printf(" --part2 Extract \"partition 2\" to directory.\n"); From bddcb244bbda2d8a4464fe141df6b7e106e83d67 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 20:23:01 +0800 Subject: [PATCH 34/34] Update VS Project Files. --- NXTools.sln | 11 ++ lib/libes/es.vcxproj | 9 -- lib/libes/es.vcxproj.filters | 27 ----- lib/libpki/libpki.vcxproj | 144 +++++++++++++++++++++++++ lib/libpki/libpki.vcxproj.filters | 51 +++++++++ lib/libpki/libpki.vcxproj.user | 4 + programs/nstool/nstool.vcxproj | 17 +-- programs/nstool/nstool.vcxproj.filters | 18 ++-- 8 files changed, 233 insertions(+), 48 deletions(-) create mode 100644 lib/libpki/libpki.vcxproj create mode 100644 lib/libpki/libpki.vcxproj.filters create mode 100644 lib/libpki/libpki.vcxproj.user diff --git a/NXTools.sln b/NXTools.sln index ade6849..2305e4c 100644 --- a/NXTools.sln +++ b/NXTools.sln @@ -44,6 +44,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcompress", "lib\libcompr EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx-hb", "lib\libnx-hb\libnx-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpki", "lib\libpki\libpki.vcxproj", "{B9113734-6E84-44FF-8CF7-58199AA815C5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -108,6 +110,14 @@ Global {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x64.Build.0 = Release|x64 {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.ActiveCfg = Release|Win32 {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.Build.0 = Release|Win32 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x64.ActiveCfg = Debug|x64 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x64.Build.0 = Debug|x64 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x86.ActiveCfg = Debug|Win32 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x86.Build.0 = Debug|Win32 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x64.ActiveCfg = Release|x64 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x64.Build.0 = Release|x64 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x86.ActiveCfg = Release|Win32 + {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -120,6 +130,7 @@ Global {AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} {CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} + {B9113734-6E84-44FF-8CF7-58199AA815C5} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62} diff --git a/lib/libes/es.vcxproj b/lib/libes/es.vcxproj index fa5b849..ae9f9c3 100644 --- a/lib/libes/es.vcxproj +++ b/lib/libes/es.vcxproj @@ -122,21 +122,12 @@ - - - - - - - - - diff --git a/lib/libes/es.vcxproj.filters b/lib/libes/es.vcxproj.filters index 7f614e2..f933a32 100644 --- a/lib/libes/es.vcxproj.filters +++ b/lib/libes/es.vcxproj.filters @@ -18,27 +18,9 @@ - - Header Files - - - Header Files - Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -47,18 +29,9 @@ - - Source Files - Source Files - - Source Files - - - Source Files - Source Files diff --git a/lib/libpki/libpki.vcxproj b/lib/libpki/libpki.vcxproj new file mode 100644 index 0000000..7347aca --- /dev/null +++ b/lib/libpki/libpki.vcxproj @@ -0,0 +1,144 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 15.0 + {B9113734-6E84-44FF-8CF7-58199AA815C5} + libpki + 10.0.16299.0 + + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + StaticLibrary + true + v141 + MultiByte + + + StaticLibrary + false + v141 + true + MultiByte + + + + + + + + + + + + + + + + + + + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libfnd\include;..\libcrypto\include;..\libpki\include; + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + + + + + Level3 + Disabled + true + true + ..\libfnd\include;..\libcrypto\include;..\libpki\include; + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + Disabled + true + true + ..\libfnd\include;..\libcrypto\include;..\libpki\include; + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + + + Level3 + MaxSpeed + true + true + true + true + ..\libfnd\include;..\libcrypto\include;..\libpki\include; + _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/lib/libpki/libpki.vcxproj.filters b/lib/libpki/libpki.vcxproj.filters new file mode 100644 index 0000000..ab6f249 --- /dev/null +++ b/lib/libpki/libpki.vcxproj.filters @@ -0,0 +1,51 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + + + + \ No newline at end of file diff --git a/lib/libpki/libpki.vcxproj.user b/lib/libpki/libpki.vcxproj.user new file mode 100644 index 0000000..be25078 --- /dev/null +++ b/lib/libpki/libpki.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/programs/nstool/nstool.vcxproj b/programs/nstool/nstool.vcxproj index 791ca14..848e899 100644 --- a/programs/nstool/nstool.vcxproj +++ b/programs/nstool/nstool.vcxproj @@ -90,7 +90,7 @@ true _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -105,7 +105,7 @@ true WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -122,7 +122,7 @@ true WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -141,7 +141,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include + ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include Console @@ -169,6 +169,9 @@ {91ba9e79-8242-4f7d-b997-0dfec95ea22b} + + {b9113734-6e84-44ff-8cf7-58199aa815c5} + @@ -178,7 +181,6 @@ - @@ -190,6 +192,8 @@ + + @@ -202,7 +206,6 @@ - @@ -214,6 +217,8 @@ + + diff --git a/programs/nstool/nstool.vcxproj.filters b/programs/nstool/nstool.vcxproj.filters index 11e8b67..3d906bf 100644 --- a/programs/nstool/nstool.vcxproj.filters +++ b/programs/nstool/nstool.vcxproj.filters @@ -30,9 +30,6 @@ Header Files - - Header Files - Header Files @@ -66,6 +63,12 @@ Header Files + + Header Files + + + Header Files + Header Files @@ -98,9 +101,6 @@ Source Files - - Source Files - Source Files @@ -134,6 +134,12 @@ Source Files + + Source Files + + + Source Files + Source Files