Make XCI comply to review and style guidelines
This commit is contained in:
		| @@ -2,58 +2,69 @@ | |||||||
| // Licensed under GPLv2 or any later version | // Licensed under GPLv2 or any later version | ||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
|  | #include <mbedtls/cipher.h> | ||||||
| #include "core/crypto/aes_util.h" | #include "core/crypto/aes_util.h" | ||||||
| #include "mbedtls/cipher.h" | #include "core/crypto/key_manager.h" | ||||||
|  |  | ||||||
| namespace Core::Crypto { | namespace Core::Crypto { | ||||||
| static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), "CTR mode is incorrect."); |  | ||||||
| static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), "ECB mode is incorrect."); |  | ||||||
| static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), "XTS mode is incorrect."); |  | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | static_assert(static_cast<size_t>(Mode::CTR) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_CTR), | ||||||
| Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) { |               "CTR has incorrect value."); | ||||||
|     mbedtls_cipher_init(encryption_context.get()); | static_assert(static_cast<size_t>(Mode::ECB) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_ECB), | ||||||
|     mbedtls_cipher_init(decryption_context.get()); |               "ECB has incorrect value."); | ||||||
|  | static_assert(static_cast<size_t>(Mode::XTS) == static_cast<size_t>(MBEDTLS_CIPHER_AES_128_XTS), | ||||||
|  |               "XTS has incorrect value."); | ||||||
|  |  | ||||||
|  | // Structure to hide mbedtls types from header file | ||||||
|  | struct CipherContext { | ||||||
|  |     mbedtls_cipher_context_t encryption_context; | ||||||
|  |     mbedtls_cipher_context_t decryption_context; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename Key, size_t KeySize> | ||||||
|  | Crypto::AESCipher<Key, KeySize>::AESCipher(Key key, Mode mode) | ||||||
|  |     : ctx(std::make_unique<CipherContext>()) { | ||||||
|  |     mbedtls_cipher_init(&ctx->encryption_context); | ||||||
|  |     mbedtls_cipher_init(&ctx->decryption_context); | ||||||
|  |  | ||||||
|     ASSERT_MSG((mbedtls_cipher_setup( |     ASSERT_MSG((mbedtls_cipher_setup( | ||||||
|             encryption_context.get(), |                     &ctx->encryption_context, | ||||||
|             mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) || |                     mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) || | ||||||
|                 mbedtls_cipher_setup(decryption_context.get(), |                 mbedtls_cipher_setup( | ||||||
|                                      mbedtls_cipher_info_from_type( |                     &ctx->decryption_context, | ||||||
|                                              static_cast<mbedtls_cipher_type_t>(mode)))) == 0, |                     mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode)))) == 0, | ||||||
|                "Failed to initialize mbedtls ciphers."); |                "Failed to initialize mbedtls ciphers."); | ||||||
|  |  | ||||||
|     ASSERT( |     ASSERT( | ||||||
|             !mbedtls_cipher_setkey(encryption_context.get(), key.data(), KeySize * 8, MBEDTLS_ENCRYPT)); |         !mbedtls_cipher_setkey(&ctx->encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT)); | ||||||
|     ASSERT( |     ASSERT( | ||||||
|             !mbedtls_cipher_setkey(decryption_context.get(), key.data(), KeySize * 8, MBEDTLS_DECRYPT)); |         !mbedtls_cipher_setkey(&ctx->decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT)); | ||||||
|     //"Failed to set key on mbedtls ciphers."); |     //"Failed to set key on mbedtls ciphers."); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | template <typename Key, size_t KeySize> | ||||||
| AESCipher<Key, KeySize>::~AESCipher() { | AESCipher<Key, KeySize>::~AESCipher() { | ||||||
|     mbedtls_cipher_free(encryption_context.get()); |     mbedtls_cipher_free(&ctx->encryption_context); | ||||||
|     mbedtls_cipher_free(decryption_context.get()); |     mbedtls_cipher_free(&ctx->decryption_context); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | template <typename Key, size_t KeySize> | ||||||
| void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | void AESCipher<Key, KeySize>::SetIV(std::vector<u8> iv) { | ||||||
|     ASSERT_MSG((mbedtls_cipher_set_iv(encryption_context.get(), iv.data(), iv.size()) || |     ASSERT_MSG((mbedtls_cipher_set_iv(&ctx->encryption_context, iv.data(), iv.size()) || | ||||||
|                 mbedtls_cipher_set_iv(decryption_context.get(), iv.data(), iv.size())) == 0, |                 mbedtls_cipher_set_iv(&ctx->decryption_context, iv.data(), iv.size())) == 0, | ||||||
|                "Failed to set IV on mbedtls ciphers."); |                "Failed to set IV on mbedtls ciphers."); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | template <typename Key, size_t KeySize> | ||||||
| void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op)  { | void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op op) { | ||||||
|     size_t written = 0; |     size_t written = 0; | ||||||
|  |  | ||||||
|     const auto context = op == Op::Encrypt ? encryption_context.get() : decryption_context.get(); |     const auto context = op == Op::Encrypt ? &ctx->encryption_context : &ctx->decryption_context; | ||||||
|  |  | ||||||
|     mbedtls_cipher_reset(context); |     mbedtls_cipher_reset(context); | ||||||
|  |  | ||||||
|     if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { |     if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) { | ||||||
|         mbedtls_cipher_update(context, src, size, |         mbedtls_cipher_update(context, src, size, dest, &written); | ||||||
|                               dest, &written); |  | ||||||
|         if (written != size) |         if (written != size) | ||||||
|             LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", |             LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", | ||||||
|                         size, written); |                         size, written); | ||||||
| @@ -62,11 +73,9 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||||||
|  |  | ||||||
|         for (size_t offset = 0; offset < size; offset += block_size) { |         for (size_t offset = 0; offset < size; offset += block_size) { | ||||||
|             auto length = std::min<size_t>(block_size, size - offset); |             auto length = std::min<size_t>(block_size, size - offset); | ||||||
|             mbedtls_cipher_update(context, src + offset, length, |             mbedtls_cipher_update(context, src + offset, length, dest + offset, &written); | ||||||
|                                   dest + offset, &written); |  | ||||||
|             if (written != length) |             if (written != length) | ||||||
|                 LOG_WARNING(Crypto, |                 LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.", | ||||||
|                             "Not all data was decrypted requested={:016X}, actual={:016X}.", |  | ||||||
|                             length, written); |                             length, written); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| @@ -74,9 +83,9 @@ void AESCipher<Key, KeySize>::Transcode(const u8* src, size_t size, u8* dest, Op | |||||||
|     mbedtls_cipher_finish(context, nullptr, nullptr); |     mbedtls_cipher_finish(context, nullptr, nullptr); | ||||||
| } | } | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | template <typename Key, size_t KeySize> | ||||||
| void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, | void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, | ||||||
|                                            Op op) { |                                            size_t sector_size, Op op) { | ||||||
|     if (size % sector_size > 0) { |     if (size % sector_size > 0) { | ||||||
|         LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size."); |         LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size."); | ||||||
|         return; |         return; | ||||||
| @@ -84,12 +93,11 @@ void AESCipher<Key, KeySize>::XTSTranscode(const u8* src, size_t size, u8* dest, | |||||||
|  |  | ||||||
|     for (size_t i = 0; i < size; i += sector_size) { |     for (size_t i = 0; i < size; i += sector_size) { | ||||||
|         SetIV(CalculateNintendoTweak(sector_id++)); |         SetIV(CalculateNintendoTweak(sector_id++)); | ||||||
|         Transcode<u8, u8>(src + i, sector_size, |         Transcode<u8, u8>(src + i, sector_size, dest + i, op); | ||||||
|                           dest + i, op); |  | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| template<typename Key, size_t KeySize> | template <typename Key, size_t KeySize> | ||||||
| std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) { | std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id) { | ||||||
|     std::vector<u8> out(0x10); |     std::vector<u8> out(0x10); | ||||||
|     for (size_t i = 0xF; i <= 0xF; --i) { |     for (size_t i = 0xF; i <= 0xF; --i) { | ||||||
| @@ -101,4 +109,4 @@ std::vector<u8> AESCipher<Key, KeySize>::CalculateNintendoTweak(size_t sector_id | |||||||
|  |  | ||||||
| template class AESCipher<Key128>; | template class AESCipher<Key128>; | ||||||
| template class AESCipher<Key256>; | template class AESCipher<Key256>; | ||||||
| } | } // namespace Core::Crypto | ||||||
| @@ -20,7 +20,7 @@ enum class Op { | |||||||
|     Decrypt, |     Decrypt, | ||||||
| }; | }; | ||||||
|  |  | ||||||
| struct mbedtls_cipher_context_t; | struct CipherContext; | ||||||
|  |  | ||||||
| template <typename Key, size_t KeySize = sizeof(Key)> | template <typename Key, size_t KeySize = sizeof(Key)> | ||||||
| class AESCipher { | class AESCipher { | ||||||
| @@ -44,15 +44,16 @@ public: | |||||||
|     template <typename Source, typename Dest> |     template <typename Source, typename Dest> | ||||||
|     void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, |     void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id, | ||||||
|                       size_t sector_size, Op op) { |                       size_t sector_size, Op op) { | ||||||
|         XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, sector_size, op); |         XTSTranscode(reinterpret_cast<const u8*>(src), size, reinterpret_cast<u8*>(dest), sector_id, | ||||||
|  |                      sector_size, op); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, Op op); |     void XTSTranscode(const u8* src, size_t size, u8* dest, size_t sector_id, size_t sector_size, | ||||||
|  |                       Op op); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     std::unique_ptr<mbedtls_cipher_context_t> encryption_context; |     std::unique_ptr<CipherContext> ctx; | ||||||
|     std::unique_ptr<mbedtls_cipher_context_t> decryption_context; |  | ||||||
|  |  | ||||||
|     static std::vector<u8> CalculateNintendoTweak(size_t sector_id); |     static std::vector<u8> CalculateNintendoTweak(size_t sector_id); | ||||||
| }; | }; | ||||||
| } // namespace Crypto | } // namespace Core::Crypto | ||||||
|   | |||||||
| @@ -6,7 +6,8 @@ | |||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
| #include "core/crypto/ctr_encryption_layer.h" | #include "core/crypto/ctr_encryption_layer.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) | CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset) | ||||||
|     : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), |     : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR), | ||||||
|       iv(16, 0) {} |       iv(16, 0) {} | ||||||
| @@ -21,29 +22,28 @@ size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const { | |||||||
|         std::vector<u8> raw = base->ReadBytes(length, offset); |         std::vector<u8> raw = base->ReadBytes(length, offset); | ||||||
|         if (raw.size() != length) |         if (raw.size() != length) | ||||||
|             return Read(data, raw.size(), offset); |             return Read(data, raw.size(), offset); | ||||||
|         cipher.Transcode(raw.data(), length, data, Op::DECRYPT); |         cipher.Transcode(raw.data(), length, data, Op::Decrypt); | ||||||
|         return length; |         return length; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // offset does not fall on block boundary (0x10) |     // offset does not fall on block boundary (0x10) | ||||||
|     std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); |     std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset); | ||||||
|     UpdateIV(base_offset + offset - sector_offset); |     UpdateIV(base_offset + offset - sector_offset); | ||||||
|     cipher.Transcode(block.data(), block.size(), block.data(), Op::DECRYPT); |     cipher.Transcode(block.data(), block.size(), block.data(), Op::Decrypt); | ||||||
|     size_t read = 0x10 - sector_offset; |     size_t read = 0x10 - sector_offset; | ||||||
|  |  | ||||||
|     if (length + sector_offset < 0x10) { |     if (length + sector_offset < 0x10) { | ||||||
|         memcpy_s(data, length, block.data() + sector_offset, std::min<u64>(length, read)); |         memcpy(data, block.data() + sector_offset, std::min<u64>(length, read)); | ||||||
|         return read; |         return read; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     memcpy_s(data, length, block.data() + sector_offset, read); |     memcpy(data, block.data() + sector_offset, read); | ||||||
|     return read + Read(data + read, length - read, offset + read); |     return read + Read(data + read, length - read, offset + read); | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTREncryptionLayer::SetIV(std::vector<u8> iv_) { | void CTREncryptionLayer::SetIV(const std::vector<u8>& iv_) { | ||||||
|     const auto length = std::min<size_t>(iv_.size(), iv.size()); |     const auto length = std::min(iv_.size(), iv.size()); | ||||||
|     for (size_t i = 0; i < length; ++i) |     iv.assign(iv_.cbegin(), iv_.cbegin() + length); | ||||||
|         iv[i] = iv_[i]; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| void CTREncryptionLayer::UpdateIV(size_t offset) const { | void CTREncryptionLayer::UpdateIV(size_t offset) const { | ||||||
| @@ -54,4 +54,4 @@ void CTREncryptionLayer::UpdateIV(size_t offset) const { | |||||||
|     } |     } | ||||||
|     cipher.SetIV(iv); |     cipher.SetIV(iv); | ||||||
| } | } | ||||||
| } // namespace Crypto | } // namespace Core::Crypto | ||||||
|   | |||||||
| @@ -4,19 +4,20 @@ | |||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "aes_util.h" | #include "core/crypto/aes_util.h" | ||||||
| #include "encryption_layer.h" | #include "core/crypto/encryption_layer.h" | ||||||
| #include "key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| // Sits on top of a VirtualFile and provides CTR-mode AES decription. | // Sits on top of a VirtualFile and provides CTR-mode AES decription. | ||||||
| struct CTREncryptionLayer : public EncryptionLayer { | class CTREncryptionLayer : public EncryptionLayer { | ||||||
|  | public: | ||||||
|     CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); |     CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset); | ||||||
|  |  | ||||||
|     size_t Read(u8* data, size_t length, size_t offset) const override; |     size_t Read(u8* data, size_t length, size_t offset) const override; | ||||||
|  |  | ||||||
|     void SetIV(std::vector<u8> iv); |     void SetIV(const std::vector<u8>& iv); | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     size_t base_offset; |     size_t base_offset; | ||||||
| @@ -28,4 +29,4 @@ private: | |||||||
|     void UpdateIV(size_t offset) const; |     void UpdateIV(size_t offset) const; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace Crypto | } // namespace Core::Crypto | ||||||
|   | |||||||
| @@ -4,7 +4,7 @@ | |||||||
|  |  | ||||||
| #include "core/crypto/encryption_layer.h" | #include "core/crypto/encryption_layer.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {} | EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {} | ||||||
|  |  | ||||||
| @@ -39,4 +39,4 @@ size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) { | |||||||
| bool EncryptionLayer::Rename(std::string_view name) { | bool EncryptionLayer::Rename(std::string_view name) { | ||||||
|     return base->Rename(name); |     return base->Rename(name); | ||||||
| } | } | ||||||
| } // namespace Crypto | } // namespace Core::Crypto | ||||||
|   | |||||||
| @@ -3,9 +3,10 @@ | |||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include "core/file_sys/vfs.h" | #include "core/file_sys/vfs.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| // Basically non-functional class that implements all of the methods that are irrelevant to an | // Basically non-functional class that implements all of the methods that are irrelevant to an | ||||||
| // EncryptionLayer. Reduces duplicate code. | // EncryptionLayer. Reduces duplicate code. | ||||||
| @@ -27,4 +28,4 @@ protected: | |||||||
|     FileSys::VirtualFile base; |     FileSys::VirtualFile base; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| } // namespace Crypto | } // namespace Core::Crypto | ||||||
|   | |||||||
| @@ -5,238 +5,15 @@ | |||||||
| #include <fstream> | #include <fstream> | ||||||
| #include <locale> | #include <locale> | ||||||
| #include <sstream> | #include <sstream> | ||||||
|  | #include <mbedtls/sha256.h> | ||||||
| #include "common/assert.h" | #include "common/assert.h" | ||||||
|  | #include "common/common_paths.h" | ||||||
|  | #include "common/file_util.h" | ||||||
| #include "common/logging/log.h" | #include "common/logging/log.h" | ||||||
| #include "core/crypto/key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
| #include "mbedtls/sha256.h" | #include "core/settings.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
| KeyManager keys = {}; |  | ||||||
|  |  | ||||||
| std::unordered_map<KeyIndex<S128KeyType>, SHA256Hash> KeyManager::s128_hash_prod = { |  | ||||||
|     {{S128KeyType::MASTER, 0, 0}, |  | ||||||
|      "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 1, 0}, |  | ||||||
|      "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 2, 0}, |  | ||||||
|      "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 3, 0}, |  | ||||||
|      "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 4, 0}, |  | ||||||
|      "75ff1d95d26113550ee6fcc20acb58e97edeb3a2ff52543ed5aec63bdcc3da50"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 0, 0}, |  | ||||||
|      "4543CD1B7CAD7EE0466A3DE2086A0EF923805DCEA6C741541CDDB14F54F97B40"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 1, 0}, |  | ||||||
|      "4A11DA019D26470C9B805F1721364830DC0096DD66EAC453B0D14455E5AF5CF8"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 2, 0}, |  | ||||||
|      "CCA867360B3318246FBF0B8A86473176ED486DFE229772B941A02E84D50A3155"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 3, 0}, |  | ||||||
|      "E65C383CDF526DFFAA77682868EBFA9535EE60D8075C961BBC1EDE5FBF7E3C5F"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 4, 0}, |  | ||||||
|      "28ae73d6ae8f7206fca549e27097714e599df1208e57099416ff429b71370162"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 0, 0}, |  | ||||||
|      "94D6F38B9D0456644E21DFF4707D092B70179B82D1AA2F5B6A76B8F9ED948264"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 1, 0}, |  | ||||||
|      "7794F24FA879D378FEFDC8776B949B88AD89386410BE9025D463C619F1530509"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 2, 0}, |  | ||||||
|      "5304BDDE6AC8E462961B5DB6E328B1816D245D36D6574BB78938B74D4418AF35"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 3, 0}, |  | ||||||
|      "BE1E52C4345A979DDD4924375B91C902052C2E1CF8FBF2FAA42E8F26D5125B60"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 4, 0}, |  | ||||||
|      "631b45d349ab8f76a050fe59512966fb8dbaf0755ef5b6903048bf036cfa611e"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 0, 0}, |  | ||||||
|      "C2FA30CAC6AE1680466CB54750C24550E8652B3B6F38C30B49DADF067B5935E9"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 1, 0}, |  | ||||||
|      "0D6B8F3746AD910D36438A859C11E8BE4310112425D63751D09B5043B87DE598"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 2, 0}, |  | ||||||
|      "D09E18D3DB6BC7393536896F728528736FBEFCDD15C09D9D612FDE5C7BDCD821"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 3, 0}, |  | ||||||
|      "47C6F9F7E99BB1F56DCDC93CDBD340EA82DCCD74DD8F3535ADA20ECF79D438ED"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 4, 0}, |  | ||||||
|      "128610de8424cb29e08f9ee9a81c9e6ffd3c6662854aad0c8f937e0bcedc4d88"_array32}, |  | ||||||
|     {{S128KeyType::ETICKET_RSA_KEK, 0, 0}, |  | ||||||
|      "46cccf288286e31c931379de9efa288c95c9a15e40b00a4c563a8be244ece515"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "592957F44FE5DB5EC6B095F568910E31A226D3B7FE42D64CFB9CE4051E90AEB6"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "C2252A0FBF9D339ABC3D681351D00452F926E7CA0C6CA85F659078DE3FA647F3"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "7C7722824B2F7C4938C40F3EA93E16CB69D3285EB133490EF8ECCD2C4B52DF41"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "AFBB8EBFB2094F1CF71E330826AE06D64414FCA128C464618DF30EED92E62BE6"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "5dc10eb81918da3f2fa90f69c8542511963656cfb31fb7c779581df8faf1f2f5"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "AA2C65F0E27F730807A13F2ED5B99BE5183165B87C50B6ED48F5CAC2840687EB"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "860185F2313A14F7006A029CB21A52750E7718C1E94FFB98C0AE2207D1A60165"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "7283FB1EFBD42438DADF363FDB776ED355C98737A2AAE75D0E9283CE1C12A2E4"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "9881C2D3AB70B14C8AA12016FC73ADAD93C6AD9FB59A9ECAD312B6F89E2413EC"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "eaa6a8d242b89e174928fa9549a0f66ec1562e2576fac896f438a2b3c1fb6005"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "194CF6BD14554DA8D457E14CBFE04E55C8FB8CA52E0AFB3D7CB7084AE435B801"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "CE1DB7BB6E5962384889DB7A396AFD614F82F69DC38A33D2DEAF47F3E4B964B7"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "42238DE5685DEF4FDE7BE42C0097CEB92447006386D6B5D5AAA2C9AFD2E28422"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "1F6847F268E9D9C5D1AD4D7E226A63B833BF02071446957A962EF065521879C1"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "644007f9913c3602399d4d75cc34faeb7f1faad18b23e34187b16fdc45f4980f"_array32}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| std::unordered_map<KeyIndex<S256KeyType>, SHA256Hash> KeyManager::s256_hash_prod = { |  | ||||||
|     {{S256KeyType::HEADER, 0, 0}, |  | ||||||
|      "8E03DE24818D96CE4F2A09B43AF979E679974F7570713A61EED8B314864A11D5"_array32}, |  | ||||||
|     {{S256KeyType::SD_SAVE, 0, 0}, |  | ||||||
|      "13020ee72d0f8b8f9112dc738b829fdb017102499a7c2259b52aeefc0a273f5c"_array32}, |  | ||||||
|     {{S256KeyType::SD_NCA, 0, 0}, |  | ||||||
|      "8a1c05b4f88bae5b04d77f632e6acfc8893c4a05fd701f53585daafc996b532a"_array32}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| // TODO(DarkLordZach): Find missing hashes for dev keys. |  | ||||||
|  |  | ||||||
| std::unordered_map<KeyIndex<S128KeyType>, SHA256Hash> KeyManager::s128_hash_dev = { |  | ||||||
|     {{S128KeyType::MASTER, 0, 0}, |  | ||||||
|      "779dd8b533a2fb670f27b308cb8d0151c4a107568b817429172b7f80aa592c25"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 1, 0}, |  | ||||||
|      "0175c8bc49771576f75527be719098db4ebaf77707206749415663aa3a9ea9cc"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 2, 0}, |  | ||||||
|      "4f0b4d724e5a8787268157c7ce0767c26d2e2021832aa7020f306d6e260eea42"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 3, 0}, |  | ||||||
|      "7b5a29586c1f84f66fbfabb94518fc45408bb8e5445253d063dda7cfef2a818c"_array32}, |  | ||||||
|     {{S128KeyType::MASTER, 4, 0}, |  | ||||||
|      "87a61dbb05a8755de7fe069562aab38ebfb266c9eb835f09fa62dacc89c98341"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 0, 0}, |  | ||||||
|      "166510bc63ae50391ebe4ee4ff90ca31cd0e2dd0ff6be839a2f573ec146cc23a"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 1, 0}, |  | ||||||
|      "f74cd01b86743139c920ec54a8116c669eea805a0be1583e13fc5bc8de68645b"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 2, 0}, |  | ||||||
|      "d0cdecd513bb6aa3d9dc6244c977dc8a5a7ea157d0a8747d79e7581146e1f768"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 3, 0}, |  | ||||||
|      "aa39394d626b3b79f5b7ccc07378b5996b6d09bf0eb6771b0b40c9077fbfde8c"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE1, 4, 0}, |  | ||||||
|      "8f4754b8988c0e673fc2bbea0534cdd6075c815c9270754ae980aef3e4f0a508"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 0, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 1, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 2, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 3, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::PACKAGE2, 4, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 0, 0}, |  | ||||||
|      "C2FA30CAC6AE1680466CB54750C24550E8652B3B6F38C30B49DADF067B5935E9"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 1, 0}, |  | ||||||
|      "0D6B8F3746AD910D36438A859C11E8BE4310112425D63751D09B5043B87DE598"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 2, 0}, |  | ||||||
|      "D09E18D3DB6BC7393536896F728528736FBEFCDD15C09D9D612FDE5C7BDCD821"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 3, 0}, |  | ||||||
|      "47C6F9F7E99BB1F56DCDC93CDBD340EA82DCCD74DD8F3535ADA20ECF79D438ED"_array32}, |  | ||||||
|     {{S128KeyType::TITLEKEK, 4, 0}, |  | ||||||
|      "128610de8424cb29e08f9ee9a81c9e6ffd3c6662854aad0c8f937e0bcedc4d88"_array32}, |  | ||||||
|     {{S128KeyType::ETICKET_RSA_KEK, 0, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Application)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::System)}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| std::unordered_map<KeyIndex<S256KeyType>, SHA256Hash> KeyManager::s256_hash_dev = { |  | ||||||
|     {{S256KeyType::HEADER, 0, 0}, |  | ||||||
|      "ecde86a76e37ac4fd7591d3aa55c00cc77d8595fc27968052ec18a177d939060"_array32}, |  | ||||||
|     {{S256KeyType::SD_SAVE, 0, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
|     {{S256KeyType::SD_NCA, 0, 0}, |  | ||||||
|      "0000000000000000000000000000000000000000000000000000000000000000"_array32}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { |  | ||||||
|     {"master_key_00", {S128KeyType::MASTER, 0, 0}}, |  | ||||||
|     {"master_key_01", {S128KeyType::MASTER, 1, 0}}, |  | ||||||
|     {"master_key_02", {S128KeyType::MASTER, 2, 0}}, |  | ||||||
|     {"master_key_03", {S128KeyType::MASTER, 3, 0}}, |  | ||||||
|     {"master_key_04", {S128KeyType::MASTER, 4, 0}}, |  | ||||||
|     {"package1_key_00", {S128KeyType::PACKAGE1, 0, 0}}, |  | ||||||
|     {"package1_key_01", {S128KeyType::PACKAGE1, 1, 0}}, |  | ||||||
|     {"package1_key_02", {S128KeyType::PACKAGE1, 2, 0}}, |  | ||||||
|     {"package1_key_03", {S128KeyType::PACKAGE1, 3, 0}}, |  | ||||||
|     {"package1_key_04", {S128KeyType::PACKAGE1, 4, 0}}, |  | ||||||
|     {"package2_key_00", {S128KeyType::PACKAGE2, 0, 0}}, |  | ||||||
|     {"package2_key_01", {S128KeyType::PACKAGE2, 1, 0}}, |  | ||||||
|     {"package2_key_02", {S128KeyType::PACKAGE2, 2, 0}}, |  | ||||||
|     {"package2_key_03", {S128KeyType::PACKAGE2, 3, 0}}, |  | ||||||
|     {"package2_key_04", {S128KeyType::PACKAGE2, 4, 0}}, |  | ||||||
|     {"titlekek_00", {S128KeyType::TITLEKEK, 0, 0}}, |  | ||||||
|     {"titlekek_01", {S128KeyType::TITLEKEK, 1, 0}}, |  | ||||||
|     {"titlekek_02", {S128KeyType::TITLEKEK, 2, 0}}, |  | ||||||
|     {"titlekek_03", {S128KeyType::TITLEKEK, 3, 0}}, |  | ||||||
|     {"titlekek_04", {S128KeyType::TITLEKEK, 4, 0}}, |  | ||||||
|     {"eticket_rsa_kek", {S128KeyType::ETICKET_RSA_KEK, 0, 0}}, |  | ||||||
|     {"key_area_key_application_00", |  | ||||||
|      {S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, |  | ||||||
|     {"key_area_key_application_01", |  | ||||||
|      {S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, |  | ||||||
|     {"key_area_key_application_02", |  | ||||||
|      {S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, |  | ||||||
|     {"key_area_key_application_03", |  | ||||||
|      {S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, |  | ||||||
|     {"key_area_key_application_04", |  | ||||||
|      {S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Application)}}, |  | ||||||
|     {"key_area_key_ocean_00", {S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}}, |  | ||||||
|     {"key_area_key_ocean_01", {S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}}, |  | ||||||
|     {"key_area_key_ocean_02", {S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}}, |  | ||||||
|     {"key_area_key_ocean_03", {S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}}, |  | ||||||
|     {"key_area_key_ocean_04", {S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}}, |  | ||||||
|     {"key_area_key_system_00", |  | ||||||
|      {S128KeyType::KEY_AREA, 0, static_cast<u64>(KeyAreaKeyType::System)}}, |  | ||||||
|     {"key_area_key_system_01", |  | ||||||
|      {S128KeyType::KEY_AREA, 1, static_cast<u64>(KeyAreaKeyType::System)}}, |  | ||||||
|     {"key_area_key_system_02", |  | ||||||
|      {S128KeyType::KEY_AREA, 2, static_cast<u64>(KeyAreaKeyType::System)}}, |  | ||||||
|     {"key_area_key_system_03", |  | ||||||
|      {S128KeyType::KEY_AREA, 3, static_cast<u64>(KeyAreaKeyType::System)}}, |  | ||||||
|     {"key_area_key_system_04", |  | ||||||
|      {S128KeyType::KEY_AREA, 4, static_cast<u64>(KeyAreaKeyType::System)}}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { |  | ||||||
|     {"header_key", {S256KeyType::HEADER, 0, 0}}, |  | ||||||
|     {"sd_card_save_key", {S256KeyType::SD_SAVE, 0, 0}}, |  | ||||||
|     {"sd_card_nca_key", {S256KeyType::SD_NCA, 0, 0}}, |  | ||||||
| }; |  | ||||||
|  |  | ||||||
| static u8 ToHexNibble(char c1) { | static u8 ToHexNibble(char c1) { | ||||||
|     if (c1 >= 65 && c1 <= 70) |     if (c1 >= 65 && c1 <= 70) | ||||||
| @@ -271,8 +48,20 @@ std::array<u8, 32> operator""_array32(const char* str, size_t len) { | |||||||
|     return HexStringToArray<32>(str); |     return HexStringToArray<32>(str); | ||||||
| } | } | ||||||
|  |  | ||||||
| void KeyManager::SetValidationMode(bool dev) { | KeyManager::KeyManager() { | ||||||
|     dev_mode = dev; |     // Initialize keys | ||||||
|  |     std::string keys_dir = FileUtil::GetHactoolConfigurationPath(); | ||||||
|  |     if (Settings::values.use_dev_keys) { | ||||||
|  |         dev_mode = true; | ||||||
|  |         if (FileUtil::Exists(keys_dir + DIR_SEP + "dev.keys")) | ||||||
|  |             LoadFromFile(keys_dir + DIR_SEP + "dev.keys", false); | ||||||
|  |     } else { | ||||||
|  |         dev_mode = false; | ||||||
|  |         if (FileUtil::Exists(keys_dir + DIR_SEP + "prod.keys")) | ||||||
|  |             LoadFromFile(keys_dir + DIR_SEP + "prod.keys", false); | ||||||
|  |     } | ||||||
|  |     if (FileUtil::Exists(keys_dir + DIR_SEP + "title.keys")) | ||||||
|  |         LoadFromFile(keys_dir + DIR_SEP + "title.keys", true); | ||||||
| } | } | ||||||
|  |  | ||||||
| void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) { | void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) { | ||||||
| @@ -299,7 +88,7 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) { | |||||||
|             auto rights_id_raw = HexStringToArray<16>(out[0]); |             auto rights_id_raw = HexStringToArray<16>(out[0]); | ||||||
|             u128 rights_id = *reinterpret_cast<std::array<u64, 2>*>(&rights_id_raw); |             u128 rights_id = *reinterpret_cast<std::array<u64, 2>*>(&rights_id_raw); | ||||||
|             Key128 key = HexStringToArray<16>(out[1]); |             Key128 key = HexStringToArray<16>(out[1]); | ||||||
|             SetKey(S128KeyType::TITLEKEY, key, rights_id[1], rights_id[0]); |             SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||||||
|         } else { |         } else { | ||||||
|             std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower); |             std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower); | ||||||
|             if (s128_file_id.find(out[0]) != s128_file_id.end()) { |             if (s128_file_id.find(out[0]) != s128_file_id.end()) { | ||||||
| @@ -315,24 +104,24 @@ void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) { | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) { | bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const { | ||||||
|     return s128_keys.find({id, field1, field2}) != s128_keys.end(); |     return s128_keys.find({id, field1, field2}) != s128_keys.end(); | ||||||
| } | } | ||||||
|  |  | ||||||
| bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) { | bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) const { | ||||||
|     return s256_keys.find({id, field1, field2}) != s256_keys.end(); |     return s256_keys.find({id, field1, field2}) != s256_keys.end(); | ||||||
| } | } | ||||||
|  |  | ||||||
| Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) { | Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) const { | ||||||
|     if (!HasKey(id, field1, field2)) |     if (!HasKey(id, field1, field2)) | ||||||
|         return {}; |         return {}; | ||||||
|     return s128_keys[{id, field1, field2}]; |     return s128_keys.at({id, field1, field2}); | ||||||
| } | } | ||||||
|  |  | ||||||
| Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) { | Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) const { | ||||||
|     if (!HasKey(id, field1, field2)) |     if (!HasKey(id, field1, field2)) | ||||||
|         return {}; |         return {}; | ||||||
|     return s256_keys[{id, field1, field2}]; |     return s256_keys.at({id, field1, field2}); | ||||||
| } | } | ||||||
|  |  | ||||||
| void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | ||||||
| @@ -343,68 +132,53 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | |||||||
|     s256_keys[{id, field1, field2}] = key; |     s256_keys[{id, field1, field2}] = key; | ||||||
| } | } | ||||||
|  |  | ||||||
| bool KeyManager::ValidateKey(S128KeyType key, u64 field1, u64 field2) { | std::unordered_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = { | ||||||
|     auto& hash = dev_mode ? s128_hash_dev : s128_hash_prod; |     {"master_key_00", {S128KeyType::Master, 0, 0}}, | ||||||
|  |     {"master_key_01", {S128KeyType::Master, 1, 0}}, | ||||||
|  |     {"master_key_02", {S128KeyType::Master, 2, 0}}, | ||||||
|  |     {"master_key_03", {S128KeyType::Master, 3, 0}}, | ||||||
|  |     {"master_key_04", {S128KeyType::Master, 4, 0}}, | ||||||
|  |     {"package1_key_00", {S128KeyType::Package1, 0, 0}}, | ||||||
|  |     {"package1_key_01", {S128KeyType::Package1, 1, 0}}, | ||||||
|  |     {"package1_key_02", {S128KeyType::Package1, 2, 0}}, | ||||||
|  |     {"package1_key_03", {S128KeyType::Package1, 3, 0}}, | ||||||
|  |     {"package1_key_04", {S128KeyType::Package1, 4, 0}}, | ||||||
|  |     {"package2_key_00", {S128KeyType::Package2, 0, 0}}, | ||||||
|  |     {"package2_key_01", {S128KeyType::Package2, 1, 0}}, | ||||||
|  |     {"package2_key_02", {S128KeyType::Package2, 2, 0}}, | ||||||
|  |     {"package2_key_03", {S128KeyType::Package2, 3, 0}}, | ||||||
|  |     {"package2_key_04", {S128KeyType::Package2, 4, 0}}, | ||||||
|  |     {"titlekek_00", {S128KeyType::Titlekek, 0, 0}}, | ||||||
|  |     {"titlekek_01", {S128KeyType::Titlekek, 1, 0}}, | ||||||
|  |     {"titlekek_02", {S128KeyType::Titlekek, 2, 0}}, | ||||||
|  |     {"titlekek_03", {S128KeyType::Titlekek, 3, 0}}, | ||||||
|  |     {"titlekek_04", {S128KeyType::Titlekek, 4, 0}}, | ||||||
|  |     {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}}, | ||||||
|  |     {"key_area_key_application_00", | ||||||
|  |      {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Application)}}, | ||||||
|  |     {"key_area_key_application_01", | ||||||
|  |      {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Application)}}, | ||||||
|  |     {"key_area_key_application_02", | ||||||
|  |      {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Application)}}, | ||||||
|  |     {"key_area_key_application_03", | ||||||
|  |      {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Application)}}, | ||||||
|  |     {"key_area_key_application_04", | ||||||
|  |      {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Application)}}, | ||||||
|  |     {"key_area_key_ocean_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||||||
|  |     {"key_area_key_ocean_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||||||
|  |     {"key_area_key_ocean_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||||||
|  |     {"key_area_key_ocean_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||||||
|  |     {"key_area_key_ocean_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::Ocean)}}, | ||||||
|  |     {"key_area_key_system_00", {S128KeyType::KeyArea, 0, static_cast<u64>(KeyAreaKeyType::System)}}, | ||||||
|  |     {"key_area_key_system_01", {S128KeyType::KeyArea, 1, static_cast<u64>(KeyAreaKeyType::System)}}, | ||||||
|  |     {"key_area_key_system_02", {S128KeyType::KeyArea, 2, static_cast<u64>(KeyAreaKeyType::System)}}, | ||||||
|  |     {"key_area_key_system_03", {S128KeyType::KeyArea, 3, static_cast<u64>(KeyAreaKeyType::System)}}, | ||||||
|  |     {"key_area_key_system_04", {S128KeyType::KeyArea, 4, static_cast<u64>(KeyAreaKeyType::System)}}, | ||||||
|  | }; | ||||||
|  |  | ||||||
|     KeyIndex<S128KeyType> id = {key, field1, field2}; | std::unordered_map<std::string, KeyIndex<S256KeyType>> KeyManager::s256_file_id = { | ||||||
|     if (key == S128KeyType::SD_SEED || key == S128KeyType::TITLEKEY || |     {"header_key", {S256KeyType::Header, 0, 0}}, | ||||||
|         hash.find(id) == hash.end()) { |     {"sd_card_save_key", {S256KeyType::SDSave, 0, 0}}, | ||||||
|         LOG_WARNING(Crypto, "Could not validate [{}]", id.DebugInfo()); |     {"sd_card_nca_key", {S256KeyType::SDNCA, 0, 0}}, | ||||||
|         return true; | }; | ||||||
|     } | } // namespace Core::Crypto | ||||||
|  |  | ||||||
|     if (!HasKey(key, field1, field2)) { |  | ||||||
|         LOG_CRITICAL(Crypto, |  | ||||||
|                      "System has requested validation of [{}], but user has not added it. Add this " |  | ||||||
|                      "key to use functionality.", |  | ||||||
|                      id.DebugInfo()); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SHA256Hash key_hash{}; |  | ||||||
|     const auto a_key = GetKey(key, field1, field2); |  | ||||||
|     mbedtls_sha256(a_key.data(), a_key.size(), key_hash.data(), 0); |  | ||||||
|     if (key_hash != hash[id]) { |  | ||||||
|         LOG_CRITICAL(Crypto, |  | ||||||
|                      "The hash of the provided key for [{}] does not match the one on file. This " |  | ||||||
|                      "means you probably have an incorrect key. If you believe this to be in " |  | ||||||
|                      "error, contact the yuzu devs.", |  | ||||||
|                      id.DebugInfo()); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| bool KeyManager::ValidateKey(S256KeyType key, u64 field1, u64 field2) { |  | ||||||
|     auto& hash = dev_mode ? s256_hash_dev : s256_hash_prod; |  | ||||||
|  |  | ||||||
|     KeyIndex<S256KeyType> id = {key, field1, field2}; |  | ||||||
|     if (hash.find(id) == hash.end()) { |  | ||||||
|         LOG_ERROR(Crypto, "Could not validate [{}]", id.DebugInfo()); |  | ||||||
|         return true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     if (!HasKey(key, field1, field2)) { |  | ||||||
|         LOG_ERROR(Crypto, |  | ||||||
|                   "System has requested validation of [{}], but user has not added it. Add this " |  | ||||||
|                   "key to use functionality.", |  | ||||||
|                   id.DebugInfo()); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     SHA256Hash key_hash{}; |  | ||||||
|     const auto a_key = GetKey(key, field1, field2); |  | ||||||
|     mbedtls_sha256(a_key.data(), a_key.size(), key_hash.data(), 0); |  | ||||||
|     if (key_hash != hash[id]) { |  | ||||||
|         LOG_CRITICAL(Crypto, |  | ||||||
|                      "The hash of the provided key for [{}] does not match the one on file. This " |  | ||||||
|                      "means you probably have an incorrect key. If you believe this to be in " |  | ||||||
|                      "error, contact the yuzu devs.", |  | ||||||
|                      id.DebugInfo()); |  | ||||||
|         return false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return true; |  | ||||||
| } |  | ||||||
| } // namespace Crypto |  | ||||||
|   | |||||||
| @@ -3,36 +3,37 @@ | |||||||
| // Refer to the license.txt file included. | // Refer to the license.txt file included. | ||||||
|  |  | ||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| #include <array> | #include <array> | ||||||
| #include <unordered_map> | #include <unordered_map> | ||||||
| #include <vector> | #include <vector> | ||||||
| #include <fmt/format.h> | #include <fmt/format.h> | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| typedef std::array<u8, 0x10> Key128; | using Key128 = std::array<u8, 0x10>; | ||||||
| typedef std::array<u8, 0x20> Key256; | using Key256 = std::array<u8, 0x20>; | ||||||
| typedef std::array<u8, 0x20> SHA256Hash; | using SHA256Hash = std::array<u8, 0x20>; | ||||||
|  |  | ||||||
| static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); | static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); | ||||||
| static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); | static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); | ||||||
|  |  | ||||||
| enum class S256KeyType : u64 { | enum class S256KeyType : u64 { | ||||||
|     HEADER,  // |     Header, // | ||||||
|     SD_SAVE, // |     SDSave, // | ||||||
|     SD_NCA,  // |     SDNCA,  // | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class S128KeyType : u64 { | enum class S128KeyType : u64 { | ||||||
|     MASTER,          // f1=crypto revision |     Master,        // f1=crypto revision | ||||||
|     PACKAGE1,        // f1=crypto revision |     Package1,      // f1=crypto revision | ||||||
|     PACKAGE2,        // f1=crypto revision |     Package2,      // f1=crypto revision | ||||||
|     TITLEKEK,        // f1=crypto revision |     Titlekek,      // f1=crypto revision | ||||||
|     ETICKET_RSA_KEK, // |     ETicketRSAKek, // | ||||||
|     KEY_AREA,        // f1=crypto revision f2=type {app, ocean, system} |     KeyArea,       // f1=crypto revision f2=type {app, ocean, system} | ||||||
|     SD_SEED,         // |     SDSeed,        // | ||||||
|     TITLEKEY,        // f1=rights id LSB f2=rights id MSB |     Titlekey,      // f1=rights id LSB f2=rights id MSB | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum class KeyAreaKeyType : u8 { | enum class KeyAreaKeyType : u8 { | ||||||
| @@ -47,7 +48,7 @@ struct KeyIndex { | |||||||
|     u64 field1; |     u64 field1; | ||||||
|     u64 field2; |     u64 field2; | ||||||
|  |  | ||||||
|     std::string DebugInfo() { |     std::string DebugInfo() const { | ||||||
|         u8 key_size = 16; |         u8 key_size = 16; | ||||||
|         if (std::is_same_v<KeyType, S256KeyType>) |         if (std::is_same_v<KeyType, S256KeyType>) | ||||||
|             key_size = 32; |             key_size = 32; | ||||||
| @@ -60,15 +61,20 @@ struct KeyIndex { | |||||||
|  |  | ||||||
| template <typename KeyType> | template <typename KeyType> | ||||||
| bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { | bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { | ||||||
|     return lhs.type == rhs.type && lhs.field1 == rhs.field1 && lhs.field2 == rhs.field2; |     return std::tie(lhs.type, lhs.field1, lhs.field2) == std::tie(rhs.type, rhs.field1, rhs.field2); | ||||||
| } | } | ||||||
|  |  | ||||||
| } // namespace Crypto | template <typename KeyType> | ||||||
|  | bool operator!=(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) { | ||||||
|  |     return !operator==(lhs, rhs); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | } // namespace Core::Crypto | ||||||
|  |  | ||||||
| namespace std { | namespace std { | ||||||
| template <typename KeyType> | template <typename KeyType> | ||||||
| struct hash<Crypto::KeyIndex<KeyType>> { | struct hash<Core::Crypto::KeyIndex<KeyType>> { | ||||||
|     size_t operator()(const Crypto::KeyIndex<KeyType>& k) const { |     size_t operator()(const Core::Crypto::KeyIndex<KeyType>& k) const { | ||||||
|         using std::hash; |         using std::hash; | ||||||
|  |  | ||||||
|         return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^ |         return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^ | ||||||
| @@ -77,41 +83,32 @@ struct hash<Crypto::KeyIndex<KeyType>> { | |||||||
| }; | }; | ||||||
| } // namespace std | } // namespace std | ||||||
|  |  | ||||||
| namespace Crypto { | namespace Core::Crypto { | ||||||
|  |  | ||||||
| std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); | std::array<u8, 0x10> operator"" _array16(const char* str, size_t len); | ||||||
| std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); | std::array<u8, 0x20> operator"" _array32(const char* str, size_t len); | ||||||
|  |  | ||||||
| struct KeyManager { | class KeyManager { | ||||||
|     void SetValidationMode(bool dev); | public: | ||||||
|     void LoadFromFile(std::string_view filename, bool is_title_keys); |     KeyManager(); | ||||||
|  |  | ||||||
|     bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0); |     bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; | ||||||
|     bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0); |     bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; | ||||||
|  |  | ||||||
|     Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0); |     Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0) const; | ||||||
|     Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0); |     Key256 GetKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0) const; | ||||||
|  |  | ||||||
|     void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); |     void SetKey(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0); | ||||||
|     void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); |     void SetKey(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0); | ||||||
|  |  | ||||||
|     bool ValidateKey(S128KeyType key, u64 field1 = 0, u64 field2 = 0); |  | ||||||
|     bool ValidateKey(S256KeyType key, u64 field1 = 0, u64 field2 = 0); |  | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys; |     std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys; | ||||||
|     std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys; |     std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys; | ||||||
|  |  | ||||||
|     bool dev_mode = false; |     bool dev_mode; | ||||||
|  |     void LoadFromFile(std::string_view filename, bool is_title_keys); | ||||||
|  |  | ||||||
|     static std::unordered_map<KeyIndex<S128KeyType>, SHA256Hash> s128_hash_prod; |  | ||||||
|     static std::unordered_map<KeyIndex<S256KeyType>, SHA256Hash> s256_hash_prod; |  | ||||||
|     static std::unordered_map<KeyIndex<S128KeyType>, SHA256Hash> s128_hash_dev; |  | ||||||
|     static std::unordered_map<KeyIndex<S256KeyType>, SHA256Hash> s256_hash_dev; |  | ||||||
|     static std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id; |     static std::unordered_map<std::string, KeyIndex<S128KeyType>> s128_file_id; | ||||||
|     static std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id; |     static std::unordered_map<std::string, KeyIndex<S256KeyType>> s256_file_id; | ||||||
| }; | }; | ||||||
|  | } // namespace Core::Crypto | ||||||
| extern KeyManager keys; |  | ||||||
|  |  | ||||||
| } // namespace Crypto |  | ||||||
|   | |||||||
| @@ -30,8 +30,8 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||||||
|         return; |         return; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     const static std::array<std::string, 0x4> partition_names = {"update", "normal", "secure", |     static constexpr std::array<const char*, 0x4> partition_names = {"update", "normal", "secure", | ||||||
|                                                                  "logo"}; |                                                                      "logo"}; | ||||||
|  |  | ||||||
|     for (XCIPartition partition : |     for (XCIPartition partition : | ||||||
|          {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { |          {XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) { | ||||||
| @@ -93,12 +93,9 @@ VirtualDir XCI::GetLogoPartition() const { | |||||||
| } | } | ||||||
|  |  | ||||||
| std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { | std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { | ||||||
|     for (const auto& nca : ncas) { |     auto iter = std::find_if(ncas.begin(), ncas.end(), | ||||||
|         if (nca->GetType() == type) |                              [type](std::shared_ptr<NCA> nca) { return nca->GetType() == type; }); | ||||||
|             return nca; |     return iter == ncas.end() ? nullptr : *iter; | ||||||
|     } |  | ||||||
|  |  | ||||||
|     return nullptr; |  | ||||||
| } | } | ||||||
|  |  | ||||||
| VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { | VirtualFile XCI::GetNCAFileByType(NCAContentType type) const { | ||||||
| @@ -133,7 +130,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | |||||||
|         return Loader::ResultStatus::ErrorInvalidFormat; |         return Loader::ResultStatus::ErrorInvalidFormat; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     for (VirtualFile file : partitions[static_cast<size_t>(part)]->GetFiles()) { |     for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) { | ||||||
|         if (file->GetExtension() != "nca") |         if (file->GetExtension() != "nca") | ||||||
|             continue; |             continue; | ||||||
|         auto nca = std::make_shared<NCA>(file); |         auto nca = std::make_shared<NCA>(file); | ||||||
|   | |||||||
| @@ -9,10 +9,9 @@ | |||||||
| #include "core/crypto/aes_util.h" | #include "core/crypto/aes_util.h" | ||||||
| #include "core/crypto/ctr_encryption_layer.h" | #include "core/crypto/ctr_encryption_layer.h" | ||||||
| #include "core/file_sys/content_archive.h" | #include "core/file_sys/content_archive.h" | ||||||
|  | #include "core/file_sys/romfs.h" | ||||||
| #include "core/file_sys/vfs_offset.h" | #include "core/file_sys/vfs_offset.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "mbedtls/cipher.h" |  | ||||||
| #include "romfs.h" |  | ||||||
|  |  | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
|  |  | ||||||
| @@ -77,7 +76,7 @@ bool IsValidNCA(const NCAHeader& header) { | |||||||
|     return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); |     return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||||||
| } | } | ||||||
|  |  | ||||||
| Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { | Core::Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { | ||||||
|     u8 master_key_id = header.crypto_type; |     u8 master_key_id = header.crypto_type; | ||||||
|     if (header.crypto_type_2 > master_key_id) |     if (header.crypto_type_2 > master_key_id) | ||||||
|         master_key_id = header.crypto_type_2; |         master_key_id = header.crypto_type_2; | ||||||
| @@ -85,16 +84,12 @@ Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { | |||||||
|         --master_key_id; |         --master_key_id; | ||||||
|  |  | ||||||
|     std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); |     std::vector<u8> key_area(header.key_area.begin(), header.key_area.end()); | ||||||
|     if (!Crypto::keys.ValidateKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index)) { |     Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | ||||||
|         status = Loader::ResultStatus::ErrorEncrypted; |         keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index), | ||||||
|         return {}; |         Core::Crypto::Mode::ECB); | ||||||
|     } |     cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt); | ||||||
|     Crypto::AESCipher<Crypto::Key128> cipher( |  | ||||||
|         Crypto::keys.GetKey(Crypto::S128KeyType::KEY_AREA, master_key_id, header.key_index), |  | ||||||
|         Crypto::Mode::ECB); |  | ||||||
|     cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Crypto::Op::DECRYPT); |  | ||||||
|  |  | ||||||
|     Crypto::Key128 out; |     Core::Crypto::Key128 out; | ||||||
|     if (type == NCASectionCryptoType::XTS) |     if (type == NCASectionCryptoType::XTS) | ||||||
|         std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); |         std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); | ||||||
|     else if (type == NCASectionCryptoType::CTR) |     else if (type == NCASectionCryptoType::CTR) | ||||||
| @@ -102,8 +97,8 @@ Crypto::Key128 NCA::GetKeyAreaKey(NCASectionCryptoType type) { | |||||||
|     else |     else | ||||||
|         LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", |         LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", | ||||||
|                      static_cast<u8>(type)); |                      static_cast<u8>(type)); | ||||||
|  |     u128 out_128{}; | ||||||
|     u128 out_128 = *reinterpret_cast<u128*>(&out); |     memcpy(out_128.data(), out.data(), 16); | ||||||
|     LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", |     LOG_DEBUG(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}", | ||||||
|               master_key_id, header.key_index, out_128[1], out_128[0]); |               master_key_id, header.key_index, out_128[1], out_128[0]); | ||||||
|  |  | ||||||
| @@ -121,9 +116,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_o | |||||||
|     case NCASectionCryptoType::CTR: |     case NCASectionCryptoType::CTR: | ||||||
|         LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); |         LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | ||||||
|         { |         { | ||||||
|             auto out = std::make_shared<Crypto::CTREncryptionLayer>( |             auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( | ||||||
|                 std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset); |                 std::move(in), GetKeyAreaKey(NCASectionCryptoType::CTR), starting_offset); | ||||||
|             std::vector<u8> iv(16, 0); |             std::vector<u8> iv(16); | ||||||
|             for (u8 i = 0; i < 8; ++i) |             for (u8 i = 0; i < 8; ++i) | ||||||
|                 iv[i] = header.raw.section_ctr[0x8 - i - 1]; |                 iv[i] = header.raw.section_ctr[0x8 - i - 1]; | ||||||
|             out->SetIV(iv); |             out->SetIV(iv); | ||||||
| @@ -146,13 +141,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||||||
|  |  | ||||||
|     if (!IsValidNCA(header)) { |     if (!IsValidNCA(header)) { | ||||||
|         NCAHeader dec_header{}; |         NCAHeader dec_header{}; | ||||||
|         if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { |         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | ||||||
|             status = Loader::ResultStatus::ErrorEncrypted; |             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); | ||||||
|             return; |         cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, | ||||||
|         } |                             Core::Crypto::Op::Decrypt); | ||||||
|         Crypto::AESCipher<Crypto::Key256> cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), |  | ||||||
|                                                  Crypto::Mode::XTS); |  | ||||||
|         cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200, Crypto::Op::DECRYPT); |  | ||||||
|         if (IsValidNCA(dec_header)) { |         if (IsValidNCA(dec_header)) { | ||||||
|             header = dec_header; |             header = dec_header; | ||||||
|             encrypted = true; |             encrypted = true; | ||||||
| @@ -171,14 +163,10 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | |||||||
|  |  | ||||||
|     if (encrypted) { |     if (encrypted) { | ||||||
|         auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); |         auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET); | ||||||
|         if (!Crypto::keys.ValidateKey(Crypto::S256KeyType::HEADER)) { |         Core::Crypto::AESCipher<Core::Crypto::Key256> cipher( | ||||||
|             status = Loader::ResultStatus::ErrorEncrypted; |             keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS); | ||||||
|             return; |  | ||||||
|         } |  | ||||||
|         Crypto::AESCipher<Crypto::Key256> cipher(Crypto::keys.GetKey(Crypto::S256KeyType::HEADER), |  | ||||||
|                                                  Crypto::Mode::XTS); |  | ||||||
|         cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, |         cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE, | ||||||
|                             Crypto::Op::DECRYPT); |                             Core::Crypto::Op::Decrypt); | ||||||
|     } else { |     } else { | ||||||
|         file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); |         file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -9,12 +9,12 @@ | |||||||
| #include <string> | #include <string> | ||||||
| #include <vector> | #include <vector> | ||||||
|  |  | ||||||
| #include "core/loader/loader.h" |  | ||||||
| #include "common/common_funcs.h" | #include "common/common_funcs.h" | ||||||
| #include "common/common_types.h" | #include "common/common_types.h" | ||||||
| #include "common/swap.h" | #include "common/swap.h" | ||||||
| #include "core/crypto/key_manager.h" | #include "core/crypto/key_manager.h" | ||||||
| #include "core/file_sys/partition_filesystem.h" | #include "core/file_sys/partition_filesystem.h" | ||||||
|  | #include "core/loader/loader.h" | ||||||
|  |  | ||||||
| namespace FileSys { | namespace FileSys { | ||||||
| enum class NCAContentType : u8 { | enum class NCAContentType : u8 { | ||||||
| @@ -107,7 +107,8 @@ private: | |||||||
|  |  | ||||||
|     bool encrypted; |     bool encrypted; | ||||||
|  |  | ||||||
|     Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); |     Core::Crypto::KeyManager keys; | ||||||
|  |     Core::Crypto::Key128 GetKeyAreaKey(NCASectionCryptoType type); | ||||||
|  |  | ||||||
|     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); |     VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -297,10 +297,9 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block | |||||||
|  |  | ||||||
|         if (f1_vs != f2_vs) |         if (f1_vs != f2_vs) | ||||||
|             return false; |             return false; | ||||||
|         for (size_t j = 0; j < f1_vs; ++j) { |         auto iters = std::mismatch(f1_v.begin(), f1_v.end(), f2_v.begin(), f2_v.end()); | ||||||
|             if (f1_v[j] != f2_v[j]) |         if (iters.first != f1_v.end() && iters.second != f2_v.end()) | ||||||
|                 return false; |             return false; | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   | |||||||
| @@ -28,14 +28,17 @@ AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) | |||||||
|       nca_loader(std::make_unique<AppLoader_NCA>( |       nca_loader(std::make_unique<AppLoader_NCA>( | ||||||
|           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} |           xci->GetNCAFileByType(FileSys::NCAContentType::Program))) {} | ||||||
|  |  | ||||||
|  | AppLoader_XCI::~AppLoader_XCI() = default; | ||||||
|  |  | ||||||
| FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { | FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) { | ||||||
|     FileSys::XCI xci(file); |     FileSys::XCI xci(file); | ||||||
|  |  | ||||||
|     if (xci.GetStatus() == ResultStatus::Success && |     if (xci.GetStatus() == ResultStatus::Success && | ||||||
|         xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr && |         xci.GetNCAByType(FileSys::NCAContentType::Program) != nullptr && | ||||||
|         AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) == |         AppLoader_NCA::IdentifyType(xci.GetNCAFileByType(FileSys::NCAContentType::Program)) == | ||||||
|             FileType::NCA) |             FileType::NCA) { | ||||||
|         return FileType::XCI; |         return FileType::XCI; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return FileType::Error; |     return FileType::Error; | ||||||
| } | } | ||||||
| @@ -62,6 +65,4 @@ ResultStatus AppLoader_XCI::ReadProgramId(u64& out_program_id) { | |||||||
|     return nca_loader->ReadProgramId(out_program_id); |     return nca_loader->ReadProgramId(out_program_id); | ||||||
| } | } | ||||||
|  |  | ||||||
| AppLoader_XCI::~AppLoader_XCI() = default; |  | ||||||
|  |  | ||||||
| } // namespace Loader | } // namespace Loader | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ namespace Loader { | |||||||
| class AppLoader_XCI final : public AppLoader { | class AppLoader_XCI final : public AppLoader { | ||||||
| public: | public: | ||||||
|     explicit AppLoader_XCI(FileSys::VirtualFile file); |     explicit AppLoader_XCI(FileSys::VirtualFile file); | ||||||
|  |     ~AppLoader_XCI(); | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * Returns the type of the file |      * Returns the type of the file | ||||||
| @@ -30,8 +31,6 @@ public: | |||||||
|     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |     ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; | ||||||
|     ResultStatus ReadProgramId(u64& out_program_id) override; |     ResultStatus ReadProgramId(u64& out_program_id) override; | ||||||
|  |  | ||||||
|     ~AppLoader_XCI(); |  | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     FileSys::ProgramMetadata metadata; |     FileSys::ProgramMetadata metadata; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,7 +13,6 @@ | |||||||
| #include <QMessageBox> | #include <QMessageBox> | ||||||
| #include <QtGui> | #include <QtGui> | ||||||
| #include <QtWidgets> | #include <QtWidgets> | ||||||
| #include <core/crypto/key_manager.h> |  | ||||||
| #include "common/common_paths.h" | #include "common/common_paths.h" | ||||||
| #include "common/logging/backend.h" | #include "common/logging/backend.h" | ||||||
| #include "common/logging/filter.h" | #include "common/logging/filter.h" | ||||||
| @@ -24,6 +23,7 @@ | |||||||
| #include "common/scope_exit.h" | #include "common/scope_exit.h" | ||||||
| #include "common/string_util.h" | #include "common/string_util.h" | ||||||
| #include "core/core.h" | #include "core/core.h" | ||||||
|  | #include "core/crypto/key_manager.h" | ||||||
| #include "core/gdbstub/gdbstub.h" | #include "core/gdbstub/gdbstub.h" | ||||||
| #include "core/loader/loader.h" | #include "core/loader/loader.h" | ||||||
| #include "core/settings.h" | #include "core/settings.h" | ||||||
| @@ -89,19 +89,6 @@ GMainWindow::GMainWindow() : config(new Config()), emu_thread(nullptr) { | |||||||
|     ui.setupUi(this); |     ui.setupUi(this); | ||||||
|     statusBar()->hide(); |     statusBar()->hide(); | ||||||
|  |  | ||||||
|     // Initialize keys |  | ||||||
|     std::string keys_dir = FileUtil::GetHactoolConfigurationPath(); |  | ||||||
|     if (Settings::values.use_dev_keys) { |  | ||||||
|         Crypto::keys.SetValidationMode(true); |  | ||||||
|         if (FileUtil::Exists(keys_dir + DIR_SEP + "dev.keys")) |  | ||||||
|             Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "dev.keys", false); |  | ||||||
|     } else { |  | ||||||
|         if (FileUtil::Exists(keys_dir + DIR_SEP + "prod.keys")) |  | ||||||
|             Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "prod.keys", false); |  | ||||||
|     } |  | ||||||
|     if (FileUtil::Exists(keys_dir + DIR_SEP + "title.keys")) |  | ||||||
|         Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "title.keys", true); |  | ||||||
|  |  | ||||||
|     default_theme_paths = QIcon::themeSearchPaths(); |     default_theme_paths = QIcon::themeSearchPaths(); | ||||||
|     UpdateUITheme(); |     UpdateUITheme(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -73,19 +73,6 @@ static void InitializeLogging() { | |||||||
| int main(int argc, char** argv) { | int main(int argc, char** argv) { | ||||||
|     Config config; |     Config config; | ||||||
|  |  | ||||||
|     // Initialize keys |  | ||||||
|     std::string keys_dir = FileUtil::GetHactoolConfigurationPath(); |  | ||||||
|     if (Settings::values.use_dev_keys) { |  | ||||||
|         Crypto::keys.SetValidationMode(true); |  | ||||||
|         if (FileUtil::Exists(keys_dir + DIR_SEP + "dev.keys")) |  | ||||||
|             Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "dev.keys", false); |  | ||||||
|     } else { |  | ||||||
|         if (FileUtil::Exists(keys_dir + DIR_SEP + "prod.keys")) |  | ||||||
|             Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "prod.keys", false); |  | ||||||
|     } |  | ||||||
|     if (FileUtil::Exists(keys_dir + DIR_SEP + "title.keys")) |  | ||||||
|         Crypto::keys.LoadFromFile(keys_dir + DIR_SEP + "title.keys", true); |  | ||||||
|  |  | ||||||
|     int option_index = 0; |     int option_index = 0; | ||||||
|     bool use_gdbstub = Settings::values.use_gdbstub; |     bool use_gdbstub = Settings::values.use_gdbstub; | ||||||
|     u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); |     u32 gdb_port = static_cast<u32>(Settings::values.gdbstub_port); | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user