diff options
| -rw-r--r-- | src/core/file_sys/content_archive.cpp | 44 | ||||
| -rw-r--r-- | src/core/file_sys/content_archive.h | 2 |
2 files changed, 39 insertions, 7 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index 79e70f6ef..3529166ac 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -76,12 +76,17 @@ bool IsValidNCA(const NCAHeader& header) { | |||
| 76 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | 76 | return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); |
| 77 | } | 77 | } |
| 78 | 78 | ||
| 79 | boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { | 79 | u8 NCA::GetCryptoRevision() const { |
| 80 | u8 master_key_id = header.crypto_type; | 80 | u8 master_key_id = header.crypto_type; |
| 81 | if (header.crypto_type_2 > master_key_id) | 81 | if (header.crypto_type_2 > master_key_id) |
| 82 | master_key_id = header.crypto_type_2; | 82 | master_key_id = header.crypto_type_2; |
| 83 | if (master_key_id > 0) | 83 | if (master_key_id > 0) |
| 84 | --master_key_id; | 84 | --master_key_id; |
| 85 | return master_key_id; | ||
| 86 | } | ||
| 87 | |||
| 88 | boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const { | ||
| 89 | const auto master_key_id = GetCryptoRevision(); | ||
| 85 | 90 | ||
| 86 | if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) | 91 | if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) |
| 87 | return boost::none; | 92 | return boost::none; |
| @@ -108,33 +113,58 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty | |||
| 108 | return out; | 113 | return out; |
| 109 | } | 114 | } |
| 110 | 115 | ||
| 111 | VirtualFile NCA::Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const { | 116 | boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() const { |
| 117 | const auto master_key_id = GetCryptoRevision(); | ||
| 118 | |||
| 119 | u128 rights_id{}; | ||
| 120 | memcpy(rights_id.data(), header.rights_id.data(), 16); | ||
| 121 | if (rights_id == u128{}) | ||
| 122 | return boost::none; | ||
| 123 | |||
| 124 | auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]); | ||
| 125 | if (titlekey == Core::Crypto::Key128{}) | ||
| 126 | return boost::none; | ||
| 127 | Core::Crypto::AESCipher<Core::Crypto::Key128> cipher( | ||
| 128 | keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB); | ||
| 129 | cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt); | ||
| 130 | |||
| 131 | return titlekey; | ||
| 132 | } | ||
| 133 | |||
| 134 | VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) const { | ||
| 112 | if (!encrypted) | 135 | if (!encrypted) |
| 113 | return in; | 136 | return in; |
| 114 | 137 | ||
| 115 | switch (header.raw.header.crypto_type) { | 138 | switch (s_header.raw.header.crypto_type) { |
| 116 | case NCASectionCryptoType::NONE: | 139 | case NCASectionCryptoType::NONE: |
| 117 | LOG_DEBUG(Crypto, "called with mode=NONE"); | 140 | LOG_DEBUG(Crypto, "called with mode=NONE"); |
| 118 | return in; | 141 | return in; |
| 119 | case NCASectionCryptoType::CTR: | 142 | case NCASectionCryptoType::CTR: |
| 120 | LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); | 143 | LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); |
| 121 | { | 144 | { |
| 122 | const auto key = GetKeyAreaKey(NCASectionCryptoType::CTR); | 145 | boost::optional<Core::Crypto::Key128> key = boost::none; |
| 146 | if (std::find_if_not(header.rights_id.begin(), header.rights_id.end(), | ||
| 147 | [](char c) { return c == 0; }) == header.rights_id.end()) { | ||
| 148 | key = GetKeyAreaKey(NCASectionCryptoType::CTR); | ||
| 149 | } else { | ||
| 150 | key = GetTitlekey(); | ||
| 151 | } | ||
| 152 | |||
| 123 | if (key == boost::none) | 153 | if (key == boost::none) |
| 124 | return nullptr; | 154 | return nullptr; |
| 125 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( | 155 | auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>( |
| 126 | std::move(in), key.value(), starting_offset); | 156 | std::move(in), key.value(), starting_offset); |
| 127 | std::vector<u8> iv(16); | 157 | std::vector<u8> iv(16); |
| 128 | for (u8 i = 0; i < 8; ++i) | 158 | for (u8 i = 0; i < 8; ++i) |
| 129 | iv[i] = header.raw.section_ctr[0x8 - i - 1]; | 159 | iv[i] = s_header.raw.section_ctr[0x8 - i - 1]; |
| 130 | out->SetIV(iv); | 160 | out->SetIV(iv); |
| 131 | return std::static_pointer_cast<VfsFile>(out); | 161 | return std::static_pointer_cast<VfsFile>(out); |
| 132 | } | 162 | } |
| 133 | case NCASectionCryptoType::XTS: | 163 | case NCASectionCryptoType::XTS: |
| 134 | // TODO(DarkLordZach): Implement XTSEncryptionLayer and title key encryption. | 164 | // TODO(DarkLordZach): Implement XTSEncryptionLayer. |
| 135 | default: | 165 | default: |
| 136 | LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", | 166 | LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}", |
| 137 | static_cast<u8>(header.raw.header.crypto_type)); | 167 | static_cast<u8>(s_header.raw.header.crypto_type)); |
| 138 | return nullptr; | 168 | return nullptr; |
| 139 | } | 169 | } |
| 140 | } | 170 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 6492163b5..a8879d9a8 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -95,7 +95,9 @@ protected: | |||
| 95 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | 95 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; |
| 96 | 96 | ||
| 97 | private: | 97 | private: |
| 98 | u8 GetCryptoRevision() const; | ||
| 98 | boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; | 99 | boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; |
| 100 | boost::optional<Core::Crypto::Key128> GetTitlekey() const; | ||
| 99 | VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; | 101 | VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset) const; |
| 100 | 102 | ||
| 101 | std::vector<VirtualDir> dirs; | 103 | std::vector<VirtualDir> dirs; |