Merge pull request #40 from jakcron/nstool-esfiletypes

Implement nstool support for .tik and .cert
This commit is contained in:
Jack 2018-08-06 20:26:44 +08:00 committed by GitHub
commit af94654a2d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
42 changed files with 1684 additions and 320 deletions

View file

@ -10,8 +10,10 @@
"/usr/include",
"${workspaceRoot}",
"${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"

View file

@ -27,7 +27,14 @@
"memory": "cpp",
"tuple": "cpp",
"__locale": "cpp",
"cinttypes": "cpp"
"cinttypes": "cpp",
"__bit_reference": "cpp",
"algorithm": "cpp",
"__functional_base_03": "cpp",
"__tuple": "cpp",
"chrono": "cpp",
"functional": "cpp",
"limits": "cpp",
"ratio": "cpp"
}
}

View file

@ -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)

View file

@ -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}

View file

@ -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

View file

@ -121,6 +121,7 @@
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="include\crypto\aes.h" />
<ClInclude Include="include\crypto\base64.h" />
<ClInclude Include="include\crypto\ecdsa.h" />
<ClInclude Include="include\crypto\rsa.h" />
<ClInclude Include="include\crypto\sha.h" />
@ -137,6 +138,7 @@
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\aes_wrapper.cpp" />
<ClCompile Include="source\base64_wrapper.cpp" />
<ClCompile Include="source\libpolarssl\source\aes.c" />
<ClCompile Include="source\libpolarssl\source\base64.c" />
<ClCompile Include="source\libpolarssl\source\bignum.c" />

View file

@ -66,6 +66,9 @@
<ClInclude Include="include\crypto\ecdsa.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
<ClInclude Include="include\crypto\base64.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\aes_wrapper.cpp">
@ -101,6 +104,9 @@
<ClCompile Include="source\libpolarssl\source\sha2.c">
<Filter>Source Files\polarssl</Filter>
</ClCompile>
<ClCompile Include="source\base64_wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />

View file

@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#include <cstring>
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);
}
}

View file

@ -0,0 +1,30 @@
#include <crypto/base64.h>
#include <polarssl/base64.h>
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);
}

View file

@ -122,21 +122,14 @@
<None Include="makefile" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\CertificateBody.cpp" />
<ClCompile Include="source\SectionHeader_V2.cpp" />
<ClCompile Include="source\SignatureBlock.cpp" />
<ClCompile Include="source\TicketBody_V2.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\es\cert.h" />
<ClInclude Include="include\es\CertificateBody.h" />
<ClInclude Include="include\es\SectionHeader_V2.h" />
<ClInclude Include="include\es\sign.h" />
<ClInclude Include="include\es\SignatureBlock.h" />
<ClInclude Include="include\es\SignedData.h" />
<ClInclude Include="include\es\ticket.h" />
<ClInclude Include="include\es\TicketBody_V2.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\SectionHeader_V2.cpp" />
<ClCompile Include="source\TicketBody_V2.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -18,38 +18,9 @@
<None Include="makefile" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\CertificateBody.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SectionHeader_V2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SignatureBlock.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\TicketBody_V2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\es\cert.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\CertificateBody.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\SectionHeader_V2.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\sign.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\SignatureBlock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\SignedData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\es\ticket.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -57,4 +28,12 @@
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\SectionHeader_V2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\TicketBody_V2.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -1,6 +1,7 @@
#pragma once
#include <string>
#include <fnd/ISerialisable.h>
#include <fnd/List.h>
#include <es/ticket.h>
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<es::ticket::PropertyMaskFlags>& getPropertyFlags() const;
void setPropertyFlags(const fnd::List<es::ticket::PropertyMaskFlags>& 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<es::ticket::PropertyMaskFlags> mPropertyFlags;
byte_t mReservedRegion[ticket::kReservedRegionSize]; // explicitly reserved
uint64_t mTicketId;
uint64_t mDeviceId;

View file

@ -26,9 +26,8 @@ void es::TicketBody_V2::operator=(const TicketBody_V2 & other)
mEncType = other.mEncType;
mTicketVersion = other.mTicketVersion;
mLicenseType = other.mLicenseType;
mPreInstall = other.mPreInstall;
mSharedTitle = other.mSharedTitle;
mAllowAllContent = other.mAllowAllContent;
mCommonKeyId = other.mCommonKeyId;
mPropertyFlags = other.mPropertyFlags;
memcpy(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize);
mTicketId = other.mTicketId;
mDeviceId = other.mDeviceId;
@ -48,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) \
@ -74,19 +71,22 @@ 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;
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);
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);
@ -112,14 +112,17 @@ 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();
mLicenseType = (ticket::LicenseType)body->license_type;
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);
mCommonKeyId = body->common_key_id;
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();
@ -144,9 +147,8 @@ void es::TicketBody_V2::clear()
mEncType = ticket::AES128_CBC;
mTicketVersion = 0;
mLicenseType = ticket::LICENSE_PERMANENT;
mPreInstall = false;
mSharedTitle = false;
mAllowAllContent = false;
mCommonKeyId = 0;
mPropertyFlags.clear();
memset(mReservedRegion, 0, ticket::kReservedRegionSize);
mTicketId = 0;
mDeviceId = 0;
@ -224,34 +226,14 @@ void es::TicketBody_V2::setCommonKeyId(byte_t id)
mCommonKeyId = id;
}
bool es::TicketBody_V2::isPreInstall() const
const fnd::List<es::ticket::PropertyMaskFlags>& es::TicketBody_V2::getPropertyFlags() const
{
return mPreInstall;
return mPropertyFlags;
}
void es::TicketBody_V2::setIsPreInstall(bool isPreInstall)
void es::TicketBody_V2::setPropertyFlags(const fnd::List<es::ticket::PropertyMaskFlags>& 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

View file

@ -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++)
{

View file

@ -1,9 +1,9 @@
#pragma once
#include <string>
#include <fnd/ISerialisable.h>
#include <es/cert.h>
#include <pki/cert.h>
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;

View file

@ -0,0 +1,14 @@
#pragma once
#include <pki/sign.h>
#include <crypto/sha.h>
namespace pki
{
namespace sign
{
pki::sign::SignatureAlgo getSignatureAlgo(pki::sign::SignatureId sign_id);
pki::sign::HashAlgo getHashAlgo(pki::sign::SignatureId sign_id);
}
}

View file

@ -1,9 +1,9 @@
#pragma once
#include <string>
#include <fnd/ISerialisable.h>
#include <es/sign.h>
#include <pki/sign.h>
namespace es
namespace pki
{
class SignatureBlock
: public fnd::ISerialisable
@ -24,8 +24,8 @@ namespace es
// variables
void clear();
es::sign::SignType getSignType() const;
void setSignType(es::sign::SignType 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<byte_t> mRawBinary;
// variables
es::sign::SignType mSignType;
pki::sign::SignatureId mSignType;
bool mIsLittleEndian;
fnd::Vec<byte_t> mSignature;
};

View file

@ -1,9 +1,9 @@
#pragma once
#include <string>
#include <fnd/ISerialisable.h>
#include <es/SignatureBlock.h>
#include <pki/SignatureBlock.h>
namespace es
namespace pki
{
template <class T>
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 <class T>
inline const es::SignatureBlock& SignedData<T>::getSignature() const
inline const pki::SignatureBlock& SignedData<T>::getSignature() const
{
return mSignature;
}

View file

@ -5,7 +5,7 @@
#include <crypto/rsa.h>
#include <crypto/ecdsa.h>
namespace es
namespace pki
{
namespace cert
{

View file

@ -5,21 +5,37 @@
#include <crypto/rsa.h>
#include <crypto/ecdsa.h>
namespace es
namespace pki
{
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;
static const std::string kRootIssuerStr = "Root";
static const std::string kIdentDelimiter = "-";
}
#pragma pack(push,1)
struct sRsa4096SignBlock

144
lib/libpki/libpki.vcxproj Normal file
View file

@ -0,0 +1,144 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>15.0</VCProjectVersion>
<ProjectGuid>{B9113734-6E84-44FF-8CF7-58199AA815C5}</ProjectGuid>
<RootNamespace>libpki</RootNamespace>
<WindowsTargetPlatformVersion>10.0.16299.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v141</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libpki\include;</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libpki\include;</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libpki\include;</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\libfnd\include;..\libcrypto\include;..\libpki\include;</AdditionalIncludeDirectories>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="include\pki\cert.h" />
<ClInclude Include="include\pki\CertificateBody.h" />
<ClInclude Include="include\pki\sign.h" />
<ClInclude Include="include\pki\SignatureBlock.h" />
<ClInclude Include="include\pki\SignedData.h" />
<ClInclude Include="include\pki\SignUtils.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\CertificateBody.cpp" />
<ClCompile Include="source\SignatureBlock.cpp" />
<ClCompile Include="source\SignUtils.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="include\pki\cert.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\pki\CertificateBody.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\pki\sign.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\pki\SignatureBlock.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\pki\SignedData.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="include\pki\SignUtils.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\CertificateBody.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SignatureBlock.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SignUtils.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

47
lib/libpki/makefile Normal file
View file

@ -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)

View file

@ -1,16 +1,16 @@
#include <es/CertificateBody.h>
#include <pki/CertificateBody.h>
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();
@ -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
@ -155,13 +155,13 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size)
}
}
const fnd::Vec<byte_t>& es::CertificateBody::getBytes() const
const fnd::Vec<byte_t>& 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;
}

View file

@ -0,0 +1,45 @@
#include <pki/SignUtils.h>
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;
}

View file

@ -1,16 +1,16 @@
#include <es/SignatureBlock.h>
#include <pki/SignatureBlock.h>
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,37 +18,37 @@ 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;
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;
@ -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();
@ -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,50 +133,50 @@ 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);
}
const fnd::Vec<byte_t>& es::SignatureBlock::getBytes() const
const fnd::Vec<byte_t>& pki::SignatureBlock::getBytes() const
{
return mRawBinary;
}
void es::SignatureBlock::clear()
void pki::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
pki::sign::SignatureId pki::SignatureBlock::getSignType() const
{
return mSignType;
}
void es::SignatureBlock::setSignType(es::sign::SignType 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<byte_t>& es::SignatureBlock::getSignature() const
const fnd::Vec<byte_t>& pki::SignatureBlock::getSignature() const
{
return mSignature;
}
void es::SignatureBlock::setSignature(const fnd::Vec<byte_t>& signature)
void pki::SignatureBlock::setSignature(const fnd::Vec<byte_t>& signature)
{
mSignature = signature;
}

View file

@ -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

View file

@ -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 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")

View file

@ -90,7 +90,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -105,7 +105,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -122,7 +122,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -141,7 +141,7 @@
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<AdditionalIncludeDirectories>..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
@ -157,6 +157,9 @@
<ProjectReference Include="..\..\lib\libcrypto\crypto.vcxproj">
<Project>{6adbb60d-dba0-411d-bd2d-a355ef8e0fe1}</Project>
</ProjectReference>
<ProjectReference Include="..\..\lib\libes\es.vcxproj">
<Project>{7be99936-0d40-410d-944b-4513c2eff8dc}</Project>
</ProjectReference>
<ProjectReference Include="..\..\lib\libfnd\fnd.vcxproj">
<Project>{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}</Project>
</ProjectReference>
@ -166,12 +169,19 @@
<ProjectReference Include="..\..\lib\libnx\nx.vcxproj">
<Project>{91ba9e79-8242-4f7d-b997-0dfec95ea22b}</Project>
</ProjectReference>
<ProjectReference Include="..\..\lib\libpki\libpki.vcxproj">
<Project>{b9113734-6e84-44ff-8cf7-58199aa815c5}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\AesCtrWrappedIFile.h" />
<ClInclude Include="source\AssetProcess.h" />
<ClInclude Include="source\CnmtProcess.h" />
<ClInclude Include="source\ElfSymbolParser.h" />
<ClInclude Include="source\EsTikProcess.h" />
<ClInclude Include="source\HashTreeMeta.h" />
<ClInclude Include="source\HashTreeWrappedIFile.h" />
<ClInclude Include="source\NacpProcess.h" />
@ -182,6 +192,8 @@
<ClInclude Include="source\nstool.h" />
<ClInclude Include="source\OffsetAdjustedIFile.h" />
<ClInclude Include="source\PfsProcess.h" />
<ClInclude Include="source\PkiCertProcess.h" />
<ClInclude Include="source\PkiValidator.h" />
<ClInclude Include="source\RoMetadataProcess.h" />
<ClInclude Include="source\RomfsProcess.h" />
<ClInclude Include="source\SdkApiString.h" />
@ -194,6 +206,7 @@
<ClCompile Include="source\AssetProcess.cpp" />
<ClCompile Include="source\CnmtProcess.cpp" />
<ClCompile Include="source\ElfSymbolParser.cpp" />
<ClCompile Include="source\EsTikProcess.cpp" />
<ClCompile Include="source\HashTreeMeta.cpp" />
<ClCompile Include="source\HashTreeWrappedIFile.cpp" />
<ClCompile Include="source\main.cpp" />
@ -204,15 +217,14 @@
<ClCompile Include="source\NsoProcess.cpp" />
<ClCompile Include="source\OffsetAdjustedIFile.cpp" />
<ClCompile Include="source\PfsProcess.cpp" />
<ClCompile Include="source\PkiCertProcess.cpp" />
<ClCompile Include="source\PkiValidator.cpp" />
<ClCompile Include="source\RoMetadataProcess.cpp" />
<ClCompile Include="source\RomfsProcess.cpp" />
<ClCompile Include="source\SdkApiString.cpp" />
<ClCompile Include="source\UserSettings.cpp" />
<ClCompile Include="source\XciProcess.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>

View file

@ -15,21 +15,69 @@
</Filter>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="source\AesCtrWrappedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\AssetProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\CnmtProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\ElfSymbolParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\EsTikProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\HashTreeMeta.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\HashTreeWrappedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NacpProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NcaProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NpdmProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NroProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NsoProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\nstool.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\OffsetAdjustedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\PfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\PkiCertProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\PkiValidator.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\RoMetadataProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\RomfsProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\UserSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -39,103 +87,73 @@
<ClInclude Include="source\XciProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\AesCtrWrappedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\OffsetAdjustedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\CnmtProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\HashTreeMeta.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\HashTreeWrappedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NsoProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\AssetProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NroProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\NacpProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\ElfSymbolParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\RoMetadataProcess.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="source\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NpdmProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\PfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\UserSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\XciProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\RomfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\AesCtrWrappedIFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NcaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\OffsetAdjustedIFile.cpp">
<ClCompile Include="source\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\CnmtProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\ElfSymbolParser.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\EsTikProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\HashTreeMeta.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\HashTreeWrappedIFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NsoProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NroProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SdkApiString.cpp">
<ClCompile Include="source\main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NacpProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\ElfSymbolParser.cpp">
<ClCompile Include="source\NcaProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NpdmProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NroProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\NsoProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\OffsetAdjustedIFile.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\PfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\PkiCertProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\PkiValidator.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\RoMetadataProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="makefile" />
<ClCompile Include="source\RomfsProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\SdkApiString.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\UserSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\XciProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -0,0 +1,269 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <pki/SignUtils.h>
#include "OffsetAdjustedIFile.h"
#include "EsTikProcess.h"
#include "PkiValidator.h"
EsTikProcess::EsTikProcess() :
mFile(nullptr),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false)
{
}
EsTikProcess::~EsTikProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void EsTikProcess::process()
{
importTicket();
if (mVerify)
verifyTicket();
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::setCertificateChain(const fnd::List<pki::SignedData<pki::CertificateBody>>& certs)
{
mCerts = certs;
}
void EsTikProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
void EsTikProcess::setVerifyMode(bool 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()
{
#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();
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);
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)
{
std::cout << " Flags:" << std::endl;
for (size_t i = 0; i < body.getPropertyFlags().size(); i++)
{
std::cout << " " << getPropertyFlagStr(body.getPropertyFlags()[i]) << std::endl;
}
}
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);
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
#undef _HEXDUMP_U
#undef _SPLIT_VER
}
const char* EsTikProcess::getSignTypeStr(uint32_t type) const
{
const char* str = nullptr;
switch(type)
{
case (pki::sign::SIGN_ID_RSA4096_SHA1):
str = "RSA4096-SHA1";
break;
case (pki::sign::SIGN_ID_RSA2048_SHA1):
str = "RSA2048-SHA1";
break;
case (pki::sign::SIGN_ID_ECDSA240_SHA1):
str = "ECDSA240-SHA1";
break;
case (pki::sign::SIGN_ID_RSA4096_SHA256):
str = "RSA4096-SHA256";
break;
case (pki::sign::SIGN_ID_RSA2048_SHA256):
str = "RSA2048-SHA256";
break;
case (pki::sign::SIGN_ID_ECDSA240_SHA256):
str = "ECDSA240-SHA256";
break;
default:
str = "Unknown";
break;
}
return str;
}
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;
}

View file

@ -0,0 +1,45 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/Vec.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <es/TicketBody_V2.h>
#include "nstool.h"
class EsTikProcess
{
public:
EsTikProcess();
~EsTikProcess();
void process();
void setInputFile(fnd::IFile* file, bool ownIFile);
void setKeyset(const sKeyset* keyset);
void setCertificateChain(const fnd::List<pki::SignedData<pki::CertificateBody>>& certs);
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;
fnd::List<pki::SignedData<pki::CertificateBody>> mCerts;
pki::SignedData<es::TicketBody_V2> mTik;
void importTicket();
void verifyTicket();
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;
};

View file

@ -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
{

View file

@ -0,0 +1,213 @@
#include <iostream>
#include <iomanip>
#include <fnd/SimpleTextOutput.h>
#include <pki/SignUtils.h>
#include "OffsetAdjustedIFile.h"
#include "PkiCertProcess.h"
#include "PkiValidator.h"
PkiCertProcess::PkiCertProcess() :
mFile(nullptr),
mOwnIFile(false),
mCliOutputMode(_BIT(OUTPUT_BASIC)),
mVerify(false)
{
}
PkiCertProcess::~PkiCertProcess()
{
if (mOwnIFile)
{
delete mFile;
}
}
void PkiCertProcess::process()
{
if (mFile == nullptr)
{
throw fnd::Exception(kModuleName, "No file reader set.");
}
importCerts();
if (mVerify)
validateCerts();
if (_HAS_BIT(mCliOutputMode, OUTPUT_BASIC))
displayCerts();
}
void PkiCertProcess::setInputFile(fnd::IFile* file, bool ownIFile)
{
mFile = file;
mOwnIFile = ownIFile;
}
void PkiCertProcess::setKeyset(const sKeyset* keyset)
{
mKeyset = keyset;
}
void PkiCertProcess::setCliOutputMode(CliOutputMode mode)
{
mCliOutputMode = mode;
}
void PkiCertProcess::setVerifyMode(bool verify)
{
mVerify = verify;
}
void PkiCertProcess::importCerts()
{
fnd::Vec<byte_t> scratch;
scratch.alloc(mFile->size());
mFile->read(scratch.data(), 0, scratch.size());
pki::SignedData<pki::CertificateBody> 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 PkiCertProcess::validateCerts()
{
PkiValidator pki;
try
{
pki.setRootKey(mKeyset->pki.root_sign_key);
pki.addCertificates(mCert);
}
catch (const fnd::Exception& e)
{
std::cout << "[WARNING] " << e.error() << std::endl;
return;
}
}
void PkiCertProcess::displayCerts()
{
for (size_t i = 0; i < mCert.size(); i++)
{
displayCert(mCert[i]);
}
}
void PkiCertProcess::displayCert(const pki::SignedData<pki::CertificateBody>& 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 << "[NNPKI Certificate]" << std::endl;
std::cout << " SignType " << getSignTypeStr(cert.getSignature().getSignType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
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: " << getPublicKeyTypeStr(cert.getBody().getPublicKeyType());
if (_HAS_BIT(mCliOutputMode, OUTPUT_EXTENDED))
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() == pki::cert::RSA4096)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " Modulus:" << std::endl;
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);
}
else if (cert.getBody().getPublicKeyType() == pki::cert::RSA2048)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " Public Exponent:" << std::endl;
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);
}
else if (cert.getBody().getPublicKeyType() == pki::cert::ECDSA240)
{
std::cout << " PublicKey:" << std::endl;
std::cout << " R:" << std::endl;
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, getHexDumpLen(crypto::ecdsa::kEcdsa240Size), 0x10, 6);
}
#undef _HEXDUMP_L
#undef _HEXDUMP_U
#undef _SPLIT_VER
}
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)
{
case (pki::sign::SIGN_ID_RSA4096_SHA1):
str = "RSA4096-SHA1";
break;
case (pki::sign::SIGN_ID_RSA2048_SHA1):
str = "RSA2048-SHA1";
break;
case (pki::sign::SIGN_ID_ECDSA240_SHA1):
str = "ECDSA240-SHA1";
break;
case (pki::sign::SIGN_ID_RSA4096_SHA256):
str = "RSA4096-SHA256";
break;
case (pki::sign::SIGN_ID_RSA2048_SHA256):
str = "RSA2048-SHA256";
break;
case (pki::sign::SIGN_ID_ECDSA240_SHA256):
str = "ECDSA240-SHA256";
break;
default:
str = "Unknown";
break;
}
return str;
}
const char* PkiCertProcess::getEndiannessStr(bool isLittleEndian) const
{
return isLittleEndian ? "LittleEndian" : "BigEndian";
}
const char* PkiCertProcess::getPublicKeyTypeStr(pki::cert::PublicKeyType type) const
{
const char* str;
switch (type)
{
case (pki::cert::RSA4096):
str = "RSA4096";
break;
case (pki::cert::RSA2048):
str = "RSA2048";
break;
case (pki::cert::ECDSA240):
str = "ECDSA240";
break;
default:
str = "Unknown";
break;
}
return str;
}

View file

@ -0,0 +1,45 @@
#pragma once
#include <string>
#include <fnd/types.h>
#include <fnd/IFile.h>
#include <fnd/List.h>
#include <fnd/Vec.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include "nstool.h"
class PkiCertProcess
{
public:
PkiCertProcess();
~PkiCertProcess();
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 = "PkiCertProcess";
static const size_t kSmallHexDumpLen = 0x10;
fnd::IFile* mFile;
bool mOwnIFile;
const sKeyset* mKeyset;
CliOutputMode mCliOutputMode;
bool mVerify;
fnd::List<pki::SignedData<pki::CertificateBody>> mCert;
void importCerts();
void validateCerts();
void displayCerts();
void displayCert(const pki::SignedData<pki::CertificateBody>& 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;
};

View file

@ -0,0 +1,197 @@
#include "PkiValidator.h"
#include <iostream>
#include <iomanip>
#include <sstream>
#include <pki/SignUtils.h>
PkiValidator::PkiValidator()
{
clearCertificates();
}
void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key)
{
// save a copy of the certificate bank
fnd::List<pki::SignedData<pki::CertificateBody>> 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<pki::SignedData<pki::CertificateBody>>& certs)
{
for (size_t i = 0; i < certs.size(); i++)
{
addCertificate(certs[i]);
}
}
void PkiValidator::addCertificate(const pki::SignedData<pki::CertificateBody>& cert)
{
std::string cert_ident;
pki::sign::SignatureAlgo cert_sign_algo;
pki::sign::HashAlgo cert_hash_algo;
fnd::Vec<byte_t> cert_hash;
try
{
makeCertIdent(cert, cert_ident);
if (doesCertExist(cert_ident) == true)
{
throw fnd::Exception(kModuleName, "Certificate already exists");
}
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 (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 (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;
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)
{
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, pki::sign::SignatureId signature_id, const fnd::Vec<byte_t>& signature, const fnd::Vec<byte_t>& hash) const
{
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 == pki::sign::kRootIssuerStr)
{
if (sign_algo != pki::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 pki::CertificateBody& issuer_cert = getCert(issuer).getBody();
pki::cert::PublicKeyType issuer_pubk_type = issuer_cert.getPublicKeyType();
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 == 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 == pki::cert::ECDSA240 && sign_algo == pki::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 pki::SignedData<pki::CertificateBody>& 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 + pki::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 pki::SignedData<pki::CertificateBody>& 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(pki::sign::HashAlgo hash_algo) const
{
crypto::sha::HashType hash_type = crypto::sha::HASH_SHA1;
switch (hash_algo)
{
case (pki::sign::HASH_ALGO_SHA1):
hash_type = crypto::sha::HASH_SHA1;
break;
case (pki::sign::HASH_ALGO_SHA256):
hash_type = crypto::sha::HASH_SHA256;
break;
};
return hash_type;
}

View file

@ -0,0 +1,34 @@
#pragma once
#include <fnd/types.h>
#include <fnd/List.h>
#include <fnd/Vec.h>
#include <crypto/rsa.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <string>
class PkiValidator
{
public:
PkiValidator();
void setRootKey(const crypto::rsa::sRsa4096Key& root_key);
void addCertificates(const fnd::List<pki::SignedData<pki::CertificateBody>>& certs);
void addCertificate(const pki::SignedData<pki::CertificateBody>& cert);
void clearCertificates();
void validateSignature(const std::string& issuer, pki::sign::SignatureId signature_id, const fnd::Vec<byte_t>& signature, const fnd::Vec<byte_t>& hash) const;
private:
const std::string kModuleName = "NNPkiValidator";
crypto::rsa::sRsa4096Key mRootKey;
fnd::List<pki::SignedData<pki::CertificateBody>> mCertificateBank;
void makeCertIdent(const pki::SignedData<pki::CertificateBody>& 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 pki::SignedData<pki::CertificateBody>& getCert(const std::string& ident) const;
crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(pki::sign::HashAlgo hash_algo) const;
};

View file

@ -1,5 +1,6 @@
#include "UserSettings.h"
#include "version.h"
#include "PkiValidator.h"
#include <vector>
#include <string>
#include <algorithm>
@ -22,6 +23,10 @@
#include <nx/nso.h>
#include <nx/nro.h>
#include <nx/aset.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <pki/SignUtils.h>
#include <es/TicketBody_V2.h>
UserSettings::UserSettings()
{}
@ -41,34 +46,36 @@ void UserSettings::showHelp()
printf("Usage: nstool [options... ] <file>\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]\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 <dir> --logo <dir> --normal <dir> --secure <dir>] <.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 <dir>] <file>\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 <key> --titlekey <key>] [--part0 <dir> ...] <.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(" --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");
printf(" --part3 Extract \"partition 3\" to directory.\n");
printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n");
printf(" nstool [--listapi --listsym] [--insttype <inst. type>] <file>\n");
printf(" --listapi Print SDK API List.\n");
@ -182,6 +189,11 @@ const sOptional<std::string>& UserSettings::getAssetNacpPath() const
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)
{
// show help text
@ -296,6 +308,18 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& 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] == "--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.");
@ -415,6 +439,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"};
@ -484,17 +509,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)
@ -521,6 +550,79 @@ 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
if (args.ticket_path.isSet)
{
fnd::SimpleFile tik_file;
fnd::Vec<byte_t> tik_raw;
pki::SignedData<es::TicketBody_V2> 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<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)
{
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
@ -688,6 +790,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_PKI_CERT;
else if (str == "tik")
type = FILE_ES_TIK;
else if (str == "aset" || str == "asset")
type = FILE_HB_ASSET;
else
@ -746,6 +852,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 pki certificate
else if (determineValidEsCertFromSample(scratch))
file_type = FILE_PKI_CERT;
// test 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;
@ -848,6 +960,50 @@ bool UserSettings::determineValidNacpFromSample(const fnd::Vec<byte_t>& sample)
return true;
}
bool UserSettings::determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const
{
pki::SignatureBlock sign;
try
{
sign.fromBytes(sample.data(), sample.size());
}
catch (...)
{
return false;
}
if (sign.isLittleEndian() == true)
return false;
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;
}
bool UserSettings::determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const
{
pki::SignatureBlock sign;
try
{
sign.fromBytes(sample.data(), sample.size());
}
catch (...)
{
return false;
}
if (sign.isLittleEndian() == false)
return false;
if (sign.getSignType() != pki::sign::SIGN_ID_RSA2048_SHA256)
return false;
return true;
}
nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str)
{
std::string str = type_str;

View file

@ -3,6 +3,9 @@
#include <string>
#include <fnd/types.h>
#include <fnd/Vec.h>
#include <fnd/List.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <nx/npdm.h>
#include "nstool.h"
@ -39,6 +42,7 @@ public:
const sOptional<std::string>& getNcaPart3Path() const;
const sOptional<std::string>& getAssetIconPath() const;
const sOptional<std::string>& getAssetNacpPath() const;
const fnd::List<pki::SignedData<pki::CertificateBody>>& getCertificateChain() const;
private:
const std::string kModuleName = "UserSettings";
@ -62,6 +66,8 @@ private:
sOptional<std::string> fs_path;
sOptional<std::string> nca_titlekey;
sOptional<std::string> nca_bodykey;
sOptional<std::string> ticket_path;
sOptional<std::string> cert_path;
sOptional<std::string> part0_path;
sOptional<std::string> part1_path;
sOptional<std::string> part2_path;
@ -94,6 +100,8 @@ private:
sOptional<std::string> mAssetIconPath;
sOptional<std::string> mAssetNacpPath;
fnd::List<pki::SignedData<pki::CertificateBody>> mCertChain;
bool mListApi;
bool mListSymbols;
nx::npdm::InstructionType mInstructionType;
@ -107,5 +115,7 @@ private:
bool determineValidNcaFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidCnmtFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidNacpFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidEsCertFromSample(const fnd::Vec<byte_t>& sample) const;
bool determineValidEsTikFromSample(const fnd::Vec<byte_t>& sample) const;
nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
};

View file

@ -11,6 +11,8 @@
#include "NsoProcess.h"
#include "NroProcess.h"
#include "NacpProcess.h"
#include "PkiCertProcess.h"
#include "EsTikProcess.h"
#include "AssetProcess.h"
#ifdef _WIN32
@ -173,6 +175,29 @@ int main(int argc, char** argv)
nacp.process();
}
else if (user_set.getFileType() == FILE_PKI_CERT)
{
PkiCertProcess 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.setCertificateChain(user_set.getCertificateChain());
tik.setCliOutputMode(user_set.getCliOutputMode());
tik.setVerifyMode(user_set.isVerifyFile());
tik.process();
}
else if (user_set.getFileType() == FILE_HB_ASSET)
{
AssetProcess obj;

View file

@ -27,6 +27,8 @@ enum FileType
FILE_NSO,
FILE_NRO,
FILE_NACP,
FILE_PKI_CERT,
FILE_ES_TIK,
FILE_HB_ASSET,
FILE_INVALID = -1,
};
@ -62,7 +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];
@ -84,12 +85,17 @@ struct sKeyset
crypto::rsa::sRsa2048Key header_sign_key;
crypto::aes::sAes128Key header_key;
} xci;
struct sTicketData
{
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)