summaryrefslogtreecommitdiffstats
path: root/src/core/crypto
diff options
context:
space:
mode:
authorZach Hilman <zachhilman@gmail.com>2018-07-28 05:55:23 +0200
committerZach Hilman <zachhilman@gmail.com>2018-08-01 06:16:54 +0200
commitdf5b75694f5abde94ccf05fa6c7a557b1ba9079b (patch)
tree70f0cf96b1a9834360fb1c5d5547939693ecd577 /src/core/crypto
parentMerge pull request #871 from bunnei/audio-config (diff)
downloadyuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.gz
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.bz2
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.lz
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.xz
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.tar.zst
yuzu-df5b75694f5abde94ccf05fa6c7a557b1ba9079b.zip
Diffstat (limited to 'src/core/crypto')
-rw-r--r--src/core/crypto/aes_util.cpp6
-rw-r--r--src/core/crypto/aes_util.h118
-rw-r--r--src/core/crypto/ctr_encryption_layer.cpp56
-rw-r--r--src/core/crypto/ctr_encryption_layer.h31
-rw-r--r--src/core/crypto/encryption_layer.cpp42
-rw-r--r--src/core/crypto/encryption_layer.h30
-rw-r--r--src/core/crypto/key_manager.cpp410
-rw-r--r--src/core/crypto/key_manager.h116
-rw-r--r--src/core/crypto/sha_util.cpp5
-rw-r--r--src/core/crypto/sha_util.h20
10 files changed, 834 insertions, 0 deletions
diff --git a/src/core/crypto/aes_util.cpp b/src/core/crypto/aes_util.cpp
new file mode 100644
index 000000000..46326cdec
--- /dev/null
+++ b/src/core/crypto/aes_util.cpp
@@ -0,0 +1,6 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace Crypto {
+} // namespace Crypto
diff --git a/src/core/crypto/aes_util.h b/src/core/crypto/aes_util.h
new file mode 100644
index 000000000..9807b9234
--- /dev/null
+++ b/src/core/crypto/aes_util.h
@@ -0,0 +1,118 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/assert.h"
+#include "core/file_sys/vfs.h"
+#include "mbedtls/cipher.h"
+
+namespace Crypto {
+
+enum class Mode {
+ CTR = MBEDTLS_CIPHER_AES_128_CTR,
+ ECB = MBEDTLS_CIPHER_AES_128_ECB,
+ XTS = MBEDTLS_CIPHER_AES_128_XTS,
+};
+
+enum class Op {
+ ENCRYPT,
+ DECRYPT,
+};
+
+template <typename Key, size_t KeySize = sizeof(Key)>
+struct AESCipher {
+ static_assert(std::is_same_v<Key, std::array<u8, KeySize>>, "Key must be std::array of u8.");
+ static_assert(KeySize == 0x10 || KeySize == 0x20, "KeySize must be 128 or 256.");
+
+ AESCipher(Key key, Mode mode) {
+ mbedtls_cipher_init(&encryption_context);
+ mbedtls_cipher_init(&decryption_context);
+
+ ASSERT_MSG((mbedtls_cipher_setup(
+ &encryption_context,
+ mbedtls_cipher_info_from_type(static_cast<mbedtls_cipher_type_t>(mode))) ||
+ mbedtls_cipher_setup(&decryption_context,
+ mbedtls_cipher_info_from_type(
+ static_cast<mbedtls_cipher_type_t>(mode)))) == 0,
+ "Failed to initialize mbedtls ciphers.");
+
+ ASSERT(
+ !mbedtls_cipher_setkey(&encryption_context, key.data(), KeySize * 8, MBEDTLS_ENCRYPT));
+ ASSERT(
+ !mbedtls_cipher_setkey(&decryption_context, key.data(), KeySize * 8, MBEDTLS_DECRYPT));
+ //"Failed to set key on mbedtls ciphers.");
+ }
+
+ ~AESCipher() {
+ mbedtls_cipher_free(&encryption_context);
+ mbedtls_cipher_free(&decryption_context);
+ }
+
+ void SetIV(std::vector<u8> iv) {
+ ASSERT_MSG((mbedtls_cipher_set_iv(&encryption_context, iv.data(), iv.size()) ||
+ mbedtls_cipher_set_iv(&decryption_context, iv.data(), iv.size())) == 0,
+ "Failed to set IV on mbedtls ciphers.");
+ }
+
+ template <typename Source, typename Dest>
+ void Transcode(const Source* src, size_t size, Dest* dest, Op op) {
+ size_t written = 0;
+
+ const auto context = op == Op::ENCRYPT ? &encryption_context : &decryption_context;
+
+ mbedtls_cipher_reset(context);
+
+ if (mbedtls_cipher_get_cipher_mode(context) == MBEDTLS_MODE_XTS) {
+ mbedtls_cipher_update(context, reinterpret_cast<const u8*>(src), size,
+ reinterpret_cast<u8*>(dest), &written);
+ if (written != size)
+ LOG_WARNING(Crypto, "Not all data was decrypted requested={:016X}, actual={:016X}.",
+ size, written);
+ } else {
+ const auto block_size = mbedtls_cipher_get_block_size(context);
+
+ for (size_t offset = 0; offset < size; offset += block_size) {
+ auto length = std::min<size_t>(block_size, size - offset);
+ mbedtls_cipher_update(context, reinterpret_cast<const u8*>(src) + offset, length,
+ reinterpret_cast<u8*>(dest) + offset, &written);
+ if (written != length)
+ LOG_WARNING(Crypto,
+ "Not all data was decrypted requested={:016X}, actual={:016X}.",
+ length, written);
+ }
+ }
+
+ mbedtls_cipher_finish(context, nullptr, nullptr);
+ }
+
+ template <typename Source, typename Dest>
+ void XTSTranscode(const Source* src, size_t size, Dest* dest, size_t sector_id,
+ size_t sector_size, Op op) {
+ if (size % sector_size > 0) {
+ LOG_CRITICAL(Crypto, "Data size must be a multiple of sector size.");
+ return;
+ }
+
+ for (size_t i = 0; i < size; i += sector_size) {
+ SetIV(CalculateNintendoTweak(sector_id++));
+ Transcode<u8, u8>(reinterpret_cast<const u8*>(src) + i, sector_size,
+ reinterpret_cast<u8*>(dest) + i, op);
+ }
+ }
+
+private:
+ mbedtls_cipher_context_t encryption_context;
+ mbedtls_cipher_context_t decryption_context;
+
+ static std::vector<u8> CalculateNintendoTweak(size_t sector_id) {
+ std::vector<u8> out(0x10);
+ for (size_t i = 0xF; i <= 0xF; --i) {
+ out[i] = sector_id & 0xFF;
+ sector_id >>= 8;
+ }
+ return out;
+ }
+};
+} // namespace Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.cpp b/src/core/crypto/ctr_encryption_layer.cpp
new file mode 100644
index 000000000..8799496e2
--- /dev/null
+++ b/src/core/crypto/ctr_encryption_layer.cpp
@@ -0,0 +1,56 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/crypto/ctr_encryption_layer.h"
+
+namespace Crypto {
+CTREncryptionLayer::CTREncryptionLayer(FileSys::VirtualFile base_, Key128 key_, size_t base_offset)
+ : EncryptionLayer(std::move(base_)), base_offset(base_offset), cipher(key_, Mode::CTR),
+ iv(16, 0) {}
+
+size_t CTREncryptionLayer::Read(u8* data, size_t length, size_t offset) const {
+ if (length == 0)
+ return 0;
+
+ const auto sector_offset = offset & 0xF;
+ if (sector_offset == 0) {
+ UpdateIV(base_offset + offset);
+ std::vector<u8> raw = base->ReadBytes(length, offset);
+ if (raw.size() != length)
+ return Read(data, raw.size(), offset);
+ cipher.Transcode(raw.data(), length, data, Op::DECRYPT);
+ return length;
+ }
+
+ // offset does not fall on block boundary (0x10)
+ std::vector<u8> block = base->ReadBytes(0x10, offset - sector_offset);
+ UpdateIV(base_offset + offset - sector_offset);
+ cipher.Transcode(block.data(), block.size(), block.data(), Op::DECRYPT);
+ size_t read = 0x10 - sector_offset;
+
+ if (length + sector_offset < 0x10) {
+ memcpy_s(data, length, block.data() + sector_offset, std::min<u64>(length, read));
+ return read;
+ }
+
+ memcpy_s(data, length, block.data() + sector_offset, read);
+ return read + Read(data + read, length - read, offset + read);
+}
+
+void CTREncryptionLayer::SetIV(std::vector<u8> iv_) {
+ const auto length = std::min<size_t>(iv_.size(), iv.size());
+ for (size_t i = 0; i < length; ++i)
+ iv[i] = iv_[i];
+}
+
+void CTREncryptionLayer::UpdateIV(size_t offset) const {
+ offset >>= 4;
+ for (size_t i = 0; i < 8; ++i) {
+ iv[16 - i - 1] = offset & 0xFF;
+ offset >>= 8;
+ }
+ cipher.SetIV(iv);
+}
+} // namespace Crypto
diff --git a/src/core/crypto/ctr_encryption_layer.h b/src/core/crypto/ctr_encryption_layer.h
new file mode 100644
index 000000000..fe53e714b
--- /dev/null
+++ b/src/core/crypto/ctr_encryption_layer.h
@@ -0,0 +1,31 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "aes_util.h"
+#include "encryption_layer.h"
+#include "key_manager.h"
+
+namespace Crypto {
+
+// Sits on top of a VirtualFile and provides CTR-mode AES decription.
+struct CTREncryptionLayer : public EncryptionLayer {
+ CTREncryptionLayer(FileSys::VirtualFile base, Key128 key, size_t base_offset);
+
+ size_t Read(u8* data, size_t length, size_t offset) const override;
+
+ void SetIV(std::vector<u8> iv);
+
+private:
+ size_t base_offset;
+
+ // Must be mutable as operations modify cipher contexts.
+ mutable AESCipher<Key128> cipher;
+ mutable std::vector<u8> iv;
+
+ void UpdateIV(size_t offset) const;
+};
+
+} // namespace Crypto
diff --git a/src/core/crypto/encryption_layer.cpp b/src/core/crypto/encryption_layer.cpp
new file mode 100644
index 000000000..4e243051e
--- /dev/null
+++ b/src/core/crypto/encryption_layer.cpp
@@ -0,0 +1,42 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/crypto/encryption_layer.h"
+
+namespace Crypto {
+
+EncryptionLayer::EncryptionLayer(FileSys::VirtualFile base_) : base(std::move(base_)) {}
+
+std::string EncryptionLayer::GetName() const {
+ return base->GetName();
+}
+
+size_t EncryptionLayer::GetSize() const {
+ return base->GetSize();
+}
+
+bool EncryptionLayer::Resize(size_t new_size) {
+ return false;
+}
+
+std::shared_ptr<FileSys::VfsDirectory> EncryptionLayer::GetContainingDirectory() const {
+ return base->GetContainingDirectory();
+}
+
+bool EncryptionLayer::IsWritable() const {
+ return false;
+}
+
+bool EncryptionLayer::IsReadable() const {
+ return true;
+}
+
+size_t EncryptionLayer::Write(const u8* data, size_t length, size_t offset) {
+ return 0;
+}
+
+bool EncryptionLayer::Rename(std::string_view name) {
+ return base->Rename(name);
+}
+} // namespace Crypto
diff --git a/src/core/crypto/encryption_layer.h b/src/core/crypto/encryption_layer.h
new file mode 100644
index 000000000..2312870d8
--- /dev/null
+++ b/src/core/crypto/encryption_layer.h
@@ -0,0 +1,30 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+#include "core/file_sys/vfs.h"
+
+namespace Crypto {
+
+// Basically non-functional class that implements all of the methods that are irrelevant to an
+// EncryptionLayer. Reduces duplicate code.
+struct EncryptionLayer : public FileSys::VfsFile {
+ explicit EncryptionLayer(FileSys::VirtualFile base);
+
+ size_t Read(u8* data, size_t length, size_t offset) const override = 0;
+
+ std::string GetName() const override;
+ size_t GetSize() const override;
+ bool Resize(size_t new_size) override;
+ std::shared_ptr<FileSys::VfsDirectory> GetContainingDirectory() const override;
+ bool IsWritable() const override;
+ bool IsReadable() const override;
+ size_t Write(const u8* data, size_t length, size_t offset) override;
+ bool Rename(std::string_view name) override;
+
+protected:
+ FileSys::VirtualFile base;
+};
+
+} // namespace Crypto
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
new file mode 100644
index 000000000..05c6a70a2
--- /dev/null
+++ b/src/core/crypto/key_manager.cpp
@@ -0,0 +1,410 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fstream>
+#include <locale>
+#include <sstream>
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/crypto/key_manager.h"
+#include "mbedtls/sha256.h"
+
+namespace 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) {
+ if (c1 >= 65 && c1 <= 70)
+ return c1 - 55;
+ if (c1 >= 97 && c1 <= 102)
+ return c1 - 87;
+ if (c1 >= 48 && c1 <= 57)
+ return c1 - 48;
+ throw std::logic_error("Invalid hex digit");
+}
+
+template <size_t Size>
+static std::array<u8, Size> HexStringToArray(std::string_view str) {
+ std::array<u8, Size> out{};
+ for (size_t i = 0; i < 2 * Size; i += 2) {
+ auto d1 = str[i];
+ auto d2 = str[i + 1];
+ out[i / 2] = (ToHexNibble(d1) << 4) | ToHexNibble(d2);
+ }
+ return out;
+}
+
+std::array<u8, 16> operator""_array16(const char* str, size_t len) {
+ if (len != 32)
+ throw std::logic_error("Not of correct size.");
+ return HexStringToArray<16>(str);
+}
+
+std::array<u8, 32> operator""_array32(const char* str, size_t len) {
+ if (len != 64)
+ throw std::logic_error("Not of correct size.");
+ return HexStringToArray<32>(str);
+}
+
+void KeyManager::SetValidationMode(bool dev) {
+ dev_mode = dev;
+}
+
+void KeyManager::LoadFromFile(std::string_view filename_, bool is_title_keys) {
+ const auto filename = std::string(filename_);
+ std::ifstream file(filename);
+ if (!file.is_open())
+ return;
+
+ std::string line;
+ while (std::getline(file, line)) {
+ std::vector<std::string> out;
+ std::stringstream stream(line);
+ std::string item;
+ while (std::getline(stream, item, '='))
+ out.push_back(std::move(item));
+
+ if (out.size() != 2)
+ continue;
+
+ out[0].erase(std::remove(out[0].begin(), out[0].end(), ' '), out[0].end());
+ out[1].erase(std::remove(out[1].begin(), out[1].end(), ' '), out[1].end());
+
+ if (is_title_keys) {
+ auto rights_id_raw = HexStringToArray<16>(out[0]);
+ u128 rights_id = *reinterpret_cast<std::array<u64, 2>*>(&rights_id_raw);
+ Key128 key = HexStringToArray<16>(out[1]);
+ SetKey(S128KeyType::TITLEKEY, key, rights_id[1], rights_id[0]);
+ } else {
+ std::transform(out[0].begin(), out[0].end(), out[0].begin(), ::tolower);
+ if (s128_file_id.find(out[0]) != s128_file_id.end()) {
+ const auto index = s128_file_id[out[0]];
+ Key128 key = HexStringToArray<16>(out[1]);
+ SetKey(index.type, key, index.field1, index.field2);
+ } else if (s256_file_id.find(out[0]) != s256_file_id.end()) {
+ const auto index = s256_file_id[out[0]];
+ Key256 key = HexStringToArray<32>(out[1]);
+ SetKey(index.type, key, index.field1, index.field2);
+ }
+ }
+ }
+}
+
+bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) {
+ return s128_keys.find({id, field1, field2}) != s128_keys.end();
+}
+
+bool KeyManager::HasKey(S256KeyType id, u64 field1, u64 field2) {
+ return s256_keys.find({id, field1, field2}) != s256_keys.end();
+}
+
+Key128 KeyManager::GetKey(S128KeyType id, u64 field1, u64 field2) {
+ if (!HasKey(id, field1, field2))
+ return {};
+ return s128_keys[{id, field1, field2}];
+}
+
+Key256 KeyManager::GetKey(S256KeyType id, u64 field1, u64 field2) {
+ if (!HasKey(id, field1, field2))
+ return {};
+ return s256_keys[{id, field1, field2}];
+}
+
+void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) {
+ s128_keys[{id, field1, field2}] = key;
+}
+
+void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) {
+ s256_keys[{id, field1, field2}] = key;
+}
+
+bool KeyManager::ValidateKey(S128KeyType key, u64 field1, u64 field2) {
+ auto& hash = dev_mode ? s128_hash_dev : s128_hash_prod;
+
+ KeyIndex<S128KeyType> id = {key, field1, field2};
+ if (key == S128KeyType::SD_SEED || key == S128KeyType::TITLEKEY ||
+ hash.find(id) == hash.end()) {
+ LOG_WARNING(Crypto, "Could not validate [{}]", id.DebugInfo());
+ return true;
+ }
+
+ 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
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
new file mode 100644
index 000000000..155989e46
--- /dev/null
+++ b/src/core/crypto/key_manager.h
@@ -0,0 +1,116 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+#include <array>
+#include <unordered_map>
+#include <vector>
+#include "common/common_types.h"
+
+namespace Crypto {
+
+typedef std::array<u8, 0x10> Key128;
+typedef std::array<u8, 0x20> Key256;
+typedef std::array<u8, 0x20> SHA256Hash;
+
+static_assert(sizeof(Key128) == 16, "Key128 must be 128 bytes big.");
+static_assert(sizeof(Key256) == 32, "Key128 must be 128 bytes big.");
+
+enum class S256KeyType : u64 {
+ HEADER, //
+ SD_SAVE, //
+ SD_NCA, //
+};
+
+enum class S128KeyType : u64 {
+ MASTER, // f1=crypto revision
+ PACKAGE1, // f1=crypto revision
+ PACKAGE2, // f1=crypto revision
+ TITLEKEK, // f1=crypto revision
+ ETICKET_RSA_KEK, //
+ KEY_AREA, // f1=crypto revision f2=type {app, ocean, system}
+ SD_SEED, //
+ TITLEKEY, // f1=rights id LSB f2=rights id MSB
+};
+
+enum class KeyAreaKeyType : u8 {
+ Application,
+ Ocean,
+ System,
+};
+
+template <typename KeyType>
+struct KeyIndex {
+ KeyType type;
+ u64 field1;
+ u64 field2;
+
+ std::string DebugInfo() {
+ u8 key_size = 16;
+ if (std::is_same_v<KeyType, S256KeyType>)
+ key_size = 32;
+ return fmt::format("key_size={:02X}, key={:02X}, field1={:016X}, field2={:016X}", key_size,
+ static_cast<u8>(type), field1, field2);
+ }
+};
+
+// The following two (== and hash) are so KeyIndex can be a key in unordered_map
+
+template <typename KeyType>
+bool operator==(const KeyIndex<KeyType>& lhs, const KeyIndex<KeyType>& rhs) {
+ return lhs.type == rhs.type && lhs.field1 == rhs.field1 && lhs.field2 == rhs.field2;
+}
+
+} // namespace Crypto
+
+namespace std {
+template <typename KeyType>
+struct hash<Crypto::KeyIndex<KeyType>> {
+ size_t operator()(const Crypto::KeyIndex<KeyType>& k) const {
+ using std::hash;
+
+ return ((hash<u64>()(static_cast<u64>(k.type)) ^ (hash<u64>()(k.field1) << 1)) >> 1) ^
+ (hash<u64>()(k.field2) << 1);
+ }
+};
+} // namespace std
+
+namespace Crypto {
+
+std::array<u8, 0x10> operator"" _array16(const char* str, size_t len);
+std::array<u8, 0x20> operator"" _array32(const char* str, size_t len);
+
+struct KeyManager {
+ void SetValidationMode(bool dev);
+ void LoadFromFile(std::string_view filename, bool is_title_keys);
+
+ bool HasKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0);
+ bool HasKey(S256KeyType id, u64 field1 = 0, u64 field2 = 0);
+
+ Key128 GetKey(S128KeyType id, u64 field1 = 0, u64 field2 = 0);
+ Key256 GetKey(S256KeyType id, 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);
+
+ bool ValidateKey(S128KeyType key, u64 field1 = 0, u64 field2 = 0);
+ bool ValidateKey(S256KeyType key, u64 field1 = 0, u64 field2 = 0);
+
+private:
+ std::unordered_map<KeyIndex<S128KeyType>, Key128> s128_keys;
+ std::unordered_map<KeyIndex<S256KeyType>, Key256> s256_keys;
+
+ bool dev_mode = false;
+
+ 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<S256KeyType>> s256_file_id;
+};
+
+extern KeyManager keys;
+
+} // namespace Crypto
diff --git a/src/core/crypto/sha_util.cpp b/src/core/crypto/sha_util.cpp
new file mode 100644
index 000000000..180008a85
--- /dev/null
+++ b/src/core/crypto/sha_util.cpp
@@ -0,0 +1,5 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+namespace Crypto {} // namespace Crypto
diff --git a/src/core/crypto/sha_util.h b/src/core/crypto/sha_util.h
new file mode 100644
index 000000000..fa3fa9d33
--- /dev/null
+++ b/src/core/crypto/sha_util.h
@@ -0,0 +1,20 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/assert.h"
+#include "core/file_sys/vfs.h"
+#include "key_manager.h"
+#include "mbedtls/cipher.h"
+
+namespace Crypto {
+typedef std::array<u8, 0x20> SHA256Hash;
+
+inline SHA256Hash operator"" _HASH(const char* data, size_t len) {
+ if (len != 0x40)
+ return {};
+}
+
+} // namespace Crypto