From 9fb67312945bf1a39d203ac265dc5727f3f987b8 Mon Sep 17 00:00:00 2001 From: jakcron Date: Mon, 6 Aug 2018 19:36:42 +0800 Subject: [PATCH] [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());