2018-07-29 12:01:11 +00:00
|
|
|
#include <iostream>
|
|
|
|
#include <iomanip>
|
2018-07-10 15:01:34 +00:00
|
|
|
#include <fnd/SimpleTextOutput.h>
|
2018-10-06 08:45:09 +00:00
|
|
|
#include <fnd/OffsetAdjustedIFile.h>
|
2018-08-07 07:17:51 +00:00
|
|
|
#include <nn/pki/SignUtils.h>
|
2018-07-10 15:01:34 +00:00
|
|
|
#include "EsTikProcess.h"
|
2018-08-06 11:36:42 +00:00
|
|
|
#include "PkiValidator.h"
|
2018-07-10 15:01:34 +00:00
|
|
|
|
2018-07-29 12:01:11 +00:00
|
|
|
|
|
|
|
|
2018-07-10 15:01:34 +00:00
|
|
|
EsTikProcess::EsTikProcess() :
|
2018-09-23 03:29:22 +00:00
|
|
|
mFile(),
|
2018-07-10 15:01:34 +00:00
|
|
|
mCliOutputMode(_BIT(OUTPUT_BASIC)),
|
|
|
|
mVerify(false)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void EsTikProcess::process()
|
|
|
|
{
|
2018-08-06 11:36:42 +00:00
|
|
|
importTicket();
|
2018-07-10 15:01:34 +00:00
|
|
|
|
2018-08-06 11:36:42 +00:00
|
|
|
if (mVerify)
|
|
|
|
verifyTicket();
|
2018-07-10 15:01:34 +00:00
|
|
|
|
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
|
|
|
|
displayTicket();
|
|
|
|
}
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
void EsTikProcess::setInputFile(const fnd::SharedPtr<fnd::IFile>& file)
|
2018-07-10 15:01:34 +00:00
|
|
|
{
|
|
|
|
mFile = file;
|
|
|
|
}
|
|
|
|
|
2018-08-21 12:03:19 +00:00
|
|
|
void EsTikProcess::setKeyCfg(const KeyConfiguration& keycfg)
|
2018-07-10 15:01:34 +00:00
|
|
|
{
|
2018-08-21 12:03:19 +00:00
|
|
|
mKeyCfg = keycfg;
|
2018-07-10 15:01:34 +00:00
|
|
|
}
|
|
|
|
|
2018-08-07 08:13:18 +00:00
|
|
|
void EsTikProcess::setCertificateChain(const fnd::List<nn::pki::SignedData<nn::pki::CertificateBody>>& certs)
|
2018-08-06 11:36:42 +00:00
|
|
|
{
|
|
|
|
mCerts = certs;
|
|
|
|
}
|
|
|
|
|
2018-07-10 15:01:34 +00:00
|
|
|
void EsTikProcess::setCliOutputMode(CliOutputMode mode)
|
|
|
|
{
|
|
|
|
mCliOutputMode = mode;
|
|
|
|
}
|
|
|
|
|
|
|
|
void EsTikProcess::setVerifyMode(bool verify)
|
|
|
|
{
|
|
|
|
mVerify = verify;
|
|
|
|
}
|
|
|
|
|
2018-08-06 11:36:42 +00:00
|
|
|
void EsTikProcess::importTicket()
|
|
|
|
{
|
2018-08-13 17:14:21 +00:00
|
|
|
fnd::Vec<byte_t> scratch;
|
|
|
|
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
if (*mFile == nullptr)
|
2018-08-06 11:36:42 +00:00
|
|
|
{
|
|
|
|
throw fnd::Exception(kModuleName, "No file reader set.");
|
|
|
|
}
|
|
|
|
|
2018-09-23 03:29:22 +00:00
|
|
|
scratch.alloc((*mFile)->size());
|
|
|
|
(*mFile)->read(scratch.data(), 0, scratch.size());
|
2018-08-06 11:36:42 +00:00
|
|
|
mTik.fromBytes(scratch.data(), scratch.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
void EsTikProcess::verifyTicket()
|
|
|
|
{
|
|
|
|
PkiValidator pki_validator;
|
|
|
|
fnd::Vec<byte_t> tik_hash;
|
|
|
|
|
2018-08-07 08:13:18 +00:00
|
|
|
switch (nn::pki::sign::getHashAlgo(mTik.getSignature().getSignType()))
|
2018-08-06 11:36:42 +00:00
|
|
|
{
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::HASH_ALGO_SHA1):
|
2018-08-07 08:35:03 +00:00
|
|
|
tik_hash.alloc(fnd::sha::kSha1HashLen);
|
|
|
|
fnd::sha::Sha1(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
|
2018-08-06 11:36:42 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::HASH_ALGO_SHA256):
|
2018-08-07 08:35:03 +00:00
|
|
|
tik_hash.alloc(fnd::sha::kSha256HashLen);
|
|
|
|
fnd::sha::Sha256(mTik.getBody().getBytes().data(), mTik.getBody().getBytes().size(), tik_hash.data());
|
2018-08-06 11:36:42 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
2018-08-21 12:03:19 +00:00
|
|
|
pki_validator.setKeyCfg(mKeyCfg);
|
2018-08-06 11:36:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-10 15:01:34 +00:00
|
|
|
void EsTikProcess::displayTicket()
|
|
|
|
{
|
2018-08-12 05:22:53 +00:00
|
|
|
#define _SPLIT_VER(ver) (uint32_t)((ver>>10) & 0x3f) << "." << (uint32_t)((ver>>4) & 0x3f) << "." << (uint32_t)((ver>>0) & 0xf)
|
2018-07-10 15:01:34 +00:00
|
|
|
|
2018-08-07 08:13:18 +00:00
|
|
|
const nn::es::TicketBody_V2& body = mTik.getBody();
|
2018-07-10 15:01:34 +00:00
|
|
|
|
2018-07-29 12:01:11 +00:00
|
|
|
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;
|
2018-08-24 11:22:23 +00:00
|
|
|
if (body.getTitleKeyEncType() == nn::es::ticket::RSA2048)
|
|
|
|
{
|
|
|
|
std::cout << " Data:" << std::endl;
|
|
|
|
for (size_t i = 0; i < 0x10; i++)
|
|
|
|
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey() + 0x10*i, 0x10, true, ":") << std::endl;
|
|
|
|
}
|
|
|
|
else if (body.getTitleKeyEncType() == nn::es::ticket::AES128_CBC)
|
|
|
|
{
|
|
|
|
std::cout << " Data:" << std::endl;
|
|
|
|
std::cout << " " << fnd::SimpleTextOutput::arrayToString(body.getEncTitleKey(), 0x10, true, ":") << std::endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
std::cout << " Data: <cannot display>" << std::endl;
|
|
|
|
}
|
2018-07-10 15:01:34 +00:00
|
|
|
|
2018-08-12 05:22:53 +00:00
|
|
|
std::cout << " Version: v" << _SPLIT_VER(body.getTicketVersion());
|
2018-07-29 12:01:11 +00:00
|
|
|
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
|
2018-08-12 05:22:53 +00:00
|
|
|
std::cout << " (" << (uint32_t)body.getTicketVersion() << ")";
|
|
|
|
std::cout << std::endl;
|
2018-07-29 12:01:11 +00:00
|
|
|
|
|
|
|
std::cout << " License Type: " << getLicenseTypeStr(body.getLicenseType()) << std::endl;
|
2018-07-10 15:01:34 +00:00
|
|
|
|
|
|
|
if (body.getPropertyFlags().size() > 0)
|
|
|
|
{
|
2018-07-29 12:01:11 +00:00
|
|
|
std::cout << " Flags:" << std::endl;
|
2018-07-10 15:01:34 +00:00
|
|
|
for (size_t i = 0; i < body.getPropertyFlags().size(); i++)
|
|
|
|
{
|
2018-07-29 12:01:11 +00:00
|
|
|
std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl;
|
2018-07-10 15:01:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-29 12:01:11 +00:00
|
|
|
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 << " ";
|
2018-07-10 15:01:34 +00:00
|
|
|
fnd::SimpleTextOutput::hexDump(body.getRightsId(), 16);
|
|
|
|
|
2018-07-29 12:01:11 +00:00
|
|
|
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;
|
2018-07-10 15:01:34 +00:00
|
|
|
|
|
|
|
#undef _SPLIT_VER
|
|
|
|
}
|
|
|
|
|
2018-07-29 12:01:11 +00:00
|
|
|
const char* EsTikProcess::getSignTypeStr(uint32_t type) const
|
|
|
|
{
|
|
|
|
const char* str = nullptr;
|
|
|
|
switch(type)
|
|
|
|
{
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_RSA4096_SHA1):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "RSA4096-SHA1";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_RSA2048_SHA1):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "RSA2048-SHA1";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA1):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "ECDSA240-SHA1";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_RSA4096_SHA256):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "RSA4096-SHA256";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_RSA2048_SHA256):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "RSA2048-SHA256";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::pki::sign::SIGN_ID_ECDSA240_SHA256):
|
2018-07-29 19:18:02 +00:00
|
|
|
str = "ECDSA240-SHA256";
|
2018-07-29 12:01:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
str = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2018-07-10 15:01:34 +00:00
|
|
|
const char* EsTikProcess::getTitleKeyPersonalisationStr(byte_t flag) const
|
|
|
|
{
|
|
|
|
const char* str = nullptr;
|
|
|
|
switch(flag)
|
|
|
|
{
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::AES128_CBC):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Generic (AESCBC)";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::RSA2048):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Personalised (RSA2048)";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
str = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* EsTikProcess::getLicenseTypeStr(byte_t flag) const
|
|
|
|
{
|
|
|
|
const char* str = nullptr;
|
|
|
|
switch(flag)
|
|
|
|
{
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_PERMANENT):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Permanent";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_DEMO):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Demo";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_TRIAL):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Trial";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_RENTAL):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Rental";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_SUBSCRIPTION):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Subscription";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::LICENSE_SERVICE):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "Service";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
str = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* EsTikProcess::getPropertyFlagStr(byte_t flag) const
|
|
|
|
{
|
|
|
|
const char* str = nullptr;
|
|
|
|
switch(flag)
|
|
|
|
{
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::FLAG_PRE_INSTALL):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "PreInstall";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::FLAG_SHARED_TITLE):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "SharedTitle";
|
|
|
|
break;
|
2018-08-07 08:13:18 +00:00
|
|
|
case (nn::es::ticket::FLAG_ALLOW_ALL_CONTENT):
|
2018-07-10 15:01:34 +00:00
|
|
|
str = "AllContent";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
str = "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|