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", "/usr/include",
"${workspaceRoot}", "${workspaceRoot}",
"${workspaceRoot}/lib/libcrypto/include", "${workspaceRoot}/lib/libcrypto/include",
"${workspaceRoot}/lib/libcrypto/source/polarssl/libinclude",
"${workspaceRoot}/lib/libcompress/include", "${workspaceRoot}/lib/libcompress/include",
"${workspaceRoot}/lib/libes/include", "${workspaceRoot}/lib/libes/include",
"${workspaceRoot}/lib/libpki/include",
"${workspaceRoot}/lib/libfnd/include", "${workspaceRoot}/lib/libfnd/include",
"${workspaceRoot}/lib/libnx/include", "${workspaceRoot}/lib/libnx/include",
"${workspaceRoot}/lib/libnx-hb/include" "${workspaceRoot}/lib/libnx-hb/include"

View file

@ -27,7 +27,14 @@
"memory": "cpp", "memory": "cpp",
"tuple": "cpp", "tuple": "cpp",
"__locale": "cpp", "__locale": "cpp",
"cinttypes": "cpp" "cinttypes": "cpp",
"__bit_reference": "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 Keys
ticket_commonkey_## : AES128 Key (0x10 bytes) 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 Keys
nca_header_key : AES128-XTS Key (0x20 bytes) nca_header_key : AES128-XTS Key (0x20 bytes)
nca_header_sign_key_modulus : RSA2048 Modulus (0x100 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 EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx-hb", "lib\libnx-hb\libnx-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}" Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx-hb", "lib\libnx-hb\libnx-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpki", "lib\libpki\libpki.vcxproj", "{B9113734-6E84-44FF-8CF7-58199AA815C5}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64 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|x64.Build.0 = Release|x64
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.ActiveCfg = Release|Win32 {738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.ActiveCfg = Release|Win32
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.Build.0 = 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 EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@ -120,6 +130,7 @@ Global
{AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298} {AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
{CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C} {CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {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 EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62} SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62}

View file

@ -10,12 +10,13 @@ Tools & Libraries for NX (Nintendo Switch).
# Libraries # Libraries
* __libfnd__ - Foundation library. * __libfnd__ - Foundation library.
* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls) * __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) * __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4)
* __libes__ - Handling of (NX relevant) eShop file type processing. (eTickets, etc) * __libpki__ - Processes Nintendo's proprietary PKI.
* __libnx__ - Handling of NX file types. * __libes__ - Processes Nintendo's eShop file types.
* __libnx-hb__ - Handling of NX (homebrew extensions) file types. * __libnx__ - Processes NX file types.
* __libnx-hb__ - Processes NX file types (homebrew extensions).
# Building # Building

View file

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

View file

@ -66,6 +66,9 @@
<ClInclude Include="include\crypto\ecdsa.h"> <ClInclude Include="include\crypto\ecdsa.h">
<Filter>Header Files\crypto</Filter> <Filter>Header Files\crypto</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="include\crypto\base64.h">
<Filter>Header Files\crypto</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="source\aes_wrapper.cpp"> <ClCompile Include="source\aes_wrapper.cpp">
@ -101,6 +104,9 @@
<ClCompile Include="source\libpolarssl\source\sha2.c"> <ClCompile Include="source\libpolarssl\source\sha2.c">
<Filter>Source Files\polarssl</Filter> <Filter>Source Files\polarssl</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\base64_wrapper.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="makefile" /> <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" /> <None Include="makefile" />
</ItemGroup> </ItemGroup>
<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\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\ticket.h" />
<ClInclude Include="include\es\TicketBody_V2.h" /> <ClInclude Include="include\es\TicketBody_V2.h" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<ClCompile Include="source\SectionHeader_V2.cpp" />
<ClCompile Include="source\TicketBody_V2.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets"> <ImportGroup Label="ExtensionTargets">
</ImportGroup> </ImportGroup>

View file

@ -18,38 +18,9 @@
<None Include="makefile" /> <None Include="makefile" />
</ItemGroup> </ItemGroup>
<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"> <ClInclude Include="include\es\SectionHeader_V2.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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"> <ClInclude Include="include\es\ticket.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -57,4 +28,12 @@
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
</ItemGroup> </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> </Project>

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fnd/ISerialisable.h> #include <fnd/ISerialisable.h>
#include <fnd/List.h>
#include <es/ticket.h> #include <es/ticket.h>
namespace es namespace es
@ -42,14 +43,8 @@ namespace es
byte_t getCommonKeyId() const; byte_t getCommonKeyId() const;
void setCommonKeyId(byte_t id); void setCommonKeyId(byte_t id);
bool isPreInstall() const; const fnd::List<es::ticket::PropertyMaskFlags>& getPropertyFlags() const;
void setIsPreInstall(bool isPreInstall); void setPropertyFlags(const fnd::List<es::ticket::PropertyMaskFlags>& flags);
bool isSharedTitle() const;
void setIsSharedTitle(bool isSharedTitle);
bool allowAllContent() const;
void setAllowAllContent(bool allowAllContent);
const byte_t* getReservedRegion() const; const byte_t* getReservedRegion() const;
void setReservedRegion(const byte_t* data, size_t len); void setReservedRegion(const byte_t* data, size_t len);
@ -91,9 +86,7 @@ namespace es
uint16_t mTicketVersion; uint16_t mTicketVersion;
ticket::LicenseType mLicenseType; ticket::LicenseType mLicenseType;
byte_t mCommonKeyId; byte_t mCommonKeyId;
bool mPreInstall; fnd::List<es::ticket::PropertyMaskFlags> mPropertyFlags;
bool mSharedTitle;
bool mAllowAllContent;
byte_t mReservedRegion[ticket::kReservedRegionSize]; // explicitly reserved byte_t mReservedRegion[ticket::kReservedRegionSize]; // explicitly reserved
uint64_t mTicketId; uint64_t mTicketId;
uint64_t mDeviceId; uint64_t mDeviceId;

View file

@ -26,9 +26,8 @@ void es::TicketBody_V2::operator=(const TicketBody_V2 & other)
mEncType = other.mEncType; mEncType = other.mEncType;
mTicketVersion = other.mTicketVersion; mTicketVersion = other.mTicketVersion;
mLicenseType = other.mLicenseType; mLicenseType = other.mLicenseType;
mPreInstall = other.mPreInstall; mCommonKeyId = other.mCommonKeyId;
mSharedTitle = other.mSharedTitle; mPropertyFlags = other.mPropertyFlags;
mAllowAllContent = other.mAllowAllContent;
memcpy(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize); memcpy(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize);
mTicketId = other.mTicketId; mTicketId = other.mTicketId;
mDeviceId = other.mDeviceId; mDeviceId = other.mDeviceId;
@ -48,9 +47,7 @@ bool es::TicketBody_V2::operator==(const TicketBody_V2 & other) const
&& (mEncType == other.mEncType) \ && (mEncType == other.mEncType) \
&& (mTicketVersion == other.mTicketVersion) \ && (mTicketVersion == other.mTicketVersion) \
&& (mLicenseType == other.mLicenseType) \ && (mLicenseType == other.mLicenseType) \
&& (mPreInstall == other.mPreInstall) \ && (mPropertyFlags == other.mPropertyFlags) \
&& (mSharedTitle == other.mSharedTitle) \
&& (mAllowAllContent == other.mAllowAllContent) \
&& (memcmp(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize) == 0) \ && (memcmp(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize) == 0) \
&& (mTicketId == other.mTicketId) \ && (mTicketId == other.mTicketId) \
&& (mDeviceId == other.mDeviceId) \ && (mDeviceId == other.mDeviceId) \
@ -74,19 +71,22 @@ void es::TicketBody_V2::toBytes()
body->format_version = (ticket::kFormatVersion); 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); memcpy(body->enc_title_key, mEncTitleKey, ticket::kEncTitleKeySize);
body->title_key_enc_type = (mEncType); body->title_key_enc_type = (mEncType);
body->ticket_version = (mTicketVersion); body->ticket_version = (mTicketVersion);
body->license_type = mLicenseType;
body->common_key_id = mCommonKeyId;
byte_t property_mask = 0; byte_t property_mask = 0;
property_mask |= mPreInstall ? _BIT(ticket::FLAG_PRE_INSTALL) : 0; for (size_t i = 0; i < mPropertyFlags.size(); i++)
property_mask |= mSharedTitle ? _BIT(ticket::FLAG_SHARED_TITLE) : 0; {
property_mask |= mAllowAllContent ? _BIT(ticket::FLAG_ALLOW_ALL_CONTENT) : 0; property_mask |= _BIT(mPropertyFlags[i]);
}
body->property_mask = (property_mask); body->property_mask = (property_mask);
memcpy(body->reserved_region, mReservedRegion, ticket::kReservedRegionSize); memcpy(body->reserved_region, mReservedRegion, ticket::kReservedRegionSize);
body->ticket_id = (mTicketId); body->ticket_id = (mTicketId);
body->device_id = (mDeviceId); body->device_id = (mDeviceId);
memcmp(body->rights_id, mRightsId, ticket::kRightsIdSize); memcpy(body->rights_id, mRightsId, ticket::kRightsIdSize);
body->account_id = (mAccountId); body->account_id = (mAccountId);
body->sect_total_size = (mSectTotalSize); body->sect_total_size = (mSectTotalSize);
body->sect_header_offset = (mSectHeaderOffset); 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"); 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); memcpy(mEncTitleKey, body->enc_title_key, ticket::kEncTitleKeySize);
mEncType = (ticket::TitleKeyEncType)body->title_key_enc_type; mEncType = (ticket::TitleKeyEncType)body->title_key_enc_type;
mTicketVersion = body->ticket_version.get(); mTicketVersion = body->ticket_version.get();
mLicenseType = (ticket::LicenseType)body->license_type; mLicenseType = (ticket::LicenseType)body->license_type;
mPreInstall = _HAS_BIT(body->property_mask, ticket::FLAG_PRE_INSTALL); mCommonKeyId = body->common_key_id;
mSharedTitle = _HAS_BIT(body->property_mask, ticket::FLAG_SHARED_TITLE); for (size_t i = 0; i < mPropertyFlags.size(); i++)
mAllowAllContent = _HAS_BIT(body->property_mask, ticket::FLAG_ALLOW_ALL_CONTENT); {
if (_HAS_BIT(body->property_mask, i))
mPropertyFlags.addElement((ticket::PropertyMaskFlags)i);
}
memcpy(mReservedRegion, body->reserved_region, ticket::kReservedRegionSize); memcpy(mReservedRegion, body->reserved_region, ticket::kReservedRegionSize);
mTicketId = body->ticket_id.get(); mTicketId = body->ticket_id.get();
mDeviceId = body->device_id.get(); mDeviceId = body->device_id.get();
@ -144,9 +147,8 @@ void es::TicketBody_V2::clear()
mEncType = ticket::AES128_CBC; mEncType = ticket::AES128_CBC;
mTicketVersion = 0; mTicketVersion = 0;
mLicenseType = ticket::LICENSE_PERMANENT; mLicenseType = ticket::LICENSE_PERMANENT;
mPreInstall = false; mCommonKeyId = 0;
mSharedTitle = false; mPropertyFlags.clear();
mAllowAllContent = false;
memset(mReservedRegion, 0, ticket::kReservedRegionSize); memset(mReservedRegion, 0, ticket::kReservedRegionSize);
mTicketId = 0; mTicketId = 0;
mDeviceId = 0; mDeviceId = 0;
@ -224,34 +226,14 @@ void es::TicketBody_V2::setCommonKeyId(byte_t id)
mCommonKeyId = 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; mPropertyFlags = flags;
}
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;
} }
const byte_t * es::TicketBody_V2::getReservedRegion() const 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"); 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; m_Size = new_size;
} }
@ -159,6 +164,10 @@ namespace fnd
{ {
fnd::Exception("Vec", "Failed to allocate memory for vector"); 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++) for (size_t i = 0; i < _MIN(m_Size, new_size); i++)
{ {

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fnd/ISerialisable.h> #include <fnd/ISerialisable.h>
#include <es/cert.h> #include <pki/cert.h>
namespace es namespace pki
{ {
class CertificateBody class CertificateBody
: public fnd::ISerialisable : public fnd::ISerialisable
@ -27,7 +27,7 @@ namespace es
const std::string& getIssuer() const; const std::string& getIssuer() const;
void setIssuer(const std::string& issuer); void setIssuer(const std::string& issuer);
es::cert::PublicKeyType getPublicKeyType() const; pki::cert::PublicKeyType getPublicKeyType() const;
void setPublicKeyType(cert::PublicKeyType type); void setPublicKeyType(cert::PublicKeyType type);
const std::string& getSubject() const; 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 #pragma once
#include <string> #include <string>
#include <fnd/ISerialisable.h> #include <fnd/ISerialisable.h>
#include <es/sign.h> #include <pki/sign.h>
namespace es namespace pki
{ {
class SignatureBlock class SignatureBlock
: public fnd::ISerialisable : public fnd::ISerialisable
@ -24,8 +24,8 @@ namespace es
// variables // variables
void clear(); void clear();
es::sign::SignType getSignType() const; pki::sign::SignatureId getSignType() const;
void setSignType(es::sign::SignType type); void setSignType(pki::sign::SignatureId type);
bool isLittleEndian() const; bool isLittleEndian() const;
void setLittleEndian(bool isLE); void setLittleEndian(bool isLE);
@ -41,7 +41,7 @@ namespace es
fnd::Vec<byte_t> mRawBinary; fnd::Vec<byte_t> mRawBinary;
// variables // variables
es::sign::SignType mSignType; pki::sign::SignatureId mSignType;
bool mIsLittleEndian; bool mIsLittleEndian;
fnd::Vec<byte_t> mSignature; fnd::Vec<byte_t> mSignature;
}; };

View file

@ -1,9 +1,9 @@
#pragma once #pragma once
#include <string> #include <string>
#include <fnd/ISerialisable.h> #include <fnd/ISerialisable.h>
#include <es/SignatureBlock.h> #include <pki/SignatureBlock.h>
namespace es namespace pki
{ {
template <class T> template <class T>
class SignedData class SignedData
@ -25,7 +25,7 @@ namespace es
// variables // variables
void clear(); void clear();
const es::SignatureBlock& getSignature() const; const pki::SignatureBlock& getSignature() const;
void setSignature(const SignatureBlock& signature); void setSignature(const SignatureBlock& signature);
const T& getBody() const; const T& getBody() const;
@ -111,7 +111,7 @@ namespace es
} }
template <class T> template <class T>
inline const es::SignatureBlock& SignedData<T>::getSignature() const inline const pki::SignatureBlock& SignedData<T>::getSignature() const
{ {
return mSignature; return mSignature;
} }

View file

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

View file

@ -5,21 +5,37 @@
#include <crypto/rsa.h> #include <crypto/rsa.h>
#include <crypto/ecdsa.h> #include <crypto/ecdsa.h>
namespace es namespace pki
{ {
namespace sign namespace sign
{ {
enum SignType enum SignatureId
{ {
SIGN_RSA4096_SHA1 = 0x10000, SIGN_ID_RSA4096_SHA1 = 0x10000,
SIGN_RSA2048_SHA1, SIGN_ID_RSA2048_SHA1,
SIGN_ECDSA240_SHA1, SIGN_ID_ECDSA240_SHA1,
SIGN_RSA4096_SHA256, SIGN_ID_RSA4096_SHA256,
SIGN_RSA2048_SHA256, SIGN_ID_RSA2048_SHA256,
SIGN_ECDSA240_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 size_t kEcdsaSigSize = 0x3C;
static const std::string kRootIssuerStr = "Root";
static const std::string kIdentDelimiter = "-";
} }
#pragma pack(push,1) #pragma pack(push,1)
struct sRsa4096SignBlock 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(); clear();
} }
es::CertificateBody::CertificateBody(const CertificateBody& other) pki::CertificateBody::CertificateBody(const CertificateBody& other)
{ {
*this = other; *this = other;
} }
void es::CertificateBody::operator=(const CertificateBody& other) void pki::CertificateBody::operator=(const CertificateBody& other)
{ {
mRawBinary = other.mRawBinary; mRawBinary = other.mRawBinary;
mIssuer = other.mIssuer; mIssuer = other.mIssuer;
@ -22,7 +22,7 @@ void es::CertificateBody::operator=(const CertificateBody& other)
mEcdsa240PublicKey = other.mEcdsa240PublicKey; mEcdsa240PublicKey = other.mEcdsa240PublicKey;
} }
bool es::CertificateBody::operator==(const CertificateBody& other) const bool pki::CertificateBody::operator==(const CertificateBody& other) const
{ {
return (mIssuer == other.mIssuer) \ return (mIssuer == other.mIssuer) \
&& (mSubject == other.mSubject) \ && (mSubject == other.mSubject) \
@ -33,12 +33,12 @@ bool es::CertificateBody::operator==(const CertificateBody& other) const
&& (mEcdsa240PublicKey == other.mEcdsa240PublicKey); && (mEcdsa240PublicKey == other.mEcdsa240PublicKey);
} }
bool es::CertificateBody::operator!=(const CertificateBody& other) const bool pki::CertificateBody::operator!=(const CertificateBody& other) const
{ {
return !(*this == other); return !(*this == other);
} }
void es::CertificateBody::toBytes() void pki::CertificateBody::toBytes()
{ {
// get public key size // get public key size
size_t pubkeySize = 0; 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(); clear();
@ -129,10 +129,10 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size)
hdr = (const sCertificateHeader*)mRawBinary.data(); hdr = (const sCertificateHeader*)mRawBinary.data();
if (hdr->issuer[0] != 0) 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(); mPublicKeyType = (cert::PublicKeyType)hdr->key_type.get();
if (hdr->subject[0] != 0) 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(); mCertId = hdr->cert_id.get();
// save public key // 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; return mRawBinary;
} }
void es::CertificateBody::clear() void pki::CertificateBody::clear()
{ {
mIssuer.clear(); mIssuer.clear();
mSubject.clear(); mSubject.clear();
@ -173,12 +173,12 @@ void es::CertificateBody::clear()
memset(&mEcdsa240PublicKey, 0, sizeof(crypto::ecdsa::sEcdsa240Point)); memset(&mEcdsa240PublicKey, 0, sizeof(crypto::ecdsa::sEcdsa240Point));
} }
const std::string& es::CertificateBody::getIssuer() const const std::string& pki::CertificateBody::getIssuer() const
{ {
return mIssuer; return mIssuer;
} }
void es::CertificateBody::setIssuer(const std::string& issuer) void pki::CertificateBody::setIssuer(const std::string& issuer)
{ {
if (issuer.size() > cert::kIssuerSize) if (issuer.size() > cert::kIssuerSize)
{ {
@ -188,22 +188,22 @@ void es::CertificateBody::setIssuer(const std::string& issuer)
mIssuer = issuer; mIssuer = issuer;
} }
es::cert::PublicKeyType es::CertificateBody::getPublicKeyType() const pki::cert::PublicKeyType pki::CertificateBody::getPublicKeyType() const
{ {
return mPublicKeyType; return mPublicKeyType;
} }
void es::CertificateBody::setPublicKeyType(cert::PublicKeyType type) void pki::CertificateBody::setPublicKeyType(cert::PublicKeyType type)
{ {
mPublicKeyType = type; mPublicKeyType = type;
} }
const std::string& es::CertificateBody::getSubject() const const std::string& pki::CertificateBody::getSubject() const
{ {
return mSubject; return mSubject;
} }
void es::CertificateBody::setSubject(const std::string& subject) void pki::CertificateBody::setSubject(const std::string& subject)
{ {
if (subject.size() > cert::kSubjectSize) if (subject.size() > cert::kSubjectSize)
{ {
@ -213,42 +213,42 @@ void es::CertificateBody::setSubject(const std::string& subject)
mSubject = subject; mSubject = subject;
} }
uint32_t es::CertificateBody::getCertId() const uint32_t pki::CertificateBody::getCertId() const
{ {
return mCertId; return mCertId;
} }
void es::CertificateBody::setCertId(uint32_t id) void pki::CertificateBody::setCertId(uint32_t id)
{ {
mCertId = id; mCertId = id;
} }
const crypto::rsa::sRsa4096Key& es::CertificateBody::getRsa4098PublicKey() const const crypto::rsa::sRsa4096Key& pki::CertificateBody::getRsa4098PublicKey() const
{ {
return mRsa4096PublicKey; return mRsa4096PublicKey;
} }
void es::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key) void pki::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key)
{ {
mRsa4096PublicKey = key; mRsa4096PublicKey = key;
} }
const crypto::rsa::sRsa2048Key& es::CertificateBody::getRsa2048PublicKey() const const crypto::rsa::sRsa2048Key& pki::CertificateBody::getRsa2048PublicKey() const
{ {
return mRsa2048PublicKey; return mRsa2048PublicKey;
} }
void es::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key) void pki::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key)
{ {
mRsa2048PublicKey = key; mRsa2048PublicKey = key;
} }
const crypto::ecdsa::sEcdsa240Point& es::CertificateBody::getEcdsa240PublicKey() const const crypto::ecdsa::sEcdsa240Point& pki::CertificateBody::getEcdsa240PublicKey() const
{ {
return mEcdsa240PublicKey; return mEcdsa240PublicKey;
} }
void es::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key) void pki::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key)
{ {
mEcdsa240PublicKey = 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(); clear();
} }
es::SignatureBlock::SignatureBlock(const SignatureBlock& other) pki::SignatureBlock::SignatureBlock(const SignatureBlock& other)
{ {
*this = other; *this = other;
} }
void es::SignatureBlock::operator=(const SignatureBlock& other) void pki::SignatureBlock::operator=(const SignatureBlock& other)
{ {
mRawBinary = other.mRawBinary; mRawBinary = other.mRawBinary;
mSignType = other.mSignType; mSignType = other.mSignType;
@ -18,37 +18,37 @@ void es::SignatureBlock::operator=(const SignatureBlock& other)
mSignature = other.mSignature; mSignature = other.mSignature;
} }
bool es::SignatureBlock::operator==(const SignatureBlock& other) const bool pki::SignatureBlock::operator==(const SignatureBlock& other) const
{ {
return (mSignType == other.mSignType) \ return (mSignType == other.mSignType) \
&& (mIsLittleEndian == other.mIsLittleEndian) \ && (mIsLittleEndian == other.mIsLittleEndian) \
&& (mSignature == other.mSignature); && (mSignature == other.mSignature);
} }
bool es::SignatureBlock::operator!=(const SignatureBlock& other) const bool pki::SignatureBlock::operator!=(const SignatureBlock& other) const
{ {
return !(*this == other); return !(*this == other);
} }
void es::SignatureBlock::toBytes() void pki::SignatureBlock::toBytes()
{ {
size_t totalSize = 0; size_t totalSize = 0;
size_t sigSize = 0; size_t sigSize = 0;
switch (mSignType) switch (mSignType)
{ {
case (sign::SIGN_RSA4096_SHA1): case (sign::SIGN_ID_RSA4096_SHA1):
case (sign::SIGN_RSA4096_SHA256): case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock); totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size; sigSize = crypto::rsa::kRsa4096Size;
break; break;
case (sign::SIGN_RSA2048_SHA1): case (sign::SIGN_ID_RSA2048_SHA1):
case (sign::SIGN_RSA2048_SHA256): case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock); totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size; sigSize = crypto::rsa::kRsa2048Size;
break; break;
case (sign::SIGN_ECDSA240_SHA1): case (sign::SIGN_ID_ECDSA240_SHA1):
case (sign::SIGN_ECDSA240_SHA256): case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock); totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize; sigSize = sign::kEcdsaSigSize;
break; break;
@ -68,7 +68,7 @@ void es::SignatureBlock::toBytes()
memcpy(mRawBinary.data() + 4, mSignature.data(), sigSize); 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(); clear();
@ -80,18 +80,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
signType = ((be_uint32_t*)src)->get(); signType = ((be_uint32_t*)src)->get();
switch (signType) switch (signType)
{ {
case (sign::SIGN_RSA4096_SHA1): case (sign::SIGN_ID_RSA4096_SHA1):
case (sign::SIGN_RSA4096_SHA256): case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock); totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size; sigSize = crypto::rsa::kRsa4096Size;
break; break;
case (sign::SIGN_RSA2048_SHA1): case (sign::SIGN_ID_RSA2048_SHA1):
case (sign::SIGN_RSA2048_SHA256): case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock); totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size; sigSize = crypto::rsa::kRsa2048Size;
break; break;
case (sign::SIGN_ECDSA240_SHA1): case (sign::SIGN_ID_ECDSA240_SHA1):
case (sign::SIGN_ECDSA240_SHA256): case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock); totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize; sigSize = sign::kEcdsaSigSize;
break; break;
@ -103,18 +103,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
signType = ((le_uint32_t*)src)->get(); signType = ((le_uint32_t*)src)->get();
switch (signType) switch (signType)
{ {
case (sign::SIGN_RSA4096_SHA1): case (sign::SIGN_ID_RSA4096_SHA1):
case (sign::SIGN_RSA4096_SHA256): case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock); totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size; sigSize = crypto::rsa::kRsa4096Size;
break; break;
case (sign::SIGN_RSA2048_SHA1): case (sign::SIGN_ID_RSA2048_SHA1):
case (sign::SIGN_RSA2048_SHA256): case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock); totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size; sigSize = crypto::rsa::kRsa2048Size;
break; break;
case (sign::SIGN_ECDSA240_SHA1): case (sign::SIGN_ID_ECDSA240_SHA1):
case (sign::SIGN_ECDSA240_SHA256): case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock); totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize; sigSize = sign::kEcdsaSigSize;
break; break;
@ -133,50 +133,50 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
mRawBinary.alloc(totalSize); mRawBinary.alloc(totalSize);
memcpy(mRawBinary.data(), src, totalSize); memcpy(mRawBinary.data(), src, totalSize);
mSignType = (sign::SignType)signType; mSignType = (sign::SignatureId)signType;
mSignature.alloc(sigSize); mSignature.alloc(sigSize);
memcpy(mSignature.data(), mRawBinary.data() + 4, 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; return mRawBinary;
} }
void es::SignatureBlock::clear() void pki::SignatureBlock::clear()
{ {
mRawBinary.clear(); mRawBinary.clear();
mSignType = sign::SIGN_RSA4096_SHA1; mSignType = sign::SIGN_ID_RSA4096_SHA1;
mIsLittleEndian = false; mIsLittleEndian = false;
mSignature.clear(); mSignature.clear();
} }
es::sign::SignType es::SignatureBlock::getSignType() const pki::sign::SignatureId pki::SignatureBlock::getSignType() const
{ {
return mSignType; return mSignType;
} }
void es::SignatureBlock::setSignType(es::sign::SignType type) void pki::SignatureBlock::setSignType(pki::sign::SignatureId type)
{ {
mSignType = type; mSignType = type;
} }
bool es::SignatureBlock::isLittleEndian() const bool pki::SignatureBlock::isLittleEndian() const
{ {
return mIsLittleEndian; return mIsLittleEndian;
} }
void es::SignatureBlock::setLittleEndian(bool isLE) void pki::SignatureBlock::setLittleEndian(bool isLE)
{ {
mIsLittleEndian = isLE; mIsLittleEndian = isLE;
} }
const fnd::Vec<byte_t>& es::SignatureBlock::getSignature() const const fnd::Vec<byte_t>& pki::SignatureBlock::getSignature() const
{ {
return mSignature; return mSignature;
} }
void es::SignatureBlock::setSignature(const fnd::Vec<byte_t>& signature) void pki::SignatureBlock::setSignature(const fnd::Vec<byte_t>& signature)
{ {
mSignature = 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 main: build
rebuild: clean 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))) OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
# External dependencies # External dependencies
DEPENDS = nx-hb nx crypto compress fnd DEPENDS = nx-hb nx es pki crypto compress fnd
LIB_DIR = ../../lib LIB_DIR = ../../lib
LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep)) LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep))
INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include") INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include")

View file

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

View file

@ -15,21 +15,69 @@
</Filter> </Filter>
</ItemGroup> </ItemGroup>
<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"> <ClInclude Include="source\NcaProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\NpdmProcess.h"> <ClInclude Include="source\NpdmProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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"> <ClInclude Include="source\nstool.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\OffsetAdjustedIFile.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\PfsProcess.h"> <ClInclude Include="source\PfsProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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"> <ClInclude Include="source\RomfsProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="source\SdkApiString.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="source\UserSettings.h"> <ClInclude Include="source\UserSettings.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </ClInclude>
@ -39,103 +87,73 @@
<ClInclude Include="source\XciProcess.h"> <ClInclude Include="source\XciProcess.h">
<Filter>Header Files</Filter> <Filter>Header Files</Filter>
</ClInclude> </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>
<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"> <ClCompile Include="source\AesCtrWrappedIFile.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NcaProcess.cpp"> <ClCompile Include="source\AssetProcess.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="source\OffsetAdjustedIFile.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\CnmtProcess.cpp"> <ClCompile Include="source\CnmtProcess.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </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"> <ClCompile Include="source\HashTreeMeta.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\HashTreeWrappedIFile.cpp"> <ClCompile Include="source\HashTreeWrappedIFile.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NsoProcess.cpp"> <ClCompile Include="source\main.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">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\NacpProcess.cpp"> <ClCompile Include="source\NacpProcess.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </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> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="source\RoMetadataProcess.cpp"> <ClCompile Include="source\RoMetadataProcess.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
</ItemGroup> <ClCompile Include="source\RomfsProcess.cpp">
<ItemGroup> <Filter>Source Files</Filter>
<None Include="makefile" /> </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> </ItemGroup>
</Project> </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 // process partition
processPartitions(); 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) void NcaProcess::setInputFile(fnd::IFile* file, bool ownIFile)
@ -695,9 +674,10 @@ void NcaProcess::displayHeader()
printf(" Partitions:\n"); printf(" Partitions:\n");
for (size_t i = 0; i < mHdr.getPartitions().size(); i++) 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(" Offset: 0x%" PRIx64 "\n", (uint64_t)info.offset);
printf(" Size: 0x%" PRIx64 "\n", (uint64_t)info.size); printf(" Size: 0x%" PRIx64 "\n", (uint64_t)info.size);
printf(" Format Type: %s\n", getFormatTypeStr(info.format_type)); printf(" Format Type: %s\n", getFormatTypeStr(info.format_type));
@ -790,7 +770,7 @@ void NcaProcess::processPartitions()
pfs.setListFs(mListFs); pfs.setListFs(mListFs);
if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM) 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 else
{ {
@ -811,7 +791,7 @@ void NcaProcess::processPartitions()
romfs.setListFs(mListFs); romfs.setListFs(mListFs);
if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM) 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 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 "UserSettings.h"
#include "version.h" #include "version.h"
#include "PkiValidator.h"
#include <vector> #include <vector>
#include <string> #include <string>
#include <algorithm> #include <algorithm>
@ -22,6 +23,10 @@
#include <nx/nso.h> #include <nx/nso.h>
#include <nx/nro.h> #include <nx/nro.h>
#include <nx/aset.h> #include <nx/aset.h>
#include <pki/SignedData.h>
#include <pki/CertificateBody.h>
#include <pki/SignUtils.h>
#include <es/TicketBody_V2.h>
UserSettings::UserSettings() UserSettings::UserSettings()
{} {}
@ -41,34 +46,36 @@ void UserSettings::showHelp()
printf("Usage: nstool [options... ] <file>\n"); printf("Usage: nstool [options... ] <file>\n");
printf("\n General Options:\n"); printf("\n General Options:\n");
printf(" -d, --dev Use devkit keyset\n"); printf(" -d, --dev Use devkit keyset.\n");
printf(" -k, --keyset Specify keyset file\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(" -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(" -y, --verify Verify file.\n");
printf("\n Output Options:\n"); printf("\n Output Options:\n");
printf(" --showkeys Show keys generated\n"); printf(" --showkeys Show keys generated.\n");
printf(" --showlayout Show layout metadata\n"); printf(" --showlayout Show layout metadata.\n");
printf(" -v, --verbose Verbose output\n"); printf(" -v, --verbose Verbose output.\n");
printf("\n XCI (GameCard Image)\n"); printf("\n XCI (GameCard Image)\n");
printf(" nstool [--listfs] [--update <dir> --logo <dir> --normal <dir> --secure <dir>] <.xci file>\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(" --listfs Print file system in embedded partitions.\n");
printf(" --update Extract \"update\" partition to directory\n"); printf(" --update Extract \"update\" partition to directory.\n");
printf(" --logo Extract \"logo\" partition to directory\n"); printf(" --logo Extract \"logo\" partition to directory.\n");
printf(" --normal Extract \"normal\" partition to directory\n"); printf(" --normal Extract \"normal\" partition to directory.\n");
printf(" --secure Extract \"secure\" partition to directory\n"); printf(" --secure Extract \"secure\" partition to directory.\n");
printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n"); printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n");
printf(" nstool [--listfs] [--fsdir <dir>] <file>\n"); printf(" nstool [--listfs] [--fsdir <dir>] <file>\n");
printf(" --listfs Print file system\n"); printf(" --listfs Print file system.\n");
printf(" --fsdir Extract file system to directory\n"); printf(" --fsdir Extract file system to directory.\n");
printf("\n NCA (Nintendo Content Archive)\n"); printf("\n NCA (Nintendo Content Archive)\n");
printf(" nstool [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>\n"); printf(" nstool [--listfs] [--bodykey <key> --titlekey <key>] [--part0 <dir> ...] <.nca file>\n");
printf(" --listfs Print file system in embedded partitions\n"); printf(" --listfs Print file system in embedded partitions.\n");
printf(" --titlekey Specify title key extracted from ticket\n"); printf(" --titlekey Specify title key extracted from ticket.\n");
printf(" --bodykey Specify body encryption key\n"); printf(" --bodykey Specify body encryption key.\n");
printf(" --part0 Extract \"partition 0\" to directory \n"); printf(" --tik Specify ticket to source title key.\n");
printf(" --part1 Extract \"partition 1\" to directory \n"); printf(" --cert Specify certificate chain to verify ticket.\n");
printf(" --part2 Extract \"partition 2\" to directory \n"); printf(" --part0 Extract \"partition 0\" to directory.\n");
printf(" --part3 Extract \"partition 3\" 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("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n");
printf(" nstool [--listapi --listsym] [--insttype <inst. type>] <file>\n"); printf(" nstool [--listapi --listsym] [--insttype <inst. type>] <file>\n");
printf(" --listapi Print SDK API List.\n"); printf(" --listapi Print SDK API List.\n");
@ -182,6 +189,11 @@ const sOptional<std::string>& UserSettings::getAssetNacpPath() const
return mAssetNacpPath; return mAssetNacpPath;
} }
const fnd::List<pki::SignedData<pki::CertificateBody>>& UserSettings::getCertificateChain() const
{
return mCertChain;
}
void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args) void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCmdArgs& cmd_args)
{ {
// show help text // show help text
@ -296,6 +308,18 @@ void UserSettings::populateCmdArgs(const std::vector<std::string>& arg_list, sCm
cmd_args.nca_bodykey = arg_list[i+1]; 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") else if (arg_list[i] == "--part0")
{ {
if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter."); if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter.");
@ -415,6 +439,7 @@ void UserSettings::populateKeyset(sCmdArgs& args)
const std::string kKekGenSource = "aes_kek_generation"; const std::string kKekGenSource = "aes_kek_generation";
const std::string kKeyGenSource = "aes_key_generation"; const std::string kKeyGenSource = "aes_key_generation";
const std::string kAcidBase = "acid"; const std::string kAcidBase = "acid";
const std::string kPkiRootBase = "pki_root";
const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" }; const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" };
const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"}; const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"};
const std::string kNcaBodyKeakIndexName[3] = {"application", "ocean", "system"}; 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); _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kKeyStr), mKeyset.xci.header_key.key, 0x10);
// store rsa keys // 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[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, 0x100); _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[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, 0x100); _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[0]), mKeyset.acid_sign_key.priv_exponent, crypto::rsa::kRsa2048Size);
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, 0x100); _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 // save keydata from input args
if (args.nca_bodykey.isSet) 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 _SAVE_KEYDATA
#undef _CONCAT_3_STRINGS #undef _CONCAT_3_STRINGS
#undef _CONCAT_2_STRINGS #undef _CONCAT_2_STRINGS
@ -688,6 +790,10 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
type = FILE_NRO; type = FILE_NRO;
else if (str == "nacp") else if (str == "nacp")
type = FILE_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") else if (str == "aset" || str == "asset")
type = FILE_HB_ASSET; type = FILE_HB_ASSET;
else else
@ -746,6 +852,12 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// test nso // test nso
else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _TYPE_PTR(nx::sNroHeader)->st_magic.get() == nx::nro::kNroStructMagic) else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _TYPE_PTR(nx::sNroHeader)->st_magic.get() == nx::nro::kNroStructMagic)
file_type = FILE_NRO; 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 // test hb asset
else if (_ASSERT_SIZE(sizeof(nx::sAssetHeader)) && _TYPE_PTR(nx::sAssetHeader)->st_magic.get() == nx::aset::kAssetStructMagic) else if (_ASSERT_SIZE(sizeof(nx::sAssetHeader)) && _TYPE_PTR(nx::sAssetHeader)->st_magic.get() == nx::aset::kAssetStructMagic)
file_type = FILE_HB_ASSET; file_type = FILE_HB_ASSET;
@ -848,6 +960,50 @@ bool UserSettings::determineValidNacpFromSample(const fnd::Vec<byte_t>& sample)
return true; 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) nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str)
{ {
std::string str = type_str; std::string str = type_str;

View file

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

View file

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

View file

@ -27,6 +27,8 @@ enum FileType
FILE_NSO, FILE_NSO,
FILE_NRO, FILE_NRO,
FILE_NACP, FILE_NACP,
FILE_PKI_CERT,
FILE_ES_TIK,
FILE_HB_ASSET, FILE_HB_ASSET,
FILE_INVALID = -1, FILE_INVALID = -1,
}; };
@ -62,7 +64,6 @@ struct sOptional
struct sKeyset struct sKeyset
{ {
crypto::rsa::sRsa2048Key acid_sign_key; crypto::rsa::sRsa2048Key acid_sign_key;
crypto::aes::sAes128Key package1_key[kMasterKeyNum]; crypto::aes::sAes128Key package1_key[kMasterKeyNum];
crypto::rsa::sRsa2048Key package2_sign_key; crypto::rsa::sRsa2048Key package2_sign_key;
crypto::aes::sAes128Key package2_key[kMasterKeyNum]; crypto::aes::sAes128Key package2_key[kMasterKeyNum];
@ -84,12 +85,17 @@ struct sKeyset
crypto::rsa::sRsa2048Key header_sign_key; crypto::rsa::sRsa2048Key header_sign_key;
crypto::aes::sAes128Key header_key; crypto::aes::sAes128Key header_key;
} xci; } xci;
struct sTicketData struct sTicketData
{ {
crypto::rsa::sRsa2048Key sign_key; crypto::rsa::sRsa2048Key sign_key;
crypto::aes::sAes128Key titlekey_kek[kMasterKeyNum]; crypto::aes::sAes128Key titlekey_kek[kMasterKeyNum];
} ticket; } ticket;
struct sPkiData
{
crypto::rsa::sRsa4096Key root_sign_key;
} pki;
}; };
inline byte_t charToByte(char chr) inline byte_t charToByte(char chr)