From 0abb1e474e3ce21cc5b390ad76ae7199474b757d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Sun, 10 Oct 2021 12:57:24 -0700 Subject: [PATCH] spl: refactor for accuracy/move into libstrat --- libstratosphere/include/stratosphere/spl.hpp | 3 +- .../stratosphere/spl/impl/spl_api_impl.hpp | 84 ++ ...spl_smc.hpp => spl_secure_monitor_api.hpp} | 19 +- .../include/stratosphere/spl/spl_types.hpp | 29 +- .../source/ams/ams_exosphere_api.cpp | 24 +- .../source/spl/impl/spl_api_impl.cpp | 897 ++++++++++++++++++ .../source/spl/impl/spl_ctr_drbg.hpp | 277 ++++++ .../spl/impl/spl_device_address_mapper.hpp | 39 + .../source/spl/impl/spl_key_slot_cache.hpp | 138 +++ ...spl_smc.cpp => spl_secure_monitor_api.cpp} | 14 +- libstratosphere/source/spl/spl_api.cpp | 8 +- .../include/vapours/results/spl_results.hpp | 20 +- 12 files changed, 1497 insertions(+), 55 deletions(-) create mode 100644 libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp rename libstratosphere/include/stratosphere/spl/smc/{spl_smc.hpp => spl_secure_monitor_api.hpp} (81%) create mode 100644 libstratosphere/source/spl/impl/spl_api_impl.cpp create mode 100644 libstratosphere/source/spl/impl/spl_ctr_drbg.hpp create mode 100644 libstratosphere/source/spl/impl/spl_device_address_mapper.hpp create mode 100644 libstratosphere/source/spl/impl/spl_key_slot_cache.hpp rename libstratosphere/source/spl/smc/{spl_smc.cpp => spl_secure_monitor_api.cpp} (96%) diff --git a/libstratosphere/include/stratosphere/spl.hpp b/libstratosphere/include/stratosphere/spl.hpp index 63cf88f3..0afcba70 100644 --- a/libstratosphere/include/stratosphere/spl.hpp +++ b/libstratosphere/include/stratosphere/spl.hpp @@ -18,7 +18,8 @@ #include #include -#include +#include +#include #include #include #include diff --git a/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp b/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp new file mode 100644 index 00000000..b64d8414 --- /dev/null +++ b/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 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::impl { + + constexpr inline s32 AesKeySlotMin = 16; + constexpr inline s32 AesKeySlotCount = 9; + constexpr inline s32 AesKeySlotMax = AesKeySlotMin + AesKeySlotCount - 1; + + /* Initialization. */ + void Initialize(); + + /* General. */ + Result GetConfig(u64 *out, spl::ConfigItem key); + Result ModularExponentiate(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); + Result SetConfig(spl::ConfigItem key, u64 value); + Result GenerateRandomBytes(void *out, size_t size); + Result IsDevelopment(bool *out); + Result SetBootReason(BootReasonValue boot_reason); + Result GetBootReason(BootReasonValue *out); + + ALWAYS_INLINE bool GetConfigBool(spl::ConfigItem key) { + u64 v; + R_ABORT_UNLESS(::ams::spl::impl::GetConfig(std::addressof(v), key)); + return v != 0; + } + + /* Crypto. */ + Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); + Result LoadAesKey(s32 keyslot, 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 ComputeCtr(void *dst, size_t dst_size, s32 keyslot, const void *src, size_t src_size, const IvCtr &iv_ctr); + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *data, size_t size); + + Result AllocateAesKeySlot(s32 *out_keyslot); + Result DeallocateAesKeySlot(s32 keyslot); + Result TestAesKeySlot(s32 *out_index, s32 keyslot); + + os::SystemEvent *GetAesKeySlotAvailableEvent(); + + /* RSA. */ + Result DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + + /* SSL */ + Result DecryptAndStoreSslClientCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result ModularExponentiateWithSslClientCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + + /* ES */ + Result LoadEsDeviceKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result PrepareEsTitleKey(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 PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation); + Result DecryptAndStoreDrmDeviceCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result ModularExponentiateWithDrmDeviceCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + Result PrepareEsArchiveKey(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 LoadPreparedAesKey(s32 keyslot, const AccessKey &access_key); + + /* FS */ + Result DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result DecryptGcMessage(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 LoadPreparedAesKey(s32 keyslot, const AccessKey &access_key); + Result GetPackage2Hash(void *dst, const size_t size); + + /* Manu. */ + Result ReencryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); + +} diff --git a/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp b/libstratosphere/include/stratosphere/spl/smc/spl_secure_monitor_api.hpp similarity index 81% rename from libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp rename to libstratosphere/include/stratosphere/spl/smc/spl_secure_monitor_api.hpp index 969e0694..e2ff504c 100644 --- a/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp +++ b/libstratosphere/include/stratosphere/spl/smc/spl_secure_monitor_api.hpp @@ -20,24 +20,24 @@ namespace ams::spl::smc { /* Helpers for converting arguments. */ - inline u32 GetComputeAesMode(CipherMode mode, u32 keyslot) { + constexpr ALWAYS_INLINE u32 GetComputeAesMode(CipherMode mode, u32 keyslot) { return static_cast((static_cast(mode) << 4) | (keyslot & 7)); } - inline u32 GetPrepareEsDeviceUniqueKeyOption(EsCommonKeyType type, u32 generation) { + constexpr ALWAYS_INLINE u32 GetPrepareEsDeviceUniqueKeyOption(EsDeviceUniqueKeyType type, u32 generation) { return static_cast((static_cast(type) << 6) | (generation & 0x3F)); } /* Functions. */ - Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords); - Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem which); + Result SetConfig(AsyncOperationKey *out_op, spl::ConfigItem key, const u64 *value, size_t num_qwords, const void *sign); + Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem key); Result GetResult(Result *out, AsyncOperationKey op); Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); Result ModularExponentiate(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); Result GenerateRandomBytes(void *out, size_t size); Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option); Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source); - Result ComputeAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + Result ComputeAes(AsyncOperationKey *out_op, u32 dst_addr, u32 mode, const IvCtr &iv_ctr, u32 src_addr, size_t size); Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which); Result ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size); Result ReencryptDeviceUniqueData(void *data, size_t size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); @@ -59,12 +59,13 @@ namespace ams::spl::smc { Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id); /* Helpers. */ - inline Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { - return SetConfig(which, nullptr, value, num_qwords); + ALWAYS_INLINE Result SetConfig(spl::ConfigItem key, const u64 *value, size_t num_qwords) { + AsyncOperationKey dummy_op; + return SetConfig(std::addressof(dummy_op), key, value, num_qwords, nullptr); } - inline Result SetConfig(spl::ConfigItem which, const u64 value) { - return SetConfig(which, std::addressof(value), 1); + ALWAYS_INLINE Result SetConfig(spl::ConfigItem key, const u64 value) { + return SetConfig(key, std::addressof(value), 1); } } diff --git a/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libstratosphere/include/stratosphere/spl/spl_types.hpp index 2fbb1435..5b1b459b 100644 --- a/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libstratosphere/include/stratosphere/spl/spl_types.hpp @@ -54,7 +54,7 @@ namespace ams::spl { enum class Result { Success = 0, - NotImplemented = 1, + NotSupported = 1, InvalidArgument = 2, InProgress = 3, NoAsyncOperation = 4, @@ -69,7 +69,7 @@ namespace ams::spl { /* Convert to the list of known SecureMonitorErrors. */ const auto converted = R_MAKE_NAMESPACE_RESULT(::ams::spl, static_cast(smc_result)); - R_UNLESS(spl::ResultSecureMonitorError::Includes(converted), spl::ResultUnknownSecureMonitorError()); + R_UNLESS(spl::ResultSecureMonitorError::Includes(converted), spl::ResultUnexpectedSecureMonitorResult()); /* Return the error. */ return converted; @@ -95,7 +95,7 @@ namespace ams::spl { DrmDeviceCert = 2, }; - enum class EsCommonKeyType { + enum class EsDeviceUniqueKeyType { TitleKey = 0, ArchiveKey = 1, }; @@ -105,6 +105,9 @@ namespace ams::spl { }; } + constexpr inline size_t AesKeySize = crypto::AesEncryptor128::KeySize; + constexpr inline size_t AesBlockSize = crypto::AesEncryptor128::BlockSize; + enum class HardwareType { Icosa = 0, Copper = 1, @@ -168,40 +171,40 @@ namespace ams::spl { struct AesKey { union { - u8 data[AES_128_KEY_SIZE]; - u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u8 data[AesKeySize]; + u64 data64[AesKeySize / sizeof(u64)]; }; }; static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); struct IvCtr { union { - u8 data[AES_128_KEY_SIZE]; - u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u8 data[AesKeySize]; + u64 data64[AesKeySize / sizeof(u64)]; }; }; static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); struct Cmac { union { - u8 data[AES_128_KEY_SIZE]; - u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u8 data[AesKeySize]; + u64 data64[AesKeySize / sizeof(u64)]; }; }; static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { union { - u8 data[AES_128_KEY_SIZE]; - u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u8 data[AesKeySize]; + u64 data64[AesKeySize / sizeof(u64)]; }; }; static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { union { - u8 data[AES_128_KEY_SIZE]; - u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u8 data[AesKeySize]; + u64 data64[AesKeySize / sizeof(u64)]; }; }; static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); diff --git a/libstratosphere/source/ams/ams_exosphere_api.cpp b/libstratosphere/source/ams/ams_exosphere_api.cpp index 9df6fdfe..5db63628 100644 --- a/libstratosphere/source/ams/ams_exosphere_api.cpp +++ b/libstratosphere/source/ams/ams_exosphere_api.cpp @@ -14,34 +14,32 @@ * along with this program. If not, see . */ #include -#include -#include namespace ams::exosphere { ApiInfo GetApiInfo() { u64 exosphere_cfg; - if (spl::smc::GetConfig(std::addressof(exosphere_cfg), 1, spl::ConfigItem::ExosphereApiVersion) != spl::smc::Result::Success) { - R_ABORT_UNLESS(ResultNotPresent()); + if (R_FAILED(spl::impl::GetConfig(std::addressof(exosphere_cfg), spl::ConfigItem::ExosphereApiVersion))) { + R_ABORT_UNLESS(exosphere::ResultNotPresent()); } return ApiInfo{ util::BitPack64{exosphere_cfg} }; } void ForceRebootToRcm() { - R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 1))); + R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 1)); } void ForceRebootToIramPayload() { - R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 2))); + R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 2)); } void ForceRebootToFatalError() { - R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 3))); + R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsReboot, 3)); } void ForceShutdown() { - R_ABORT_UNLESS(spl::smc::ConvertResult(spl::smc::SetConfig(spl::ConfigItem::ExosphereNeedsShutdown, 1))); + R_ABORT_UNLESS(spl::impl::SetConfig(spl::ConfigItem::ExosphereNeedsShutdown, 1)); } void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size) { @@ -67,19 +65,21 @@ namespace ams::exosphere { } bool IsRcmBugPatched() { - return GetBooleanConfigItem(spl::ConfigItem::ExosphereHasRcmBugPatch); + return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereHasRcmBugPatch); } bool ShouldBlankProdInfo() { - return GetBooleanConfigItem(spl::ConfigItem::ExosphereBlankProdInfo); + return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereBlankProdInfo); } bool ShouldAllowWritesToProdInfo() { - return GetBooleanConfigItem(spl::ConfigItem::ExosphereAllowCalWrites); + return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereAllowCalWrites); } u64 GetDeviceId() { - return GetU64ConfigItem(spl::ConfigItem::DeviceId); + u64 device_id; + R_ABORT_UNLESS(spl::impl::GetConfig(std::addressof(device_id), spl::ConfigItem::DeviceId)); + return device_id; } } diff --git a/libstratosphere/source/spl/impl/spl_api_impl.cpp b/libstratosphere/source/spl/impl/spl_api_impl.cpp new file mode 100644 index 00000000..a2a1681c --- /dev/null +++ b/libstratosphere/source/spl/impl/spl_api_impl.cpp @@ -0,0 +1,897 @@ +/* + * Copyright (c) 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 . + */ +#include +#include "spl_ctr_drbg.hpp" +#include "spl_device_address_mapper.hpp" +#include "spl_key_slot_cache.hpp" + +namespace ams::spl::impl { + + namespace { + + /* Drbg type. */ + using Drbg = CtrDrbg; + + /* Convenient defines. */ + constexpr size_t DeviceAddressSpaceAlign = 4_MB; + + constexpr u32 WorkBufferBase = 0x80000000u; + constexpr u32 ComputeAesInMapBase = 0x90000000u; + constexpr u32 ComputeAesOutMapBase = 0xC0000000u; + constexpr size_t ComputeAesSizeMax = static_cast(ComputeAesOutMapBase - ComputeAesInMapBase); + + constexpr size_t DeviceUniqueDataIvSize = 0x10; + constexpr size_t DeviceUniqueDataPaddingSize = 0x08; + constexpr size_t DeviceUniqueDataDeviceIdSize = 0x08; + constexpr size_t DeviceUniqueDataGmacSize = 0x10; + + constexpr size_t DeviceUniqueDataPlainMetaDataSize = DeviceUniqueDataIvSize + DeviceUniqueDataGmacSize; + constexpr size_t DeviceUniqueDataMetaDataSize = DeviceUniqueDataPlainMetaDataSize + DeviceUniqueDataPaddingSize + DeviceUniqueDataDeviceIdSize; + + constexpr size_t Rsa2048BlockSize = 0x100; + constexpr size_t LabelDigestSizeMax = 0x20; + + constexpr size_t WorkBufferSizeMax = 0x800; + + constexpr const KeySource KeyGenerationSource = { + .data = { 0x89, 0x61, 0x5E, 0xE0, 0x5C, 0x31, 0xB6, 0x80, 0x5F, 0xE5, 0x8F, 0x3D, 0xA2, 0x4F, 0x7A, 0xA8 } + }; + + constexpr const KeySource AesKeyDecryptionSource = { + .data = { 0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E } + }; + + constexpr s32 PhysicalAesKeySlotCount = 6; + + /* KeySlot management. */ + constinit AesKeySlotCache g_aes_keyslot_cache; + constinit util::optional g_aes_keyslot_cache_entry[PhysicalAesKeySlotCount]; + + constinit bool g_is_physical_keyslot_allowed = false; + constinit bool g_is_modern_device_unique_data = true; + + constexpr inline bool IsVirtualAesKeySlot(s32 keyslot) { + return AesKeySlotMin <= keyslot && keyslot <= AesKeySlotMax; + } + + constexpr inline bool IsPhysicalAesKeySlot(s32 keyslot) { + return keyslot < PhysicalAesKeySlotCount; + } + + constexpr inline s32 GetVirtualAesKeySlotIndex(s32 keyslot) { + AMS_ASSERT(IsVirtualAesKeySlot(keyslot)); + return keyslot - AesKeySlotMin; + } + + constexpr inline s32 MakeVirtualAesKeySlot(s32 index) { + const s32 virt_slot = index + AesKeySlotMin; + AMS_ASSERT(IsVirtualKeySlot(virt_slot)); + return virt_slot; + } + + enum class AesKeySlotContentType { + None = 0, + AesKey = 1, + PreparedKey = 2, + }; + + struct AesKeySlotContents { + AesKeySlotContentType type; + union { + struct { + AccessKey access_key; + KeySource key_source; + } aes_key; + struct { + AccessKey access_key; + } prepared_key; + }; + }; + + constinit bool g_is_aes_keyslot_allocated[AesKeySlotCount]; + constinit AesKeySlotContents g_aes_keyslot_contents[AesKeySlotCount]; + constinit AesKeySlotContents g_aes_physical_keyslot_contents_for_backwards_compatibility[PhysicalAesKeySlotCount]; + + void ClearPhysicalAesKeySlot(s32 keyslot) { + AMS_ASSERT(IsPhysicalAesKeySlot(keyslot)); + + AccessKey access_key = {}; + KeySource key_source = {}; + smc::LoadAesKey(keyslot, access_key, key_source); + } + + s32 GetPhysicalAesKeySlot(s32 keyslot, bool load) { + s32 phys_slot = -1; + AesKeySlotContents *contents = nullptr; + + if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) { + /* On 1.0.0, we allow the use of physical keyslots. */ + phys_slot = keyslot; + contents = std::addressof(g_aes_physical_keyslot_contents_for_backwards_compatibility[phys_slot]); + + /* If the physical slot is already loaded, we're good. */ + if (g_aes_keyslot_cache.FindPhysical(phys_slot)) { + return phys_slot; + } + } else { + /* This should be a virtual keyslot. */ + AMS_ASSERT(IsVirtualAesKeySlot(keyslot)); + + /* Try to find a physical slot in the cache. */ + if (g_aes_keyslot_cache.Find(std::addressof(phys_slot), keyslot)) { + return phys_slot; + } + + /* Allocate a physical slot. */ + phys_slot = g_aes_keyslot_cache.Allocate(keyslot); + contents = std::addressof(g_aes_keyslot_contents[GetVirtualAesKeySlotIndex(keyslot)]); + } + + /* Ensure the contents of the keyslot. */ + if (load) { + switch (contents->type) { + case AesKeySlotContentType::None: + ClearPhysicalAesKeySlot(phys_slot); + break; + case AesKeySlotContentType::AesKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadAesKey(phys_slot, contents->aes_key.access_key, contents->aes_key.key_source))); + break; + case AesKeySlotContentType::PreparedKey: + R_ABORT_UNLESS(smc::ConvertResult(smc::LoadPreparedAesKey(phys_slot, contents->prepared_key.access_key))); + break; + AMS_UNREACHABLE_DEFAULT_CASE(); + } + } + + return phys_slot; + } + + /* Type definitions. */ + class ScopedAesKeySlot { + private: + s32 m_slot_index; + bool m_allocated; + public: + ScopedAesKeySlot() : m_slot_index(-1), m_allocated(false) { + /* ... */ + } + ~ScopedAesKeySlot() { + if (m_allocated) { + DeallocateAesKeySlot(m_slot_index); + } + } + + s32 GetIndex() const { + return m_slot_index; + } + + Result Allocate() { + R_TRY(AllocateAesKeySlot(std::addressof(m_slot_index))); + m_allocated = true; + return ResultSuccess(); + } + }; + + struct SeLinkedListEntry { + u32 num_entries; + u32 address; + u32 size; + }; + + struct SeCryptContext { + SeLinkedListEntry in; + SeLinkedListEntry out; + }; + + /* Global variables. */ + alignas(os::MemoryPageSize) constinit u8 g_work_buffer[WorkBufferSizeMax]; + constinit util::TypedStorage g_drbg; + constinit os::InterruptName g_interrupt_name; + constinit os::InterruptEventType g_interrupt; + constinit util::TypedStorage g_aes_keyslot_available_event; + constinit os::SdkMutex g_operation_lock; + constinit dd::DeviceAddressSpaceType g_device_address_space; + constinit u32 g_work_buffer_mapped_address; + + constinit BootReasonValue g_boot_reason; + constinit bool g_is_boot_reason_initialized; + + /* Initialization functionality. */ + void InitializeAsyncOperation() { + u64 interrupt_number; + impl::GetConfig(std::addressof(interrupt_number), ConfigItem::SecurityEngineInterruptNumber); + g_interrupt_name = static_cast(interrupt_number); + + os::InitializeInterruptEvent(std::addressof(g_interrupt), g_interrupt_name, os::EventClearMode_AutoClear); + } + + void InitializeDeviceAddressSpace() { + /* Create device address space. */ + R_ABORT_UNLESS(dd::CreateDeviceAddressSpace(std::addressof(g_device_address_space), 0, (1ul << 32))); + + /* Attach to the security engine. */ + R_ABORT_UNLESS(dd::AttachDeviceAddressSpace(std::addressof(g_device_address_space), dd::DeviceName_Se)); + + /* Map work buffer into the device. */ + const uintptr_t work_buffer_address = reinterpret_cast(g_work_buffer); + g_work_buffer_mapped_address = WorkBufferBase + (work_buffer_address % DeviceAddressSpaceAlign); + + R_ABORT_UNLESS(dd::MapDeviceAddressSpaceAligned(std::addressof(g_device_address_space), dd::GetCurrentProcessHandle(), work_buffer_address, dd::DeviceAddressSpaceMemoryRegionAlignment, g_work_buffer_mapped_address, dd::MemoryPermission_ReadWrite)); + } + + void InitializeCtrDrbg() { + u8 seed[Drbg::SeedSize]; + AMS_ABORT_UNLESS(smc::GenerateRandomBytes(seed, sizeof(seed)) == smc::Result::Success); + + util::ConstructAt(g_drbg); + util::GetReference(g_drbg).Initialize(seed, sizeof(seed), nullptr, 0, nullptr, 0); + } + + void InitializeKeySlots() { + const auto fw_ver = hos::GetVersion(); + g_is_physical_keyslot_allowed = fw_ver < hos::Version_2_0_0; + g_is_modern_device_unique_data = fw_ver >= hos::Version_5_0_0; + + for (s32 i = 0; i < PhysicalAesKeySlotCount; i++) { + g_aes_keyslot_cache_entry[i].emplace(i); + g_aes_keyslot_cache.AddEntry(std::addressof(g_aes_keyslot_cache_entry[i].value())); + } + + util::ConstructAt(g_aes_keyslot_available_event, os::EventClearMode_ManualClear, true); + util::GetReference(g_aes_keyslot_available_event).Signal(); + } + + void WaitOperation() { + os::WaitInterruptEvent(std::addressof(g_interrupt)); + } + + smc::Result WaitAndGetResult(smc::AsyncOperationKey op_key) { + WaitOperation(); + + smc::Result async_res; + if (const smc::Result res = smc::GetResult(std::addressof(async_res), op_key); res != smc::Result::Success) { + return res; + } + + return async_res; + } + + smc::Result WaitAndGetResultData(void *dst, size_t size, smc::AsyncOperationKey op_key) { + WaitOperation(); + + smc::Result async_res; + if (const smc::Result res = smc::GetResultData(std::addressof(async_res), dst, size, op_key); res != smc::Result::Success) { + return res; + } + + return async_res; + } + + smc::Result DecryptAes(void *dst, s32 keyslot, const void *src) { + struct DecryptAesLayout { + SeCryptContext crypt_ctx; + u8 padding[8]; + u8 in_buffer[crypto::AesEncryptor128::BlockSize]; + u8 out_buffer[crypto::AesEncryptor128::BlockSize]; + }; + + auto &layout = *reinterpret_cast(g_work_buffer); + + layout.crypt_ctx.in.num_entries = 0; + layout.crypt_ctx.in.address = g_work_buffer_mapped_address + offsetof(DecryptAesLayout, in_buffer); + layout.crypt_ctx.in.size = sizeof(layout.in_buffer); + layout.crypt_ctx.out.num_entries = 0; + layout.crypt_ctx.out.address = g_work_buffer_mapped_address + offsetof(DecryptAesLayout, out_buffer); + layout.crypt_ctx.out.size = sizeof(layout.out_buffer); + + std::memcpy(layout.in_buffer, src, sizeof(layout.in_buffer)); + + os::FlushDataCache(std::addressof(layout), sizeof(layout)); + { + std::scoped_lock lk(g_operation_lock); + + smc::AsyncOperationKey op_key; + const IvCtr iv_ctr = {}; + const u32 mode = smc::GetComputeAesMode(smc::CipherMode::CbcDecrypt, GetPhysicalAesKeySlot(keyslot, true)); + const u32 dst_ll_addr = g_work_buffer_mapped_address + offsetof(DecryptAesLayout, crypt_ctx.out); + const u32 src_ll_addr = g_work_buffer_mapped_address + offsetof(DecryptAesLayout, crypt_ctx.in); + + smc::Result res = smc::ComputeAes(std::addressof(op_key), dst_ll_addr, mode, iv_ctr, src_ll_addr, sizeof(layout.out_buffer)); + if (res != smc::Result::Success) { + return res; + } + + res = WaitAndGetResult(op_key); + if (res != smc::Result::Success) { + return res; + } + } + os::FlushDataCache(std::addressof(layout.out_buffer), sizeof(layout.out_buffer)); + + std::memcpy(dst, layout.out_buffer, sizeof(layout.out_buffer)); + + return smc::Result::Success; + } + + Result GenerateRandomBytesImpl(void *out, size_t size) { + AMS_ASSERT(size <= Drbg::RequestSizeMax); + + if (!util::GetReference(g_drbg).Generate(out, size, nullptr, 0)) { + /* We need to reseed. */ + { + u8 seed[Drbg::SeedSize]; + + if (smc::Result res = smc::GenerateRandomBytes(seed, sizeof(seed)); res != smc::Result::Success) { + return smc::ConvertResult(res); + } + + util::GetReference(g_drbg).Reseed(seed, sizeof(seed), nullptr, 0); + } + util::GetReference(g_drbg).Generate(out, size, nullptr, 0); + } + + return ResultSuccess(); + } + + Result DecryptAndStoreDeviceUniqueKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct DecryptAndStoreDeviceUniqueKeyLayout { + u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate size. */ + R_UNLESS(src_size <= sizeof(DecryptAndStoreDeviceUniqueKeyLayout), spl::ResultInvalidBufferSize()); + + std::memcpy(layout.data, src, src_size); + + if (g_is_modern_device_unique_data) { + return smc::ConvertResult(smc::DecryptDeviceUniqueData(layout.data, src_size, access_key, key_source, static_cast(option))); + } else { + return smc::ConvertResult(smc::DecryptAndStoreGcKey(layout.data, src_size, access_key, key_source, option)); + } + } + + Result ModularExponentiateWithStorageKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, smc::ModularExponentiateWithStorageKeyMode mode) { + struct ModularExponentiateWithStorageKeyLayout { + u8 base[Rsa2048BlockSize]; + u8 mod[Rsa2048BlockSize]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize()); + R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize()); + R_UNLESS(out_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize()); + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout.base) - base_size; + const size_t mod_ofs = sizeof(layout.mod) - mod_size; + + std::memset(layout.base, 0, sizeof(layout.base)); + std::memset(layout.mod, 0, sizeof(layout.mod)); + + std::memcpy(layout.base + base_ofs, base, base_size); + std::memcpy(layout.mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + { + std::scoped_lock lk(g_operation_lock); + + smc::AsyncOperationKey op_key; + smc::Result res = smc::ModularExponentiateWithStorageKey(std::addressof(op_key), layout.base, layout.mod, mode); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + + res = WaitAndGetResultData(g_work_buffer, out_size, op_key); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + } + + /* Copy result. */ + if (out != g_work_buffer) { + std::memcpy(out, g_work_buffer, out_size); + } + + return ResultSuccess(); + } + + Result PrepareEsDeviceUniqueKey(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, smc::EsDeviceUniqueKeyType type, u32 generation) { + struct PrepareEsDeviceUniqueKeyLayout { + u8 base[Rsa2048BlockSize]; + u8 mod[Rsa2048BlockSize]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize()); + R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize()); + R_UNLESS(label_digest_size <= LabelDigestSizeMax, spl::ResultInvalidBufferSize()); + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout.base) - base_size; + const size_t mod_ofs = sizeof(layout.mod) - mod_size; + + std::memset(layout.base, 0, sizeof(layout.base)); + std::memset(layout.mod, 0, sizeof(layout.mod)); + + std::memcpy(layout.base + base_ofs, base, base_size); + std::memcpy(layout.mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + { + std::scoped_lock lk(g_operation_lock); + + smc::AsyncOperationKey op_key; + smc::Result res = smc::PrepareEsDeviceUniqueKey(std::addressof(op_key), layout.base, layout.mod, label_digest, label_digest_size, smc::GetPrepareEsDeviceUniqueKeyOption(type, generation)); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + + res = WaitAndGetResultData(g_work_buffer, sizeof(*out_access_key), op_key); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + } + + std::memcpy(out_access_key, g_work_buffer, sizeof(*out_access_key)); + return ResultSuccess(); + } + + } + + /* Initialization. */ + void Initialize() { + /* Initialize async operation. */ + InitializeAsyncOperation(); + + /* Initialize device address space for the SE. */ + InitializeDeviceAddressSpace(); + + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + + /* Initialize the keyslot cache. */ + InitializeKeySlots(); + } + + /* General. */ + Result GetConfig(u64 *out, ConfigItem key) { + /* Nintendo explicitly blacklists package2 hash, which must be gotten via bespoke api. */ + R_UNLESS(key != ConfigItem::Package2Hash, spl::ResultInvalidArgument()); + + smc::Result res = smc::GetConfig(out, 1, key); + + /* Nintendo has some special handling here for hardware type/hardware state. */ + if (key == ConfigItem::HardwareType && res == smc::Result::InvalidArgument) { + *out = static_cast(HardwareType::Icosa); + res = smc::Result::Success; + } + if (key == ConfigItem::HardwareState && res == smc::Result::InvalidArgument) { + *out = HardwareState_Development; + res = smc::Result::Success; + } + + return smc::ConvertResult(res); + } + + Result ModularExponentiate(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) { + struct ModularExponentiateLayout { + u8 base[Rsa2048BlockSize]; + u8 exp[Rsa2048BlockSize]; + u8 mod[Rsa2048BlockSize]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + R_UNLESS(base_size <= sizeof(layout.base), spl::ResultInvalidBufferSize()); + R_UNLESS(exp_size <= sizeof(layout.exp), spl::ResultInvalidBufferSize()); + R_UNLESS(mod_size <= sizeof(layout.mod), spl::ResultInvalidBufferSize()); + R_UNLESS(out_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize()); + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout.base) - base_size; + const size_t mod_ofs = sizeof(layout.mod) - mod_size; + + std::memset(layout.base, 0, sizeof(layout.base)); + std::memset(layout.mod, 0, sizeof(layout.mod)); + + std::memcpy(layout.base + base_ofs, base, base_size); + std::memcpy(layout.mod + mod_ofs, mod, mod_size); + + std::memcpy(layout.exp, exp, exp_size); + + /* Do exp mod operation. */ + { + std::scoped_lock lk(g_operation_lock); + + smc::AsyncOperationKey op_key; + smc::Result res = smc::ModularExponentiate(std::addressof(op_key), layout.base, layout.exp, exp_size, layout.mod); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + + res = WaitAndGetResultData(g_work_buffer, out_size, op_key); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + } + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess(); + } + + Result SetConfig(ConfigItem key, u64 value) { + return smc::ConvertResult(smc::SetConfig(key, value)); + } + + Result GenerateRandomBytes(void *out, size_t size) { + for (size_t offset = 0; offset < size; offset += Drbg::RequestSizeMax) { + R_TRY(GenerateRandomBytesImpl(static_cast(out) + offset, std::min(size - offset, Drbg::RequestSizeMax))); + } + + return ResultSuccess(); + } + + Result IsDevelopment(bool *out) { + u64 hardware_state; + R_TRY(impl::GetConfig(std::addressof(hardware_state), ConfigItem::HardwareState)); + + *out = (hardware_state == HardwareState_Development); + return ResultSuccess(); + } + + Result SetBootReason(BootReasonValue boot_reason) { + R_UNLESS(!g_is_boot_reason_initialized, spl::ResultBootReasonAlreadyInitialized()); + + g_boot_reason = boot_reason; + g_is_boot_reason_initialized = true; + return ResultSuccess(); + } + + Result GetBootReason(BootReasonValue *out) { + R_UNLESS(g_is_boot_reason_initialized, spl::ResultBootReasonNotInitialized()); + + *out = g_boot_reason; + return ResultSuccess(); + } + + /* Crypto. */ + Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) { + return smc::ConvertResult(smc::GenerateAesKek(out_access_key, key_source, generation, option)); + } + + Result LoadAesKey(s32 keyslot, const AccessKey &access_key, const KeySource &key_source) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalAesKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadAesKey(phys_slot, access_key, key_source))); + + /* Update our contents. */ + const s32 index = GetVirtualAesKeySlotIndex(keyslot); + + g_aes_keyslot_contents[index].type = AesKeySlotContentType::AesKey; + g_aes_keyslot_contents[index].aes_key.access_key = access_key; + g_aes_keyslot_contents[index].aes_key.key_source = key_source; + + return ResultSuccess(); + } + + Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { + ScopedAesKeySlot keyslot_holder; + R_TRY(keyslot_holder.Allocate()); + + R_TRY(LoadAesKey(keyslot_holder.GetIndex(), access_key, KeyGenerationSource)); + + return smc::ConvertResult(DecryptAes(out_key, keyslot_holder.GetIndex(), std::addressof(key_source))); + } + + Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { + AccessKey access_key; + R_TRY(GenerateAesKek(std::addressof(access_key), AesKeyDecryptionSource, generation, option)); + + return GenerateAesKey(out_key, access_key, key_source); + } + + Result ComputeCtr(void *dst, size_t dst_size, s32 keyslot, const void *src, size_t src_size, const IvCtr &iv_ctr) { + /* Succeed immediately if there's nothing to compute. */ + R_SUCCEED_IF(src_size == 0); + + /* Validate sizes. */ + R_UNLESS(src_size <= dst_size, spl::ResultInvalidBufferSize()); + R_UNLESS(util::IsAligned(src_size, AesBlockSize), spl::ResultInvalidBufferSize()); + + /* We can only map 4_MB aligned buffers for the SE, so determine where to map our buffers. */ + const uintptr_t src_addr = reinterpret_cast(src); + const uintptr_t dst_addr = reinterpret_cast(dst); + + const uintptr_t src_addr_aligned = util::AlignDown(src_addr, dd::DeviceAddressSpaceMemoryRegionAlignment); + const uintptr_t dst_addr_aligned = util::AlignDown(dst_addr, dd::DeviceAddressSpaceMemoryRegionAlignment); + + const size_t src_size_aligned = util::AlignUp(src_addr + src_size, dd::DeviceAddressSpaceMemoryRegionAlignment) - src_addr_aligned; + const size_t dst_size_aligned = util::AlignUp(dst_addr + dst_size, dd::DeviceAddressSpaceMemoryRegionAlignment) - dst_addr_aligned; + + const u32 src_se_map_addr = ComputeAesInMapBase + (src_addr_aligned % DeviceAddressSpaceAlign); + const u32 dst_se_map_addr = ComputeAesOutMapBase + (dst_addr_aligned % DeviceAddressSpaceAlign); + + const u32 src_se_addr = ComputeAesInMapBase + (src_addr % DeviceAddressSpaceAlign); + const u32 dst_se_addr = ComputeAesOutMapBase + (dst_addr % DeviceAddressSpaceAlign); + + /* Validate aligned sizes. */ + R_UNLESS(src_size_aligned <= ComputeAesSizeMax, spl::ResultInvalidBufferSize()); + R_UNLESS(dst_size_aligned <= ComputeAesSizeMax, spl::ResultInvalidBufferSize()); + + /* Helpers for mapping/unmapping. */ + DeviceAddressMapper src_mapper(std::addressof(g_device_address_space), src_addr_aligned, src_size_aligned, src_se_map_addr, dd::MemoryPermission_ReadOnly); + DeviceAddressMapper dst_mapper(std::addressof(g_device_address_space), dst_addr_aligned, dst_size_aligned, dst_se_map_addr, dd::MemoryPermission_WriteOnly); + + /* Setup SE linked list entries. */ + auto &crypt_ctx = *reinterpret_cast(g_work_buffer); + crypt_ctx.in.num_entries = 0; + crypt_ctx.in.address = src_se_addr; + crypt_ctx.in.size = src_size; + crypt_ctx.out.num_entries = 0; + crypt_ctx.out.address = dst_se_addr; + crypt_ctx.out.size = dst_size; + + os::FlushDataCache(std::addressof(crypt_ctx), sizeof(crypt_ctx)); + os::FlushDataCache(src, src_size); + os::FlushDataCache(dst, dst_size); + { + std::scoped_lock lk(g_operation_lock); + + const u32 mode = smc::GetComputeAesMode(smc::CipherMode::Ctr, GetPhysicalAesKeySlot(keyslot, true)); + const u32 dst_ll_addr = g_work_buffer_mapped_address + offsetof(SeCryptContext, out); + const u32 src_ll_addr = g_work_buffer_mapped_address + offsetof(SeCryptContext, in); + + smc::AsyncOperationKey op_key; + smc::Result res = smc::ComputeAes(std::addressof(op_key), dst_ll_addr, mode, iv_ctr, src_ll_addr, src_size); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + + res = WaitAndGetResult(op_key); + if (res != smc::Result::Success) { + return smc::ConvertResult(res); + } + } + os::FlushDataCache(dst, dst_size); + + return ResultSuccess(); + } + + Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *data, size_t size) { + R_UNLESS(size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize()); + + std::memcpy(g_work_buffer, data, size); + return smc::ConvertResult(smc::ComputeCmac(out_cmac, GetPhysicalAesKeySlot(keyslot, true), g_work_buffer, size)); + } + + Result AllocateAesKeySlot(s32 *out_keyslot) { + /* Find an unused keyslot. */ + for (s32 i = 0; i < AesKeySlotCount; ++i) { + if (!g_is_aes_keyslot_allocated[i]) { + g_is_aes_keyslot_allocated[i] = true; + g_aes_keyslot_contents[i].type = AesKeySlotContentType::None; + *out_keyslot = MakeVirtualAesKeySlot(i); + return ResultSuccess(); + } + } + + util::GetReference(g_aes_keyslot_available_event).Clear(); + return spl::ResultNoAvailableKeySlot(); + } + + Result DeallocateAesKeySlot(s32 keyslot) { + /* Only virtual keyslots can be freed. */ + R_UNLESS(IsVirtualAesKeySlot(keyslot), spl::ResultInvalidKeySlot()); + + /* Check that the virtual keyslot is allocated. */ + const s32 index = GetVirtualAesKeySlotIndex(keyslot); + R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot()); + + /* Clear the physical keyslot, if we're cached. */ + s32 phys_slot; + if (g_aes_keyslot_cache.Release(std::addressof(phys_slot), keyslot)) { + ClearPhysicalAesKeySlot(phys_slot); + } + + /* Clear the virtual keyslot. */ + g_aes_keyslot_contents[index].type = AesKeySlotContentType::None; + g_is_aes_keyslot_allocated[index] = false; + + util::GetReference(g_aes_keyslot_available_event).Signal(); + return ResultSuccess(); + } + + Result TestAesKeySlot(s32 *out_index, s32 keyslot) { + if (g_is_physical_keyslot_allowed && IsPhysicalAesKeySlot(keyslot)) { + *out_index = keyslot; + return ResultSuccess(); + } + + R_UNLESS(IsVirtualAesKeySlot(keyslot), spl::ResultInvalidKeySlot()); + + const s32 index = GetVirtualAesKeySlotIndex(keyslot); + R_UNLESS(g_is_aes_keyslot_allocated[index], spl::ResultInvalidKeySlot()); + + *out_index = index; + return ResultSuccess(); + } + + os::SystemEvent *GetAesKeySlotAvailableEvent() { + return util::GetPointer(g_aes_keyslot_available_event); + } + + /* RSA. */ + Result DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct DecryptDeviceUniqueDataLayout { + u8 data[Rsa2048BlockSize + DeviceUniqueDataMetaDataSize]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate size. */ + R_UNLESS(src_size >= DeviceUniqueDataMetaDataSize, spl::ResultInvalidBufferSize()); + R_UNLESS(src_size <= sizeof(DecryptDeviceUniqueDataLayout), spl::ResultInvalidBufferSize()); + + std::memcpy(layout.data, src, src_size); + + smc::Result smc_res; + size_t copy_size = 0; + if (g_is_modern_device_unique_data) { + copy_size = std::min(dst_size, src_size - DeviceUniqueDataMetaDataSize); + smc_res = smc::DecryptDeviceUniqueData(layout.data, src_size, access_key, key_source, static_cast(option)); + } else { + smc_res = smc::DecryptDeviceUniqueData(std::addressof(copy_size), layout.data, src_size, access_key, key_source, option); + copy_size = std::min(dst_size, copy_size); + } + + if (smc_res == smc::Result::Success) { + std::memcpy(dst, layout.data, copy_size); + } + + return smc::ConvertResult(smc_res); + } + + /* SSL */ + Result DecryptAndStoreSslClientCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreSslKey)); + } + + Result ModularExponentiateWithSslClientCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return ModularExponentiateWithStorageKey(out, out_size, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::Ssl); + } + + /* ES */ + Result LoadEsDeviceKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + if (g_is_modern_device_unique_data) { + return DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, option); + } else { + struct LoadEsDeviceKeyLayout { + u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10]; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate size. */ + R_UNLESS(src_size <= sizeof(layout.data), spl::ResultInvalidBufferSize()); + + std::memcpy(layout.data, src, src_size); + + return smc::ConvertResult(smc::LoadEsDeviceKey(layout.data, src_size, access_key, key_source, option)); + } + } + + Result PrepareEsTitleKey(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) { + return PrepareEsDeviceUniqueKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, smc::EsDeviceUniqueKeyType::TitleKey, generation); + } + + Result PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) { + return smc::ConvertResult(smc::PrepareCommonEsTitleKey(out_access_key, key_source, generation)); + } + + Result DecryptAndStoreDrmDeviceCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreDrmDeviceCertKey)); + } + + Result ModularExponentiateWithDrmDeviceCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return ModularExponentiateWithStorageKey(out, out_size, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::DrmDeviceCert); + } + + Result PrepareEsArchiveKey(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) { + return PrepareEsDeviceUniqueKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, smc::EsDeviceUniqueKeyType::ArchiveKey, generation); + } + + /* FS */ + Result DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + return DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, option); + } + + Result DecryptGcMessage(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) { + /* Validate sizes. */ + R_UNLESS(dst_size <= sizeof(g_work_buffer), spl::ResultInvalidBufferSize()); + R_UNLESS(label_digest_size == LabelDigestSizeMax, spl::ResultInvalidBufferSize()); + + /* Nintendo doesn't check this result code, but we will. */ + R_TRY(ModularExponentiateWithStorageKey(g_work_buffer, Rsa2048BlockSize, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::Gc)); + + const auto data_size = crypto::DecodeRsa2048OaepSha256(dst, dst_size, label_digest, label_digest_size, g_work_buffer, Rsa2048BlockSize); + R_UNLESS(data_size > 0, spl::ResultDecryptionFailed()); + + *out_size = static_cast(data_size); + return ResultSuccess(); + } + + Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) { + return smc::ConvertResult(smc::GenerateSpecificAesKey(out_key, key_source, generation, which)); + } + + Result LoadPreparedAesKey(s32 keyslot, const AccessKey &access_key) { + /* Ensure we can load into the slot. */ + const s32 phys_slot = GetPhysicalAesKeySlot(keyslot, false); + R_TRY(smc::ConvertResult(smc::LoadPreparedAesKey(phys_slot, access_key))); + + /* Update our contents. */ + const s32 index = GetVirtualAesKeySlotIndex(keyslot); + + g_aes_keyslot_contents[index].type = AesKeySlotContentType::PreparedKey; + g_aes_keyslot_contents[index].prepared_key.access_key = access_key; + + return ResultSuccess(); + } + + Result GetPackage2Hash(void *dst, const size_t size) { + u64 hash[4]; + R_UNLESS(size >= sizeof(hash), spl::ResultInvalidBufferSize()); + + const smc::Result smc_res = smc::GetConfig(hash, 4, ConfigItem::Package2Hash); + if (smc_res != smc::Result::Success) { + return smc::ConvertResult(smc_res); + } + + std::memcpy(dst, hash, sizeof(hash)); + return ResultSuccess(); + } + + /* Manu. */ + Result ReencryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) { + struct ReencryptDeviceUniqueDataLayout { + u8 data[DeviceUniqueDataMetaDataSize + 2 * Rsa2048BlockSize + 0x10]; + AccessKey access_key_dec; + KeySource source_dec; + AccessKey access_key_enc; + KeySource source_enc; + }; + auto &layout = *reinterpret_cast(g_work_buffer); + + /* Validate size. */ + R_UNLESS(src_size > DeviceUniqueDataMetaDataSize, spl::ResultInvalidBufferSize()); + R_UNLESS(src_size <= sizeof(layout.data), spl::ResultInvalidBufferSize()); + + std::memcpy(layout.data, src, src_size); + layout.access_key_dec = access_key_dec; + layout.source_dec = source_dec; + layout.access_key_enc = access_key_enc; + layout.source_enc = source_enc; + + const smc::Result smc_res = smc::ReencryptDeviceUniqueData(layout.data, src_size, layout.access_key_dec, layout.source_dec, layout.access_key_enc, layout.source_enc, option); + if (smc_res == smc::Result::Success) { + std::memcpy(dst, layout.data, std::min(dst_size, src_size)); + } + + return smc::ConvertResult(smc_res); + } + +} diff --git a/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp b/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp new file mode 100644 index 00000000..b615f02c --- /dev/null +++ b/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp @@ -0,0 +1,277 @@ +/* + * Copyright (c) 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::impl { + + constexpr inline int BitsPerByte = BITSIZEOF(u8); + + /* Nintendo implements CTR_DRBG for their csrng. We will do the same. */ + template + class CtrDrbg { + public: + static constexpr int KeyLen = KeySize * BitsPerByte; + static constexpr int OutLen = BlockCipher::BlockSize * BitsPerByte; + static constexpr int SeedLen = KeyLen + OutLen; + static constexpr int MaxNumberOfBitsPerRequest = (1 << 19); + static constexpr int ReseedInterval = 0x7FFFFFF0; + + static constexpr size_t OutSize = OutLen / BitsPerByte; + static constexpr size_t SeedSize = SeedLen / BitsPerByte; + static constexpr size_t RequestSizeMax = MaxNumberOfBitsPerRequest / BitsPerByte; + + static_assert(SeedSize % OutSize == 0); + private: + class Bcc { + private: + u8 *m_buffer; + const BlockCipher *m_cipher; + size_t m_offset; + public: + Bcc(u8 *buffer, const BlockCipher *cipher) : m_buffer(buffer), m_cipher(cipher), m_offset(0) { /* ... */ } + + void Process(const void *data, size_t size) { + const u8 *data_8 = static_cast(data); + size_t remaining = size; + + while (m_offset + remaining >= OutSize) { + const size_t xor_size = OutSize - m_offset; + + Xor(m_buffer + m_offset, data_8, xor_size); + m_cipher->EncryptBlock(m_buffer, OutSize, m_buffer, OutSize); + + data_8 += xor_size; + remaining -= xor_size; + + m_offset = 0; + } + + Xor(m_buffer + m_offset, data_8, remaining); + m_offset += remaining; + } + + void Flush() { + if (m_offset != 0) { + m_cipher->EncryptBlock(m_buffer, OutSize, m_buffer, OutSize); + m_offset = 0; + } + } + }; + private: + BlockCipher m_block_cipher; + u8 m_v[OutSize]; + u8 m_key[KeySize]; + u8 m_work1[SeedSize]; + u8 m_work2[SeedSize]; + int m_reseed_counter; + private: + static void Xor(void *dst, const void *src, size_t size) { + const u8 *src_u8 = static_cast(src); + u8 *dst_u8 = static_cast(dst); + + for (size_t i = 0; i < size; i++) { + dst_u8[i] ^= src_u8[i]; + } + } + + static void Increment(void *v) { + u8 *v_8 = static_cast(v); + + for (int i = OutSize - 1; i >= 0; --i) { + if ((++v_8[i]) != 0) { + break; + } + } + } + private: + void DeriveSeed(void *seed, const void *a, size_t a_size, const void *b, size_t b_size, const void *c, size_t c_size) { + /* Determine sizes. */ + const u32 in_size = a_size + b_size + c_size; + const u32 out_size = SeedSize; + + /* Create header/footer. */ + u32 header[2]; + util::StoreBigEndian(header + 0, in_size); + util::StoreBigEndian(header + 1, out_size); + const u8 footer = 0x80; + + /* Create seed as 000102... */ + u8 *seed_8 = static_cast(seed); + for (size_t i = 0; i < KeySize; ++i) { + seed_8[i] = i; + } + + /* Initialize block cipher. */ + m_block_cipher.Initialize(seed_8, KeySize); + + /* Perform derivation. */ + for (u32 block = 0; block < SeedSize / OutSize; ++block) { + /* Create the block index value. */ + u32 block_value; + util::StoreBigEndian(std::addressof(block_value), block); + + /* Get the target block. */ + u8 *target = seed_8 + block * OutSize; + std::memset(target, 0, OutSize); + + /* Create block processor. */ + Bcc bcc(target, std::addressof(m_block_cipher)); + + /* Process block value. */ + bcc.Process(std::addressof(block_value), sizeof(block_value)); + bcc.Flush(); + + /* Process header/data. */ + bcc.Process(header, sizeof(header)); + bcc.Process(a, a_size); + bcc.Process(b, b_size); + bcc.Process(c, c_size); + bcc.Process(footer, std::addressof(footer)); + bcc.Flush(); + } + + /* Initialize block cipher. */ + m_block_cipher.Initialize(seed_8, KeySize); + + /* Encrypt seed. */ + m_block_cipher.EncryptBlock(seed_8, OutSize, seed_8 + KeySize, OutSize); + for (size_t offset = 0; offset < SeedSize - OutSize; offset += OutSize) { + m_block_cipher.EncryptBlock(seed_8 + offset + OutSize, OutSize, seed_8 + offset, OutSize); + } + } + + void UpdateStates(void *key, void *v, const void *provided_data) { + /* Initialize block cipher. */ + m_block_cipher.Initialize(key, KeySize); + + /* Update work. */ + for (size_t offset = 0; offset < SeedSize; offset += OutSize) { + Increment(v); + m_block_cipher.EncryptBlock(std::addressof(m_work2[offset]), OutSize, v, OutSize); + } + + /* Xor work with provided data. */ + Xor(m_work2, provided_data, SeedSize); + + /* Copy to key/v. */ + std::memcpy(key, m_work2 + 0, KeySize); + std::memcpy(v, m_work2 + KeySize, OutSize); + } + public: + constexpr CtrDrbg() = default; + + void Initialize(const void *entropy, size_t entropy_size, const void *nonce, size_t nonce_size, const void *personalization, size_t personalization_size) { + /* Handle init. */ + if constexpr (UseDerivation) { + this->DeriveSeed(m_work1, entropy, entropy_size, nonce, nonce_size, personalization, personalization_size); + } else { + AMS_ASSERT(entropy_size == SeedSize); + AMS_ASSERT(nonce_size == 0); + AMS_ASSERT(personalization_size <= SeedSize); + AMS_UNUSED(entropy_size, nonce, nonce_size); + + std::memcpy(m_work1, entropy, SeedSize); + Xor(m_work1, personalization, personalization_size); + } + + /* Clear key/v. */ + std::memset(m_key, 0, sizeof(m_key)); + std::memset(m_v, 0, sizeof(m_v)); + + /* Set key/v. */ + this->UpdateStates(m_key, m_v, m_work1); + + /* Set reseed counter. */ + m_reseed_counter = 1; + } + + void Reseed(const void *entropy, size_t entropy_size, const void *addl, size_t addl_size) { + /* Handle init. */ + if constexpr (UseDerivation) { + this->DeriveSeed(m_work1, entropy, entropy_size, addl, addl_size, nullptr, 0); + } else { + AMS_ASSERT(entropy_size == SeedSize); + AMS_ASSERT(addl_size <= SeedSize); + AMS_UNUSED(entropy_size); + + std::memcpy(m_work1, entropy, SeedSize); + Xor(m_work1, addl, addl_size); + } + + /* Set key/v. */ + this->UpdateStates(m_key, m_v, m_work1); + + /* Set reseed counter. */ + m_reseed_counter = 1; + } + + bool Generate(void *out, size_t size, const void *addl, size_t addl_size) { + /* Check that the request is small enough. */ + if (size > RequestSizeMax) { + return false; + } + + /* Check if we need reseed. */ + if (m_reseed_counter > ReseedInterval) { + return false; + } + + /* Clear work buffer. */ + std::memset(m_work1, 0, sizeof(m_work1)); + + /* Process additional input, if we have any. */ + if (addl_size > 0) { + if constexpr (UseDerivation) { + this->DeriveSeed(m_work1, addl, addl_size, nullptr, 0, nullptr, 0); + } else { + AMS_ASSERT(addl_size <= SeedSize); + std::memcpy(m_work1, addl, addl_size); + } + + /* Set key/v. */ + this->UpdateStates(m_key, m_v, m_work1); + } + + /* Get buffer and aligned size. */ + u8 *out_8 = static_cast(out); + const size_t aligned_size = util::AlignDown(size, OutSize); + + /* Generate ctr bytes. */ + m_block_cipher.Initialize(m_key, KeySize); + for (size_t offset = 0; offset < aligned_size; offset += OutSize) { + Increment(m_v); + m_block_cipher.EncryptBlock(out_8 + offset, OutSize, m_v, OutSize); + } + + /* Handle any unaligned data. */ + if (size > aligned_size) { + u8 temp[OutSize]; + Increment(m_v); + m_block_cipher.EncryptBlock(temp, sizeof(temp), m_v, OutSize); + std::memcpy(out_8 + aligned_size, temp, size - aligned_size); + } + + /* Set key/v. */ + this->UpdateStates(m_key, m_v, m_work1); + + /* Increment reseed counter. */ + ++m_reseed_counter; + return true; + } + }; + +} diff --git a/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp b/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp new file mode 100644 index 00000000..606b77b0 --- /dev/null +++ b/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) 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::impl { + + class DeviceAddressMapper { + private: + dd::DeviceAddressSpaceType *m_das; + u64 m_process_address; + size_t m_size; + dd::DeviceVirtualAddress m_device_address; + public: + DeviceAddressMapper(dd::DeviceAddressSpaceType *das, u64 process_address, size_t size, dd::DeviceVirtualAddress device_address, dd::MemoryPermission permission) + : m_das(das), m_process_address(process_address), m_size(size), m_device_address(device_address) + { + R_ABORT_UNLESS(dd::MapDeviceAddressSpaceAligned(m_das, dd::GetCurrentProcessHandle(), m_process_address, m_size, m_device_address, permission)); + } + + ~DeviceAddressMapper() { + dd::UnmapDeviceAddressSpace(m_das, dd::GetCurrentProcessHandle(), m_process_address, m_size, m_device_address); + } + }; + +} diff --git a/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp b/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp new file mode 100644 index 00000000..640f9c45 --- /dev/null +++ b/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 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::impl { + + class AesKeySlotCacheEntry : public util::IntrusiveListBaseNode { + NON_COPYABLE(AesKeySlotCacheEntry); + NON_MOVEABLE(AesKeySlotCacheEntry); + private: + friend class AesKeySlotCache; + public: + static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; + private: + const s32 m_aes_keyslot_index; + s32 m_virtual_aes_keyslot; + public: + explicit AesKeySlotCacheEntry(s32 idx) : m_aes_keyslot_index(idx), m_virtual_aes_keyslot(-1) { /* ... */ } + + bool Contains(s32 virtual_slot) const { + return virtual_slot == m_virtual_aes_keyslot; + } + + s32 GetPhysicalAesKeySlotIndex() const { return m_aes_keyslot_index; } + + s32 GetVirtualAesKeySlotIndex() const { return m_virtual_aes_keyslot; } + + void SetVirtualAesKeySlot(s32 virtual_slot) { + m_virtual_aes_keyslot = virtual_slot; + } + + void ClearVirtualAesKeySlot() { + m_virtual_aes_keyslot = -1; + } + }; + + class AesKeySlotCache { + NON_COPYABLE(AesKeySlotCache); + NON_MOVEABLE(AesKeySlotCache); + private: + using AesKeySlotCacheEntryList = util::IntrusiveListBaseTraits::ListType; + private: + AesKeySlotCacheEntryList m_mru_list; + public: + constexpr AesKeySlotCache() : m_mru_list() { /* ... */ } + + s32 Allocate(s32 virtual_slot) { + return this->AllocateFromLru(virtual_slot); + } + + bool Find(s32 *out, s32 virtual_slot) { + for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalAesKeySlotIndex(); + + this->UpdateMru(it); + return true; + } + } + + return false; + } + + bool Release(s32 *out, s32 virtual_slot) { + for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { + if (it->Contains(virtual_slot)) { + *out = it->GetPhysicalAesKeySlotIndex(); + it->ClearVirtualAesKeySlot(); + + this->UpdateLru(it); + return true; + } + } + + return false; + } + + bool FindPhysical(s32 physical_slot) { + for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { + if (it->GetPhysicalAesKeySlotIndex() == physical_slot) { + this->UpdateMru(it); + + if (it->GetVirtualAesKeySlotIndex() == physical_slot) { + return true; + } else { + it->SetVirtualAesKeySlot(physical_slot); + return false; + } + } + } + AMS_ABORT(); + } + + void AddEntry(AesKeySlotCacheEntry *entry) { + m_mru_list.push_front(*entry); + } + private: + s32 AllocateFromLru(s32 virtual_slot) { + AMS_ASSERT(!m_mru_list.empty()); + + auto it = m_mru_list.rbegin(); + it->SetVirtualAesKeySlot(virtual_slot); + + auto *entry = std::addressof(*it); + m_mru_list.pop_back(); + m_mru_list.push_front(*entry); + + return entry->GetPhysicalAesKeySlotIndex(); + } + + void UpdateMru(AesKeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + m_mru_list.erase(it); + m_mru_list.push_front(*entry); + } + + void UpdateLru(AesKeySlotCacheEntryList::iterator it) { + auto *entry = std::addressof(*it); + m_mru_list.erase(it); + m_mru_list.push_back(*entry); + } + }; + +} diff --git a/libstratosphere/source/spl/smc/spl_smc.cpp b/libstratosphere/source/spl/smc/spl_secure_monitor_api.cpp similarity index 96% rename from libstratosphere/source/spl/smc/spl_smc.cpp rename to libstratosphere/source/spl/smc/spl_secure_monitor_api.cpp index 3233074a..439bb15e 100644 --- a/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/libstratosphere/source/spl/smc/spl_secure_monitor_api.cpp @@ -17,25 +17,27 @@ namespace ams::spl::smc { - Result SetConfig(spl::ConfigItem which, const void *address, const u64 *value, size_t num_qwords) { + Result SetConfig(AsyncOperationKey *out_op, spl::ConfigItem key, const u64 *value, size_t num_qwords, const void *sign) { svc::SecureMonitorArguments args; args.r[0] = static_cast(FunctionId::SetConfig); - args.r[1] = static_cast(which); - args.r[2] = reinterpret_cast(address); + args.r[1] = static_cast(key); + args.r[2] = reinterpret_cast(sign); + for (size_t i = 0; i < std::min(static_cast(4), num_qwords); i++) { args.r[3 + i] = value[i]; } svc::CallSecureMonitor(std::addressof(args)); + out_op->value = args.r[1]; return static_cast(args.r[0]); } - Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem which) { + Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem key) { svc::SecureMonitorArguments args; args.r[0] = static_cast(FunctionId::GetConfig); - args.r[1] = static_cast(which); + args.r[1] = static_cast(key); svc::CallSecureMonitor(std::addressof(args)); for (size_t i = 0; i < std::min(static_cast(4), num_qwords); i++) { @@ -124,7 +126,7 @@ namespace ams::spl::smc { return static_cast(args.r[0]); } - Result ComputeAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { + Result ComputeAes(AsyncOperationKey *out_op, u32 dst_addr, u32 mode, const IvCtr &iv_ctr, u32 src_addr, size_t size) { svc::SecureMonitorArguments args; args.r[0] = static_cast(FunctionId::ComputeAes); diff --git a/libstratosphere/source/spl/spl_api.cpp b/libstratosphere/source/spl/spl_api.cpp index 6bfff1bd..9e5ec476 100644 --- a/libstratosphere/source/spl/spl_api.cpp +++ b/libstratosphere/source/spl/spl_api.cpp @@ -29,9 +29,9 @@ namespace ams::spl { Manu }; - os::SdkMutex g_mutex; - s32 g_initialize_count = 0; - InitializeMode g_initialize_mode = InitializeMode::None; + constinit os::SdkMutex g_mutex; + constinit s32 g_initialize_count = 0; + constinit InitializeMode g_initialize_mode = InitializeMode::None; Result AllocateAesKeySlotImpl(s32 *out) { return serviceDispatchOut(splCryptoGetServiceSession(), 21, *out); @@ -63,7 +63,7 @@ namespace ams::spl { auto is_event_initialized = false; while (true) { R_TRY_CATCH(static_cast<::ams::Result>(f())) { - R_CATCH(spl::ResultOutOfKeySlots) { + R_CATCH(spl::ResultNoAvailableKeySlot) { if (!is_event_initialized) { GetAesKeySlotAvailableEvent(std::addressof(event)); is_event_initialized = true; diff --git a/libvapours/include/vapours/results/spl_results.hpp b/libvapours/include/vapours/results/spl_results.hpp index 919c712f..c15e5cd3 100644 --- a/libvapours/include/vapours/results/spl_results.hpp +++ b/libvapours/include/vapours/results/spl_results.hpp @@ -22,7 +22,7 @@ namespace ams::spl { R_DEFINE_NAMESPACE_RESULT_MODULE(26); R_DEFINE_ERROR_RANGE(SecureMonitorError, 0, 99); - R_DEFINE_ERROR_RESULT(SecureMonitorNotImplemented, 1); + R_DEFINE_ERROR_RESULT(SecureMonitorNotSupported, 1); R_DEFINE_ERROR_RESULT(SecureMonitorInvalidArgument, 2); R_DEFINE_ERROR_RESULT(SecureMonitorBusy, 3); R_DEFINE_ERROR_RESULT(SecureMonitorNoAsyncOperation, 4); @@ -30,14 +30,14 @@ namespace ams::spl { R_DEFINE_ERROR_RESULT(SecureMonitorNotPermitted, 6); R_DEFINE_ERROR_RESULT(SecureMonitorNotInitialized, 7); - R_DEFINE_ERROR_RESULT(InvalidSize, 100); - R_DEFINE_ERROR_RESULT(UnknownSecureMonitorError, 101); - R_DEFINE_ERROR_RESULT(DecryptionFailed, 102); - - R_DEFINE_ERROR_RESULT(OutOfKeySlots, 104); - R_DEFINE_ERROR_RESULT(InvalidKeySlot, 105); - R_DEFINE_ERROR_RESULT(BootReasonAlreadySet, 106); - R_DEFINE_ERROR_RESULT(BootReasonNotSet, 107); - R_DEFINE_ERROR_RESULT(InvalidArgument, 108); + R_DEFINE_ERROR_RESULT(InvalidBufferSize, 100); + R_DEFINE_ERROR_RESULT(UnexpectedSecureMonitorResult, 101); + R_DEFINE_ERROR_RESULT(DecryptionFailed, 102); + R_DEFINE_ERROR_RESULT(InvalidDeviceUniqueDataType, 103); + R_DEFINE_ERROR_RESULT(NoAvailableKeySlot, 104); + R_DEFINE_ERROR_RESULT(InvalidKeySlot, 105); + R_DEFINE_ERROR_RESULT(BootReasonAlreadyInitialized, 106); + R_DEFINE_ERROR_RESULT(BootReasonNotInitialized, 107); + R_DEFINE_ERROR_RESULT(InvalidArgument, 108); }