summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/crypto/key_manager.cpp249
-rw-r--r--src/core/crypto/key_manager.h29
3 files changed, 277 insertions, 2 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index e4a676e91..8ad8dc3f4 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -70,6 +70,7 @@ add_library(core STATIC
70 file_sys/vfs_real.cpp 70 file_sys/vfs_real.cpp
71 file_sys/vfs_real.h 71 file_sys/vfs_real.h
72 file_sys/vfs_static.h 72 file_sys/vfs_static.h
73 file_sys/vfs_types.h
73 file_sys/vfs_vector.cpp 74 file_sys/vfs_vector.cpp
74 file_sys/vfs_vector.h 75 file_sys/vfs_vector.h
75 file_sys/xts_archive.cpp 76 file_sys/xts_archive.cpp
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index b37b09772..1328cdd47 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -4,18 +4,30 @@
4 4
5#include <algorithm> 5#include <algorithm>
6#include <array> 6#include <array>
7#include <bitset>
7#include <fstream> 8#include <fstream>
8#include <locale> 9#include <locale>
10#include <map>
9#include <sstream> 11#include <sstream>
10#include <string_view> 12#include <string_view>
11#include <tuple> 13#include <tuple>
12#include <vector> 14#include <vector>
15#include <mbedtls/bignum.h>
16#include <mbedtls/cipher.h>
17#include <mbedtls/cmac.h>
18#include <mbedtls/sha256.h>
19#include "common/common_funcs.h"
13#include "common/common_paths.h" 20#include "common/common_paths.h"
14#include "common/file_util.h" 21#include "common/file_util.h"
15#include "common/hex_util.h" 22#include "common/hex_util.h"
16#include "common/logging/log.h" 23#include "common/logging/log.h"
17#include "core/crypto/aes_util.h" 24#include "core/crypto/aes_util.h"
18#include "core/crypto/key_manager.h" 25#include "core/crypto/key_manager.h"
26#include "core/file_sys/content_archive.h"
27#include "core/file_sys/nca_metadata.h"
28#include "core/file_sys/partition_filesystem.h"
29#include "core/file_sys/registered_cache.h"
30#include "core/hle/service/filesystem/filesystem.h"
19#include "core/loader/loader.h" 31#include "core/loader/loader.h"
20#include "core/settings.h" 32#include "core/settings.h"
21 33
@@ -23,6 +35,13 @@ namespace Core::Crypto {
23 35
24constexpr u64 CURRENT_CRYPTO_REVISION = 0x5; 36constexpr u64 CURRENT_CRYPTO_REVISION = 0x5;
25 37
38using namespace Common;
39
40const static std::array<SHA256Hash, 4> eticket_source_hashes{
41 "B71DB271DC338DF380AA2C4335EF8873B1AFD408E80B3582D8719FC81C5E511C"_array32, // eticket_rsa_kek_source
42 "E8965A187D30E57869F562D04383C996DE487BBA5761363D2D4D32391866A85C"_array32, // eticket_rsa_kekek_source
43};
44
26Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) { 45Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, Key128 key_seed) {
27 Key128 out{}; 46 Key128 out{};
28 47
@@ -129,9 +148,131 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
129 return source; ///< Return unaltered source to satisfy output requirement. 148 return source; ///< Return unaltered source to satisfy output requirement.
130 }); 149 });
131 150
151 keys.SetKey(S256KeyType::SDKey, sd_keys[0], static_cast<u64>(SDKeyType::Save));
152 keys.SetKey(S256KeyType::SDKey, sd_keys[1], static_cast<u64>(SDKeyType::NCA));
153
132 return Loader::ResultStatus::Success; 154 return Loader::ResultStatus::Success;
133} 155}
134 156
157std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save) {
158 if (!ticket_save.IsOpen())
159 return {};
160
161 std::vector<u8> buffer(ticket_save.GetSize());
162 ticket_save.ReadBytes(buffer.data(), buffer.size());
163
164 std::vector<TicketRaw> out;
165 u32 magic{};
166 for (std::size_t offset = 0; offset + 0x4 < buffer.size(); ++offset) {
167 if (buffer[offset] == 0x4 && buffer[offset + 1] == 0x0 && buffer[offset + 2] == 0x1 &&
168 buffer[offset + 3] == 0x0) {
169 TicketRaw next{};
170 std::memcpy(&next, buffer.data() + offset, sizeof(TicketRaw));
171 offset += next.size();
172 out.push_back(next);
173 }
174 }
175
176 return out;
177}
178
179template <size_t size>
180static std::array<u8, size> operator^(const std::array<u8, size>& lhs,
181 const std::array<u8, size>& rhs) {
182 std::array<u8, size> out{};
183 for (size_t i = 0; i < size; ++i)
184 out[i] = lhs[i] ^ rhs[i];
185 return out;
186}
187
188template <size_t target_size, size_t in_size>
189static std::array<u8, target_size> MGF1(const std::array<u8, in_size>& seed) {
190 std::array<u8, in_size + 4> seed_exp{};
191 std::memcpy(seed_exp.data(), seed.data(), in_size);
192
193 std::vector<u8> out;
194 size_t i = 0;
195 while (out.size() < target_size) {
196 out.resize(out.size() + 0x20, 0);
197 seed_exp[in_size + 3] = i;
198 mbedtls_sha256(seed_exp.data(), seed_exp.size(), out.data() + out.size() - 0x20, 0);
199 ++i;
200 }
201
202 std::array<u8, target_size> target{};
203 std::memcpy(target.data(), out.data(), target_size);
204 return target;
205}
206
207boost::optional<std::pair<Key128, Key128>> ParseTicket(const TicketRaw& ticket,
208 const RSAKeyPair<2048>& key) {
209 u32 cert_authority;
210 std::memcpy(&cert_authority, ticket.data() + 0x140, sizeof(cert_authority));
211 if (cert_authority == 0)
212 return boost::none;
213 if (cert_authority != Common::MakeMagic('R', 'o', 'o', 't'))
214 LOG_INFO(Crypto,
215 "Attempting to parse ticket with non-standard certificate authority {:08X}.",
216 cert_authority);
217
218 Key128 rights_id{};
219 std::memcpy(rights_id.data(), ticket.data() + 0x2A0, sizeof(Key128));
220
221 Key128 key_temp{};
222
223 if (!std::any_of(ticket.begin() + 0x190, ticket.begin() + 0x280, [](u8 b) { return b != 0; })) {
224 std::memcpy(key_temp.data(), ticket.data() + 0x180, key_temp.size());
225 return std::pair<Key128, Key128>{rights_id, key_temp};
226 }
227
228 mbedtls_mpi D; // RSA Private Exponent
229 mbedtls_mpi N; // RSA Modulus
230 mbedtls_mpi S; // Input
231 mbedtls_mpi M; // Output
232
233 mbedtls_mpi_init(&D);
234 mbedtls_mpi_init(&N);
235 mbedtls_mpi_init(&S);
236 mbedtls_mpi_init(&M);
237
238 mbedtls_mpi_read_binary(&D, key.decryption_key.data(), key.decryption_key.size());
239 mbedtls_mpi_read_binary(&N, key.modulus.data(), key.modulus.size());
240 mbedtls_mpi_read_binary(&S, ticket.data() + 0x180, 0x100);
241
242 mbedtls_mpi_exp_mod(&M, &S, &D, &N, nullptr);
243
244 std::array<u8, 0x100> rsa_step{};
245 mbedtls_mpi_write_binary(&M, rsa_step.data(), rsa_step.size());
246
247 u8 m_0 = rsa_step[0];
248 std::array<u8, 0x20> m_1{};
249 std::memcpy(m_1.data(), rsa_step.data() + 0x01, m_1.size());
250 std::array<u8, 0xDF> m_2{};
251 std::memcpy(m_2.data(), rsa_step.data() + 0x21, m_2.size());
252
253 if (m_0 != 0)
254 return boost::none;
255
256 m_1 = m_1 ^ MGF1<0x20>(m_2);
257 m_2 = m_2 ^ MGF1<0xDF>(m_1);
258
259 u64 offset = 0;
260 for (size_t i = 0x20; i < m_2.size() - 0x10; ++i) {
261 if (m_2[i] == 0x1) {
262 offset = i + 1;
263 break;
264 } else if (m_2[i] != 0x0) {
265 return boost::none;
266 }
267 }
268
269 ASSERT(offset > 0);
270
271 std::memcpy(key_temp.data(), m_2.data() + offset, key_temp.size());
272
273 return std::pair<Key128, Key128>{rights_id, key_temp};
274}
275
135KeyManager::KeyManager() { 276KeyManager::KeyManager() {
136 // Initialize keys 277 // Initialize keys
137 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath(); 278 const std::string hactool_keys_dir = FileUtil::GetHactoolConfigurationPath();
@@ -609,6 +750,114 @@ void KeyManager::DeriveBase() {
609 SetKey(S256KeyType::Header, out); 750 SetKey(S256KeyType::Header, out);
610 } 751 }
611} 752}
753
754void KeyManager::DeriveETicket(PartitionDataManager data) {
755 // ETicket keys
756 const auto es = Service::FileSystem::GetUnionContents()->GetEntry(
757 0x0100000000000033, FileSys::ContentRecordType::Program);
758
759 if (es == nullptr)
760 return;
761
762 const auto exefs = es->GetExeFS();
763 if (exefs == nullptr)
764 return;
765
766 const auto main = exefs->GetFile("main");
767 if (main == nullptr)
768 return;
769
770 const auto bytes = main->ReadAllBytes();
771
772 using namespace Common;
773 const auto eticket_kek = FindKeyFromHex(bytes, eticket_source_hashes[0]);
774 const auto eticket_kekek = FindKeyFromHex(bytes, eticket_source_hashes[1]);
775
776 const auto seed3 = data.GetRSAKekSeed3();
777 const auto mask0 = data.GetRSAKekMask0();
778
779 if (eticket_kek != Key128{})
780 SetKey(S128KeyType::Source, eticket_kek, static_cast<size_t>(SourceKeyType::ETicketKek));
781 if (eticket_kekek != Key128{})
782 SetKey(S128KeyType::Source, eticket_kekek,
783 static_cast<size_t>(SourceKeyType::ETicketKekek));
784 if (seed3 != Key128{})
785 SetKey(S128KeyType::RSAKek, seed3, static_cast<size_t>(RSAKekType::Seed3));
786 if (mask0 != Key128{})
787 SetKey(S128KeyType::RSAKek, mask0, static_cast<size_t>(RSAKekType::Mask0));
788
789 if (eticket_kek == Key128{} || eticket_kekek == Key128{} || seed3 == Key128{} ||
790 mask0 == Key128{})
791 return;
792
793 Key128 rsa_oaep_kek{};
794 for (size_t i = 0; i < rsa_oaep_kek.size(); ++i)
795 rsa_oaep_kek[i] = seed3[i] ^ mask0[i];
796
797 if (rsa_oaep_kek == Key128{})
798 return;
799
800 SetKey(S128KeyType::Source, rsa_oaep_kek,
801 static_cast<u64>(SourceKeyType::RSAOaepKekGeneration));
802
803 Key128 temp_kek{};
804 Key128 temp_kekek{};
805 Key128 eticket_final{};
806
807 // Derive ETicket RSA Kek
808 AESCipher<Key128> es_master(GetKey(S128KeyType::Master), Mode::ECB);
809 es_master.Transcode(rsa_oaep_kek.data(), rsa_oaep_kek.size(), temp_kek.data(), Op::Decrypt);
810 AESCipher<Key128> es_kekek(temp_kek, Mode::ECB);
811 es_kekek.Transcode(eticket_kekek.data(), eticket_kekek.size(), temp_kekek.data(), Op::Decrypt);
812 AESCipher<Key128> es_kek(temp_kekek, Mode::ECB);
813 es_kek.Transcode(eticket_kek.data(), eticket_kek.size(), eticket_final.data(), Op::Decrypt);
814
815 if (eticket_final == Key128{})
816 return;
817
818 SetKey(S128KeyType::ETicketRSAKek, eticket_final);
819
820 // Titlekeys
821 data.DecryptProdInfo(GetKey(S128KeyType::BIS),
822 GetKey(S128KeyType::BIS, 0, static_cast<u64>(BISKeyType::Tweak)));
823
824 const auto eticket_extended_kek = data.GetETicketExtendedKek();
825
826 std::vector<u8> extended_iv(0x10);
827 std::memcpy(extended_iv.data(), eticket_extended_kek.data(), extended_iv.size());
828 std::array<u8, 0x230> extended_dec{};
829 AESCipher<Key128> rsa_1(eticket_final, Mode::CTR);
830 rsa_1.SetIV(extended_iv);
831 rsa_1.Transcode(eticket_extended_kek.data() + 0x10, eticket_extended_kek.size() - 0x10,
832 extended_dec.data(), Op::Decrypt);
833
834 RSAKeyPair<2048> rsa_key{};
835 std::memcpy(rsa_key.decryption_key.data(), extended_dec.data(), rsa_key.decryption_key.size());
836 std::memcpy(rsa_key.modulus.data(), extended_dec.data() + 0x100, rsa_key.modulus.size());
837 std::memcpy(rsa_key.exponent.data(), extended_dec.data() + 0x200, rsa_key.exponent.size());
838
839 const FileUtil::IOFile save1(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
840 "/system/save/80000000000000e1",
841 "rb+");
842 const FileUtil::IOFile save2(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir) +
843 "/system/save/80000000000000e2",
844 "rb+");
845
846 auto res = GetTicketblob(save1);
847 const auto res2 = GetTicketblob(save2);
848 std::copy(res2.begin(), res2.end(), std::back_inserter(res));
849
850 for (const auto& raw : res) {
851 const auto pair = ParseTicket(raw, rsa_key);
852 if (pair == boost::none)
853 continue;
854 auto [rid, key] = pair.value();
855 u128 rights_id{};
856 std::memcpy(rights_id.data(), rid.data(), rid.size());
857 SetKey(S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
858 }
859}
860
612void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) { 861void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
613 if (key == Key128{}) 862 if (key == Key128{})
614 return; 863 return;
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 8de65ec4e..58afcdcac 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -5,11 +5,18 @@
5#pragma once 5#pragma once
6 6
7#include <array> 7#include <array>
8#include <map>
8#include <string> 9#include <string>
9#include <boost/container/flat_map.hpp> 10#include <boost/container/flat_map.hpp>
10#include <boost/optional.hpp> 11#include <boost/optional.hpp>
11#include <fmt/format.h> 12#include <fmt/format.h>
12#include "common/common_types.h" 13#include "common/common_types.h"
14#include "core/file_sys/vfs_types.h"
15#include "partition_data_manager.h"
16
17namespace FileUtil {
18class IOFile;
19}
13 20
14namespace Loader { 21namespace Loader {
15enum class ResultStatus : u16; 22enum class ResultStatus : u16;
@@ -22,9 +29,18 @@ constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180;
22using Key128 = std::array<u8, 0x10>; 29using Key128 = std::array<u8, 0x10>;
23using Key256 = std::array<u8, 0x20>; 30using Key256 = std::array<u8, 0x20>;
24using SHA256Hash = std::array<u8, 0x20>; 31using SHA256Hash = std::array<u8, 0x20>;
32using TicketRaw = std::array<u8, 0x400>;
25 33
26static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big."); 34static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
27static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big."); 35static_assert(sizeof(Key256) == 32, "Key256 must be 256 bytes big.");
36
37template <size_t bit_size, size_t byte_size = (bit_size >> 3)>
38struct RSAKeyPair {
39 std::array<u8, byte_size> encryption_key;
40 std::array<u8, byte_size> decryption_key;
41 std::array<u8, byte_size> modulus;
42 std::array<u8, 4> exponent;
43};
28 44
29enum class KeyCategory : u8 { 45enum class KeyCategory : u8 {
30 Standard, 46 Standard,
@@ -140,6 +156,8 @@ public:
140 156
141 bool BaseDeriveNecessary(); 157 bool BaseDeriveNecessary();
142 void DeriveBase(); 158 void DeriveBase();
159 void DeriveETicket(PartitionDataManager data);
160
143private: 161private:
144 std::map<KeyIndex<S128KeyType>, Key128> s128_keys; 162 std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
145 std::map<KeyIndex<S256KeyType>, Key256> s256_keys; 163 std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
@@ -166,6 +184,13 @@ Key128 GenerateKeyEncryptionKey(Key128 source, Key128 master, Key128 kek_seed, K
166Key128 DeriveKeyblobKey(Key128 sbk, Key128 tsec, Key128 source); 184Key128 DeriveKeyblobKey(Key128 sbk, Key128 tsec, Key128 source);
167 185
168boost::optional<Key128> DeriveSDSeed(); 186boost::optional<Key128> DeriveSDSeed();
169Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, const KeyManager& keys); 187Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& keys);
188
189std::vector<TicketRaw> GetTicketblob(const FileUtil::IOFile& ticket_save);
190
191// Returns a pair of {rights_id, titlekey}. Fails if the ticket has no certificate authority (offset
192// 0x140-0x144 is zero)
193boost::optional<std::pair<Key128, Key128>> ParseTicket(
194 const TicketRaw& ticket, const RSAKeyPair<2048>& eticket_extended_key);
170 195
171} // namespace Core::Crypto 196} // namespace Core::Crypto