diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp index d21497229..84e5a921c 100644 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ b/stratosphere/spl/source/spl_api_impl.cpp @@ -14,8 +14,8 @@ * along with this program. If not, see . */ #include "spl_api_impl.hpp" - #include "spl_ctr_drbg.hpp" +#include "spl_key_slot_cache.hpp" namespace ams::spl::impl { @@ -34,21 +34,161 @@ namespace ams::spl::impl { constexpr size_t WorkBufferSizeMax = 0x800; - constexpr size_t MaxAesKeyslots = 6; - constexpr size_t MaxAesKeyslotsDeprecated = 4; + constexpr s32 MaxPhysicalAesKeyslots = 6; + constexpr s32 MaxPhysicalAesKeyslotsDeprecated = 4; - /* Max Keyslots helper. */ - inline size_t GetMaxKeyslots() { - return (hos::GetVersion() >= hos::Version_6_0_0) ? MaxAesKeyslots : MaxAesKeyslotsDeprecated; + constexpr s32 MaxVirtualAesKeyslots = 9; + + /* Keyslot management. */ + KeySlotCache g_keyslot_cache; + std::optional g_keyslot_cache_entry[MaxPhysicalAesKeyslots]; + + inline s32 GetMaxPhysicalKeyslots() { + return (hos::GetVersion() >= hos::Version_6_0_0) ? MaxPhysicalAesKeyslots : MaxPhysicalAesKeyslotsDeprecated; + } + + constexpr s32 VirtualKeySlotMin = 16; + constexpr s32 VirtualKeySlotMax = VirtualKeySlotMin + MaxVirtualAesKeyslots - 1; + + constexpr inline bool IsVirtualKeySlot(s32 keyslot) { + return VirtualKeySlotMin <= keyslot && keyslot <= VirtualKeySlotMax; + } + + inline bool IsPhysicalKeySlot(s32 keyslot) { + return keyslot < GetMaxPhysicalKeyslots(); + } + + constexpr inline s32 GetVirtualKeySlotIndex(s32 keyslot) { + AMS_ASSERT(IsVirtualKeySlot(keyslot)); + return keyslot - VirtualKeySlotMin; + } + + constexpr inline s32 MakeVirtualKeySlot(s32 index) { + const s32 virt_slot = index + VirtualKeySlotMin; + AMS_ASSERT(IsVirtualKeySlot(virt_slot)); + return virt_slot; + } + + void InitializeKeySlotCache() { + for (s32 i = 0; i < MaxPhysicalAesKeyslots; i++) { + g_keyslot_cache_entry[i].emplace(i); + g_keyslot_cache.AddEntry(std::addressof(g_keyslot_cache_entry[i].value())); + } + } + + enum class KeySlotContentType { + None = 0, + AesKey = 1, + TitleKey = 2, + }; + + struct KeySlotContents { + KeySlotContentType type; + union { + struct { + AccessKey access_key; + KeySource key_source; + } aes_key; + struct { + AccessKey access_key; + } title_key; + }; + }; + + const void *g_keyslot_owners[MaxVirtualAesKeyslots]; + KeySlotContents g_keyslot_contents[MaxVirtualAesKeyslots]; + KeySlotContents g_physical_keyslot_contents_for_backwards_compatibility[MaxPhysicalAesKeyslots]; + + void ClearPhysicalKeyslot(s32 keyslot) { + AMS_ASSERT(IsPhysicalKeySlot(keyslot)); + + AccessKey access_key = {}; + KeySource key_source = {}; + smc::LoadAesKey(keyslot, access_key, key_source); + } + + s32 GetPhysicalKeySlot(s32 keyslot, bool load) { + s32 phys_slot = -1; + KeySlotContents *contents = nullptr; + + if (hos::GetVersion() == hos::Version_1_0_0 && IsPhysicalKeySlot(keyslot)) { + /* On 1.0.0, we allow the use of physical keyslots. */ + phys_slot = keyslot; + contents = std::addressof(g_physical_keyslot_contents_for_backwards_compatibility[phys_slot]); + + /* If the physical slot is already loaded, we're good. */ + if (g_keyslot_cache.FindPhysical(phys_slot)) { + return phys_slot; + } + } else { + /* This should be a virtual keyslot. */ + AMS_ASSERT(IsVirtualKeySlot(keyslot)); + + /* Try to find a physical slot in the cache. */ + if (g_keyslot_cache.Find(std::addressof(phys_slot), keyslot)) { + return phys_slot; + } + + /* Allocate a physical slot. */ + phys_slot = g_keyslot_cache.Allocate(keyslot); + contents = std::addressof(g_keyslot_contents[GetVirtualKeySlotIndex(keyslot)]); + } + + /* Ensure the contents of the keyslot. */ + if (load) { + switch (contents->type) { + case KeySlotContentType::None: + ClearPhysicalKeyslot(phys_slot); + break; + case KeySlotContentType::AesKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadAesKey(phys_slot, contents->aes_key.access_key, contents->aes_key.key_source))); + break; + case KeySlotContentType::TitleKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadTitleKey(phys_slot, contents->title_key.access_key))); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return phys_slot; + } + + Result LoadVirtualAesKey(s32 keyslot, const AccessKey &access_key, const KeySource &key_source) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadAesKey(phys_slot, access_key, key_source))); + + /* Update our contents. */ + const s32 index = GetVirtualKeySlotIndex(keyslot); + + g_keyslot_contents[index].type = KeySlotContentType::AesKey; + g_keyslot_contents[index].aes_key.access_key = access_key; + g_keyslot_contents[index].aes_key.key_source = key_source; + + return ResultSuccess(); + } + + Result LoadVirtualTitleKey(s32 keyslot, const AccessKey &access_key) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadTitleKey(phys_slot, access_key))); + + /* Update our contents. */ + const s32 index = GetVirtualKeySlotIndex(keyslot); + + g_keyslot_contents[index].type = KeySlotContentType::TitleKey; + g_keyslot_contents[index].title_key.access_key = access_key; + + return ResultSuccess(); } /* Type definitions. */ class ScopedAesKeyslot { private: - u32 slot; + s32 slot; bool has_slot; public: - ScopedAesKeyslot() : slot(0), has_slot(false) { + ScopedAesKeyslot() : slot(-1), has_slot(false) { /* ... */ } ~ScopedAesKeyslot() { @@ -57,7 +197,7 @@ namespace ams::spl::impl { } } - u32 GetKeyslot() const { + u32 GetKeySlot() const { return this->slot; } @@ -106,7 +246,6 @@ namespace ams::spl::impl { os::Mutex g_async_op_lock(false); - const void *g_keyslot_owners[MaxAesKeyslots]; BootReasonValue g_boot_reason; bool g_boot_reason_set; @@ -201,14 +340,21 @@ namespace ams::spl::impl { } /* Internal Keyslot utility. */ - Result ValidateAesKeyslot(u32 keyslot, const void *owner) { - R_UNLESS(keyslot < GetMaxKeyslots(), spl::ResultInvalidKeyslot()); - R_UNLESS((g_keyslot_owners[keyslot] == owner || hos::GetVersion() == hos::Version_1_0_0), spl::ResultInvalidKeyslot()); + Result ValidateAesKeyslot(s32 keyslot, const void *owner) { + /* Allow the use of physical keyslots on 1.0.0. */ + if (hos::GetVersion() == hos::Version_1_0_0) { + R_SUCCEED_IF(IsPhysicalKeySlot(keyslot)); + } + + R_UNLESS(IsVirtualKeySlot(keyslot), spl::ResultInvalidKeyslot()); + + const s32 index = GetVirtualKeySlotIndex(keyslot); + R_UNLESS(g_keyslot_owners[index] == owner, spl::ResultInvalidKeyslot()); return ResultSuccess(); } /* Helper to do a single AES block decryption. */ - smc::Result DecryptAesBlock(u32 keyslot, void *dst, const void *src) { + smc::Result DecryptAesBlock(s32 keyslot, void *dst, const void *src) { struct DecryptAesBlockLayout { SeCryptContext crypt_ctx; u8 in_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); @@ -230,7 +376,7 @@ namespace ams::spl::impl { std::scoped_lock lk(g_async_op_lock); smc::AsyncOperationKey op_key; const IvCtr iv_ctr = {}; - const u32 mode = smc::GetCryptAesMode(smc::CipherMode::CbcDecrypt, keyslot); + const u32 mode = smc::GetCryptAesMode(smc::CipherMode::CbcDecrypt, GetPhysicalKeySlot(keyslot, true)); const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.out); const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, crypt_ctx.in); @@ -362,6 +508,8 @@ namespace ams::spl::impl { InitializeSeEvents(); /* Initialize DAS for the SE. */ InitializeDeviceAddressSpace(); + /* Initialize the keyslot cache. */ + InitializeKeySlotCache(); } /* General. */ @@ -473,14 +621,12 @@ namespace ams::spl::impl { return smc::ConvertResult(smc::GenerateAesKek(out_access_key, key_source, generation, option)); } - Result LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { + Result LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { R_TRY(ValidateAesKeyslot(keyslot, owner)); - return smc::ConvertResult(smc::LoadAesKey(keyslot, access_key, key_source)); + return LoadVirtualAesKey(keyslot, access_key, key_source); } Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { - smc::Result smc_rc; - static constexpr KeySource s_generate_aes_key_source = { .data = {0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8} }; @@ -488,12 +634,9 @@ namespace ams::spl::impl { ScopedAesKeyslot keyslot_holder; R_TRY(keyslot_holder.Allocate()); - smc_rc = smc::LoadAesKey(keyslot_holder.GetKeyslot(), access_key, s_generate_aes_key_source); - if (smc_rc == smc::Result::Success) { - smc_rc = DecryptAesBlock(keyslot_holder.GetKeyslot(), out_key, &key_source); - } + R_TRY(LoadVirtualAesKey(keyslot_holder.GetKeySlot(), access_key, s_generate_aes_key_source)); - return smc::ConvertResult(smc_rc); + return smc::ConvertResult(DecryptAesBlock(keyslot_holder.GetKeySlot(), out_key, &key_source)); } Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { @@ -507,7 +650,7 @@ namespace ams::spl::impl { return GenerateAesKey(out_key, access_key, key_source); } - Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { + Result CryptAesCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { R_TRY(ValidateAesKeyslot(keyslot, owner)); /* Succeed immediately if there's nothing to crypt. */ @@ -554,7 +697,7 @@ namespace ams::spl::impl { { std::scoped_lock lk(g_async_op_lock); smc::AsyncOperationKey op_key; - const u32 mode = smc::GetCryptAesMode(smc::CipherMode::Ctr, keyslot); + const u32 mode = smc::GetCryptAesMode(smc::CipherMode::Ctr, GetPhysicalKeySlot(keyslot, true)); const u32 dst_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, out); const u32 src_ll_addr = g_se_mapped_work_buffer_addr + offsetof(SeCryptContext, in); @@ -572,26 +715,22 @@ namespace ams::spl::impl { return ResultSuccess(); } - Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size) { + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size) { R_TRY(ValidateAesKeyslot(keyslot, owner)); R_UNLESS(size <= WorkBufferSizeMax, spl::ResultInvalidSize()); std::memcpy(g_work_buffer, data, size); - return smc::ConvertResult(smc::ComputeCmac(out_cmac, keyslot, g_work_buffer, size)); + return smc::ConvertResult(smc::ComputeCmac(out_cmac, GetPhysicalKeySlot(keyslot, true), g_work_buffer, size)); } - Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner) { - if (hos::GetVersion() <= hos::Version_1_0_0) { - /* On 1.0.0, keyslots were kind of a wild west. */ - *out_keyslot = 0; - return ResultSuccess(); - } - - for (size_t i = 0; i < GetMaxKeyslots(); i++) { - if (g_keyslot_owners[i] == 0) { - g_keyslot_owners[i] = owner; - *out_keyslot = static_cast(i); + Result AllocateAesKeyslot(s32 *out_keyslot, const void *owner) { + /* Find a virtual keyslot. */ + for (s32 i = 0; i < MaxVirtualAesKeyslots; i++) { + if (g_keyslot_owners[i] == nullptr) { + g_keyslot_owners[i] = owner; + g_keyslot_contents[i] = { .type = KeySlotContentType::None }; + *out_keyslot = MakeVirtualKeySlot(i); return ResultSuccess(); } } @@ -600,22 +739,24 @@ namespace ams::spl::impl { return spl::ResultOutOfKeyslots(); } - Result FreeAesKeyslot(u32 keyslot, const void *owner) { - if (hos::GetVersion() <= hos::Version_1_0_0) { - /* On 1.0.0, keyslots were kind of a wild west. */ - return ResultSuccess(); - } + Result FreeAesKeyslot(s32 keyslot, const void *owner) { + /* Only virtual keyslots can be freed. */ + R_UNLESS(IsVirtualKeySlot(keyslot), spl::ResultInvalidKeyslot()); + /* Ensure the keyslot is owned. */ R_TRY(ValidateAesKeyslot(keyslot, owner)); - /* Clear the keyslot. */ - { - AccessKey access_key = {}; - KeySource key_source = {}; - - smc::LoadAesKey(keyslot, access_key, key_source); + /* Clear the physical keyslot, if we're cached. */ + s32 phys_slot; + if (g_keyslot_cache.Release(std::addressof(phys_slot), keyslot)) { + ClearPhysicalKeyslot(phys_slot); } - g_keyslot_owners[keyslot] = nullptr; + + /* Clear the virtual keyslot. */ + const auto index = GetVirtualKeySlotIndex(keyslot); + g_keyslot_owners[index] = nullptr; + g_keyslot_contents[index].type = KeySlotContentType::None; + os::SignalSystemEvent(std::addressof(g_se_keyslot_available_event)); return ResultSuccess(); } @@ -701,7 +842,7 @@ namespace ams::spl::impl { return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, smc::EsKeyType::ElicenseKey); } - Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result LoadElicenseKey(s32 keyslot, const void *owner, const AccessKey &access_key) { /* Right now, this is just literally the same function as LoadTitleKey in N's impl. */ return LoadTitleKey(keyslot, owner, access_key); } @@ -730,9 +871,9 @@ namespace ams::spl::impl { return smc::ConvertResult(smc::GenerateSpecificAesKey(out_key, key_source, generation, which)); } - Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result LoadTitleKey(s32 keyslot, const void *owner, const AccessKey &access_key) { R_TRY(ValidateAesKeyslot(keyslot, owner)); - return smc::ConvertResult(smc::LoadTitleKey(keyslot, access_key)); + return LoadVirtualTitleKey(keyslot, access_key); } Result GetPackage2Hash(void *dst, const size_t size) { @@ -783,9 +924,9 @@ namespace ams::spl::impl { /* Helper. */ Result FreeAesKeyslots(const void *owner) { - for (size_t i = 0; i < GetMaxKeyslots(); i++) { - if (g_keyslot_owners[i] == owner) { - FreeAesKeyslot(i, owner); + for (s32 slot = VirtualKeySlotMin; slot <= VirtualKeySlotMax; ++slot) { + if (g_keyslot_owners[GetVirtualKeySlotIndex(slot)] == owner) { + FreeAesKeyslot(slot, owner); } } return ResultSuccess(); diff --git a/stratosphere/spl/source/spl_api_impl.hpp b/stratosphere/spl/source/spl_api_impl.hpp index 9422104ea..483f29e53 100644 --- a/stratosphere/spl/source/spl_api_impl.hpp +++ b/stratosphere/spl/source/spl_api_impl.hpp @@ -32,13 +32,13 @@ namespace ams::spl::impl { /* Crypto. */ Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); - Result LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source); + Result LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source); Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source); Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option); - Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr); - Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size); - Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner); - Result FreeAesKeyslot(u32 keyslot, const void *owner); + Result CryptAesCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr); + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size); + Result AllocateAesKeyslot(s32 *out_keyslot, const void *owner); + Result FreeAesKeyslot(s32 keyslot, const void *owner); /* RSA. */ Result DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); @@ -54,13 +54,13 @@ namespace ams::spl::impl { Result ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); Result DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); Result UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); - Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result LoadElicenseKey(s32 keyslot, const void *owner, const AccessKey &access_key); /* FS */ Result ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); Result DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size); Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); - Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result LoadTitleKey(s32 keyslot, const void *owner, const AccessKey &access_key); Result GetPackage2Hash(void *dst, const size_t size); /* Manu. */ diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp index f358764b3..472036a3d 100644 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -27,7 +27,7 @@ namespace ams::spl { return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); } - Result CryptoService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + Result CryptoService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { return impl::LoadAesKey(keyslot, this, access_key, key_source); } @@ -39,19 +39,19 @@ namespace ams::spl { return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); } - Result CryptoService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + Result CryptoService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result CryptoService::ComputeCmac(sf::Out out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf) { + Result CryptoService::ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { return impl::ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); } - Result CryptoService::AllocateAesKeyslot(sf::Out out_keyslot) { + Result CryptoService::AllocateAesKeyslot(sf::Out out_keyslot) { return impl::AllocateAesKeyslot(out_keyslot.GetPointer(), this); } - Result CryptoService::FreeAesKeyslot(u32 keyslot) { + Result CryptoService::FreeAesKeyslot(s32 keyslot) { return impl::FreeAesKeyslot(keyslot, this); } diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp index 58841575c..8eec93dd8 100644 --- a/stratosphere/spl/source/spl_crypto_service.hpp +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -25,13 +25,13 @@ namespace ams::spl { protected: /* Actual commands. */ virtual Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option); - virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); virtual Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source); virtual Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option); - virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - virtual Result ComputeCmac(sf::Out out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf); - virtual Result AllocateAesKeyslot(sf::Out out_keyslot); - virtual Result FreeAesKeyslot(u32 keyslot); + virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); + virtual Result AllocateAesKeyslot(sf::Out out_keyslot); + virtual Result FreeAesKeyslot(s32 keyslot); virtual void GetAesKeyslotAvailableEvent(sf::OutCopyHandle out_hnd); public: DEFINE_SERVICE_DISPATCH_TABLE { @@ -48,9 +48,9 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(DecryptAesKey), MAKE_SERVICE_COMMAND_META(CryptAesCtr), MAKE_SERVICE_COMMAND_META(ComputeCmac), - MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(FreeAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), }; }; diff --git a/stratosphere/spl/source/spl_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp index ead676cd4..a82c95955 100644 --- a/stratosphere/spl/source/spl_deprecated_service.cpp +++ b/stratosphere/spl/source/spl_deprecated_service.cpp @@ -30,7 +30,7 @@ namespace ams::spl { return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); } - Result DeprecatedService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + Result DeprecatedService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { return impl::LoadAesKey(keyslot, this, access_key, key_source); } @@ -70,15 +70,15 @@ namespace ams::spl { return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); } - Result DeprecatedService::CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, u32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { + Result DeprecatedService::CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result DeprecatedService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + Result DeprecatedService::CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { return impl::CryptAesCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); } - Result DeprecatedService::ComputeCmac(sf::Out out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf) { + Result DeprecatedService::ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { return impl::ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); } @@ -94,7 +94,7 @@ namespace ams::spl { return impl::UnwrapTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); } - Result DeprecatedService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + Result DeprecatedService::LoadTitleKey(s32 keyslot, AccessKey access_key) { return impl::LoadTitleKey(keyslot, this, access_key); } @@ -106,11 +106,11 @@ namespace ams::spl { return impl::UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); } - Result DeprecatedService::AllocateAesKeyslot(sf::Out out_keyslot) { + Result DeprecatedService::AllocateAesKeyslot(sf::Out out_keyslot) { return impl::AllocateAesKeyslot(out_keyslot.GetPointer(), this); } - Result DeprecatedService::FreeAesKeyslot(u32 keyslot) { + Result DeprecatedService::FreeAesKeyslot(s32 keyslot) { return impl::FreeAesKeyslot(keyslot, this); } diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp index 6274ed336..76c86138b 100644 --- a/stratosphere/spl/source/spl_deprecated_service.hpp +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -63,7 +63,7 @@ namespace ams::spl { virtual Result GetConfig(sf::Out out, u32 which); virtual Result ExpMod(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); virtual Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option); - virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source); + virtual Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); virtual Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source); virtual Result SetConfig(u32 which, u64 value); virtual Result GenerateRandomBytes(const sf::OutPointerBuffer &out); @@ -73,17 +73,17 @@ namespace ams::spl { virtual Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which); virtual Result DecryptRsaPrivateKey(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); virtual Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option); - virtual Result CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, u32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); - virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, u32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - virtual Result ComputeCmac(sf::Out out_cmac, u32 keyslot, const sf::InPointerBuffer &in_buf); + virtual Result CryptAesCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); + virtual Result CryptAesCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); virtual Result ImportEsKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); virtual Result UnwrapTitleKeyDeprecated(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); virtual Result UnwrapTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result LoadTitleKey(s32 keyslot, AccessKey access_key); virtual Result UnwrapCommonTitleKeyDeprecated(sf::Out out_access_key, KeySource key_source); virtual Result UnwrapCommonTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation); - virtual Result AllocateAesKeyslot(sf::Out out_keyslot); - virtual Result FreeAesKeyslot(u32 keyslot); + virtual Result AllocateAesKeyslot(sf::Out out_keyslot); + virtual Result FreeAesKeyslot(s32 keyslot); virtual void GetAesKeyslotAvailableEvent(sf::OutCopyHandle out_hnd); virtual Result SetBootReason(BootReasonValue boot_reason); virtual Result GetBootReason(sf::Out out); @@ -117,9 +117,9 @@ namespace ams::spl { MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKeyDeprecated, hos::Version_2_0_0, hos::Version_2_0_0), MAKE_SERVICE_COMMAND_META(UnwrapCommonTitleKey, hos::Version_3_0_0), - MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(FreeAesKeyslot, hos::Version_2_0_0), - MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent, hos::Version_2_0_0), + MAKE_SERVICE_COMMAND_META(AllocateAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(FreeAesKeyslot /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), + MAKE_SERVICE_COMMAND_META(GetAesKeyslotAvailableEvent /* Atmosphere extension: This was added in hos::Version_2_0_0, but is allowed on older firmware by atmosphere. */), MAKE_SERVICE_COMMAND_META(SetBootReason, hos::Version_3_0_0), MAKE_SERVICE_COMMAND_META(GetBootReason, hos::Version_3_0_0), diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp index 723e1db9c..d1e21179e 100644 --- a/stratosphere/spl/source/spl_es_service.cpp +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -46,7 +46,7 @@ namespace ams::spl { return impl::UnwrapElicenseKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); } - Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { + Result EsService::LoadElicenseKey(s32 keyslot, AccessKey access_key) { return impl::LoadElicenseKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 82989d4ba..829cc4a86 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -31,7 +31,7 @@ namespace ams::spl { virtual Result ImportDrmKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); virtual Result DrmExpMod(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod); virtual Result UnwrapElicenseKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); + virtual Result LoadElicenseKey(s32 keyslot, AccessKey access_key); public: DEFINE_SERVICE_DISPATCH_TABLE { MAKE_SERVICE_COMMAND_META(GetConfig), diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index 8e201b9ae..f74ec8e7a 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -34,7 +34,7 @@ namespace ams::spl { return impl::GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); } - Result FsService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + Result FsService::LoadTitleKey(s32 keyslot, AccessKey access_key) { return impl::LoadTitleKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 7b13303ee..b9a48781f 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -28,7 +28,7 @@ namespace ams::spl { virtual Result ImportLotusKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); virtual Result DecryptLotusMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); virtual Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which); - virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result LoadTitleKey(s32 keyslot, AccessKey access_key); virtual Result GetPackage2Hash(const sf::OutPointerBuffer &dst); public: DEFINE_SERVICE_DISPATCH_TABLE { diff --git a/stratosphere/spl/source/spl_key_slot_cache.hpp b/stratosphere/spl/source/spl_key_slot_cache.hpp new file mode 100644 index 000000000..18c093dc8 --- /dev/null +++ b/stratosphere/spl/source/spl_key_slot_cache.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::spl { + + class KeySlotCacheEntry : public util::IntrusiveListBaseNode { + NON_COPYABLE(KeySlotCacheEntry); + NON_MOVEABLE(KeySlotCacheEntry); + private: + friend class KeySlotCache; + public: + static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; + private: + const s32 slot_index; + s32 virtual_slot; + public: + explicit KeySlotCacheEntry(s32 idx) : slot_index(idx), virtual_slot(-1) { /* ... */ } + + bool Contains(s32 virtual_slot) const { + return virtual_slot == this->virtual_slot; + } + + s32 GetPhysicalKeySlotIndex() const { return this->slot_index; } + + s32 GetVirtualKeySlotIndex() const { return this->virtual_slot; } + + void SetVirtualSlot(s32 virtual_slot) { + this->virtual_slot = virtual_slot; + } + + void ClearVirtualSlot() { + this->virtual_slot = -1; + } + }; + + class KeySlotCache { + NON_COPYABLE(KeySlotCache); + NON_MOVEABLE(KeySlotCache); + private: + using KeySlotCacheEntryList = util::IntrusiveListBaseTraits::ListType; + private: + KeySlotCacheEntryList mru_list; + public: + constexpr KeySlotCache() : mru_list() { /* ... */ } + + s32 Allocate(s32 virtual_slot) { + return this->AllocateFromLru(virtual_slot); + } + + bool Find(s32 *out, s32 virtual_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalKeySlotIndex(); + + this->UpdateMru(it); + return true; + } + } + + return false; + } + + bool Release(s32 *out, s32 virtual_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalKeySlotIndex(); + it->ClearVirtualSlot(); + + this->UpdateLru(it); + return true; + } + } + + return false; + } + + bool FindPhysical(s32 physical_slot) { + for (auto it = this->mru_list.begin(); it != this->mru_list.end(); ++it) { + if (it->GetPhysicalKeySlotIndex() == physical_slot) { + this->UpdateMru(it); + + if (it->GetVirtualKeySlotIndex() == physical_slot) { + return true; + } else { + it->SetVirtualSlot(physical_slot); + return false; + } + } + } + AMS_ABORT(); + } + + void AddEntry(KeySlotCacheEntry *entry) { + this->mru_list.push_front(*entry); + } + private: + s32 AllocateFromLru(s32 virtual_slot) { + AMS_ASSERT(!this->mru_list.empty()); + + auto it = this->mru_list.rbegin(); + it->SetVirtualSlot(virtual_slot); + + auto *entry = std::addressof(*it); + this->mru_list.pop_back(); + this->mru_list.push_front(*entry); + + return entry->GetPhysicalKeySlotIndex(); + } + + void UpdateMru(KeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + this->mru_list.erase(it); + this->mru_list.push_front(*entry); + } + + void UpdateLru(KeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + this->mru_list.erase(it); + this->mru_list.push_back(*entry); + } + }; + +}