[nstool] Add ticket validation via provided cert chain file.

This commit is contained in:
jakcron 2018-08-06 19:36:42 +08:00
parent 1266ba3976
commit 9fb6731294
5 changed files with 131 additions and 10 deletions

View file

@ -2,8 +2,10 @@
#include <iomanip> #include <iomanip>
#include <fnd/SimpleTextOutput.h> #include <fnd/SimpleTextOutput.h>
#include <pki/SignUtils.h>
#include "OffsetAdjustedIFile.h" #include "OffsetAdjustedIFile.h"
#include "EsTikProcess.h" #include "EsTikProcess.h"
#include "PkiValidator.h"
@ -25,17 +27,10 @@ EsTikProcess::~EsTikProcess()
void EsTikProcess::process() void EsTikProcess::process()
{ {
fnd::Vec<byte_t> scratch; importTicket();
if (mFile == nullptr) if (mVerify)
{ verifyTicket();
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)) if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayTicket(); displayTicket();
@ -52,6 +47,11 @@ void EsTikProcess::setKeyset(const sKeyset* keyset)
mKeyset = keyset; mKeyset = keyset;
} }
void EsTikProcess::setCertificateChain(const fnd::List<pki::SignedData<pki::CertificateBody>>& certs)
{
mCerts = certs;
}
void EsTikProcess::setCliOutputMode(CliOutputMode mode) void EsTikProcess::setCliOutputMode(CliOutputMode mode)
{ {
mCliOutputMode = mode; mCliOutputMode = mode;
@ -62,6 +62,48 @@ void EsTikProcess::setVerifyMode(bool verify)
mVerify = verify; mVerify = verify;
} }
void EsTikProcess::importTicket()
{
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
fnd::Vec<byte_t> 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<byte_t> 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() void EsTikProcess::displayTicket()
{ {
#define _SPLIT_VER(ver) ( (ver>>10) & 0x3f), ( (ver>>4) & 0x3f), ( (ver>>0) & 0xf) #define _SPLIT_VER(ver) ( (ver>>10) & 0x3f), ( (ver>>4) & 0x3f), ( (ver>>0) & 0xf)

View file

@ -4,6 +4,7 @@
#include <fnd/IFile.h> #include <fnd/IFile.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <pki/SignedData.h> #include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <es/TicketBody_V2.h> #include <es/TicketBody_V2.h>
#include "nstool.h" #include "nstool.h"
@ -17,6 +18,7 @@ public:
void setInputFile(fnd::IFile* file, bool ownIFile); void setInputFile(fnd::IFile* file, bool ownIFile);
void setKeyset(const sKeyset* keyset); void setKeyset(const sKeyset* keyset);
void setCertificateChain(const fnd::List<pki::SignedData<pki::CertificateBody>>& certs);
void setCliOutputMode(CliOutputMode mode); void setCliOutputMode(CliOutputMode mode);
void setVerifyMode(bool verify); void setVerifyMode(bool verify);
@ -29,8 +31,12 @@ private:
CliOutputMode mCliOutputMode; CliOutputMode mCliOutputMode;
bool mVerify; bool mVerify;
fnd::List<pki::SignedData<pki::CertificateBody>> mCerts;
pki::SignedData<es::TicketBody_V2> mTik; pki::SignedData<es::TicketBody_V2> mTik;
void importTicket();
void verifyTicket();
void displayTicket(); void displayTicket();
const char* getSignTypeStr(uint32_t type) const; const char* getSignTypeStr(uint32_t type) const;
const char* getTitleKeyPersonalisationStr(byte_t flag) const; const char* getTitleKeyPersonalisationStr(byte_t flag) const;

View file

@ -1,5 +1,6 @@
#include "UserSettings.h" #include "UserSettings.h"
#include "version.h" #include "version.h"
#include "PkiValidator.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
@ -23,6 +24,8 @@
#include <nx/nro.h> #include <nx/nro.h>
#include <nx/aset.h> #include <nx/aset.h>
#include <pki/SignedData.h> #include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <pki/SignUtils.h>
#include <es/TicketBody_V2.h> #include <es/TicketBody_V2.h>
UserSettings::UserSettings() UserSettings::UserSettings()
@ -185,6 +188,11 @@ const sOptional<std::string>& UserSettings::getAssetNacpPath() const
return mAssetNacpPath; return mAssetNacpPath;
} }
const fnd::List<pki::SignedData<pki::CertificateBody>>& UserSettings::getCertificateChain() const
{
return mCertChain;
}
void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args) void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args)
{ {
// show help text // show help text
@ -305,6 +313,12 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCm
cmd_args.ticket_path = arg_list[i+1]; 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") else if (arg_list[i] == "--part0")
{ {
if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); 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<byte_t> cert_raw;
pki::SignedData<pki::CertificateBody> 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 // get titlekey from ticket
if (args.ticket_path.isSet) if (args.ticket_path.isSet)
{ {
@ -542,11 +574,44 @@ void UserSettings::populateKeyset(sCmdArgs& args)
fnd::Vec<byte_t> tik_raw; fnd::Vec<byte_t> tik_raw;
pki::SignedData<es::TicketBody_V2> tik; pki::SignedData<es::TicketBody_V2> tik;
// open and import ticket
tik_file.open(args.ticket_path.var, fnd::SimpleFile::Read); tik_file.open(args.ticket_path.var, fnd::SimpleFile::Read);
tik_raw.alloc(tik_file.size()); tik_raw.alloc(tik_file.size());
tik_file.read(tik_raw.data(), tik_raw.size()); tik_file.read(tik_raw.data(), tik_raw.size());
tik.fromBytes(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<byte_t> 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) if (tik.getBody().getTitleKeyEncType() == es::ticket::AES128_CBC)
{ {
memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), crypto::aes::kAes128KeySize); memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), crypto::aes::kAes128KeySize);

View file

@ -3,6 +3,9 @@
#include <string> #include <string>
#include <fnd/types.h> #include <fnd/types.h>
#include <fnd/Vec.h> #include <fnd/Vec.h>
#include <fnd/List.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <nx/npdm.h> #include <nx/npdm.h>
#include "nstool.h" #include "nstool.h"
@ -39,6 +42,7 @@ public:
const sOptional<std::string>& getNcaPart3Path() const; const sOptional<std::string>& getNcaPart3Path() const;
const sOptional<std::string>& getAssetIconPath() const; const sOptional<std::string>& getAssetIconPath() const;
const sOptional<std::string>& getAssetNacpPath() const; const sOptional<std::string>& getAssetNacpPath() const;
const fnd::List<pki::SignedData<pki::CertificateBody>>& getCertificateChain() const;
private: private:
const std::string kModuleName = "UserSettings"; const std::string kModuleName = "UserSettings";
@ -63,6 +67,7 @@ private:
sOptional<std::string> nca_titlekey; sOptional<std::string> nca_titlekey;
sOptional<std::string> nca_bodykey; sOptional<std::string> nca_bodykey;
sOptional<std::string> ticket_path; sOptional<std::string> ticket_path;
sOptional<std::string> cert_path;
sOptional<std::string> part0_path; sOptional<std::string> part0_path;
sOptional<std::string> part1_path; sOptional<std::string> part1_path;
sOptional<std::string> part2_path; sOptional<std::string> part2_path;
@ -95,6 +100,8 @@ private:
sOptional<std::string> mAssetIconPath; sOptional<std::string> mAssetIconPath;
sOptional<std::string> mAssetNacpPath; sOptional<std::string> mAssetNacpPath;
fnd::List<pki::SignedData<pki::CertificateBody>> mCertChain;
bool mListApi; bool mListApi;
bool mListSymbols; bool mListSymbols;
nx::npdm::InstructionType mInstructionType; nx::npdm::InstructionType mInstructionType;

View file

@ -192,6 +192,7 @@ int main(int argc, char** argv)
tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE); tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
tik.setKeyset(&user_set.getKeyset()); tik.setKeyset(&user_set.getKeyset());
tik.setCertificateChain(user_set.getCertificateChain());
tik.setCliOutputMode(user_set.getCliOutputMode()); tik.setCliOutputMode(user_set.getCliOutputMode());
tik.setVerifyMode(user_set.isVerifyFile()); tik.setVerifyMode(user_set.isVerifyFile());