diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index 9cb7124d26e4d368de2081771ad315de4f53223d..b37b09772aa951aa20dffa7b1f580bdc60dc9f82 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -262,6 +262,28 @@ void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string&
         LoadFromFile(dir2 + DIR_SEP + filename, title);
 }
 
+bool KeyManager::BaseDeriveNecessary() {
+    const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) {
+        return !HasKey(key_type, index1, index2);
+    };
+
+    if (check_key_existence(S256KeyType::Header))
+        return true;
+
+    for (size_t i = 0; i < CURRENT_CRYPTO_REVISION; ++i) {
+        if (check_key_existence(S128KeyType::Master, i) ||
+            check_key_existence(S128KeyType::KeyArea, i,
+                                static_cast<u64>(KeyAreaKeyType::Application)) ||
+            check_key_existence(S128KeyType::KeyArea, i, static_cast<u64>(KeyAreaKeyType::Ocean)) ||
+            check_key_existence(S128KeyType::KeyArea, i,
+                                static_cast<u64>(KeyAreaKeyType::System)) ||
+            check_key_existence(S128KeyType::Titlekek, i))
+            return true;
+    }
+
+    return false;
+}
+
 bool KeyManager::HasKey(S128KeyType id, u64 field1, u64 field2) const {
     return s128_keys.find({id, field1, field2}) != s128_keys.end();
 }
@@ -412,6 +434,193 @@ void KeyManager::DeriveSDSeedLazy() {
         SetKey(S128KeyType::SDSeed, res.get());
 }
 
+static Key128 CalculateCMAC(const u8* source, size_t size, Key128 key) {
+    Key128 out{};
+
+    mbedtls_cipher_cmac(mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_128_ECB), key.data(), 0x80,
+                        source, size, out.data());
+    return out;
+}
+
+void KeyManager::DeriveBase() {
+    if (!BaseDeriveNecessary())
+        return;
+
+    if (!HasKey(S128KeyType::SecureBoot) || !HasKey(S128KeyType::TSEC))
+        return;
+
+    const auto has_bis = [this](u64 id) {
+        return HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Crypto)) &&
+               HasKey(S128KeyType::BIS, id, static_cast<u64>(BISKeyType::Tweak));
+    };
+
+    const auto copy_bis = [this](u64 id_from, u64 id_to) {
+        SetKey(S128KeyType::BIS,
+               GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Crypto)), id_to,
+               static_cast<u64>(BISKeyType::Crypto));
+
+        SetKey(S128KeyType::BIS,
+               GetKey(S128KeyType::BIS, id_from, static_cast<u64>(BISKeyType::Tweak)), id_to,
+               static_cast<u64>(BISKeyType::Tweak));
+    };
+
+    if (has_bis(2) && !has_bis(3))
+        copy_bis(2, 3);
+    else if (has_bis(3) && !has_bis(2))
+        copy_bis(3, 2);
+
+    std::bitset<32> revisions{};
+    revisions.set();
+    for (size_t i = 0; i < 32; ++i) {
+        if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i) ||
+            encrypted_keyblobs[i] == std::array<u8, 0xB0>{})
+            revisions.reset(i);
+    }
+
+    if (!revisions.any())
+        return;
+
+    const auto sbk = GetKey(S128KeyType::SecureBoot);
+    const auto tsec = GetKey(S128KeyType::TSEC);
+    const auto master_source = GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master));
+    const auto kek_generation_source =
+        GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration));
+    const auto key_generation_source =
+        GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration));
+
+    for (size_t i = 0; i < 32; ++i) {
+        if (!revisions[i])
+            continue;
+
+        // Derive keyblob key
+        const auto key = DeriveKeyblobKey(
+            sbk, tsec, GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Keyblob), i));
+
+        SetKey(S128KeyType::Keyblob, key, i);
+
+        // Derive keyblob MAC key
+        if (!HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC)))
+            continue;
+
+        const auto mac_source =
+            GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyblobMAC));
+
+        AESCipher<Key128> mac_cipher(key, Mode::ECB);
+        Key128 mac_key{};
+        mac_cipher.Transcode(mac_source.data(), mac_key.size(), mac_key.data(), Op::Decrypt);
+
+        SetKey(S128KeyType::KeyblobMAC, mac_key, i);
+
+        Key128 cmac = CalculateCMAC(encrypted_keyblobs[i].data() + 0x10, 0xA0, mac_key);
+        if (std::memcmp(cmac.data(), encrypted_keyblobs[i].data(), cmac.size()) != 0)
+            continue;
+
+        // Decrypt keyblob
+        bool has_keyblob = keyblobs[i] != std::array<u8, 0x90>{};
+
+        AESCipher<Key128> cipher(key, Mode::CTR);
+        cipher.SetIV(std::vector<u8>(encrypted_keyblobs[i].data() + 0x10,
+                                     encrypted_keyblobs[i].data() + 0x20));
+        cipher.Transcode(encrypted_keyblobs[i].data() + 0x20, keyblobs[i].size(),
+                         keyblobs[i].data(), Op::Decrypt);
+
+        if (!has_keyblob) {
+            WriteKeyToFile<0x90>(KeyCategory::Console, fmt::format("keyblob_{:02X}", i),
+                                 keyblobs[i]);
+        }
+
+        Key128 package1{};
+        std::memcpy(package1.data(), keyblobs[i].data() + 0x80, sizeof(Key128));
+        SetKey(S128KeyType::Package1, package1, i);
+
+        // Derive master key
+        if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::Master))) {
+            Key128 master_root{};
+            std::memcpy(master_root.data(), keyblobs[i].data(), sizeof(Key128));
+
+            AESCipher<Key128> master_cipher(master_root, Mode::ECB);
+
+            Key128 master{};
+            master_cipher.Transcode(master_source.data(), master_source.size(), master.data(),
+                                    Op::Decrypt);
+            SetKey(S128KeyType::Master, master, i);
+        }
+    }
+
+    revisions.set();
+    for (size_t i = 0; i < 32; ++i) {
+        if (!HasKey(S128KeyType::Master, i))
+            revisions.reset(i);
+    }
+
+    if (!revisions.any())
+        return;
+
+    for (size_t i = 0; i < 32; ++i) {
+        if (!revisions[i])
+            continue;
+
+        // Derive general purpose keys
+        if (HasKey(S128KeyType::Master, i)) {
+            for (auto kak_type :
+                 {KeyAreaKeyType::Application, KeyAreaKeyType::Ocean, KeyAreaKeyType::System}) {
+                if (HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+                           static_cast<u64>(kak_type))) {
+                    const auto source =
+                        GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::KeyAreaKey),
+                               static_cast<u64>(kak_type));
+                    const auto kek =
+                        GenerateKeyEncryptionKey(source, GetKey(S128KeyType::Master, i),
+                                                 kek_generation_source, key_generation_source);
+                    SetKey(S128KeyType::KeyArea, kek, i, static_cast<u64>(kak_type));
+                }
+            }
+
+            AESCipher<Key128> master_cipher(GetKey(S128KeyType::Master, i), Mode::ECB);
+            for (auto key_type : {SourceKeyType::Titlekek, SourceKeyType::Package2}) {
+                if (HasKey(S128KeyType::Source, static_cast<u64>(key_type))) {
+                    Key128 key{};
+                    master_cipher.Transcode(
+                        GetKey(S128KeyType::Source, static_cast<u64>(key_type)).data(), key.size(),
+                        key.data(), Op::Decrypt);
+                    SetKey(key_type == SourceKeyType::Titlekek ? S128KeyType::Titlekek
+                                                               : S128KeyType::Package2,
+                           key, i);
+                }
+            }
+        }
+    }
+
+    if (HasKey(S128KeyType::Master, 0) &&
+        HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)) &&
+        HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)) &&
+        HasKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)) &&
+        HasKey(S256KeyType::HeaderSource)) {
+        const auto header_kek = GenerateKeyEncryptionKey(
+            GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::HeaderKek)),
+            GetKey(S128KeyType::Master, 0),
+            GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKekGeneration)),
+            GetKey(S128KeyType::Source, static_cast<u64>(SourceKeyType::AESKeyGeneration)));
+        SetKey(S128KeyType::HeaderKek, header_kek);
+
+        AESCipher<Key128> header_cipher(header_kek, Mode::ECB);
+        Key256 out = GetKey(S256KeyType::HeaderSource);
+        header_cipher.Transcode(out.data(), out.size(), out.data(), Op::Decrypt);
+        SetKey(S256KeyType::Header, out);
+    }
+}
+void KeyManager::SetKeyWrapped(S128KeyType id, Key128 key, u64 field1, u64 field2) {
+    if (key == Key128{})
+        return;
+    SetKey(id, key, field1, field2);
+}
+
+void KeyManager::SetKeyWrapped(S256KeyType id, Key256 key, u64 field1, u64 field2) {
+    if (key == Key256{})
+        return;
+    SetKey(id, key, field1, field2);
+}
+
 const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> KeyManager::s128_file_id = {
     {"eticket_rsa_kek", {S128KeyType::ETicketRSAKek, 0, 0}},
     {"eticket_rsa_kek_source",
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index a729fa7a0e10ed71ca695543be736fd763a1cd80..8de65ec4e335d352b3248895028ef8c42ce0620c 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -138,9 +138,12 @@ public:
     // 8*43 and the private file to exist.
     void DeriveSDSeedLazy();
 
+    bool BaseDeriveNecessary();
+    void DeriveBase();
 private:
-    boost::container::flat_map<KeyIndex<S128KeyType>, Key128> s128_keys;
-    boost::container::flat_map<KeyIndex<S256KeyType>, Key256> s256_keys;
+    std::map<KeyIndex<S128KeyType>, Key128> s128_keys;
+    std::map<KeyIndex<S256KeyType>, Key256> s256_keys;
+
     std::array<std::array<u8, 0xB0>, 0x20> encrypted_keyblobs{};
     std::array<std::array<u8, 0x90>, 0x20> keyblobs{};
 
@@ -148,8 +151,12 @@ private:
     void LoadFromFile(const std::string& filename, bool is_title_keys);
     void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2,
                             const std::string& filename, bool title);
-    template <std::size_t Size>
-    void WriteKeyToFile(bool title_key, std::string_view keyname, const std::array<u8, Size>& key);
+    template <size_t Size>
+    void WriteKeyToFile(KeyCategory category, std::string_view keyname,
+                        const std::array<u8, Size>& key);
+
+    void SetKeyWrapped(S128KeyType id, Key128 key, u64 field1 = 0, u64 field2 = 0);
+    void SetKeyWrapped(S256KeyType id, Key256 key, u64 field1 = 0, u64 field2 = 0);
 
     static const boost::container::flat_map<std::string, KeyIndex<S128KeyType>> s128_file_id;
     static const boost::container::flat_map<std::string, KeyIndex<S256KeyType>> s256_file_id;