mirror of
https://github.com/jakcron/nstool.git
synced 2024-12-22 18:55:29 +00:00
[nstool] Add ticket validation via provided cert chain file.
This commit is contained in:
parent
1266ba3976
commit
9fb6731294
|
@ -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)
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -28,9 +30,13 @@ private:
|
||||||
const sKeyset* mKeyset;
|
const sKeyset* mKeyset;
|
||||||
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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue