diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json
index 17a1a7e..e996876 100644
--- a/.vscode/c_cpp_properties.json
+++ b/.vscode/c_cpp_properties.json
@@ -10,8 +10,10 @@
"/usr/include",
"${workspaceRoot}",
"${workspaceRoot}/lib/libcrypto/include",
+ "${workspaceRoot}/lib/libcrypto/source/polarssl/libinclude",
"${workspaceRoot}/lib/libcompress/include",
"${workspaceRoot}/lib/libes/include",
+ "${workspaceRoot}/lib/libpki/include",
"${workspaceRoot}/lib/libfnd/include",
"${workspaceRoot}/lib/libnx/include",
"${workspaceRoot}/lib/libnx-hb/include"
diff --git a/.vscode/settings.json b/.vscode/settings.json
index d1f6ffb..768b573 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -27,7 +27,14 @@
"memory": "cpp",
"tuple": "cpp",
"__locale": "cpp",
- "cinttypes": "cpp"
+ "cinttypes": "cpp",
"__bit_reference": "cpp",
+ "algorithm": "cpp",
+ "__functional_base_03": "cpp",
+ "__tuple": "cpp",
+ "chrono": "cpp",
+ "functional": "cpp",
+ "limits": "cpp",
+ "ratio": "cpp"
}
}
\ No newline at end of file
diff --git a/KEYS.md b/KEYS.md
index bd1b706..0a3098b 100644
--- a/KEYS.md
+++ b/KEYS.md
@@ -25,6 +25,10 @@ package2_sign_key_private : RSA2048 Private Exponent (0x100 bytes)
; Ticket Keys
ticket_commonkey_## : AES128 Key (0x10 bytes)
+; PKI Root Signing Key
+pki_root_sign_key_modulus : RSA4096 Modulus (0x200 bytes)
+pki_root_sign_key_private : RSA4096 Private Exponent (0x200 bytes)
+
; NCA Keys
nca_header_key : AES128-XTS Key (0x20 bytes)
nca_header_sign_key_modulus : RSA2048 Modulus (0x100 bytes)
diff --git a/NXTools.sln b/NXTools.sln
index ade6849..2305e4c 100644
--- a/NXTools.sln
+++ b/NXTools.sln
@@ -44,6 +44,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcompress", "lib\libcompr
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libnx-hb", "lib\libnx-hb\libnx-hb.vcxproj", "{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}"
EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libpki", "lib\libpki\libpki.vcxproj", "{B9113734-6E84-44FF-8CF7-58199AA815C5}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@@ -108,6 +110,14 @@ Global
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x64.Build.0 = Release|x64
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.ActiveCfg = Release|Win32
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63}.Release|x86.Build.0 = Release|Win32
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x64.ActiveCfg = Debug|x64
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x64.Build.0 = Debug|x64
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x86.ActiveCfg = Debug|Win32
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Debug|x86.Build.0 = Debug|Win32
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x64.ActiveCfg = Release|x64
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x64.Build.0 = Release|x64
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x86.ActiveCfg = Release|Win32
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -120,6 +130,7 @@ Global
{AF09FA96-4463-417D-8FE6-526063F41349} = {E0863FCC-8E72-490D-BE1B-458F12CA8298}
{CF01B5B7-730A-447F-9BB2-5EDA9B082177} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
{738CB4FC-CD9E-4B81-A04B-DEADBFA71C63} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
+ {B9113734-6E84-44FF-8CF7-58199AA815C5} = {170B4A09-1B67-4A62-93AB-116EBCFF4A8C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {07DCCACC-D10D-47C9-85AE-FB9C54DB7D62}
diff --git a/README.md b/README.md
index 1bb4f5a..377b1a2 100644
--- a/README.md
+++ b/README.md
@@ -10,12 +10,13 @@ Tools & Libraries for NX (Nintendo Switch).
# Libraries
-* __libfnd__ - Foundation library.
-* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls)
-* __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4)
-* __libes__ - Handling of (NX relevant) eShop file type processing. (eTickets, etc)
-* __libnx__ - Handling of NX file types.
-* __libnx-hb__ - Handling of NX (homebrew extensions) file types.
+* __libfnd__ - Foundation library.
+* __libcrypto__ - Cryptographic functions (AES,SHA,RSA). Wrapper for [mbedTLS](https://github.com/ARMmbed/mbedtls)
+* __libcompress__ - Compression algorithms (LZ4). Wrapper for [lz4](https://github.com/lz4/lz4)
+* __libpki__ - Processes Nintendo's proprietary PKI.
+* __libes__ - Processes Nintendo's eShop file types.
+* __libnx__ - Processes NX file types.
+* __libnx-hb__ - Processes NX file types (homebrew extensions).
# Building
diff --git a/lib/libcrypto/crypto.vcxproj b/lib/libcrypto/crypto.vcxproj
index 72a1f38..06dfa98 100644
--- a/lib/libcrypto/crypto.vcxproj
+++ b/lib/libcrypto/crypto.vcxproj
@@ -121,6 +121,7 @@
+
@@ -137,6 +138,7 @@
+
diff --git a/lib/libcrypto/crypto.vcxproj.filters b/lib/libcrypto/crypto.vcxproj.filters
index 09c9b93..0110282 100644
--- a/lib/libcrypto/crypto.vcxproj.filters
+++ b/lib/libcrypto/crypto.vcxproj.filters
@@ -66,6 +66,9 @@
Header Files\crypto
+
+ Header Files\crypto
+
@@ -101,6 +104,9 @@
Source Files\polarssl
+
+ Source Files
+
diff --git a/lib/libcrypto/include/crypto/base64.h b/lib/libcrypto/include/crypto/base64.h
new file mode 100644
index 0000000..67c6ecd
--- /dev/null
+++ b/lib/libcrypto/include/crypto/base64.h
@@ -0,0 +1,14 @@
+#pragma once
+#include
+#include
+
+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);
+}
+}
\ No newline at end of file
diff --git a/lib/libcrypto/source/base64_wrapper.cpp b/lib/libcrypto/source/base64_wrapper.cpp
new file mode 100644
index 0000000..1bc6f66
--- /dev/null
+++ b/lib/libcrypto/source/base64_wrapper.cpp
@@ -0,0 +1,30 @@
+#include
+#include
+
+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);
+}
\ No newline at end of file
diff --git a/lib/libes/es.vcxproj b/lib/libes/es.vcxproj
index ac4d213..ae9f9c3 100644
--- a/lib/libes/es.vcxproj
+++ b/lib/libes/es.vcxproj
@@ -122,21 +122,14 @@
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
diff --git a/lib/libes/es.vcxproj.filters b/lib/libes/es.vcxproj.filters
index 1f0bc28..f933a32 100644
--- a/lib/libes/es.vcxproj.filters
+++ b/lib/libes/es.vcxproj.filters
@@ -18,38 +18,9 @@
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
-
-
- Header Files
-
-
- Header Files
-
Header Files
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
Header Files
@@ -57,4 +28,12 @@
Header Files
+
+
+ Source Files
+
+
+ Source Files
+
+
\ No newline at end of file
diff --git a/lib/libes/include/es/TicketBody_V2.h b/lib/libes/include/es/TicketBody_V2.h
index 6b0be01..4741cac 100644
--- a/lib/libes/include/es/TicketBody_V2.h
+++ b/lib/libes/include/es/TicketBody_V2.h
@@ -1,6 +1,7 @@
#pragma once
#include
#include
+#include
#include
namespace es
@@ -42,14 +43,8 @@ namespace es
byte_t getCommonKeyId() const;
void setCommonKeyId(byte_t id);
- bool isPreInstall() const;
- void setIsPreInstall(bool isPreInstall);
-
- bool isSharedTitle() const;
- void setIsSharedTitle(bool isSharedTitle);
-
- bool allowAllContent() const;
- void setAllowAllContent(bool allowAllContent);
+ const fnd::List& getPropertyFlags() const;
+ void setPropertyFlags(const fnd::List& flags);
const byte_t* getReservedRegion() const;
void setReservedRegion(const byte_t* data, size_t len);
@@ -91,9 +86,7 @@ namespace es
uint16_t mTicketVersion;
ticket::LicenseType mLicenseType;
byte_t mCommonKeyId;
- bool mPreInstall;
- bool mSharedTitle;
- bool mAllowAllContent;
+ fnd::List mPropertyFlags;
byte_t mReservedRegion[ticket::kReservedRegionSize]; // explicitly reserved
uint64_t mTicketId;
uint64_t mDeviceId;
diff --git a/lib/libes/source/TicketBody_V2.cpp b/lib/libes/source/TicketBody_V2.cpp
index a9bd29c..93816c7 100644
--- a/lib/libes/source/TicketBody_V2.cpp
+++ b/lib/libes/source/TicketBody_V2.cpp
@@ -26,9 +26,8 @@ void es::TicketBody_V2::operator=(const TicketBody_V2 & other)
mEncType = other.mEncType;
mTicketVersion = other.mTicketVersion;
mLicenseType = other.mLicenseType;
- mPreInstall = other.mPreInstall;
- mSharedTitle = other.mSharedTitle;
- mAllowAllContent = other.mAllowAllContent;
+ mCommonKeyId = other.mCommonKeyId;
+ mPropertyFlags = other.mPropertyFlags;
memcpy(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize);
mTicketId = other.mTicketId;
mDeviceId = other.mDeviceId;
@@ -48,9 +47,7 @@ bool es::TicketBody_V2::operator==(const TicketBody_V2 & other) const
&& (mEncType == other.mEncType) \
&& (mTicketVersion == other.mTicketVersion) \
&& (mLicenseType == other.mLicenseType) \
- && (mPreInstall == other.mPreInstall) \
- && (mSharedTitle == other.mSharedTitle) \
- && (mAllowAllContent == other.mAllowAllContent) \
+ && (mPropertyFlags == other.mPropertyFlags) \
&& (memcmp(mReservedRegion, other.mReservedRegion, ticket::kReservedRegionSize) == 0) \
&& (mTicketId == other.mTicketId) \
&& (mDeviceId == other.mDeviceId) \
@@ -74,19 +71,22 @@ void es::TicketBody_V2::toBytes()
body->format_version = (ticket::kFormatVersion);
- strncmp(body->issuer, mIssuer.c_str(), ticket::kIssuerSize);
+ strncpy(body->issuer, mIssuer.c_str(), ticket::kIssuerSize);
memcpy(body->enc_title_key, mEncTitleKey, ticket::kEncTitleKeySize);
body->title_key_enc_type = (mEncType);
body->ticket_version = (mTicketVersion);
+ body->license_type = mLicenseType;
+ body->common_key_id = mCommonKeyId;
byte_t property_mask = 0;
- property_mask |= mPreInstall ? _BIT(ticket::FLAG_PRE_INSTALL) : 0;
- property_mask |= mSharedTitle ? _BIT(ticket::FLAG_SHARED_TITLE) : 0;
- property_mask |= mAllowAllContent ? _BIT(ticket::FLAG_ALLOW_ALL_CONTENT) : 0;
+ for (size_t i = 0; i < mPropertyFlags.size(); i++)
+ {
+ property_mask |= _BIT(mPropertyFlags[i]);
+ }
body->property_mask = (property_mask);
memcpy(body->reserved_region, mReservedRegion, ticket::kReservedRegionSize);
body->ticket_id = (mTicketId);
body->device_id = (mDeviceId);
- memcmp(body->rights_id, mRightsId, ticket::kRightsIdSize);
+ memcpy(body->rights_id, mRightsId, ticket::kRightsIdSize);
body->account_id = (mAccountId);
body->sect_total_size = (mSectTotalSize);
body->sect_header_offset = (mSectHeaderOffset);
@@ -112,14 +112,17 @@ void es::TicketBody_V2::fromBytes(const byte_t * bytes, size_t len)
throw fnd::Exception(kModuleName, "Unsupported format version");
}
- mIssuer.append(body->issuer, ticket::kIssuerSize);
+ mIssuer = std::string(body->issuer, _MIN(strlen(body->issuer), ticket::kIssuerSize));
memcpy(mEncTitleKey, body->enc_title_key, ticket::kEncTitleKeySize);
mEncType = (ticket::TitleKeyEncType)body->title_key_enc_type;
mTicketVersion = body->ticket_version.get();
mLicenseType = (ticket::LicenseType)body->license_type;
- mPreInstall = _HAS_BIT(body->property_mask, ticket::FLAG_PRE_INSTALL);
- mSharedTitle = _HAS_BIT(body->property_mask, ticket::FLAG_SHARED_TITLE);
- mAllowAllContent = _HAS_BIT(body->property_mask, ticket::FLAG_ALLOW_ALL_CONTENT);
+ mCommonKeyId = body->common_key_id;
+ for (size_t i = 0; i < mPropertyFlags.size(); i++)
+ {
+ if (_HAS_BIT(body->property_mask, i))
+ mPropertyFlags.addElement((ticket::PropertyMaskFlags)i);
+ }
memcpy(mReservedRegion, body->reserved_region, ticket::kReservedRegionSize);
mTicketId = body->ticket_id.get();
mDeviceId = body->device_id.get();
@@ -144,9 +147,8 @@ void es::TicketBody_V2::clear()
mEncType = ticket::AES128_CBC;
mTicketVersion = 0;
mLicenseType = ticket::LICENSE_PERMANENT;
- mPreInstall = false;
- mSharedTitle = false;
- mAllowAllContent = false;
+ mCommonKeyId = 0;
+ mPropertyFlags.clear();
memset(mReservedRegion, 0, ticket::kReservedRegionSize);
mTicketId = 0;
mDeviceId = 0;
@@ -224,34 +226,14 @@ void es::TicketBody_V2::setCommonKeyId(byte_t id)
mCommonKeyId = id;
}
-bool es::TicketBody_V2::isPreInstall() const
+const fnd::List& es::TicketBody_V2::getPropertyFlags() const
{
- return mPreInstall;
+ return mPropertyFlags;
}
-void es::TicketBody_V2::setIsPreInstall(bool isPreInstall)
+void es::TicketBody_V2::setPropertyFlags(const fnd::List& flags)
{
- mPreInstall = isPreInstall;
-}
-
-bool es::TicketBody_V2::isSharedTitle() const
-{
- return mSharedTitle;
-}
-
-void es::TicketBody_V2::setIsSharedTitle(bool isSharedTitle)
-{
- mSharedTitle = isSharedTitle;
-}
-
-bool es::TicketBody_V2::allowAllContent() const
-{
- return mAllowAllContent;
-}
-
-void es::TicketBody_V2::setAllowAllContent(bool allowAllContent)
-{
- mAllowAllContent = allowAllContent;
+ mPropertyFlags = flags;
}
const byte_t * es::TicketBody_V2::getReservedRegion() const
diff --git a/lib/libfnd/include/fnd/Vec.h b/lib/libfnd/include/fnd/Vec.h
index 6cc2ed3..667027a 100644
--- a/lib/libfnd/include/fnd/Vec.h
+++ b/lib/libfnd/include/fnd/Vec.h
@@ -146,6 +146,11 @@ namespace fnd
{
fnd::Exception("Vec", "Failed to allocate memory for vector");
}
+ for (size_t i = 0; i < new_size; i++)
+ {
+ m_Vec[i] = 0;
+ }
+
m_Size = new_size;
}
@@ -159,6 +164,10 @@ namespace fnd
{
fnd::Exception("Vec", "Failed to allocate memory for vector");
}
+ for (size_t i = 0; i < new_size; i++)
+ {
+ new_vec[i] = 0;
+ }
for (size_t i = 0; i < _MIN(m_Size, new_size); i++)
{
diff --git a/lib/libes/include/es/CertificateBody.h b/lib/libpki/include/pki/CertificateBody.h
similarity index 94%
rename from lib/libes/include/es/CertificateBody.h
rename to lib/libpki/include/pki/CertificateBody.h
index 20cbbe9..c02494c 100644
--- a/lib/libes/include/es/CertificateBody.h
+++ b/lib/libpki/include/pki/CertificateBody.h
@@ -1,9 +1,9 @@
#pragma once
#include
#include
-#include
+#include
-namespace es
+namespace pki
{
class CertificateBody
: public fnd::ISerialisable
@@ -27,7 +27,7 @@ namespace es
const std::string& getIssuer() const;
void setIssuer(const std::string& issuer);
- es::cert::PublicKeyType getPublicKeyType() const;
+ pki::cert::PublicKeyType getPublicKeyType() const;
void setPublicKeyType(cert::PublicKeyType type);
const std::string& getSubject() const;
diff --git a/lib/libpki/include/pki/SignUtils.h b/lib/libpki/include/pki/SignUtils.h
new file mode 100644
index 0000000..dc71e2a
--- /dev/null
+++ b/lib/libpki/include/pki/SignUtils.h
@@ -0,0 +1,14 @@
+#pragma once
+#include
+#include
+
+namespace pki
+{
+
+namespace sign
+{
+ pki::sign::SignatureAlgo getSignatureAlgo(pki::sign::SignatureId sign_id);
+ pki::sign::HashAlgo getHashAlgo(pki::sign::SignatureId sign_id);
+}
+
+}
\ No newline at end of file
diff --git a/lib/libes/include/es/SignatureBlock.h b/lib/libpki/include/pki/SignatureBlock.h
similarity index 84%
rename from lib/libes/include/es/SignatureBlock.h
rename to lib/libpki/include/pki/SignatureBlock.h
index 46d5c98..1fcad69 100644
--- a/lib/libes/include/es/SignatureBlock.h
+++ b/lib/libpki/include/pki/SignatureBlock.h
@@ -1,9 +1,9 @@
#pragma once
#include
#include
-#include
+#include
-namespace es
+namespace pki
{
class SignatureBlock
: public fnd::ISerialisable
@@ -24,8 +24,8 @@ namespace es
// variables
void clear();
- es::sign::SignType getSignType() const;
- void setSignType(es::sign::SignType type);
+ pki::sign::SignatureId getSignType() const;
+ void setSignType(pki::sign::SignatureId type);
bool isLittleEndian() const;
void setLittleEndian(bool isLE);
@@ -41,7 +41,7 @@ namespace es
fnd::Vec mRawBinary;
// variables
- es::sign::SignType mSignType;
+ pki::sign::SignatureId mSignType;
bool mIsLittleEndian;
fnd::Vec mSignature;
};
diff --git a/lib/libes/include/es/SignedData.h b/lib/libpki/include/pki/SignedData.h
similarity index 94%
rename from lib/libes/include/es/SignedData.h
rename to lib/libpki/include/pki/SignedData.h
index 366b350..abe38d6 100644
--- a/lib/libes/include/es/SignedData.h
+++ b/lib/libpki/include/pki/SignedData.h
@@ -1,9 +1,9 @@
#pragma once
#include
#include
-#include
+#include
-namespace es
+namespace pki
{
template
class SignedData
@@ -25,7 +25,7 @@ namespace es
// variables
void clear();
- const es::SignatureBlock& getSignature() const;
+ const pki::SignatureBlock& getSignature() const;
void setSignature(const SignatureBlock& signature);
const T& getBody() const;
@@ -111,7 +111,7 @@ namespace es
}
template
- inline const es::SignatureBlock& SignedData::getSignature() const
+ inline const pki::SignatureBlock& SignedData::getSignature() const
{
return mSignature;
}
diff --git a/lib/libes/include/es/cert.h b/lib/libpki/include/pki/cert.h
similarity index 98%
rename from lib/libes/include/es/cert.h
rename to lib/libpki/include/pki/cert.h
index ab4ec0a..c7e3b52 100644
--- a/lib/libes/include/es/cert.h
+++ b/lib/libpki/include/pki/cert.h
@@ -5,7 +5,7 @@
#include
#include
-namespace es
+namespace pki
{
namespace cert
{
diff --git a/lib/libes/include/es/sign.h b/lib/libpki/include/pki/sign.h
similarity index 57%
rename from lib/libes/include/es/sign.h
rename to lib/libpki/include/pki/sign.h
index 61d352f..27442cc 100644
--- a/lib/libes/include/es/sign.h
+++ b/lib/libpki/include/pki/sign.h
@@ -5,21 +5,37 @@
#include
#include
-namespace es
+namespace pki
{
namespace sign
{
- enum SignType
+ enum SignatureId
{
- SIGN_RSA4096_SHA1 = 0x10000,
- SIGN_RSA2048_SHA1,
- SIGN_ECDSA240_SHA1,
- SIGN_RSA4096_SHA256,
- SIGN_RSA2048_SHA256,
- SIGN_ECDSA240_SHA256,
+ SIGN_ID_RSA4096_SHA1 = 0x10000,
+ SIGN_ID_RSA2048_SHA1,
+ SIGN_ID_ECDSA240_SHA1,
+ SIGN_ID_RSA4096_SHA256,
+ SIGN_ID_RSA2048_SHA256,
+ SIGN_ID_ECDSA240_SHA256,
+ };
+
+ enum SignatureAlgo
+ {
+ SIGN_ALGO_RSA4096,
+ SIGN_ALGO_RSA2048,
+ SIGN_ALGO_ECDSA240
+ };
+
+ enum HashAlgo
+ {
+ HASH_ALGO_SHA1,
+ HASH_ALGO_SHA256
};
static const size_t kEcdsaSigSize = 0x3C;
+
+ static const std::string kRootIssuerStr = "Root";
+ static const std::string kIdentDelimiter = "-";
}
#pragma pack(push,1)
struct sRsa4096SignBlock
diff --git a/lib/libpki/libpki.vcxproj b/lib/libpki/libpki.vcxproj
new file mode 100644
index 0000000..7347aca
--- /dev/null
+++ b/lib/libpki/libpki.vcxproj
@@ -0,0 +1,144 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {B9113734-6E84-44FF-8CF7-58199AA815C5}
+ libpki
+ 10.0.16299.0
+
+
+
+ StaticLibrary
+ true
+ v141
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v141
+ true
+ MultiByte
+
+
+ StaticLibrary
+ true
+ v141
+ MultiByte
+
+
+ StaticLibrary
+ false
+ v141
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+ ..\libfnd\include;..\libcrypto\include;..\libpki\include;
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+ true
+ true
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+ ..\libfnd\include;..\libcrypto\include;..\libpki\include;
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+ ..\libfnd\include;..\libcrypto\include;..\libpki\include;
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+ ..\libfnd\include;..\libcrypto\include;..\libpki\include;
+ _CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/libpki/libpki.vcxproj.filters b/lib/libpki/libpki.vcxproj.filters
new file mode 100644
index 0000000..ab6f249
--- /dev/null
+++ b/lib/libpki/libpki.vcxproj.filters
@@ -0,0 +1,51 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+
+
\ No newline at end of file
diff --git a/lib/libpki/libpki.vcxproj.user b/lib/libpki/libpki.vcxproj.user
new file mode 100644
index 0000000..be25078
--- /dev/null
+++ b/lib/libpki/libpki.vcxproj.user
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/lib/libpki/makefile b/lib/libpki/makefile
new file mode 100644
index 0000000..ba0b6e8
--- /dev/null
+++ b/lib/libpki/makefile
@@ -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)
\ No newline at end of file
diff --git a/lib/libes/source/CertificateBody.cpp b/lib/libpki/source/CertificateBody.cpp
similarity index 75%
rename from lib/libes/source/CertificateBody.cpp
rename to lib/libpki/source/CertificateBody.cpp
index 11bec02..d844101 100644
--- a/lib/libes/source/CertificateBody.cpp
+++ b/lib/libpki/source/CertificateBody.cpp
@@ -1,16 +1,16 @@
-#include
+#include
-es::CertificateBody::CertificateBody()
+pki::CertificateBody::CertificateBody()
{
clear();
}
-es::CertificateBody::CertificateBody(const CertificateBody& other)
+pki::CertificateBody::CertificateBody(const CertificateBody& other)
{
*this = other;
}
-void es::CertificateBody::operator=(const CertificateBody& other)
+void pki::CertificateBody::operator=(const CertificateBody& other)
{
mRawBinary = other.mRawBinary;
mIssuer = other.mIssuer;
@@ -22,7 +22,7 @@ void es::CertificateBody::operator=(const CertificateBody& other)
mEcdsa240PublicKey = other.mEcdsa240PublicKey;
}
-bool es::CertificateBody::operator==(const CertificateBody& other) const
+bool pki::CertificateBody::operator==(const CertificateBody& other) const
{
return (mIssuer == other.mIssuer) \
&& (mSubject == other.mSubject) \
@@ -33,12 +33,12 @@ bool es::CertificateBody::operator==(const CertificateBody& other) const
&& (mEcdsa240PublicKey == other.mEcdsa240PublicKey);
}
-bool es::CertificateBody::operator!=(const CertificateBody& other) const
+bool pki::CertificateBody::operator!=(const CertificateBody& other) const
{
return !(*this == other);
}
-void es::CertificateBody::toBytes()
+void pki::CertificateBody::toBytes()
{
// get public key size
size_t pubkeySize = 0;
@@ -86,7 +86,7 @@ void es::CertificateBody::toBytes()
}
}
-void es::CertificateBody::fromBytes(const byte_t* src, size_t size)
+void pki::CertificateBody::fromBytes(const byte_t* src, size_t size)
{
clear();
@@ -129,10 +129,10 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size)
hdr = (const sCertificateHeader*)mRawBinary.data();
if (hdr->issuer[0] != 0)
- mIssuer = std::string(hdr->issuer, cert::kIssuerSize);
+ mIssuer = std::string(hdr->issuer, _MIN(strlen(hdr->issuer), cert::kIssuerSize));
mPublicKeyType = (cert::PublicKeyType)hdr->key_type.get();
if (hdr->subject[0] != 0)
- mSubject = std::string(hdr->subject, cert::kSubjectSize);
+ mSubject = std::string(hdr->subject, _MIN(strlen(hdr->subject), cert::kSubjectSize));
mCertId = hdr->cert_id.get();
// save public key
@@ -155,13 +155,13 @@ void es::CertificateBody::fromBytes(const byte_t* src, size_t size)
}
}
-const fnd::Vec& es::CertificateBody::getBytes() const
+const fnd::Vec& pki::CertificateBody::getBytes() const
{
return mRawBinary;
}
-void es::CertificateBody::clear()
+void pki::CertificateBody::clear()
{
mIssuer.clear();
mSubject.clear();
@@ -173,12 +173,12 @@ void es::CertificateBody::clear()
memset(&mEcdsa240PublicKey, 0, sizeof(crypto::ecdsa::sEcdsa240Point));
}
-const std::string& es::CertificateBody::getIssuer() const
+const std::string& pki::CertificateBody::getIssuer() const
{
return mIssuer;
}
-void es::CertificateBody::setIssuer(const std::string& issuer)
+void pki::CertificateBody::setIssuer(const std::string& issuer)
{
if (issuer.size() > cert::kIssuerSize)
{
@@ -188,22 +188,22 @@ void es::CertificateBody::setIssuer(const std::string& issuer)
mIssuer = issuer;
}
-es::cert::PublicKeyType es::CertificateBody::getPublicKeyType() const
+pki::cert::PublicKeyType pki::CertificateBody::getPublicKeyType() const
{
return mPublicKeyType;
}
-void es::CertificateBody::setPublicKeyType(cert::PublicKeyType type)
+void pki::CertificateBody::setPublicKeyType(cert::PublicKeyType type)
{
mPublicKeyType = type;
}
-const std::string& es::CertificateBody::getSubject() const
+const std::string& pki::CertificateBody::getSubject() const
{
return mSubject;
}
-void es::CertificateBody::setSubject(const std::string& subject)
+void pki::CertificateBody::setSubject(const std::string& subject)
{
if (subject.size() > cert::kSubjectSize)
{
@@ -213,42 +213,42 @@ void es::CertificateBody::setSubject(const std::string& subject)
mSubject = subject;
}
-uint32_t es::CertificateBody::getCertId() const
+uint32_t pki::CertificateBody::getCertId() const
{
return mCertId;
}
-void es::CertificateBody::setCertId(uint32_t id)
+void pki::CertificateBody::setCertId(uint32_t id)
{
mCertId = id;
}
-const crypto::rsa::sRsa4096Key& es::CertificateBody::getRsa4098PublicKey() const
+const crypto::rsa::sRsa4096Key& pki::CertificateBody::getRsa4098PublicKey() const
{
return mRsa4096PublicKey;
}
-void es::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key)
+void pki::CertificateBody::setRsa4098PublicKey(const crypto::rsa::sRsa4096Key& key)
{
mRsa4096PublicKey = key;
}
-const crypto::rsa::sRsa2048Key& es::CertificateBody::getRsa2048PublicKey() const
+const crypto::rsa::sRsa2048Key& pki::CertificateBody::getRsa2048PublicKey() const
{
return mRsa2048PublicKey;
}
-void es::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key)
+void pki::CertificateBody::setRsa2048PublicKey(const crypto::rsa::sRsa2048Key& key)
{
mRsa2048PublicKey = key;
}
-const crypto::ecdsa::sEcdsa240Point& es::CertificateBody::getEcdsa240PublicKey() const
+const crypto::ecdsa::sEcdsa240Point& pki::CertificateBody::getEcdsa240PublicKey() const
{
return mEcdsa240PublicKey;
}
-void es::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key)
+void pki::CertificateBody::setEcdsa240PublicKey(const crypto::ecdsa::sEcdsa240Point& key)
{
mEcdsa240PublicKey = key;
}
\ No newline at end of file
diff --git a/lib/libpki/source/SignUtils.cpp b/lib/libpki/source/SignUtils.cpp
new file mode 100644
index 0000000..43cfb1e
--- /dev/null
+++ b/lib/libpki/source/SignUtils.cpp
@@ -0,0 +1,45 @@
+#include
+
+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;
+}
\ No newline at end of file
diff --git a/lib/libes/source/SignatureBlock.cpp b/lib/libpki/source/SignatureBlock.cpp
similarity index 60%
rename from lib/libes/source/SignatureBlock.cpp
rename to lib/libpki/source/SignatureBlock.cpp
index 4aa591d..bf19b20 100644
--- a/lib/libes/source/SignatureBlock.cpp
+++ b/lib/libpki/source/SignatureBlock.cpp
@@ -1,16 +1,16 @@
-#include
+#include
-es::SignatureBlock::SignatureBlock()
+pki::SignatureBlock::SignatureBlock()
{
clear();
}
-es::SignatureBlock::SignatureBlock(const SignatureBlock& other)
+pki::SignatureBlock::SignatureBlock(const SignatureBlock& other)
{
*this = other;
}
-void es::SignatureBlock::operator=(const SignatureBlock& other)
+void pki::SignatureBlock::operator=(const SignatureBlock& other)
{
mRawBinary = other.mRawBinary;
mSignType = other.mSignType;
@@ -18,37 +18,37 @@ void es::SignatureBlock::operator=(const SignatureBlock& other)
mSignature = other.mSignature;
}
-bool es::SignatureBlock::operator==(const SignatureBlock& other) const
+bool pki::SignatureBlock::operator==(const SignatureBlock& other) const
{
return (mSignType == other.mSignType) \
&& (mIsLittleEndian == other.mIsLittleEndian) \
&& (mSignature == other.mSignature);
}
-bool es::SignatureBlock::operator!=(const SignatureBlock& other) const
+bool pki::SignatureBlock::operator!=(const SignatureBlock& other) const
{
return !(*this == other);
}
-void es::SignatureBlock::toBytes()
+void pki::SignatureBlock::toBytes()
{
size_t totalSize = 0;
size_t sigSize = 0;
switch (mSignType)
{
- case (sign::SIGN_RSA4096_SHA1):
- case (sign::SIGN_RSA4096_SHA256):
+ case (sign::SIGN_ID_RSA4096_SHA1):
+ case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size;
break;
- case (sign::SIGN_RSA2048_SHA1):
- case (sign::SIGN_RSA2048_SHA256):
+ case (sign::SIGN_ID_RSA2048_SHA1):
+ case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size;
break;
- case (sign::SIGN_ECDSA240_SHA1):
- case (sign::SIGN_ECDSA240_SHA256):
+ case (sign::SIGN_ID_ECDSA240_SHA1):
+ case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize;
break;
@@ -68,7 +68,7 @@ void es::SignatureBlock::toBytes()
memcpy(mRawBinary.data() + 4, mSignature.data(), sigSize);
}
-void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
+void pki::SignatureBlock::fromBytes(const byte_t* src, size_t size)
{
clear();
@@ -80,18 +80,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
signType = ((be_uint32_t*)src)->get();
switch (signType)
{
- case (sign::SIGN_RSA4096_SHA1):
- case (sign::SIGN_RSA4096_SHA256):
+ case (sign::SIGN_ID_RSA4096_SHA1):
+ case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size;
break;
- case (sign::SIGN_RSA2048_SHA1):
- case (sign::SIGN_RSA2048_SHA256):
+ case (sign::SIGN_ID_RSA2048_SHA1):
+ case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size;
break;
- case (sign::SIGN_ECDSA240_SHA1):
- case (sign::SIGN_ECDSA240_SHA256):
+ case (sign::SIGN_ID_ECDSA240_SHA1):
+ case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize;
break;
@@ -103,18 +103,18 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
signType = ((le_uint32_t*)src)->get();
switch (signType)
{
- case (sign::SIGN_RSA4096_SHA1):
- case (sign::SIGN_RSA4096_SHA256):
+ case (sign::SIGN_ID_RSA4096_SHA1):
+ case (sign::SIGN_ID_RSA4096_SHA256):
totalSize = sizeof(sRsa4096SignBlock);
sigSize = crypto::rsa::kRsa4096Size;
break;
- case (sign::SIGN_RSA2048_SHA1):
- case (sign::SIGN_RSA2048_SHA256):
+ case (sign::SIGN_ID_RSA2048_SHA1):
+ case (sign::SIGN_ID_RSA2048_SHA256):
totalSize = sizeof(sRsa2048SignBlock);
sigSize = crypto::rsa::kRsa2048Size;
break;
- case (sign::SIGN_ECDSA240_SHA1):
- case (sign::SIGN_ECDSA240_SHA256):
+ case (sign::SIGN_ID_ECDSA240_SHA1):
+ case (sign::SIGN_ID_ECDSA240_SHA256):
totalSize = sizeof(sEcdsa240SignBlock);
sigSize = sign::kEcdsaSigSize;
break;
@@ -133,50 +133,50 @@ void es::SignatureBlock::fromBytes(const byte_t* src, size_t size)
mRawBinary.alloc(totalSize);
memcpy(mRawBinary.data(), src, totalSize);
- mSignType = (sign::SignType)signType;
+ mSignType = (sign::SignatureId)signType;
mSignature.alloc(sigSize);
memcpy(mSignature.data(), mRawBinary.data() + 4, sigSize);
}
-const fnd::Vec& es::SignatureBlock::getBytes() const
+const fnd::Vec& pki::SignatureBlock::getBytes() const
{
return mRawBinary;
}
-void es::SignatureBlock::clear()
+void pki::SignatureBlock::clear()
{
mRawBinary.clear();
- mSignType = sign::SIGN_RSA4096_SHA1;
+ mSignType = sign::SIGN_ID_RSA4096_SHA1;
mIsLittleEndian = false;
mSignature.clear();
}
-es::sign::SignType es::SignatureBlock::getSignType() const
+pki::sign::SignatureId pki::SignatureBlock::getSignType() const
{
return mSignType;
}
-void es::SignatureBlock::setSignType(es::sign::SignType type)
+void pki::SignatureBlock::setSignType(pki::sign::SignatureId type)
{
mSignType = type;
}
-bool es::SignatureBlock::isLittleEndian() const
+bool pki::SignatureBlock::isLittleEndian() const
{
return mIsLittleEndian;
}
-void es::SignatureBlock::setLittleEndian(bool isLE)
+void pki::SignatureBlock::setLittleEndian(bool isLE)
{
mIsLittleEndian = isLE;
}
-const fnd::Vec& es::SignatureBlock::getSignature() const
+const fnd::Vec& pki::SignatureBlock::getSignature() const
{
return mSignature;
}
-void es::SignatureBlock::setSignature(const fnd::Vec& signature)
+void pki::SignatureBlock::setSignature(const fnd::Vec& signature)
{
mSignature = signature;
}
diff --git a/lib/makefile b/lib/makefile
index 89636d0..7928e45 100644
--- a/lib/makefile
+++ b/lib/makefile
@@ -1,4 +1,4 @@
-LIBS = libfnd libcrypto libcompress libes libnx libnx-hb
+LIBS = libfnd libcrypto libcompress libes libpki libnx libnx-hb
main: build
rebuild: clean build
diff --git a/programs/nstool/makefile b/programs/nstool/makefile
index 85aa304..ddbdcd3 100644
--- a/programs/nstool/makefile
+++ b/programs/nstool/makefile
@@ -3,7 +3,7 @@ SRC_DIR = source
OBJS = $(foreach dir,$(SRC_DIR),$(subst .cpp,.o,$(wildcard $(dir)/*.cpp))) $(foreach dir,$(SRC_DIR),$(subst .c,.o,$(wildcard $(dir)/*.c)))
# External dependencies
-DEPENDS = nx-hb nx crypto compress fnd
+DEPENDS = nx-hb nx es pki crypto compress fnd
LIB_DIR = ../../lib
LIBS = $(foreach dep,$(DEPENDS), -L"$(LIB_DIR)/lib$(dep)" -l$(dep))
INCS = $(foreach dep,$(DEPENDS), -I"$(LIB_DIR)/lib$(dep)/include")
diff --git a/programs/nstool/nstool.vcxproj b/programs/nstool/nstool.vcxproj
index 35b393f..848e899 100644
--- a/programs/nstool/nstool.vcxproj
+++ b/programs/nstool/nstool.vcxproj
@@ -90,7 +90,7 @@
true
_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
+ ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
Console
@@ -105,7 +105,7 @@
true
WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
+ ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
Console
@@ -122,7 +122,7 @@
true
WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
+ ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
Console
@@ -141,7 +141,7 @@
true
NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
true
- ..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
+ ..\..\lib\libpki\include;..\..\lib\libes\include;..\..\lib\libfnd\include;..\..\lib\libcompress\include;..\..\lib\libcrypto\include;..\..\lib\libnx\include;..\..\lib\libnx-hb\include
Console
@@ -157,6 +157,9 @@
{6adbb60d-dba0-411d-bd2d-a355ef8e0fe1}
+
+ {7be99936-0d40-410d-944b-4513c2eff8dc}
+
{4d27edb9-5110-44fe-8ce2-d46c5ad3c55b}
@@ -166,12 +169,19 @@
{91ba9e79-8242-4f7d-b997-0dfec95ea22b}
+
+ {b9113734-6e84-44ff-8cf7-58199aa815c5}
+
+
+
+
+
@@ -182,6 +192,8 @@
+
+
@@ -194,6 +206,7 @@
+
@@ -204,15 +217,14 @@
+
+
-
-
-
diff --git a/programs/nstool/nstool.vcxproj.filters b/programs/nstool/nstool.vcxproj.filters
index 4d94f58..3d906bf 100644
--- a/programs/nstool/nstool.vcxproj.filters
+++ b/programs/nstool/nstool.vcxproj.filters
@@ -15,21 +15,69 @@
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
Header Files
Header Files
+
+ Header Files
+
+
+ Header Files
+
Header Files
+
+ Header Files
+
Header Files
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
Header Files
+
+ Header Files
+
Header Files
@@ -39,103 +87,73 @@
Header Files
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Header Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
Source Files
-
- Source Files
-
-
+
Source Files
Source Files
+
+ Source Files
+
+
+ Source Files
+
Source Files
Source Files
-
- Source Files
-
-
- Source Files
-
-
- Source Files
-
-
+
Source Files
Source Files
-
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
Source Files
Source Files
-
-
-
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
\ No newline at end of file
diff --git a/programs/nstool/source/EsTikProcess.cpp b/programs/nstool/source/EsTikProcess.cpp
new file mode 100644
index 0000000..22402a8
--- /dev/null
+++ b/programs/nstool/source/EsTikProcess.cpp
@@ -0,0 +1,269 @@
+#include
+#include
+
+#include
+#include
+#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>& 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 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 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;
+}
\ No newline at end of file
diff --git a/programs/nstool/source/EsTikProcess.h b/programs/nstool/source/EsTikProcess.h
new file mode 100644
index 0000000..2ada90c
--- /dev/null
+++ b/programs/nstool/source/EsTikProcess.h
@@ -0,0 +1,45 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#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>& 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> mCerts;
+
+ pki::SignedData 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;
+};
\ No newline at end of file
diff --git a/programs/nstool/source/NcaProcess.cpp b/programs/nstool/source/NcaProcess.cpp
index 89f5d80..5080f06 100644
--- a/programs/nstool/source/NcaProcess.cpp
+++ b/programs/nstool/source/NcaProcess.cpp
@@ -286,27 +286,6 @@ void NcaProcess::process()
// process partition
processPartitions();
-
- /*
- NCA is a file container
- A hashed and signed file container
-
- To verify a NCA: (R=regular step)
- 1 - decrypt header (R)
- 2 - verify signature[0]
- 3 - validate hashes of fs_headers
- 4 - determine how to read/decrypt the partitions (R)
- 5 - validate the partitions depending on their hash method
- 6 - if this NCA is a Program or Patch, open main.npdm from partition0
- 7 - validate ACID
- 8 - use public key in ACID to verify NCA signature[1]
-
- Things to consider
- * because of the manditory steps between verifcation steps
- the NCA should be ready to be pulled to pieces before any printing is done
- so the verification text can be presented without interuption
-
- */
}
void NcaProcess::setInputFile(fnd::IFile* file, bool ownIFile)
@@ -695,9 +674,10 @@ void NcaProcess::displayHeader()
printf(" Partitions:\n");
for (size_t i = 0; i < mHdr.getPartitions().size(); i++)
{
- sPartitionInfo& info = mPartitions[i];
+ size_t index = mHdr.getPartitions()[i].index;
+ sPartitionInfo& info = mPartitions[index];
- printf(" %d:\n", (int)i);
+ printf(" %d:\n", (int)index);
printf(" Offset: 0x%" PRIx64 "\n", (uint64_t)info.offset);
printf(" Size: 0x%" PRIx64 "\n", (uint64_t)info.size);
printf(" Format Type: %s\n", getFormatTypeStr(info.format_type));
@@ -790,7 +770,7 @@ void NcaProcess::processPartitions()
pfs.setListFs(mListFs);
if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM)
{
- pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(i)));
+ pfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(index)));
}
else
{
@@ -811,7 +791,7 @@ void NcaProcess::processPartitions()
romfs.setListFs(mListFs);
if (mHdr.getContentType() == nx::nca::TYPE_PROGRAM)
{
- romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(i)));
+ romfs.setMountPointName(std::string(getContentTypeForMountStr(mHdr.getContentType())) + ":/" + std::string(getProgramPartitionNameStr(index)));
}
else
{
diff --git a/programs/nstool/source/PkiCertProcess.cpp b/programs/nstool/source/PkiCertProcess.cpp
new file mode 100644
index 0000000..80f3b20
--- /dev/null
+++ b/programs/nstool/source/PkiCertProcess.cpp
@@ -0,0 +1,213 @@
+#include
+#include
+
+#include
+#include
+#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 scratch;
+
+ scratch.alloc(mFile->size());
+ mFile->read(scratch.data(), 0, scratch.size());
+
+ pki::SignedData 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& 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;
+}
\ No newline at end of file
diff --git a/programs/nstool/source/PkiCertProcess.h b/programs/nstool/source/PkiCertProcess.h
new file mode 100644
index 0000000..31c63b2
--- /dev/null
+++ b/programs/nstool/source/PkiCertProcess.h
@@ -0,0 +1,45 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#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> mCert;
+
+ void importCerts();
+ void validateCerts();
+ void displayCerts();
+ void displayCert(const pki::SignedData& 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;
+};
\ No newline at end of file
diff --git a/programs/nstool/source/PkiValidator.cpp b/programs/nstool/source/PkiValidator.cpp
new file mode 100644
index 0000000..00b2088
--- /dev/null
+++ b/programs/nstool/source/PkiValidator.cpp
@@ -0,0 +1,197 @@
+#include "PkiValidator.h"
+#include
+#include
+#include
+#include
+
+PkiValidator::PkiValidator()
+{
+ clearCertificates();
+}
+
+void PkiValidator::setRootKey(const crypto::rsa::sRsa4096Key& root_key)
+{
+ // save a copy of the certificate bank
+ fnd::List> 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>& certs)
+{
+ for (size_t i = 0; i < certs.size(); i++)
+ {
+ addCertificate(certs[i]);
+ }
+}
+
+void PkiValidator::addCertificate(const pki::SignedData& cert)
+{
+ std::string cert_ident;
+ pki::sign::SignatureAlgo cert_sign_algo;
+ pki::sign::HashAlgo cert_hash_algo;
+ fnd::Vec 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& signature, const fnd::Vec& 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& 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& 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;
+}
\ No newline at end of file
diff --git a/programs/nstool/source/PkiValidator.h b/programs/nstool/source/PkiValidator.h
new file mode 100644
index 0000000..217dfad
--- /dev/null
+++ b/programs/nstool/source/PkiValidator.h
@@ -0,0 +1,34 @@
+#pragma once
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+class PkiValidator
+{
+public:
+ PkiValidator();
+
+ void setRootKey(const crypto::rsa::sRsa4096Key& root_key);
+ void addCertificates(const fnd::List>& certs);
+ void addCertificate(const pki::SignedData& cert);
+ void clearCertificates();
+
+ void validateSignature(const std::string& issuer, pki::sign::SignatureId signature_id, const fnd::Vec& signature, const fnd::Vec& hash) const;
+
+private:
+ const std::string kModuleName = "NNPkiValidator";
+
+
+ crypto::rsa::sRsa4096Key mRootKey;
+ fnd::List> mCertificateBank;
+
+ void makeCertIdent(const pki::SignedData& 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& getCert(const std::string& ident) const;
+ crypto::sha::HashType getCryptoHashAlgoFromEsSignHashAlgo(pki::sign::HashAlgo hash_algo) const;
+};
\ No newline at end of file
diff --git a/programs/nstool/source/UserSettings.cpp b/programs/nstool/source/UserSettings.cpp
index 7b844f5..f2c5297 100644
--- a/programs/nstool/source/UserSettings.cpp
+++ b/programs/nstool/source/UserSettings.cpp
@@ -1,5 +1,6 @@
#include "UserSettings.h"
#include "version.h"
+#include "PkiValidator.h"
#include
#include
#include
@@ -22,6 +23,10 @@
#include
#include
#include
+#include
+#include
+#include
+#include
UserSettings::UserSettings()
{}
@@ -41,34 +46,36 @@ void UserSettings::showHelp()
printf("Usage: nstool [options... ] \n");
printf("\n General Options:\n");
- printf(" -d, --dev Use devkit keyset\n");
- printf(" -k, --keyset Specify keyset file\n");
- printf(" -t, --type Specify input file type [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset]\n");
- printf(" -y, --verify Verify file\n");
+ printf(" -d, --dev Use devkit keyset.\n");
+ printf(" -k, --keyset Specify keyset file.\n");
+ printf(" -t, --type Specify input file type. [xci, pfs, romfs, nca, npdm, cnmt, nso, nro, nacp, aset, cert, tik]\n");
+ printf(" -y, --verify Verify file.\n");
printf("\n Output Options:\n");
- printf(" --showkeys Show keys generated\n");
- printf(" --showlayout Show layout metadata\n");
- printf(" -v, --verbose Verbose output\n");
+ printf(" --showkeys Show keys generated.\n");
+ printf(" --showlayout Show layout metadata.\n");
+ printf(" -v, --verbose Verbose output.\n");
printf("\n XCI (GameCard Image)\n");
printf(" nstool [--listfs] [--update --logo --normal --secure ] <.xci file>\n");
- printf(" --listfs Print file system in embedded partitions\n");
- printf(" --update Extract \"update\" partition to directory\n");
- printf(" --logo Extract \"logo\" partition to directory\n");
- printf(" --normal Extract \"normal\" partition to directory\n");
- printf(" --secure Extract \"secure\" partition to directory\n");
+ printf(" --listfs Print file system in embedded partitions.\n");
+ printf(" --update Extract \"update\" partition to directory.\n");
+ printf(" --logo Extract \"logo\" partition to directory.\n");
+ printf(" --normal Extract \"normal\" partition to directory.\n");
+ printf(" --secure Extract \"secure\" partition to directory.\n");
printf("\n PFS0/HFS0 (PartitionFs), RomFs, NSP (Ninendo Submission Package)\n");
printf(" nstool [--listfs] [--fsdir ] \n");
- printf(" --listfs Print file system\n");
- printf(" --fsdir Extract file system to directory\n");
+ printf(" --listfs Print file system.\n");
+ printf(" --fsdir Extract file system to directory.\n");
printf("\n NCA (Nintendo Content Archive)\n");
printf(" nstool [--listfs] [--bodykey --titlekey ] [--part0 ...] <.nca file>\n");
- printf(" --listfs Print file system in embedded partitions\n");
- printf(" --titlekey Specify title key extracted from ticket\n");
- printf(" --bodykey Specify body encryption key\n");
- printf(" --part0 Extract \"partition 0\" to directory \n");
- printf(" --part1 Extract \"partition 1\" to directory \n");
- printf(" --part2 Extract \"partition 2\" to directory \n");
- printf(" --part3 Extract \"partition 3\" to directory \n");
+ printf(" --listfs Print file system in embedded partitions.\n");
+ printf(" --titlekey Specify title key extracted from ticket.\n");
+ printf(" --bodykey Specify body encryption key.\n");
+ printf(" --tik Specify ticket to source title key.\n");
+ printf(" --cert Specify certificate chain to verify ticket.\n");
+ printf(" --part0 Extract \"partition 0\" to directory.\n");
+ printf(" --part1 Extract \"partition 1\" to directory.\n");
+ printf(" --part2 Extract \"partition 2\" to directory.\n");
+ printf(" --part3 Extract \"partition 3\" to directory.\n");
printf("\n NSO (Nintendo Software Object), NRO (Nintendo Relocatable Object)\n");
printf(" nstool [--listapi --listsym] [--insttype ] \n");
printf(" --listapi Print SDK API List.\n");
@@ -182,6 +189,11 @@ const sOptional& UserSettings::getAssetNacpPath() const
return mAssetNacpPath;
}
+const fnd::List>& UserSettings::getCertificateChain() const
+{
+ return mCertChain;
+}
+
void UserSettings::populateCmdArgs(const std::vector& arg_list, sCmdArgs& cmd_args)
{
// show help text
@@ -296,6 +308,18 @@ void UserSettings::populateCmdArgs(const std::vector& arg_list, sCm
cmd_args.nca_bodykey = arg_list[i+1];
}
+ else if (arg_list[i] == "--tik")
+ {
+ if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter.");
+ cmd_args.ticket_path = arg_list[i+1];
+ }
+
+ else if (arg_list[i] == "--cert")
+ {
+ if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter.");
+ cmd_args.cert_path = arg_list[i+1];
+ }
+
else if (arg_list[i] == "--part0")
{
if (!hasParamter) throw fnd::Exception(kModuleName, arg_list[i] + " requries a parameter.");
@@ -415,6 +439,7 @@ void UserSettings::populateKeyset(sCmdArgs& args)
const std::string kKekGenSource = "aes_kek_generation";
const std::string kKeyGenSource = "aes_key_generation";
const std::string kAcidBase = "acid";
+ const std::string kPkiRootBase = "pki_root";
const std::string kTicketCommonKeyBase[2] = { "titlekek", "ticket_commonkey" };
const std::string kNcaBodyBase[2] = {"key_area_key", "nca_body_keak"};
const std::string kNcaBodyKeakIndexName[3] = {"application", "ocean", "system"};
@@ -484,17 +509,21 @@ void UserSettings::populateKeyset(sCmdArgs& args)
_SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kKeyStr), mKeyset.xci.header_key.key, 0x10);
// store rsa keys
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, 0x100);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, 0x100);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[0]), mKeyset.nca.header_sign_key.priv_exponent, crypto::rsa::kRsa2048Size);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kNcaHeaderBase[1], kRsaKeySuffix[1]), mKeyset.nca.header_sign_key.modulus, crypto::rsa::kRsa2048Size);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, 0x100);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, 0x100);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[0]), mKeyset.xci.header_sign_key.priv_exponent, crypto::rsa::kRsa2048Size);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kXciHeaderBase, kRsaKeySuffix[1]), mKeyset.xci.header_sign_key.modulus, crypto::rsa::kRsa2048Size);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, 0x100);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, 0x100);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[0]), mKeyset.acid_sign_key.priv_exponent, crypto::rsa::kRsa2048Size);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kAcidBase, kRsaKeySuffix[1]), mKeyset.acid_sign_key.modulus, crypto::rsa::kRsa2048Size);
+
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, crypto::rsa::kRsa2048Size);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, crypto::rsa::kRsa2048Size);
+
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[0]), mKeyset.pki.root_sign_key.priv_exponent, crypto::rsa::kRsa4096Size);
+ _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPkiRootBase, kRsaKeySuffix[1]), mKeyset.pki.root_sign_key.modulus, crypto::rsa::kRsa4096Size);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[0]), mKeyset.package2_sign_key.priv_exponent, 0x100);
- _SAVE_KEYDATA(_CONCAT_2_STRINGS(kPackage2Base, kRsaKeySuffix[1]), mKeyset.package2_sign_key.modulus, 0x100);
// save keydata from input args
if (args.nca_bodykey.isSet)
@@ -521,6 +550,79 @@ void UserSettings::populateKeyset(sCmdArgs& args)
}
}
+ // import certificate chain
+ if (args.cert_path.isSet)
+ {
+ fnd::SimpleFile cert_file;
+ fnd::Vec cert_raw;
+ pki::SignedData 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 tik_raw;
+ pki::SignedData 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 tik_hash;
+
+ switch (pki::sign::getHashAlgo(tik.getSignature().getSignType()))
+ {
+ case (pki::sign::HASH_ALGO_SHA1):
+ tik_hash.alloc(crypto::sha::kSha1HashLen);
+ crypto::sha::Sha1(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data());
+ break;
+ case (pki::sign::HASH_ALGO_SHA256):
+ tik_hash.alloc(crypto::sha::kSha256HashLen);
+ crypto::sha::Sha256(tik.getBody().getBytes().data(), tik.getBody().getBytes().size(), tik_hash.data());
+ break;
+ }
+
+ try
+ {
+ pki_validator.setRootKey(mKeyset.pki.root_sign_key);
+ pki_validator.addCertificates(mCertChain);
+ pki_validator.validateSignature(tik.getBody().getIssuer(), tik.getSignature().getSignType(), tik.getSignature().getSignature(), tik_hash);
+ }
+ catch (const fnd::Exception& e)
+ {
+ std::cout << "[WARNING] Ticket signature could not be validated (" << e.error() << ")" << std::endl;
+ }
+
+ }
+
+ // extract title key
+ if (tik.getBody().getTitleKeyEncType() == es::ticket::AES128_CBC)
+ {
+ memcpy(mKeyset.nca.manual_title_key_aesctr.key, tik.getBody().getEncTitleKey(), crypto::aes::kAes128KeySize);
+ }
+ else
+ {
+ std::cout << "[WARNING] Titlekey not imported from ticket because it is personalised" << std::endl;
+ }
+ }
+
#undef _SAVE_KEYDATA
#undef _CONCAT_3_STRINGS
#undef _CONCAT_2_STRINGS
@@ -688,6 +790,10 @@ FileType UserSettings::getFileTypeFromString(const std::string& type_str)
type = FILE_NRO;
else if (str == "nacp")
type = FILE_NACP;
+ else if (str == "cert")
+ type = FILE_PKI_CERT;
+ else if (str == "tik")
+ type = FILE_ES_TIK;
else if (str == "aset" || str == "asset")
type = FILE_HB_ASSET;
else
@@ -746,6 +852,12 @@ FileType UserSettings::determineFileTypeFromFile(const std::string& path)
// test nso
else if (_ASSERT_SIZE(sizeof(nx::sNroHeader)) && _TYPE_PTR(nx::sNroHeader)->st_magic.get() == nx::nro::kNroStructMagic)
file_type = FILE_NRO;
+ // test pki certificate
+ else if (determineValidEsCertFromSample(scratch))
+ file_type = FILE_PKI_CERT;
+ // test ticket
+ else if (determineValidEsTikFromSample(scratch))
+ file_type = FILE_ES_TIK;
// test hb asset
else if (_ASSERT_SIZE(sizeof(nx::sAssetHeader)) && _TYPE_PTR(nx::sAssetHeader)->st_magic.get() == nx::aset::kAssetStructMagic)
file_type = FILE_HB_ASSET;
@@ -848,6 +960,50 @@ bool UserSettings::determineValidNacpFromSample(const fnd::Vec& sample)
return true;
}
+bool UserSettings::determineValidEsCertFromSample(const fnd::Vec& 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& sample) const
+{
+ pki::SignatureBlock sign;
+
+ try
+ {
+ sign.fromBytes(sample.data(), sample.size());
+ }
+ catch (...)
+ {
+ return false;
+ }
+
+ if (sign.isLittleEndian() == false)
+ return false;
+
+ if (sign.getSignType() != pki::sign::SIGN_ID_RSA2048_SHA256)
+ return false;
+
+ return true;
+}
+
nx::npdm::InstructionType UserSettings::getInstructionTypeFromString(const std::string & type_str)
{
std::string str = type_str;
diff --git a/programs/nstool/source/UserSettings.h b/programs/nstool/source/UserSettings.h
index f7e1969..fd53077 100644
--- a/programs/nstool/source/UserSettings.h
+++ b/programs/nstool/source/UserSettings.h
@@ -3,6 +3,9 @@
#include
#include
#include
+#include
+#include
+#include
#include
#include "nstool.h"
@@ -39,6 +42,7 @@ public:
const sOptional& getNcaPart3Path() const;
const sOptional& getAssetIconPath() const;
const sOptional& getAssetNacpPath() const;
+ const fnd::List>& getCertificateChain() const;
private:
const std::string kModuleName = "UserSettings";
@@ -62,6 +66,8 @@ private:
sOptional fs_path;
sOptional nca_titlekey;
sOptional nca_bodykey;
+ sOptional ticket_path;
+ sOptional cert_path;
sOptional part0_path;
sOptional part1_path;
sOptional part2_path;
@@ -94,6 +100,8 @@ private:
sOptional mAssetIconPath;
sOptional mAssetNacpPath;
+ fnd::List> mCertChain;
+
bool mListApi;
bool mListSymbols;
nx::npdm::InstructionType mInstructionType;
@@ -107,5 +115,7 @@ private:
bool determineValidNcaFromSample(const fnd::Vec& sample) const;
bool determineValidCnmtFromSample(const fnd::Vec& sample) const;
bool determineValidNacpFromSample(const fnd::Vec& sample) const;
+ bool determineValidEsCertFromSample(const fnd::Vec& sample) const;
+ bool determineValidEsTikFromSample(const fnd::Vec& sample) const;
nx::npdm::InstructionType getInstructionTypeFromString(const std::string& type_str);
};
\ No newline at end of file
diff --git a/programs/nstool/source/main.cpp b/programs/nstool/source/main.cpp
index ef1fafb..7eefe54 100644
--- a/programs/nstool/source/main.cpp
+++ b/programs/nstool/source/main.cpp
@@ -11,6 +11,8 @@
#include "NsoProcess.h"
#include "NroProcess.h"
#include "NacpProcess.h"
+#include "PkiCertProcess.h"
+#include "EsTikProcess.h"
#include "AssetProcess.h"
#ifdef _WIN32
@@ -173,6 +175,29 @@ int main(int argc, char** argv)
nacp.process();
}
+ else if (user_set.getFileType() == FILE_PKI_CERT)
+ {
+ PkiCertProcess cert;
+
+ cert.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
+ cert.setKeyset(&user_set.getKeyset());
+ cert.setCliOutputMode(user_set.getCliOutputMode());
+ cert.setVerifyMode(user_set.isVerifyFile());
+
+ cert.process();
+ }
+ else if (user_set.getFileType() == FILE_ES_TIK)
+ {
+ EsTikProcess tik;
+
+ tik.setInputFile(new fnd::SimpleFile(user_set.getInputPath(), fnd::SimpleFile::Read), OWN_IFILE);
+ tik.setKeyset(&user_set.getKeyset());
+ tik.setCertificateChain(user_set.getCertificateChain());
+ tik.setCliOutputMode(user_set.getCliOutputMode());
+ tik.setVerifyMode(user_set.isVerifyFile());
+
+ tik.process();
+ }
else if (user_set.getFileType() == FILE_HB_ASSET)
{
AssetProcess obj;
diff --git a/programs/nstool/source/nstool.h b/programs/nstool/source/nstool.h
index cf6505c..a52c571 100644
--- a/programs/nstool/source/nstool.h
+++ b/programs/nstool/source/nstool.h
@@ -27,6 +27,8 @@ enum FileType
FILE_NSO,
FILE_NRO,
FILE_NACP,
+ FILE_PKI_CERT,
+ FILE_ES_TIK,
FILE_HB_ASSET,
FILE_INVALID = -1,
};
@@ -62,7 +64,6 @@ struct sOptional
struct sKeyset
{
crypto::rsa::sRsa2048Key acid_sign_key;
-
crypto::aes::sAes128Key package1_key[kMasterKeyNum];
crypto::rsa::sRsa2048Key package2_sign_key;
crypto::aes::sAes128Key package2_key[kMasterKeyNum];
@@ -84,12 +85,17 @@ struct sKeyset
crypto::rsa::sRsa2048Key header_sign_key;
crypto::aes::sAes128Key header_key;
} xci;
-
+
struct sTicketData
{
crypto::rsa::sRsa2048Key sign_key;
crypto::aes::sAes128Key titlekey_kek[kMasterKeyNum];
} ticket;
+
+ struct sPkiData
+ {
+ crypto::rsa::sRsa4096Key root_sign_key;
+ } pki;
};
inline byte_t charToByte(char chr)