diff options
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/crypto/partition_data_manager.cpp | 586 | ||||
| -rw-r--r-- | src/core/crypto/partition_data_manager.h | 104 |
3 files changed, 692 insertions, 0 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 8ad8dc3f4..4fddaafd1 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -18,6 +18,8 @@ add_library(core STATIC | |||
| 18 | crypto/encryption_layer.h | 18 | crypto/encryption_layer.h |
| 19 | crypto/key_manager.cpp | 19 | crypto/key_manager.cpp |
| 20 | crypto/key_manager.h | 20 | crypto/key_manager.h |
| 21 | crypto/partition_data_manager.cpp | ||
| 22 | crypto/partition_data_manager.h | ||
| 21 | crypto/ctr_encryption_layer.cpp | 23 | crypto/ctr_encryption_layer.cpp |
| 22 | crypto/ctr_encryption_layer.h | 24 | crypto/ctr_encryption_layer.h |
| 23 | crypto/xts_encryption_layer.cpp | 25 | crypto/xts_encryption_layer.cpp |
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp new file mode 100644 index 000000000..60128b0e1 --- /dev/null +++ b/src/core/crypto/partition_data_manager.cpp | |||
| @@ -0,0 +1,586 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | // NOTE TO FUTURE MAINTAINERS: | ||
| 6 | // When a new version of switch cryptography is released, | ||
| 7 | // hash the new keyblob source and master key and add the hashes to | ||
| 8 | // the arrays below. | ||
| 9 | |||
| 10 | #include <array> | ||
| 11 | #include <boost/optional/optional.hpp> | ||
| 12 | #include <mbedtls/sha256.h> | ||
| 13 | #include "common/assert.h" | ||
| 14 | #include "common/common_types.h" | ||
| 15 | #include "common/hex_util.h" | ||
| 16 | #include "common/logging/log.h" | ||
| 17 | #include "common/string_util.h" | ||
| 18 | #include "core/crypto/key_manager.h" | ||
| 19 | #include "core/crypto/partition_data_manager.h" | ||
| 20 | #include "core/file_sys/vfs.h" | ||
| 21 | #include "core/file_sys/vfs_offset.h" | ||
| 22 | #include "ctr_encryption_layer.h" | ||
| 23 | #include "xts_encryption_layer.h" | ||
| 24 | |||
| 25 | using namespace Common; | ||
| 26 | |||
| 27 | namespace Core::Crypto { | ||
| 28 | |||
| 29 | struct Package2Header { | ||
| 30 | std::array<u8, 0x100> signature; | ||
| 31 | Key128 header_ctr; | ||
| 32 | std::array<Key128, 4> section_ctr; | ||
| 33 | u32_le magic; | ||
| 34 | u32_le base_offset; | ||
| 35 | INSERT_PADDING_BYTES(4); | ||
| 36 | u8 version_max; | ||
| 37 | u8 version_min; | ||
| 38 | INSERT_PADDING_BYTES(2); | ||
| 39 | std::array<u32_le, 4> section_size; | ||
| 40 | std::array<u32_le, 4> section_offset; | ||
| 41 | std::array<SHA256Hash, 4> section_hash; | ||
| 42 | }; | ||
| 43 | static_assert(sizeof(Package2Header) == 0x200, "Package2Header has incorrect size."); | ||
| 44 | |||
| 45 | struct INIHeader { | ||
| 46 | u32_le magic; | ||
| 47 | u32_le size; | ||
| 48 | u32_le process_count; | ||
| 49 | INSERT_PADDING_BYTES(4); | ||
| 50 | }; | ||
| 51 | static_assert(sizeof(INIHeader) == 0x10, "INIHeader has incorrect size."); | ||
| 52 | |||
| 53 | struct SectionHeader { | ||
| 54 | u32_le offset; | ||
| 55 | u32_le size_decompressed; | ||
| 56 | u32_le size_compressed; | ||
| 57 | u32_le attribute; | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(SectionHeader) == 0x10, "SectionHeader has incorrect size."); | ||
| 60 | |||
| 61 | struct KIPHeader { | ||
| 62 | u32_le magic; | ||
| 63 | std::array<char, 12> name; | ||
| 64 | u64_le title_id; | ||
| 65 | u32_le category; | ||
| 66 | u8 priority; | ||
| 67 | u8 core; | ||
| 68 | INSERT_PADDING_BYTES(1); | ||
| 69 | u8 flags; | ||
| 70 | std::array<SectionHeader, 6> sections; | ||
| 71 | std::array<u32, 0x20> capabilities; | ||
| 72 | }; | ||
| 73 | static_assert(sizeof(KIPHeader) == 0x100, "KIPHeader has incorrect size."); | ||
| 74 | |||
| 75 | const static std::array<SHA256Hash, 0x10> source_hashes{ | ||
| 76 | "B24BD293259DBC7AC5D63F88E60C59792498E6FC5443402C7FFE87EE8B61A3F0"_array32, // keyblob_mac_key_source | ||
| 77 | "7944862A3A5C31C6720595EFD302245ABD1B54CCDCF33000557681E65C5664A4"_array32, // master_key_source | ||
| 78 | "21E2DF100FC9E094DB51B47B9B1D6E94ED379DB8B547955BEF8FE08D8DD35603"_array32, // package2_key_source | ||
| 79 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // aes_kek_generation_source | ||
| 80 | "FBD10056999EDC7ACDB96098E47E2C3606230270D23281E671F0F389FC5BC585"_array32, // aes_key_generation_source | ||
| 81 | "C48B619827986C7F4E3081D59DB2B460C84312650E9A8E6B458E53E8CBCA4E87"_array32, // titlekek_source | ||
| 82 | "04AD66143C726B2A139FB6B21128B46F56C553B2B3887110304298D8D0092D9E"_array32, // key_area_key_application_source | ||
| 83 | "FD434000C8FF2B26F8E9A9D2D2C12F6BE5773CBB9DC86300E1BD99F8EA33A417"_array32, // key_area_key_ocean_source | ||
| 84 | "1F17B1FD51AD1C2379B58F152CA4912EC2106441E51722F38700D5937A1162F7"_array32, // key_area_key_system_source | ||
| 85 | "6B2ED877C2C52334AC51E59ABFA7EC457F4A7D01E46291E9F2EAA45F011D24B7"_array32, // sd_card_kek_source | ||
| 86 | "D482743563D3EA5DCDC3B74E97C9AC8A342164FA041A1DC80F17F6D31E4BC01C"_array32, // sd_card_save_key_source | ||
| 87 | "2E751CECF7D93A2B957BD5FFCB082FD038CC2853219DD3092C6DAB9838F5A7CC"_array32, // sd_card_nca_key_source | ||
| 88 | "1888CAED5551B3EDE01499E87CE0D86827F80820EFB275921055AA4E2ABDFFC2"_array32, // header_kek_source | ||
| 89 | "8F783E46852DF6BE0BA4E19273C4ADBAEE16380043E1B8C418C4089A8BD64AA6"_array32, // header_key_source | ||
| 90 | "D1757E52F1AE55FA882EC690BC6F954AC46A83DC22F277F8806BD55577C6EED7"_array32, // rsa_kek_seed3 | ||
| 91 | "FC02B9D37B42D7A1452E71444F1F700311D1132E301A83B16062E72A78175085"_array32, // rsa_kek_mask0 | ||
| 92 | }; | ||
| 93 | |||
| 94 | const static std::array<SHA256Hash, 0x20> keyblob_source_hashes{ | ||
| 95 | "8A06FE274AC491436791FDB388BCDD3AB9943BD4DEF8094418CDAC150FD73786"_array32, // keyblob_key_source_00 | ||
| 96 | "2D5CAEB2521FEF70B47E17D6D0F11F8CE2C1E442A979AD8035832C4E9FBCCC4B"_array32, // keyblob_key_source_01 | ||
| 97 | "61C5005E713BAE780641683AF43E5F5C0E03671117F702F401282847D2FC6064"_array32, // keyblob_key_source_02 | ||
| 98 | "8E9795928E1C4428E1B78F0BE724D7294D6934689C11B190943923B9D5B85903"_array32, // keyblob_key_source_03 | ||
| 99 | "95FA33AF95AFF9D9B61D164655B32710ED8D615D46C7D6CC3CC70481B686B402"_array32, // keyblob_key_source_04 | ||
| 100 | "3F5BE7B3C8B1ABD8C10B4B703D44766BA08730562C172A4FE0D6B866B3E2DB3E"_array32, // keyblob_key_source_05 | ||
| 101 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_06 | ||
| 102 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_07 | ||
| 103 | |||
| 104 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_08 | ||
| 105 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_09 | ||
| 106 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0A | ||
| 107 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0B | ||
| 108 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0C | ||
| 109 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0D | ||
| 110 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0E | ||
| 111 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_0F | ||
| 112 | |||
| 113 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_10 | ||
| 114 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_11 | ||
| 115 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_12 | ||
| 116 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_13 | ||
| 117 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_14 | ||
| 118 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_15 | ||
| 119 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_16 | ||
| 120 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_17 | ||
| 121 | |||
| 122 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_18 | ||
| 123 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_19 | ||
| 124 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1A | ||
| 125 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1B | ||
| 126 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1C | ||
| 127 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1D | ||
| 128 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1E | ||
| 129 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // keyblob_key_source_1F | ||
| 130 | }; | ||
| 131 | |||
| 132 | const static std::array<SHA256Hash, 0x20> master_key_hashes{ | ||
| 133 | "0EE359BE3C864BB0782E1D70A718A0342C551EED28C369754F9C4F691BECF7CA"_array32, // master_key_00 | ||
| 134 | "4FE707B7E4ABDAF727C894AAF13B1351BFE2AC90D875F73B2E20FA94B9CC661E"_array32, // master_key_01 | ||
| 135 | "79277C0237A2252EC3DFAC1F7C359C2B3D121E9DB15BB9AB4C2B4408D2F3AE09"_array32, // master_key_02 | ||
| 136 | "4F36C565D13325F65EE134073C6A578FFCB0008E02D69400836844EAB7432754"_array32, // master_key_03 | ||
| 137 | "75FF1D95D26113550EE6FCC20ACB58E97EDEB3A2FF52543ED5AEC63BDCC3DA50"_array32, // master_key_04 | ||
| 138 | "EBE2BCD6704673EC0F88A187BB2AD9F1CC82B718C389425941BDC194DC46B0DD"_array32, // master_key_05 | ||
| 139 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_06 | ||
| 140 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_07 | ||
| 141 | |||
| 142 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_08 | ||
| 143 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_09 | ||
| 144 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0A | ||
| 145 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0B | ||
| 146 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0C | ||
| 147 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0D | ||
| 148 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0E | ||
| 149 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_0F | ||
| 150 | |||
| 151 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_10 | ||
| 152 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_11 | ||
| 153 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_12 | ||
| 154 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_13 | ||
| 155 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_14 | ||
| 156 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_15 | ||
| 157 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_16 | ||
| 158 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_17 | ||
| 159 | |||
| 160 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_18 | ||
| 161 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_19 | ||
| 162 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1A | ||
| 163 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1B | ||
| 164 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1C | ||
| 165 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1D | ||
| 166 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1E | ||
| 167 | "0000000000000000000000000000000000000000000000000000000000000000"_array32, // master_key_1F | ||
| 168 | }; | ||
| 169 | |||
| 170 | std::vector<u8> DecompressBLZ(const std::vector<u8>& in) { | ||
| 171 | const auto data_size = in.size() - 0xC; | ||
| 172 | |||
| 173 | u32 compressed_size{}; | ||
| 174 | u32 init_index{}; | ||
| 175 | u32 additional_size{}; | ||
| 176 | std::memcpy(&compressed_size, in.data() + data_size, sizeof(u32)); | ||
| 177 | std::memcpy(&init_index, in.data() + data_size + 0x4, sizeof(u32)); | ||
| 178 | std::memcpy(&additional_size, in.data() + data_size + 0x8, sizeof(u32)); | ||
| 179 | |||
| 180 | std::vector<u8> out(in.size() + additional_size); | ||
| 181 | |||
| 182 | if (compressed_size == in.size()) | ||
| 183 | std::memcpy(out.data(), in.data() + in.size() - compressed_size, compressed_size); | ||
| 184 | else | ||
| 185 | std::memcpy(out.data(), in.data(), compressed_size); | ||
| 186 | |||
| 187 | auto index = in.size() - init_index; | ||
| 188 | auto out_index = out.size(); | ||
| 189 | |||
| 190 | while (out_index > 0) { | ||
| 191 | --index; | ||
| 192 | auto control = in[index]; | ||
| 193 | for (size_t i = 0; i < 8; ++i) { | ||
| 194 | if ((control & 0x80) > 0) { | ||
| 195 | ASSERT(index >= 2); | ||
| 196 | index -= 2; | ||
| 197 | u64 segment_offset = in[index] | in[index + 1] << 8; | ||
| 198 | u64 segment_size = ((segment_offset >> 12) & 0xF) + 3; | ||
| 199 | segment_offset &= 0xFFF; | ||
| 200 | segment_offset += 3; | ||
| 201 | |||
| 202 | if (out_index < segment_size) | ||
| 203 | segment_size = out_index; | ||
| 204 | |||
| 205 | ASSERT(out_index >= segment_size); | ||
| 206 | |||
| 207 | out_index -= segment_size; | ||
| 208 | |||
| 209 | for (size_t j = 0; j < segment_size; ++j) { | ||
| 210 | ASSERT(out_index + j + segment_offset < out.size()); | ||
| 211 | out[out_index + j] = out[out_index + j + segment_offset]; | ||
| 212 | } | ||
| 213 | } else { | ||
| 214 | ASSERT(out_index >= 1); | ||
| 215 | --out_index; | ||
| 216 | --index; | ||
| 217 | out[out_index] = in[index]; | ||
| 218 | } | ||
| 219 | |||
| 220 | control <<= 1; | ||
| 221 | if (out_index == 0) | ||
| 222 | return out; | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | return out; | ||
| 227 | } | ||
| 228 | |||
| 229 | u8 CalculateMaxKeyblobSourceHash() { | ||
| 230 | for (s8 i = 0x1F; i >= 0; --i) { | ||
| 231 | if (keyblob_source_hashes[i] != SHA256Hash{}) | ||
| 232 | return i + 1; | ||
| 233 | } | ||
| 234 | |||
| 235 | return 0; | ||
| 236 | } | ||
| 237 | |||
| 238 | const u8 PartitionDataManager::MAX_KEYBLOB_SOURCE_HASH = CalculateMaxKeyblobSourceHash(); | ||
| 239 | |||
| 240 | template <size_t key_size = 0x10> | ||
| 241 | std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary, std::array<u8, 0x20> hash) { | ||
| 242 | std::array<u8, 0x20> temp{}; | ||
| 243 | if (binary.size() < key_size) | ||
| 244 | return {}; | ||
| 245 | for (size_t i = 0; i < binary.size() - key_size; ++i) { | ||
| 246 | mbedtls_sha256(binary.data() + i, key_size, temp.data(), 0); | ||
| 247 | |||
| 248 | if (temp != hash) | ||
| 249 | continue; | ||
| 250 | |||
| 251 | std::array<u8, key_size> out{}; | ||
| 252 | std::memcpy(out.data(), binary.data() + i, key_size); | ||
| 253 | return out; | ||
| 254 | } | ||
| 255 | |||
| 256 | return {}; | ||
| 257 | } | ||
| 258 | |||
| 259 | std::array<Key128, 0x20> FindEncryptedMasterKeyFromHex(const std::vector<u8>& binary, Key128 key) { | ||
| 260 | SHA256Hash temp{}; | ||
| 261 | Key128 dec_temp{}; | ||
| 262 | if (binary.size() < 0x10) | ||
| 263 | return {}; | ||
| 264 | |||
| 265 | std::array<Key128, 0x20> out{}; | ||
| 266 | AESCipher<Key128> cipher(key, Mode::ECB); | ||
| 267 | for (size_t i = 0; i < binary.size() - 0x10; ++i) { | ||
| 268 | cipher.Transcode(binary.data() + i, dec_temp.size(), dec_temp.data(), Op::Decrypt); | ||
| 269 | mbedtls_sha256(dec_temp.data(), dec_temp.size(), temp.data(), 0); | ||
| 270 | |||
| 271 | for (size_t k = 0; k < 0x20; ++k) { | ||
| 272 | if (temp == master_key_hashes[k]) { | ||
| 273 | out[k] = dec_temp; | ||
| 274 | break; | ||
| 275 | } | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | return out; | ||
| 280 | } | ||
| 281 | |||
| 282 | FileSys::VirtualFile FindFileInDirWithNames(const FileSys::VirtualDir& dir, | ||
| 283 | const std::string& name) { | ||
| 284 | auto upper = name; | ||
| 285 | std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper); | ||
| 286 | for (const auto& fname : {name, name + ".bin", upper, upper + ".BIN"}) { | ||
| 287 | if (dir->GetFile(fname) != nullptr) | ||
| 288 | return dir->GetFile(fname); | ||
| 289 | } | ||
| 290 | |||
| 291 | return nullptr; | ||
| 292 | } | ||
| 293 | |||
| 294 | PartitionDataManager::PartitionDataManager(FileSys::VirtualDir sysdata_dir) | ||
| 295 | : boot0(FindFileInDirWithNames(sysdata_dir, "BOOT0")), | ||
| 296 | fuses(FindFileInDirWithNames(sysdata_dir, "fuse")), | ||
| 297 | kfuses(FindFileInDirWithNames(sysdata_dir, "kfuse")), | ||
| 298 | package2({ | ||
| 299 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-1-Normal-Main"), | ||
| 300 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-2-Normal-Sub"), | ||
| 301 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-3-SafeMode-Main"), | ||
| 302 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-4-SafeMode-Sub"), | ||
| 303 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-5-Repair-Main"), | ||
| 304 | FindFileInDirWithNames(sysdata_dir, "BCPKG2-6-Repair-Sub"), | ||
| 305 | }), | ||
| 306 | secure_monitor(FindFileInDirWithNames(sysdata_dir, "sm")), | ||
| 307 | package1_decrypted(FindFileInDirWithNames(sysdata_dir, "pkg_decr")), | ||
| 308 | secure_monitor_bytes(secure_monitor == nullptr ? std::vector<u8>{} | ||
| 309 | : secure_monitor->ReadAllBytes()), | ||
| 310 | package1_decrypted_bytes(package1_decrypted == nullptr ? std::vector<u8>{} | ||
| 311 | : package1_decrypted->ReadAllBytes()), | ||
| 312 | prodinfo(FindFileInDirWithNames(sysdata_dir, "PRODINFO")) {} | ||
| 313 | |||
| 314 | bool PartitionDataManager::HasBoot0() const { | ||
| 315 | return boot0 != nullptr; | ||
| 316 | } | ||
| 317 | |||
| 318 | FileSys::VirtualFile PartitionDataManager::GetBoot0Raw() const { | ||
| 319 | return boot0; | ||
| 320 | } | ||
| 321 | |||
| 322 | std::array<u8, 176> PartitionDataManager::GetEncryptedKeyblob(u8 index) const { | ||
| 323 | if (HasBoot0() && index < 32) | ||
| 324 | return GetEncryptedKeyblobs()[index]; | ||
| 325 | return {}; | ||
| 326 | } | ||
| 327 | |||
| 328 | std::array<std::array<u8, 176>, 32> PartitionDataManager::GetEncryptedKeyblobs() const { | ||
| 329 | if (!HasBoot0()) | ||
| 330 | return {}; | ||
| 331 | |||
| 332 | std::array<std::array<u8, 176>, 32> out{}; | ||
| 333 | for (size_t i = 0; i < 0x20; ++i) | ||
| 334 | boot0->Read(out[i].data(), out[i].size(), 0x180000 + i * 0x200); | ||
| 335 | return out; | ||
| 336 | } | ||
| 337 | |||
| 338 | std::vector<u8> PartitionDataManager::GetSecureMonitor() const { | ||
| 339 | return secure_monitor_bytes; | ||
| 340 | } | ||
| 341 | |||
| 342 | std::array<u8, 16> PartitionDataManager::GetPackage2KeySource() const { | ||
| 343 | return FindKeyFromHex(secure_monitor_bytes, source_hashes[2]); | ||
| 344 | } | ||
| 345 | |||
| 346 | std::array<u8, 16> PartitionDataManager::GetAESKekGenerationSource() const { | ||
| 347 | return FindKeyFromHex(secure_monitor_bytes, source_hashes[3]); | ||
| 348 | } | ||
| 349 | |||
| 350 | std::array<u8, 16> PartitionDataManager::GetTitlekekSource() const { | ||
| 351 | return FindKeyFromHex(secure_monitor_bytes, source_hashes[5]); | ||
| 352 | } | ||
| 353 | |||
| 354 | std::array<std::array<u8, 16>, 32> PartitionDataManager::GetTZMasterKeys( | ||
| 355 | std::array<u8, 0x10> master_key) const { | ||
| 356 | return FindEncryptedMasterKeyFromHex(secure_monitor_bytes, master_key); | ||
| 357 | } | ||
| 358 | |||
| 359 | std::array<u8, 16> PartitionDataManager::GetRSAKekSeed3() const { | ||
| 360 | return FindKeyFromHex(secure_monitor_bytes, source_hashes[14]); | ||
| 361 | } | ||
| 362 | |||
| 363 | std::array<u8, 16> PartitionDataManager::GetRSAKekMask0() const { | ||
| 364 | return FindKeyFromHex(secure_monitor_bytes, source_hashes[15]); | ||
| 365 | } | ||
| 366 | |||
| 367 | std::vector<u8> PartitionDataManager::GetPackage1Decrypted() const { | ||
| 368 | return package1_decrypted_bytes; | ||
| 369 | } | ||
| 370 | |||
| 371 | std::array<u8, 16> PartitionDataManager::GetMasterKeySource() const { | ||
| 372 | return FindKeyFromHex(package1_decrypted_bytes, source_hashes[1]); | ||
| 373 | } | ||
| 374 | |||
| 375 | std::array<u8, 16> PartitionDataManager::GetKeyblobMACKeySource() const { | ||
| 376 | return FindKeyFromHex(package1_decrypted_bytes, source_hashes[0]); | ||
| 377 | } | ||
| 378 | |||
| 379 | std::array<u8, 16> PartitionDataManager::GetKeyblobKeySource(u8 revision) const { | ||
| 380 | if (keyblob_source_hashes[revision] == SHA256Hash{}) { | ||
| 381 | LOG_WARNING(Crypto, | ||
| 382 | "No keyblob source hash for crypto revision {:02X}! Cannot derive keys...", | ||
| 383 | revision); | ||
| 384 | } | ||
| 385 | return FindKeyFromHex(package1_decrypted_bytes, keyblob_source_hashes[revision]); | ||
| 386 | } | ||
| 387 | |||
| 388 | bool PartitionDataManager::HasFuses() const { | ||
| 389 | return fuses != nullptr; | ||
| 390 | } | ||
| 391 | |||
| 392 | FileSys::VirtualFile PartitionDataManager::GetFusesRaw() const { | ||
| 393 | return fuses; | ||
| 394 | } | ||
| 395 | |||
| 396 | std::array<u8, 16> PartitionDataManager::GetSecureBootKey() const { | ||
| 397 | if (!HasFuses()) | ||
| 398 | return {}; | ||
| 399 | Key128 out{}; | ||
| 400 | fuses->Read(out.data(), out.size(), 0xA4); | ||
| 401 | return out; | ||
| 402 | } | ||
| 403 | |||
| 404 | bool PartitionDataManager::HasKFuses() const { | ||
| 405 | return kfuses != nullptr; | ||
| 406 | } | ||
| 407 | |||
| 408 | FileSys::VirtualFile PartitionDataManager::GetKFusesRaw() const { | ||
| 409 | return kfuses; | ||
| 410 | } | ||
| 411 | |||
| 412 | bool PartitionDataManager::HasPackage2(Package2Type type) const { | ||
| 413 | return package2.at(static_cast<size_t>(type)) != nullptr; | ||
| 414 | } | ||
| 415 | |||
| 416 | FileSys::VirtualFile PartitionDataManager::GetPackage2Raw(Package2Type type) const { | ||
| 417 | return package2.at(static_cast<size_t>(type)); | ||
| 418 | } | ||
| 419 | |||
| 420 | void PartitionDataManager::DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2_keys, | ||
| 421 | Package2Type type) { | ||
| 422 | FileSys::VirtualFile file = std::make_shared<FileSys::OffsetVfsFile>( | ||
| 423 | package2[static_cast<size_t>(type)], | ||
| 424 | package2[static_cast<size_t>(type)]->GetSize() - 0x4000, 0x4000); | ||
| 425 | |||
| 426 | Package2Header header{}; | ||
| 427 | if (file->ReadObject(&header) != sizeof(Package2Header)) | ||
| 428 | return; | ||
| 429 | |||
| 430 | u8 revision = 0xFF; | ||
| 431 | if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) { | ||
| 432 | for (size_t i = 0; i < 0x20; ++i) { | ||
| 433 | const std::vector<u8> iv(header.header_ctr.begin(), header.header_ctr.end()); | ||
| 434 | Package2Header temp = header; | ||
| 435 | AESCipher<Key128> cipher(package2_keys[i], Mode::CTR); | ||
| 436 | cipher.SetIV(iv); | ||
| 437 | cipher.Transcode(&temp.header_ctr, sizeof(Package2Header) - 0x100, &temp.header_ctr, | ||
| 438 | Op::Decrypt); | ||
| 439 | if (temp.magic == Common::MakeMagic('P', 'K', '2', '1')) { | ||
| 440 | header = temp; | ||
| 441 | revision = i; | ||
| 442 | break; | ||
| 443 | } | ||
| 444 | } | ||
| 445 | } | ||
| 446 | |||
| 447 | if (header.magic != Common::MakeMagic('P', 'K', '2', '1')) | ||
| 448 | return; | ||
| 449 | |||
| 450 | const std::vector<u8> s1_iv(header.section_ctr[1].begin(), header.section_ctr[1].end()); | ||
| 451 | |||
| 452 | const auto a = std::make_shared<FileSys::OffsetVfsFile>( | ||
| 453 | file, header.section_size[1], header.section_size[0] + sizeof(Package2Header)); | ||
| 454 | |||
| 455 | auto c = a->ReadAllBytes(); | ||
| 456 | |||
| 457 | AESCipher<Key128> cipher(package2_keys[revision], Mode::CTR); | ||
| 458 | cipher.SetIV(s1_iv); | ||
| 459 | cipher.Transcode(c.data(), c.size(), c.data(), Op::Decrypt); | ||
| 460 | |||
| 461 | // package2_decrypted[static_cast<size_t>(type)] = s1; | ||
| 462 | |||
| 463 | INIHeader ini{}; | ||
| 464 | std::memcpy(&ini, c.data(), sizeof(INIHeader)); | ||
| 465 | if (ini.magic != Common::MakeMagic('I', 'N', 'I', '1')) | ||
| 466 | return; | ||
| 467 | |||
| 468 | std::map<u64, KIPHeader> kips{}; | ||
| 469 | u64 offset = sizeof(INIHeader); | ||
| 470 | for (size_t i = 0; i < ini.process_count; ++i) { | ||
| 471 | KIPHeader kip{}; | ||
| 472 | std::memcpy(&kip, c.data() + offset, sizeof(KIPHeader)); | ||
| 473 | if (kip.magic != Common::MakeMagic('K', 'I', 'P', '1')) | ||
| 474 | return; | ||
| 475 | kips.emplace(offset, kip); | ||
| 476 | |||
| 477 | const auto name = | ||
| 478 | Common::StringFromFixedZeroTerminatedBuffer(kip.name.data(), kip.name.size()); | ||
| 479 | |||
| 480 | if (name != "FS" && name != "spl") { | ||
| 481 | offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + | ||
| 482 | kip.sections[1].size_compressed + kip.sections[2].size_compressed; | ||
| 483 | continue; | ||
| 484 | } | ||
| 485 | |||
| 486 | std::vector<u8> text(kip.sections[0].size_compressed); | ||
| 487 | std::vector<u8> rodata(kip.sections[1].size_compressed); | ||
| 488 | std::vector<u8> data(kip.sections[2].size_compressed); | ||
| 489 | |||
| 490 | u64 offset_sec = sizeof(KIPHeader) + offset; | ||
| 491 | std::memcpy(text.data(), c.data() + offset_sec, text.size()); | ||
| 492 | offset_sec += text.size(); | ||
| 493 | std::memcpy(rodata.data(), c.data() + offset_sec, rodata.size()); | ||
| 494 | offset_sec += rodata.size(); | ||
| 495 | std::memcpy(data.data(), c.data() + offset_sec, data.size()); | ||
| 496 | |||
| 497 | offset += sizeof(KIPHeader) + kip.sections[0].size_compressed + | ||
| 498 | kip.sections[1].size_compressed + kip.sections[2].size_compressed; | ||
| 499 | |||
| 500 | text = DecompressBLZ(text); | ||
| 501 | rodata = DecompressBLZ(rodata); | ||
| 502 | data = DecompressBLZ(data); | ||
| 503 | |||
| 504 | std::vector<u8> out(text.size() + rodata.size() + data.size()); | ||
| 505 | std::memcpy(out.data(), text.data(), text.size()); | ||
| 506 | std::memcpy(out.data() + text.size(), rodata.data(), rodata.size()); | ||
| 507 | std::memcpy(out.data() + text.size() + rodata.size(), data.data(), data.size()); | ||
| 508 | |||
| 509 | if (name == "FS") | ||
| 510 | package2_fs[static_cast<size_t>(type)] = out; | ||
| 511 | else if (name == "spl") | ||
| 512 | package2_spl[static_cast<size_t>(type)] = out; | ||
| 513 | } | ||
| 514 | } | ||
| 515 | |||
| 516 | const std::vector<u8>& PartitionDataManager::GetPackage2FSDecompressed(Package2Type type) const { | ||
| 517 | return package2_fs.at(static_cast<size_t>(type)); | ||
| 518 | } | ||
| 519 | |||
| 520 | std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyApplicationSource(Package2Type type) const { | ||
| 521 | return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[6]); | ||
| 522 | } | ||
| 523 | |||
| 524 | std::array<u8, 16> PartitionDataManager::GetKeyAreaKeyOceanSource(Package2Type type) const { | ||
| 525 | return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[7]); | ||
| 526 | } | ||
| 527 | |||
| 528 | std::array<u8, 16> PartitionDataManager::GetKeyAreaKeySystemSource(Package2Type type) const { | ||
| 529 | return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[8]); | ||
| 530 | } | ||
| 531 | |||
| 532 | std::array<u8, 16> PartitionDataManager::GetSDKekSource(Package2Type type) const { | ||
| 533 | return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[9]); | ||
| 534 | } | ||
| 535 | |||
| 536 | std::array<u8, 32> PartitionDataManager::GetSDSaveKeySource(Package2Type type) const { | ||
| 537 | return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[10]); | ||
| 538 | } | ||
| 539 | |||
| 540 | std::array<u8, 32> PartitionDataManager::GetSDNCAKeySource(Package2Type type) const { | ||
| 541 | return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[11]); | ||
| 542 | } | ||
| 543 | |||
| 544 | std::array<u8, 16> PartitionDataManager::GetHeaderKekSource(Package2Type type) const { | ||
| 545 | return FindKeyFromHex(package2_fs.at(static_cast<size_t>(type)), source_hashes[12]); | ||
| 546 | } | ||
| 547 | |||
| 548 | std::array<u8, 32> PartitionDataManager::GetHeaderKeySource(Package2Type type) const { | ||
| 549 | return FindKeyFromHex<0x20>(package2_fs.at(static_cast<size_t>(type)), source_hashes[13]); | ||
| 550 | } | ||
| 551 | |||
| 552 | const std::vector<u8>& PartitionDataManager::GetPackage2SPLDecompressed(Package2Type type) const { | ||
| 553 | return package2_spl.at(static_cast<size_t>(type)); | ||
| 554 | } | ||
| 555 | |||
| 556 | std::array<u8, 16> PartitionDataManager::GetAESKeyGenerationSource(Package2Type type) const { | ||
| 557 | return FindKeyFromHex(package2_spl.at(static_cast<size_t>(type)), source_hashes[4]); | ||
| 558 | } | ||
| 559 | |||
| 560 | bool PartitionDataManager::HasProdInfo() const { | ||
| 561 | return prodinfo != nullptr; | ||
| 562 | } | ||
| 563 | |||
| 564 | FileSys::VirtualFile PartitionDataManager::GetProdInfoRaw() const { | ||
| 565 | return prodinfo; | ||
| 566 | } | ||
| 567 | |||
| 568 | void PartitionDataManager::DecryptProdInfo(std::array<u8, 16> bis_crypt, | ||
| 569 | std::array<u8, 16> bis_tweak) { | ||
| 570 | if (prodinfo == nullptr) | ||
| 571 | return; | ||
| 572 | |||
| 573 | Key256 final_key{}; | ||
| 574 | std::memcpy(final_key.data(), bis_crypt.data(), bis_crypt.size()); | ||
| 575 | std::memcpy(final_key.data() + sizeof(Key128), bis_tweak.data(), bis_tweak.size()); | ||
| 576 | |||
| 577 | prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, final_key); | ||
| 578 | } | ||
| 579 | |||
| 580 | std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { | ||
| 581 | std::array<u8, 0x240> out{}; | ||
| 582 | if (prodinfo_decrypted != nullptr) | ||
| 583 | prodinfo_decrypted->Read(out.data(), out.size(), 0x3890); | ||
| 584 | return out; | ||
| 585 | } | ||
| 586 | } // namespace Core::Crypto | ||
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h new file mode 100644 index 000000000..85bb2a110 --- /dev/null +++ b/src/core/crypto/partition_data_manager.h | |||
| @@ -0,0 +1,104 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | #include <vector> | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/file_sys/vfs_types.h" | ||
| 11 | |||
| 12 | namespace Core::Crypto { | ||
| 13 | |||
| 14 | enum class Package2Type { | ||
| 15 | NormalMain, | ||
| 16 | NormalSub, | ||
| 17 | SafeModeMain, | ||
| 18 | SafeModeSub, | ||
| 19 | RepairMain, | ||
| 20 | RepairSub, | ||
| 21 | }; | ||
| 22 | |||
| 23 | class PartitionDataManager { | ||
| 24 | public: | ||
| 25 | const static u8 MAX_KEYBLOB_SOURCE_HASH; | ||
| 26 | |||
| 27 | explicit PartitionDataManager(FileSys::VirtualDir sysdata_dir); | ||
| 28 | |||
| 29 | // BOOT0 | ||
| 30 | bool HasBoot0() const; | ||
| 31 | FileSys::VirtualFile GetBoot0Raw() const; | ||
| 32 | std::array<u8, 0xB0> GetEncryptedKeyblob(u8 index) const; | ||
| 33 | std::array<std::array<u8, 0xB0>, 0x20> GetEncryptedKeyblobs() const; | ||
| 34 | std::vector<u8> GetSecureMonitor() const; | ||
| 35 | std::array<u8, 0x10> GetPackage2KeySource() const; | ||
| 36 | std::array<u8, 0x10> GetAESKekGenerationSource() const; | ||
| 37 | std::array<u8, 0x10> GetTitlekekSource() const; | ||
| 38 | std::array<std::array<u8, 0x10>, 0x20> GetTZMasterKeys(std::array<u8, 0x10> master_key) const; | ||
| 39 | std::array<u8, 0x10> GetRSAKekSeed3() const; | ||
| 40 | std::array<u8, 0x10> GetRSAKekMask0() const; | ||
| 41 | std::vector<u8> GetPackage1Decrypted() const; | ||
| 42 | std::array<u8, 0x10> GetMasterKeySource() const; | ||
| 43 | std::array<u8, 0x10> GetKeyblobMACKeySource() const; | ||
| 44 | std::array<u8, 0x10> GetKeyblobKeySource(u8 revision) const; | ||
| 45 | |||
| 46 | // Fuses | ||
| 47 | bool HasFuses() const; | ||
| 48 | FileSys::VirtualFile GetFusesRaw() const; | ||
| 49 | std::array<u8, 0x10> GetSecureBootKey() const; | ||
| 50 | |||
| 51 | // K-Fuses | ||
| 52 | bool HasKFuses() const; | ||
| 53 | FileSys::VirtualFile GetKFusesRaw() const; | ||
| 54 | |||
| 55 | // Package2 | ||
| 56 | bool HasPackage2(Package2Type type = Package2Type::NormalMain) const; | ||
| 57 | FileSys::VirtualFile GetPackage2Raw(Package2Type type = Package2Type::NormalMain) const; | ||
| 58 | void DecryptPackage2(std::array<std::array<u8, 16>, 0x20> package2, Package2Type type); | ||
| 59 | const std::vector<u8>& GetPackage2FSDecompressed( | ||
| 60 | Package2Type type = Package2Type::NormalMain) const; | ||
| 61 | std::array<u8, 0x10> GetKeyAreaKeyApplicationSource( | ||
| 62 | Package2Type type = Package2Type::NormalMain) const; | ||
| 63 | std::array<u8, 0x10> GetKeyAreaKeyOceanSource( | ||
| 64 | Package2Type type = Package2Type::NormalMain) const; | ||
| 65 | std::array<u8, 0x10> GetKeyAreaKeySystemSource( | ||
| 66 | Package2Type type = Package2Type::NormalMain) const; | ||
| 67 | std::array<u8, 0x10> GetSDKekSource(Package2Type type = Package2Type::NormalMain) const; | ||
| 68 | std::array<u8, 0x20> GetSDSaveKeySource(Package2Type type = Package2Type::NormalMain) const; | ||
| 69 | std::array<u8, 0x20> GetSDNCAKeySource(Package2Type type = Package2Type::NormalMain) const; | ||
| 70 | std::array<u8, 0x10> GetHeaderKekSource(Package2Type type = Package2Type::NormalMain) const; | ||
| 71 | std::array<u8, 0x20> GetHeaderKeySource(Package2Type type = Package2Type::NormalMain) const; | ||
| 72 | const std::vector<u8>& GetPackage2SPLDecompressed( | ||
| 73 | Package2Type type = Package2Type::NormalMain) const; | ||
| 74 | std::array<u8, 0x10> GetAESKeyGenerationSource( | ||
| 75 | Package2Type type = Package2Type::NormalMain) const; | ||
| 76 | |||
| 77 | // PRODINFO | ||
| 78 | bool HasProdInfo() const; | ||
| 79 | FileSys::VirtualFile GetProdInfoRaw() const; | ||
| 80 | void DecryptProdInfo(std::array<u8, 0x10> bis_crypt, std::array<u8, 0x10> bis_tweak); | ||
| 81 | std::array<u8, 0x240> GetETicketExtendedKek() const; | ||
| 82 | |||
| 83 | private: | ||
| 84 | FileSys::VirtualFile boot0; | ||
| 85 | FileSys::VirtualFile fuses; | ||
| 86 | FileSys::VirtualFile kfuses; | ||
| 87 | std::array<FileSys::VirtualFile, 6> package2; | ||
| 88 | FileSys::VirtualFile prodinfo; | ||
| 89 | FileSys::VirtualFile secure_monitor; | ||
| 90 | FileSys::VirtualFile package1_decrypted; | ||
| 91 | |||
| 92 | // Processed | ||
| 93 | std::array<FileSys::VirtualFile, 6> package2_decrypted; | ||
| 94 | FileSys::VirtualFile prodinfo_decrypted; | ||
| 95 | std::vector<u8> secure_monitor_bytes; | ||
| 96 | std::vector<u8> package1_decrypted_bytes; | ||
| 97 | std::array<std::vector<u8>, 6> package2_fs; | ||
| 98 | std::array<std::vector<u8>, 6> package2_spl; | ||
| 99 | }; | ||
| 100 | |||
| 101 | template <size_t key_size = 0x10> | ||
| 102 | std::array<u8, key_size> FindKeyFromHex(const std::vector<u8>& binary, std::array<u8, 0x20> hash); | ||
| 103 | |||
| 104 | } // namespace Core::Crypto | ||