From d8a36e39f28575460f3d69ecf689b7c6768ea281 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 --- .../program/source/smc/secmon_smc_aes.cpp | 6 +- .../program/source/smc/secmon_smc_common.hpp | 2 +- .../program/source/smc/secmon_smc_info.cpp | 4 +- .../source/smc/secmon_smc_memory_access.cpp | 2 +- .../include/stratosphere/spl.hpp | 3 +- .../stratosphere/spl/impl}/spl_api_impl.hpp | 39 +- ...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 | 64 +- ...spl_smc.cpp => spl_secure_monitor_api.cpp} | 14 +- .../libstratosphere/source/spl/spl_api.cpp | 8 +- .../include/vapours/results/spl_results.hpp | 20 +- stratosphere/spl/source/spl_api_impl.cpp | 944 ------------------ .../spl/source/spl_crypto_service.cpp | 64 -- .../spl/source/spl_crypto_service.hpp | 53 +- stratosphere/spl/source/spl_ctr_drbg.cpp | 81 -- stratosphere/spl/source/spl_ctr_drbg.hpp | 60 -- .../spl/source/spl_deprecated_service.cpp | 136 --- .../spl/source/spl_deprecated_service.hpp | 145 ++- .../source/spl_device_unique_data_service.cpp | 30 - .../source/spl_device_unique_data_service.hpp | 12 +- stratosphere/spl/source/spl_es_service.cpp | 54 - stratosphere/spl/source/spl_es_service.hpp | 42 +- stratosphere/spl/source/spl_fs_service.cpp | 46 - stratosphere/spl/source/spl_fs_service.hpp | 32 +- .../spl/source/spl_general_service.cpp | 50 - .../spl/source/spl_general_service.hpp | 39 +- stratosphere/spl/source/spl_main.cpp | 29 +- stratosphere/spl/source/spl_manu_service.cpp | 26 - stratosphere/spl/source/spl_manu_service.hpp | 7 +- .../spl/source/spl_random_service.cpp | 26 - .../spl/source/spl_random_service.hpp | 9 +- .../spl/source/spl_secure_monitor_manager.cpp | 190 ++++ .../spl/source/spl_secure_monitor_manager.hpp | 66 ++ stratosphere/spl/source/spl_ssl_service.cpp | 30 - stratosphere/spl/source/spl_ssl_service.hpp | 12 +- 40 files changed, 1898 insertions(+), 1732 deletions(-) rename {stratosphere/spl/source => libraries/libstratosphere/include/stratosphere/spl/impl}/spl_api_impl.hpp (78%) rename libraries/libstratosphere/include/stratosphere/spl/smc/{spl_smc.hpp => spl_secure_monitor_api.hpp} (81%) create mode 100644 libraries/libstratosphere/source/spl/impl/spl_api_impl.cpp create mode 100644 libraries/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp create mode 100644 libraries/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp rename {stratosphere/spl/source => libraries/libstratosphere/source/spl/impl}/spl_key_slot_cache.hpp (60%) rename libraries/libstratosphere/source/spl/smc/{spl_smc.cpp => spl_secure_monitor_api.cpp} (96%) delete mode 100644 stratosphere/spl/source/spl_api_impl.cpp delete mode 100644 stratosphere/spl/source/spl_crypto_service.cpp delete mode 100644 stratosphere/spl/source/spl_ctr_drbg.cpp delete mode 100644 stratosphere/spl/source/spl_ctr_drbg.hpp delete mode 100644 stratosphere/spl/source/spl_deprecated_service.cpp delete mode 100644 stratosphere/spl/source/spl_device_unique_data_service.cpp delete mode 100644 stratosphere/spl/source/spl_es_service.cpp delete mode 100644 stratosphere/spl/source/spl_fs_service.cpp delete mode 100644 stratosphere/spl/source/spl_general_service.cpp delete mode 100644 stratosphere/spl/source/spl_manu_service.cpp delete mode 100644 stratosphere/spl/source/spl_random_service.cpp create mode 100644 stratosphere/spl/source/spl_secure_monitor_manager.cpp create mode 100644 stratosphere/spl/source/spl_secure_monitor_manager.hpp delete mode 100644 stratosphere/spl/source/spl_ssl_service.cpp diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp index 79ab6f002..1d5f51ded 100644 --- a/exosphere/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere/program/source/smc/secmon_smc_aes.cpp @@ -417,7 +417,7 @@ namespace ams::secmon::smc { case CipherMode_CbcDecryption: se::DecryptAes128CbcAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break; case CipherMode_Ctr: se::ComputeAes128CtrAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break; case CipherMode_Cmac: - return SmcResult::NotImplemented; + return SmcResult::NotSupported; default: return SmcResult::InvalidArgument; } @@ -765,8 +765,8 @@ namespace ams::secmon::smc { const auto which = static_cast(args.r[1]); /* Validate arguments/conditions. */ - SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented); - SMC_R_UNLESS(which < SecureData_Count, NotImplemented); + SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotSupported); + SMC_R_UNLESS(which < SecureData_Count, NotSupported); /* Use a temporary buffer. */ u8 secure_data[AesKeySize]; diff --git a/exosphere/program/source/smc/secmon_smc_common.hpp b/exosphere/program/source/smc/secmon_smc_common.hpp index 944b6d101..fcf6b4c65 100644 --- a/exosphere/program/source/smc/secmon_smc_common.hpp +++ b/exosphere/program/source/smc/secmon_smc_common.hpp @@ -20,7 +20,7 @@ namespace ams::secmon::smc { enum class SmcResult : u32 { Success = 0, - NotImplemented = 1, + NotSupported = 1, InvalidArgument = 2, Busy = 3, NoAsyncOperation = 4, diff --git a/exosphere/program/source/smc/secmon_smc_info.cpp b/exosphere/program/source/smc/secmon_smc_info.cpp index 9b889b2a9..0808dce60 100644 --- a/exosphere/program/source/smc/secmon_smc_info.cpp +++ b/exosphere/program/source/smc/secmon_smc_info.cpp @@ -345,7 +345,7 @@ namespace ams::secmon::smc { PerformUserShutDown(); } } else /* if (soc_type == fuse::SocType_Mariko) */ { - return SmcResult::NotImplemented; + return SmcResult::NotSupported; } break; case ConfigItem::ExospherePayloadAddress: @@ -389,7 +389,7 @@ namespace ams::secmon::smc { /* Validate arguments. */ /* NOTE: In the future, configuration for non-NAND storage may be implemented. */ - SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotImplemented); + SMC_R_UNLESS(mmc == EmummcMmc_Nand, NotSupported); SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument); /* Get the emummc config. */ diff --git a/exosphere/program/source/smc/secmon_smc_memory_access.cpp b/exosphere/program/source/smc/secmon_smc_memory_access.cpp index e4bddfc56..50b8ae8fc 100644 --- a/exosphere/program/source/smc/secmon_smc_memory_access.cpp +++ b/exosphere/program/source/smc/secmon_smc_memory_access.cpp @@ -70,7 +70,7 @@ namespace ams::secmon::smc { SmcResult SmcWriteAddress(SmcArguments &args) { /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ AMS_UNUSED(args); - return SmcResult::NotImplemented; + return SmcResult::NotSupported; } } diff --git a/libraries/libstratosphere/include/stratosphere/spl.hpp b/libraries/libstratosphere/include/stratosphere/spl.hpp index 63cf88f3a..0afcba70c 100644 --- a/libraries/libstratosphere/include/stratosphere/spl.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl.hpp @@ -18,7 +18,8 @@ #include #include -#include +#include +#include #include #include #include diff --git a/stratosphere/spl/source/spl_api_impl.hpp b/libraries/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp similarity index 78% rename from stratosphere/spl/source/spl_api_impl.hpp rename to libraries/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp index a3fdba5c1..b64d84140 100644 --- a/stratosphere/spl/source/spl_api_impl.hpp +++ b/libraries/libstratosphere/include/stratosphere/spl/impl/spl_api_impl.hpp @@ -13,32 +13,47 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #pragma once -#include +#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 which); + 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 which, u64 value); + 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 void *owner, const AccessKey &access_key, const KeySource &key_source); + 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 *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 DeallocateAesKeySlot(s32 keyslot, const void *owner); + 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); @@ -54,20 +69,16 @@ namespace ams::spl::impl { 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 void *owner, const AccessKey &access_key); + 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 void *owner, const AccessKey &access_key); + 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); - /* Helper. */ - Result DeallocateAllAesKeySlots(const void *owner); - os::NativeHandle GetAesKeySlotAvailableEventHandle(); - } diff --git a/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp b/libraries/libstratosphere/include/stratosphere/spl/smc/spl_secure_monitor_api.hpp similarity index 81% rename from libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp rename to libraries/libstratosphere/include/stratosphere/spl/smc/spl_secure_monitor_api.hpp index 969e0694c..e2ff504c3 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/smc/spl_smc.hpp +++ b/libraries/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/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp b/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp index 2fbb1435b..5b1b459b8 100644 --- a/libraries/libstratosphere/include/stratosphere/spl/spl_types.hpp +++ b/libraries/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/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp b/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp index 9df6fdfe2..5db63628a 100644 --- a/libraries/libstratosphere/source/ams/ams_exosphere_api.cpp +++ b/libraries/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/libraries/libstratosphere/source/spl/impl/spl_api_impl.cpp b/libraries/libstratosphere/source/spl/impl/spl_api_impl.cpp new file mode 100644 index 000000000..a2a1681c9 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp b/libraries/libstratosphere/source/spl/impl/spl_ctr_drbg.hpp new file mode 100644 index 000000000..b615f02c1 --- /dev/null +++ b/libraries/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/libraries/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp b/libraries/libstratosphere/source/spl/impl/spl_device_address_mapper.hpp new file mode 100644 index 000000000..606b77b03 --- /dev/null +++ b/libraries/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/stratosphere/spl/source/spl_key_slot_cache.hpp b/libraries/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp similarity index 60% rename from stratosphere/spl/source/spl_key_slot_cache.hpp rename to libraries/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp index 9de0c8071..640f9c454 100644 --- a/stratosphere/spl/source/spl_key_slot_cache.hpp +++ b/libraries/libstratosphere/source/spl/impl/spl_key_slot_cache.hpp @@ -16,47 +16,47 @@ #pragma once #include -namespace ams::spl { +namespace ams::spl::impl { - class KeySlotCacheEntry : public util::IntrusiveListBaseNode { - NON_COPYABLE(KeySlotCacheEntry); - NON_MOVEABLE(KeySlotCacheEntry); + class AesKeySlotCacheEntry : public util::IntrusiveListBaseNode { + NON_COPYABLE(AesKeySlotCacheEntry); + NON_MOVEABLE(AesKeySlotCacheEntry); private: - friend class KeySlotCache; + friend class AesKeySlotCache; public: static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; private: - const s32 m_slot_index; - s32 m_virtual_slot; + const s32 m_aes_keyslot_index; + s32 m_virtual_aes_keyslot; public: - explicit KeySlotCacheEntry(s32 idx) : m_slot_index(idx), m_virtual_slot(-1) { /* ... */ } + 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_slot; + return virtual_slot == m_virtual_aes_keyslot; } - s32 GetPhysicalKeySlotIndex() const { return m_slot_index; } + s32 GetPhysicalAesKeySlotIndex() const { return m_aes_keyslot_index; } - s32 GetVirtualKeySlotIndex() const { return m_virtual_slot; } + s32 GetVirtualAesKeySlotIndex() const { return m_virtual_aes_keyslot; } - void SetVirtualSlot(s32 virtual_slot) { - m_virtual_slot = virtual_slot; + void SetVirtualAesKeySlot(s32 virtual_slot) { + m_virtual_aes_keyslot = virtual_slot; } - void ClearVirtualSlot() { - m_virtual_slot = -1; + void ClearVirtualAesKeySlot() { + m_virtual_aes_keyslot = -1; } }; - class KeySlotCache { - NON_COPYABLE(KeySlotCache); - NON_MOVEABLE(KeySlotCache); + class AesKeySlotCache { + NON_COPYABLE(AesKeySlotCache); + NON_MOVEABLE(AesKeySlotCache); private: - using KeySlotCacheEntryList = util::IntrusiveListBaseTraits::ListType; + using AesKeySlotCacheEntryList = util::IntrusiveListBaseTraits::ListType; private: - KeySlotCacheEntryList m_mru_list; + AesKeySlotCacheEntryList m_mru_list; public: - constexpr KeySlotCache() : m_mru_list() { /* ... */ } + constexpr AesKeySlotCache() : m_mru_list() { /* ... */ } s32 Allocate(s32 virtual_slot) { return this->AllocateFromLru(virtual_slot); @@ -65,7 +65,7 @@ namespace ams::spl { 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->GetPhysicalKeySlotIndex(); + *out = it->GetPhysicalAesKeySlotIndex(); this->UpdateMru(it); return true; @@ -78,8 +78,8 @@ namespace ams::spl { 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->GetPhysicalKeySlotIndex(); - it->ClearVirtualSlot(); + *out = it->GetPhysicalAesKeySlotIndex(); + it->ClearVirtualAesKeySlot(); this->UpdateLru(it); return true; @@ -91,13 +91,13 @@ namespace ams::spl { bool FindPhysical(s32 physical_slot) { for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { - if (it->GetPhysicalKeySlotIndex() == physical_slot) { + if (it->GetPhysicalAesKeySlotIndex() == physical_slot) { this->UpdateMru(it); - if (it->GetVirtualKeySlotIndex() == physical_slot) { + if (it->GetVirtualAesKeySlotIndex() == physical_slot) { return true; } else { - it->SetVirtualSlot(physical_slot); + it->SetVirtualAesKeySlot(physical_slot); return false; } } @@ -105,7 +105,7 @@ namespace ams::spl { AMS_ABORT(); } - void AddEntry(KeySlotCacheEntry *entry) { + void AddEntry(AesKeySlotCacheEntry *entry) { m_mru_list.push_front(*entry); } private: @@ -113,22 +113,22 @@ namespace ams::spl { AMS_ASSERT(!m_mru_list.empty()); auto it = m_mru_list.rbegin(); - it->SetVirtualSlot(virtual_slot); + it->SetVirtualAesKeySlot(virtual_slot); auto *entry = std::addressof(*it); m_mru_list.pop_back(); m_mru_list.push_front(*entry); - return entry->GetPhysicalKeySlotIndex(); + return entry->GetPhysicalAesKeySlotIndex(); } - void UpdateMru(KeySlotCacheEntryList::iterator it) { + void UpdateMru(AesKeySlotCacheEntryList::iterator it) { auto *entry = std::addressof(*it); m_mru_list.erase(it); m_mru_list.push_front(*entry); } - void UpdateLru(KeySlotCacheEntryList::iterator it) { + void UpdateLru(AesKeySlotCacheEntryList::iterator it) { auto *entry = std::addressof(*it); m_mru_list.erase(it); m_mru_list.push_back(*entry); diff --git a/libraries/libstratosphere/source/spl/smc/spl_smc.cpp b/libraries/libstratosphere/source/spl/smc/spl_secure_monitor_api.cpp similarity index 96% rename from libraries/libstratosphere/source/spl/smc/spl_smc.cpp rename to libraries/libstratosphere/source/spl/smc/spl_secure_monitor_api.cpp index 3233074a6..439bb15eb 100644 --- a/libraries/libstratosphere/source/spl/smc/spl_smc.cpp +++ b/libraries/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/libraries/libstratosphere/source/spl/spl_api.cpp b/libraries/libstratosphere/source/spl/spl_api.cpp index 6bfff1bdb..9e5ec4769 100644 --- a/libraries/libstratosphere/source/spl/spl_api.cpp +++ b/libraries/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/libraries/libvapours/include/vapours/results/spl_results.hpp b/libraries/libvapours/include/vapours/results/spl_results.hpp index 919c712f1..c15e5cd3c 100644 --- a/libraries/libvapours/include/vapours/results/spl_results.hpp +++ b/libraries/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); } diff --git a/stratosphere/spl/source/spl_api_impl.cpp b/stratosphere/spl/source/spl_api_impl.cpp deleted file mode 100644 index c7892db4e..000000000 --- a/stratosphere/spl/source/spl_api_impl.cpp +++ /dev/null @@ -1,944 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_ctr_drbg.hpp" -#include "spl_key_slot_cache.hpp" - -namespace ams::spl::impl { - - namespace { - - /* Convenient defines. */ - constexpr size_t DeviceAddressSpaceAlign = 0x400000; - constexpr u32 WorkBufferMapBase = 0x80000000u; - constexpr u32 ComputeAesInMapBase = 0x90000000u; - constexpr u32 ComputeAesOutMapBase = 0xC0000000u; - constexpr size_t ComputeAesSizeMax = static_cast(ComputeAesOutMapBase - ComputeAesInMapBase); - - constexpr size_t RsaPrivateKeySize = 0x100; - constexpr size_t DeviceUniqueDataMetaSize = 0x30; - constexpr size_t LabelDigestSizeMax = 0x20; - - constexpr size_t WorkBufferSizeMax = 0x800; - - constexpr s32 MaxPhysicalAesKeySlots = 6; - constexpr s32 MaxPhysicalAesKeySlotsDeprecated = 4; - - constexpr s32 MaxVirtualAesKeySlots = 9; - - /* KeySlot management. */ - constinit KeySlotCache g_keyslot_cache; - constinit util::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, - PreparedKey = 2, - }; - - struct KeySlotContents { - KeySlotContentType type; - union { - struct { - AccessKey access_key; - KeySource key_source; - } aes_key; - struct { - AccessKey access_key; - } prepared_key; - }; - }; - - constinit const void *g_keyslot_owners[MaxVirtualAesKeySlots]; - constinit KeySlotContents g_keyslot_contents[MaxVirtualAesKeySlots]; - constinit 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::PreparedKey: - R_ABORT_UNLESS(smc::ConvertResult(smc::LoadPreparedAesKey(phys_slot, contents->prepared_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 LoadVirtualPreparedAesKey(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::LoadPreparedAesKey(phys_slot, access_key))); - - /* Update our contents. */ - const s32 index = GetVirtualKeySlotIndex(keyslot); - - g_keyslot_contents[index].type = KeySlotContentType::PreparedKey; - g_keyslot_contents[index].prepared_key.access_key = access_key; - - return ResultSuccess(); - } - - /* Type definitions. */ - class ScopedAesKeySlot { - private: - s32 m_slot; - bool m_has_slot; - public: - ScopedAesKeySlot() : m_slot(-1), m_has_slot(false) { - /* ... */ - } - ~ScopedAesKeySlot() { - if (m_has_slot) { - DeallocateAesKeySlot(m_slot, this); - } - } - - u32 GetKeySlot() const { - return m_slot; - } - - Result Allocate() { - R_TRY(AllocateAesKeySlot(std::addressof(m_slot), this)); - m_has_slot = true; - return ResultSuccess(); - } - }; - - struct SeLinkedListEntry { - u32 num_entries; - u32 address; - u32 size; - }; - - struct SeCryptContext { - SeLinkedListEntry in; - SeLinkedListEntry out; - }; - - class DeviceAddressSpaceMapHelper { - private: - os::NativeHandle m_handle; - u64 m_dst_addr; - u64 m_src_addr; - size_t m_size; - svc::MemoryPermission m_perm; - public: - DeviceAddressSpaceMapHelper(os::NativeHandle h, u64 dst, u64 src, size_t sz, svc::MemoryPermission p) : m_handle(h), m_dst_addr(dst), m_src_addr(src), m_size(sz), m_perm(p) { - R_ABORT_UNLESS(svc::MapDeviceAddressSpaceAligned(m_handle, dd::GetCurrentProcessHandle(), m_src_addr, m_size, m_dst_addr, m_perm)); - } - ~DeviceAddressSpaceMapHelper() { - R_ABORT_UNLESS(svc::UnmapDeviceAddressSpace(m_handle, dd::GetCurrentProcessHandle(), m_src_addr, m_size, m_dst_addr)); - } - }; - - /* Global variables. */ - constinit CtrDrbg g_drbg; - constinit os::InterruptEventType g_se_event; - constinit os::SystemEventType g_se_keyslot_available_event; - - constinit os::NativeHandle g_se_das_hnd = os::InvalidNativeHandle; - constinit u32 g_se_mapped_work_buffer_addr; - alignas(os::MemoryPageSize) constinit u8 g_work_buffer[2 * WorkBufferSizeMax]; - - constinit os::SdkMutex g_async_op_lock; - - constinit BootReasonValue g_boot_reason; - constinit bool g_boot_reason_set; - - /* Boot Reason accessors. */ - BootReasonValue GetBootReason() { - return g_boot_reason; - } - - bool IsBootReasonSet() { - return g_boot_reason_set; - } - - /* Initialization functionality. */ - void InitializeCtrDrbg() { - u8 seed[CtrDrbg::SeedSize]; - AMS_ABORT_UNLESS(smc::GenerateRandomBytes(seed, sizeof(seed)) == smc::Result::Success); - - g_drbg.Initialize(seed); - } - - void InitializeSeEvents() { - u64 irq_num; - AMS_ABORT_UNLESS(smc::GetConfig(std::addressof(irq_num), 1, ConfigItem::SecurityEngineInterruptNumber) == smc::Result::Success); - os::InitializeInterruptEvent(std::addressof(g_se_event), irq_num, os::EventClearMode_AutoClear); - - R_ABORT_UNLESS(os::CreateSystemEvent(std::addressof(g_se_keyslot_available_event), os::EventClearMode_AutoClear, true)); - os::SignalSystemEvent(std::addressof(g_se_keyslot_available_event)); - } - - void InitializeDeviceAddressSpace() { - - /* Create Address Space. */ - R_ABORT_UNLESS(svc::CreateDeviceAddressSpace(std::addressof(g_se_das_hnd), 0, (1ul << 32))); - - /* Attach it to the SE. */ - R_ABORT_UNLESS(svc::AttachDeviceAddressSpace(svc::DeviceName_Se, g_se_das_hnd)); - - const u64 work_buffer_addr = reinterpret_cast(g_work_buffer); - g_se_mapped_work_buffer_addr = WorkBufferMapBase + (work_buffer_addr % DeviceAddressSpaceAlign); - - /* Map the work buffer for the SE. */ - R_ABORT_UNLESS(svc::MapDeviceAddressSpaceAligned(g_se_das_hnd, dd::GetCurrentProcessHandle(), work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, svc::MemoryPermission_ReadWrite)); - } - - /* Internal RNG functionality. */ - Result GenerateRandomBytesInternal(void *out, size_t size) { - if (!g_drbg.GenerateRandomBytes(out, size)) { - /* We need to reseed. */ - { - u8 seed[CtrDrbg::SeedSize]; - - smc::Result res = smc::GenerateRandomBytes(seed, sizeof(seed)); - if (res != smc::Result::Success) { - return smc::ConvertResult(res); - } - - g_drbg.Reseed(seed); - g_drbg.GenerateRandomBytes(out, size); - } - } - - return ResultSuccess(); - } - - /* Internal async implementation functionality. */ - void WaitSeOperationComplete() { - os::WaitInterruptEvent(std::addressof(g_se_event)); - } - - smc::Result WaitCheckStatus(smc::AsyncOperationKey op_key) { - WaitSeOperationComplete(); - - smc::Result op_res; - smc::Result res = smc::GetResult(std::addressof(op_res), op_key); - if (res != smc::Result::Success) { - return res; - } - - return op_res; - } - - smc::Result WaitGetResult(void *out_buf, size_t out_buf_size, smc::AsyncOperationKey op_key) { - WaitSeOperationComplete(); - - smc::Result op_res; - smc::Result res = smc::GetResultData(std::addressof(op_res), out_buf, out_buf_size, op_key); - if (res != smc::Result::Success) { - return res; - } - - return op_res; - } - - /* Internal KeySlot utility. */ - 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(s32 keyslot, void *dst, const void *src) { - struct DecryptAesBlockLayout { - SeCryptContext crypt_ctx; - u8 in_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); - u8 out_block[AES_BLOCK_SIZE] __attribute__((aligned(AES_BLOCK_SIZE))); - }; - DecryptAesBlockLayout *layout = reinterpret_cast(g_work_buffer); - - layout->crypt_ctx.in.num_entries = 0; - layout->crypt_ctx.in.address = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, in_block); - layout->crypt_ctx.in.size = sizeof(layout->in_block); - layout->crypt_ctx.out.num_entries = 0; - layout->crypt_ctx.out.address = g_se_mapped_work_buffer_addr + offsetof(DecryptAesBlockLayout, out_block); - layout->crypt_ctx.out.size = sizeof(layout->out_block); - - std::memcpy(layout->in_block, src, sizeof(layout->in_block)); - - os::FlushDataCache(layout, sizeof(*layout)); - { - std::scoped_lock lk(g_async_op_lock); - smc::AsyncOperationKey op_key; - const IvCtr iv_ctr = {}; - const u32 mode = smc::GetComputeAesMode(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); - - smc::Result res = smc::ComputeAes(std::addressof(op_key), mode, iv_ctr, dst_ll_addr, src_ll_addr, sizeof(layout->in_block)); - if (res != smc::Result::Success) { - return res; - } - - if ((res = WaitCheckStatus(op_key)) != smc::Result::Success) { - return res; - } - } - os::FlushDataCache(layout, sizeof(*layout)); - - std::memcpy(dst, layout->out_block, sizeof(layout->out_block)); - return smc::Result::Success; - } - - /* Implementation wrappers for API commands. */ - Result DecryptAndStoreDeviceUniqueKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { - struct DecryptAndStoreDeviceUniqueKeyLayout { - u8 data[DeviceUniqueDataMetaSize + 2 * RsaPrivateKeySize + 0x10]; - }; - DecryptAndStoreDeviceUniqueKeyLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate size. */ - R_UNLESS(src_size <= sizeof(DecryptAndStoreDeviceUniqueKeyLayout), spl::ResultInvalidSize()); - std::memcpy(layout, src, src_size); - - os::FlushDataCache(layout, sizeof(*layout)); - smc::Result smc_res; - if (hos::GetVersion() >= hos::Version_5_0_0) { - smc_res = smc::DecryptDeviceUniqueData(layout->data, src_size, access_key, key_source, static_cast(option)); - } else { - smc_res = smc::DecryptAndStoreGcKey(layout->data, src_size, access_key, key_source, option); - } - - return smc::ConvertResult(smc_res); - } - - 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[0x100]; - u8 mod[0x100]; - }; - ModularExponentiateWithStorageKeyLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate sizes. */ - R_UNLESS(base_size <= sizeof(layout->base), spl::ResultInvalidSize()); - R_UNLESS(mod_size <= sizeof(layout->mod), spl::ResultInvalidSize()); - R_UNLESS(out_size <= WorkBufferSizeMax, spl::ResultInvalidSize()); - - /* 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, 0, sizeof(*layout)); - std::memcpy(layout->base + base_ofs, base, base_size); - std::memcpy(layout->mod + mod_ofs, mod, mod_size); - - /* Do exp mod operation. */ - os::FlushDataCache(layout, sizeof(*layout)); - { - std::scoped_lock lk(g_async_op_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); - } - - if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != smc::Result::Success) { - return smc::ConvertResult(res); - } - } - os::FlushDataCache(g_work_buffer, sizeof(out_size)); - - 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, u32 generation, smc::EsCommonKeyType type) { - struct PrepareEsDeviceUniqueKeyLayout { - u8 base[0x100]; - u8 mod[0x100]; - }; - PrepareEsDeviceUniqueKeyLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate sizes. */ - R_UNLESS(base_size <= sizeof(layout->base), spl::ResultInvalidSize()); - R_UNLESS(mod_size <= sizeof(layout->mod), spl::ResultInvalidSize()); - R_UNLESS(label_digest_size <= LabelDigestSizeMax, spl::ResultInvalidSize()); - - /* 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, 0, sizeof(*layout)); - std::memcpy(layout->base + base_ofs, base, base_size); - std::memcpy(layout->mod + mod_ofs, mod, mod_size); - - /* Do exp mod operation. */ - os::FlushDataCache(layout, sizeof(*layout)); - { - std::scoped_lock lk(g_async_op_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); - } - - if ((res = WaitGetResult(g_work_buffer, sizeof(*out_access_key), op_key)) != smc::Result::Success) { - return smc::ConvertResult(res); - } - } - os::FlushDataCache(g_work_buffer, sizeof(*out_access_key)); - - std::memcpy(out_access_key, g_work_buffer, sizeof(*out_access_key)); - return ResultSuccess(); - } - - - } - - /* Initialization. */ - void Initialize() { - /* Initialize the Drbg. */ - InitializeCtrDrbg(); - - /* Initialize SE interrupt + keyslot events. */ - InitializeSeEvents(); - - /* Initialize DAS for the SE. */ - InitializeDeviceAddressSpace(); - - /* Initialize the keyslot cache. */ - InitializeKeySlotCache(); - } - - /* General. */ - Result GetConfig(u64 *out, ConfigItem which) { - /* Nintendo explicitly blacklists package2 hash here, amusingly. */ - /* This is not blacklisted in safemode, but we're never in safe mode... */ - R_UNLESS(which != ConfigItem::Package2Hash, spl::ResultInvalidArgument()); - - smc::Result res = smc::GetConfig(out, 1, which); - - /* Nintendo has some special handling here for hardware type/is_retail. */ - if (res == smc::Result::InvalidArgument) { - switch (which) { - case ConfigItem::HardwareType: - *out = static_cast(HardwareType::Icosa); - res = smc::Result::Success; - break; - case ConfigItem::HardwareState: - *out = HardwareState_Development; - res = smc::Result::Success; - break; - default: - break; - } - } - - 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[0x100]; - u8 exp[0x100]; - u8 mod[0x100]; - }; - ModularExponentiateLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate sizes. */ - R_UNLESS(base_size <= sizeof(layout->base), spl::ResultInvalidSize()); - R_UNLESS(exp_size <= sizeof(layout->exp), spl::ResultInvalidSize()); - R_UNLESS(mod_size <= sizeof(layout->mod), spl::ResultInvalidSize()); - R_UNLESS(out_size <= WorkBufferSizeMax, spl::ResultInvalidSize()); - - /* 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, 0, sizeof(*layout)); - std::memcpy(layout->base + base_ofs, base, base_size); - std::memcpy(layout->exp, exp, exp_size); - std::memcpy(layout->mod + mod_ofs, mod, mod_size); - - /* Do exp mod operation. */ - os::FlushDataCache(layout, sizeof(*layout)); - { - std::scoped_lock lk(g_async_op_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); - } - - if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != smc::Result::Success) { - return smc::ConvertResult(res); - } - } - os::FlushDataCache(g_work_buffer, sizeof(out_size)); - - std::memcpy(out, g_work_buffer, out_size); - return ResultSuccess(); - } - - Result SetConfig(ConfigItem which, u64 value) { - return smc::ConvertResult(smc::SetConfig(which, std::addressof(value), 1)); - } - - Result GenerateRandomBytes(void *out, size_t size) { - u8 *cur_dst = reinterpret_cast(out); - - for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { - const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); - - R_TRY(GenerateRandomBytesInternal(cur_dst, size)); - cur_dst += cur_size; - } - - 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(!IsBootReasonSet(), spl::ResultBootReasonAlreadySet()); - - g_boot_reason = boot_reason; - g_boot_reason_set = true; - return ResultSuccess(); - } - - Result GetBootReason(BootReasonValue *out) { - R_UNLESS(IsBootReasonSet(), spl::ResultBootReasonNotSet()); - - *out = GetBootReason(); - 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 void *owner, const AccessKey &access_key, const KeySource &key_source) { - R_TRY(ValidateAesKeySlot(keyslot, owner)); - return LoadVirtualAesKey(keyslot, access_key, key_source); - } - - Result GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { - 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} - }; - - ScopedAesKeySlot keyslot_holder; - R_TRY(keyslot_holder.Allocate()); - - R_TRY(LoadVirtualAesKey(keyslot_holder.GetKeySlot(), access_key, s_generate_aes_key_source)); - - return smc::ConvertResult(DecryptAesBlock(keyslot_holder.GetKeySlot(), out_key, std::addressof(key_source))); - } - - Result DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { - static constexpr KeySource s_decrypt_aes_key_source = { - .data = {0x11, 0x70, 0x24, 0x2B, 0x48, 0x69, 0x11, 0xF1, 0x11, 0xB0, 0x0C, 0x47, 0x7C, 0xC3, 0xEF, 0x7E} - }; - - AccessKey access_key; - R_TRY(GenerateAesKek(std::addressof(access_key), s_decrypt_aes_key_source, generation, option)); - - return GenerateAesKey(out_key, access_key, key_source); - } - - Result ComputeCtr(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. */ - if (src_size == 0) { - return ResultSuccess(); - } - - /* Validate sizes. */ - R_UNLESS(src_size <= dst_size, spl::ResultInvalidSize()); - R_UNLESS(util::IsAligned(src_size, AES_BLOCK_SIZE), spl::ResultInvalidSize()); - - /* We can only map 0x400000 aligned buffers for the SE. With that in mind, we have some math to do. */ - const uintptr_t src_addr = reinterpret_cast(src); - const uintptr_t dst_addr = reinterpret_cast(dst); - const uintptr_t src_addr_page_aligned = util::AlignDown(src_addr, os::MemoryPageSize); - const uintptr_t dst_addr_page_aligned = util::AlignDown(dst_addr, os::MemoryPageSize); - const size_t src_size_page_aligned = util::AlignUp(src_addr + src_size, os::MemoryPageSize) - src_addr_page_aligned; - const size_t dst_size_page_aligned = util::AlignUp(dst_addr + dst_size, os::MemoryPageSize) - dst_addr_page_aligned; - const u32 src_se_map_addr = ComputeAesInMapBase + (src_addr_page_aligned % DeviceAddressSpaceAlign); - const u32 dst_se_map_addr = ComputeAesOutMapBase + (dst_addr_page_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_page_aligned <= ComputeAesSizeMax, spl::ResultInvalidSize()); - R_UNLESS(dst_size_page_aligned <= ComputeAesSizeMax, spl::ResultInvalidSize()); - - /* Helpers for mapping/unmapping. */ - DeviceAddressSpaceMapHelper in_mapper(g_se_das_hnd, src_se_map_addr, src_addr_page_aligned, src_size_page_aligned, svc::MemoryPermission_Read); - DeviceAddressSpaceMapHelper out_mapper(g_se_das_hnd, dst_se_map_addr, dst_addr_page_aligned, dst_size_page_aligned, svc::MemoryPermission_Write); - - /* Setup SE linked list entries. */ - SeCryptContext *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(crypt_ctx, sizeof(*crypt_ctx)); - os::FlushDataCache(const_cast(src), src_size); - os::FlushDataCache(dst, dst_size); - { - std::scoped_lock lk(g_async_op_lock); - smc::AsyncOperationKey op_key; - const u32 mode = smc::GetComputeAesMode(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); - - smc::Result res = smc::ComputeAes(std::addressof(op_key), mode, iv_ctr, dst_ll_addr, src_ll_addr, src_size); - if (res != smc::Result::Success) { - return smc::ConvertResult(res); - } - - if ((res = WaitCheckStatus(op_key)) != smc::Result::Success) { - return smc::ConvertResult(res); - } - } - os::FlushDataCache(dst, dst_size); - - return ResultSuccess(); - } - - 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, GetPhysicalKeySlot(keyslot, true), g_work_buffer, size)); - } - - 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(); - } - } - - os::ClearSystemEvent(std::addressof(g_se_keyslot_available_event)); - return spl::ResultOutOfKeySlots(); - } - - Result DeallocateAesKeySlot(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 physical keyslot, if we're cached. */ - s32 phys_slot; - if (g_keyslot_cache.Release(std::addressof(phys_slot), keyslot)) { - ClearPhysicalKeySlot(phys_slot); - } - - /* 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(); - } - - /* 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[RsaPrivateKeySize + DeviceUniqueDataMetaSize]; - }; - DecryptDeviceUniqueDataLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate size. */ - R_UNLESS(src_size >= DeviceUniqueDataMetaSize, spl::ResultInvalidSize()); - R_UNLESS(src_size <= sizeof(DecryptDeviceUniqueDataLayout), spl::ResultInvalidSize()); - - std::memcpy(layout->data, src, src_size); - os::FlushDataCache(layout, sizeof(*layout)); - - smc::Result smc_res; - size_t copy_size = 0; - if (hos::GetVersion() >= hos::Version_5_0_0) { - copy_size = std::min(dst_size, src_size - DeviceUniqueDataMetaSize); - 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); - } - - os::FlushDataCache(layout, sizeof(*layout)); - 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 (hos::GetVersion() >= hos::Version_5_0_0) { - return DecryptAndStoreDeviceUniqueKey(src, src_size, access_key, key_source, option); - } else { - struct LoadEsDeviceKeyLayout { - u8 data[DeviceUniqueDataMetaSize + 2 * RsaPrivateKeySize + 0x10]; - }; - LoadEsDeviceKeyLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate size. */ - R_UNLESS(src_size <= sizeof(LoadEsDeviceKeyLayout), spl::ResultInvalidSize()); - - std::memcpy(layout, src, src_size); - - os::FlushDataCache(layout, sizeof(*layout)); - 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, generation, smc::EsCommonKeyType::TitleKey); - } - - 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, generation, smc::EsCommonKeyType::ArchiveKey); - } - - /* 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 <= WorkBufferSizeMax, spl::ResultInvalidSize()); - R_UNLESS(label_digest_size == LabelDigestSizeMax, spl::ResultInvalidSize()); - - /* Nintendo doesn't check this result code, but we will. */ - R_TRY(ModularExponentiateWithStorageKey(g_work_buffer, 0x100, base, base_size, mod, mod_size, smc::ModularExponentiateWithStorageKeyMode::Gc)); - - size_t data_size = crypto::DecodeRsa2048OaepSha256(dst, dst_size, label_digest, label_digest_size, g_work_buffer, 0x100); - 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 void *owner, const AccessKey &access_key) { - R_TRY(ValidateAesKeySlot(keyslot, owner)); - return LoadVirtualPreparedAesKey(keyslot, access_key); - } - - Result GetPackage2Hash(void *dst, const size_t size) { - u64 hash[4]; - R_UNLESS(size >= sizeof(hash), spl::ResultInvalidSize()); - - smc::Result smc_res; - if ((smc_res = smc::GetConfig(hash, 4, ConfigItem::Package2Hash)) != 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[DeviceUniqueDataMetaSize + 2 * RsaPrivateKeySize + 0x10]; - AccessKey access_key_dec; - KeySource source_dec; - AccessKey access_key_enc; - KeySource source_enc; - }; - ReencryptDeviceUniqueDataLayout *layout = reinterpret_cast(g_work_buffer); - - /* Validate size. */ - R_UNLESS(src_size >= DeviceUniqueDataMetaSize, spl::ResultInvalidSize()); - R_UNLESS(src_size <= sizeof(ReencryptDeviceUniqueDataLayout), spl::ResultInvalidSize()); - - std::memcpy(layout, 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; - - os::FlushDataCache(layout, sizeof(*layout)); - - 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) { - size_t copy_size = std::min(dst_size, src_size); - os::FlushDataCache(layout, copy_size); - std::memcpy(dst, layout->data, copy_size); - } - - return smc::ConvertResult(smc_res); - } - - /* Helper. */ - Result DeallocateAllAesKeySlots(const void *owner) { - for (s32 slot = VirtualKeySlotMin; slot <= VirtualKeySlotMax; ++slot) { - if (g_keyslot_owners[GetVirtualKeySlotIndex(slot)] == owner) { - DeallocateAesKeySlot(slot, owner); - } - } - return ResultSuccess(); - } - - os::NativeHandle GetAesKeySlotAvailableEventHandle() { - return os::GetReadableHandleOfSystemEvent(std::addressof(g_se_keyslot_available_event)); - } - -} diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp deleted file mode 100644 index a53cba991..000000000 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_crypto_service.hpp" - -namespace ams::spl { - - CryptoService::~CryptoService() { - /* Free any keyslots this service is using. */ - impl::DeallocateAllAesKeySlots(this); - } - - Result CryptoService::GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option) { - return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); - } - - Result CryptoService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { - return impl::LoadAesKey(keyslot, this, access_key, key_source); - } - - Result CryptoService::GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source) { - return impl::GenerateAesKey(out_key.GetPointer(), access_key, key_source); - } - - Result CryptoService::DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option) { - return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); - } - - Result CryptoService::ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { - return impl::ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); - } - - 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) { - return impl::AllocateAesKeySlot(out_keyslot.GetPointer(), this); - } - - Result CryptoService::DeallocateAesKeySlot(s32 keyslot) { - return impl::DeallocateAesKeySlot(keyslot, this); - } - - Result CryptoService::GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd) { - out_hnd.SetValue(impl::GetAesKeySlotAvailableEventHandle(), false); - return ResultSuccess(); - } - -} diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp index 3b82c3927..b7839261c 100644 --- a/stratosphere/spl/source/spl_crypto_service.hpp +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -14,24 +14,57 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_general_service.hpp" namespace ams::spl { class CryptoService : public GeneralService { public: - virtual ~CryptoService(); + explicit CryptoService(SecureMonitorManager *manager) : GeneralService(manager) { /* ... */ } + public: + virtual ~CryptoService(){ + /* Free any keyslots this service is using. */ + m_manager.DeallocateAesKeySlots(this); + } public: /* Actual commands. */ - Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option); - Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); - Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source); - Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option); - Result ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); - Result AllocateAesKeySlot(sf::Out out_keyslot); - Result DeallocateAesKeySlot(s32 keyslot); - Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd); + Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option) { + return m_manager.GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); + } + + Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { + return m_manager.LoadAesKey(keyslot, this, access_key, key_source); + } + + Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source) { + return m_manager.GenerateAesKey(out_key.GetPointer(), access_key, key_source); + } + + Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option) { + return m_manager.DecryptAesKey(out_key.GetPointer(), key_source, generation, option); + } + + Result ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + return m_manager.ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); + } + + Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { + return m_manager.ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); + } + + Result AllocateAesKeySlot(sf::Out out_keyslot) { + return m_manager.AllocateAesKeySlot(out_keyslot.GetPointer(), this); + } + + Result DeallocateAesKeySlot(s32 keyslot) { + return m_manager.DeallocateAesKeySlot(keyslot, this); + } + + Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd) { + out_hnd.SetValue(m_manager.GetAesKeySlotAvailableEvent()->GetReadableHandle(), false); + return ResultSuccess(); + } }; static_assert(spl::impl::IsICryptoInterface); diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp deleted file mode 100644 index c5f35494b..000000000 --- a/stratosphere/spl/source/spl_ctr_drbg.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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" - -namespace ams::spl { - - void CtrDrbg::Update(const void *data) { - aes128ContextCreate(std::addressof(m_aes_ctx), m_key, true); - for (size_t offset = 0; offset < sizeof(m_work[1]); offset += BlockSize) { - IncrementCounter(m_counter); - aes128EncryptBlock(std::addressof(m_aes_ctx), std::addressof(m_work[1][offset]), m_counter); - } - - Xor(m_work[1], data, sizeof(m_work[1])); - - std::memcpy(m_key, std::addressof(m_work[1][0]), sizeof(m_key)); - std::memcpy(m_counter, std::addressof(m_work[1][BlockSize]), sizeof(m_key)); - } - - void CtrDrbg::Initialize(const void *seed) { - std::memcpy(m_work[0], seed, sizeof(m_work[0])); - std::memset(m_key, 0, sizeof(m_key)); - std::memset(m_counter, 0, sizeof(m_counter)); - this->Update(m_work[0]); - m_reseed_counter = 1; - } - - void CtrDrbg::Reseed(const void *seed) { - std::memcpy(m_work[0], seed, sizeof(m_work[0])); - this->Update(m_work[0]); - m_reseed_counter = 1; - } - - bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) { - if (size > MaxRequestSize) { - return false; - } - - if (m_reseed_counter > ReseedInterval) { - return false; - } - - aes128ContextCreate(std::addressof(m_aes_ctx), m_key, true); - u8 *cur_dst = reinterpret_cast(out); - - size_t aligned_size = (size & ~(BlockSize - 1)); - for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { - IncrementCounter(m_counter); - aes128EncryptBlock(std::addressof(m_aes_ctx), cur_dst, m_counter); - cur_dst += BlockSize; - } - - if (size > aligned_size) { - IncrementCounter(m_counter); - aes128EncryptBlock(std::addressof(m_aes_ctx), m_work[1], m_counter); - std::memcpy(cur_dst, m_work[1], size - aligned_size); - } - - std::memset(m_work[0], 0, sizeof(m_work[0])); - this->Update(m_work[0]); - - m_reseed_counter++; - return true; - - } - -} diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp deleted file mode 100644 index eb5f012c0..000000000 --- a/stratosphere/spl/source/spl_ctr_drbg.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 { - - /* Nintendo implements CTR_DRBG for their csrng. We will do the same. */ - class CtrDrbg { - public: - static constexpr size_t MaxRequestSize = 0x10000; - static constexpr size_t ReseedInterval = 0x7FFFFFF0; - static constexpr size_t BlockSize = AES_BLOCK_SIZE; - static constexpr size_t SeedSize = 2 * AES_BLOCK_SIZE; - private: - Aes128Context m_aes_ctx; - u8 m_counter[BlockSize]; - u8 m_key[BlockSize]; - u8 m_work[2][SeedSize]; - u32 m_reseed_counter; - private: - static void Xor(void *dst, const void *src, size_t size) { - const u8 *src_u8 = reinterpret_cast(src); - u8 *dst_u8 = reinterpret_cast(dst); - - for (size_t i = 0; i < size; i++) { - dst_u8[i] ^= src_u8[i]; - } - } - - static void IncrementCounter(void *ctr) { - u64 *ctr_64 = reinterpret_cast(ctr); - - ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); - if (!ctr_64[1]) { - ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); - } - } - private: - void Update(const void *data); - public: - void Initialize(const void *seed); - void Reseed(const void *seed); - bool GenerateRandomBytes(void *out, size_t size); - }; - -} diff --git a/stratosphere/spl/source/spl_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp deleted file mode 100644 index d344ac721..000000000 --- a/stratosphere/spl/source/spl_deprecated_service.cpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_deprecated_service.hpp" - -namespace ams::spl { - - DeprecatedService::~DeprecatedService() { - /* Free any keyslots this service is using. */ - impl::DeallocateAllAesKeySlots(this); - } - - Result DeprecatedService::GetConfig(sf::Out out, u32 which) { - return impl::GetConfig(out.GetPointer(), static_cast(which)); - } - - Result DeprecatedService::ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) { - return impl::ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize()); - } - - Result DeprecatedService::GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option) { - return impl::GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); - } - - Result DeprecatedService::LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { - return impl::LoadAesKey(keyslot, this, access_key, key_source); - } - - Result DeprecatedService::GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source) { - return impl::GenerateAesKey(out_key.GetPointer(), access_key, key_source); - } - - Result DeprecatedService::SetConfig(u32 which, u64 value) { - return impl::SetConfig(static_cast(which), value); - } - - Result DeprecatedService::GenerateRandomBytes(const sf::OutPointerBuffer &out) { - return impl::GenerateRandomBytes(out.GetPointer(), out.GetSize()); - } - - Result DeprecatedService::DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result DeprecatedService::DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { - return impl::DecryptGcMessage(out_size.GetPointer(), out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize()); - } - - Result DeprecatedService::IsDevelopment(sf::Out is_dev) { - return impl::IsDevelopment(is_dev.GetPointer()); - } - - Result DeprecatedService::GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which) { - return impl::GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); - } - - Result DeprecatedService::DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result DeprecatedService::DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option) { - return impl::DecryptAesKey(out_key.GetPointer(), key_source, generation, option); - } - - Result DeprecatedService::ComputeCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { - return impl::ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); - } - - Result DeprecatedService::ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { - return impl::ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); - } - - 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()); - } - - Result DeprecatedService::LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result DeprecatedService::PrepareEsTitleKeyDeprecated(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { - return impl::PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), 0); - } - - Result DeprecatedService::PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { - return impl::PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); - } - - Result DeprecatedService::LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { - return impl::LoadPreparedAesKey(keyslot, this, access_key); - } - - Result DeprecatedService::PrepareCommonEsTitleKeyDeprecated(sf::Out out_access_key, KeySource key_source) { - return impl::PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, 0); - } - - Result DeprecatedService::PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation) { - return impl::PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation); - } - - Result DeprecatedService::AllocateAesKeySlot(sf::Out out_keyslot) { - return impl::AllocateAesKeySlot(out_keyslot.GetPointer(), this); - } - - Result DeprecatedService::DeallocateAesKeySlot(s32 keyslot) { - return impl::DeallocateAesKeySlot(keyslot, this); - } - - Result DeprecatedService::GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd) { - out_hnd.SetValue(impl::GetAesKeySlotAvailableEventHandle(), false); - return ResultSuccess(); - } - - Result DeprecatedService::SetBootReason(BootReasonValue boot_reason) { - return impl::SetBootReason(boot_reason); - } - - Result DeprecatedService::GetBootReason(sf::Out out) { - return impl::GetBootReason(out.GetPointer()); - } - -} diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp index 1506b805b..721838d65 100644 --- a/stratosphere/spl/source/spl_deprecated_service.hpp +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -15,41 +15,130 @@ */ #pragma once #include +#include "spl_secure_monitor_manager.hpp" namespace ams::spl { class DeprecatedService { + protected: + SecureMonitorManager &m_manager; public: - virtual ~DeprecatedService(); + explicit DeprecatedService(SecureMonitorManager *manager) : m_manager(*manager) { /* ... */ } + public: + virtual ~DeprecatedService() { + /* Free any keyslots this service is using. */ + m_manager.DeallocateAesKeySlots(this); + } public: /* Actual commands. */ - Result GetConfig(sf::Out out, u32 which); - Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); - Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option); - Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); - Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source); - Result SetConfig(u32 which, u64 value); - Result GenerateRandomBytes(const sf::OutPointerBuffer &out); - Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); - Result IsDevelopment(sf::Out is_dev); - Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which); - Result DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option); - Result ComputeCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); - Result ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); - Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); - Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result PrepareEsTitleKeyDeprecated(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); - Result PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key); - Result PrepareCommonEsTitleKeyDeprecated(sf::Out out_access_key, KeySource key_source); - Result PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation); - Result AllocateAesKeySlot(sf::Out out_keyslot); - Result DeallocateAesKeySlot(s32 keyslot); - Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd); - Result SetBootReason(BootReasonValue boot_reason); - Result GetBootReason(sf::Out out); + Result GetConfig(sf::Out out, u32 which) { + return m_manager.GetConfig(out.GetPointer(), static_cast(which)); + } + + Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) { + return m_manager.ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize()); + } + + Result GenerateAesKek(sf::Out out_access_key, KeySource key_source, u32 generation, u32 option) { + return m_manager.GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); + } + + Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) { + return m_manager.LoadAesKey(keyslot, this, access_key, key_source); + } + + Result GenerateAesKey(sf::Out out_key, AccessKey access_key, KeySource key_source) { + return m_manager.GenerateAesKey(out_key.GetPointer(), access_key, key_source); + } + + Result SetConfig(u32 which, u64 value) { + return m_manager.SetConfig(static_cast(which), value); + } + + Result GenerateRandomBytes(const sf::OutPointerBuffer &out) { + return m_manager.GenerateRandomBytes(out.GetPointer(), out.GetSize()); + } + + Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { + return m_manager.DecryptGcMessage(out_size.GetPointer(), out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize()); + } + + Result IsDevelopment(sf::Out is_dev) { + return m_manager.IsDevelopment(is_dev.GetPointer()); + } + + Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which) { + return m_manager.GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); + } + + Result DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result DecryptAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 option) { + return m_manager.DecryptAesKey(out_key.GetPointer(), key_source, generation, option); + } + + Result ComputeCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr) { + return m_manager.ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); + } + + Result ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr) { + return m_manager.ComputeCtr(out_buf.GetPointer(), out_buf.GetSize(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize(), iv_ctr); + } + + Result ComputeCmac(sf::Out out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf) { + return m_manager.ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.GetPointer(), in_buf.GetSize()); + } + + Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result PrepareEsTitleKeyDeprecated(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { + return m_manager.PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), 0); + } + + Result PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { + return m_manager.PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); + } + + Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { + return m_manager.LoadPreparedAesKey(keyslot, this, access_key); + } + + Result PrepareCommonEsTitleKeyDeprecated(sf::Out out_access_key, KeySource key_source) { + return m_manager.PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, 0); + } + + Result PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation) { + return m_manager.PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation); + } + + Result AllocateAesKeySlot(sf::Out out_keyslot) { + return m_manager.AllocateAesKeySlot(out_keyslot.GetPointer(), this); + } + + Result DeallocateAesKeySlot(s32 keyslot) { + return m_manager.DeallocateAesKeySlot(keyslot, this); + } + + Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd) { + out_hnd.SetValue(m_manager.GetAesKeySlotAvailableEvent()->GetReadableHandle(), false); + return ResultSuccess(); + } + + Result SetBootReason(BootReasonValue boot_reason) { + return m_manager.SetBootReason(boot_reason); + } + + Result GetBootReason(sf::Out out) { + return m_manager.GetBootReason(out.GetPointer()); + } }; static_assert(spl::impl::IsIDeprecatedGeneralInterface); diff --git a/stratosphere/spl/source/spl_device_unique_data_service.cpp b/stratosphere/spl/source/spl_device_unique_data_service.cpp deleted file mode 100644 index 272e76eaf..000000000 --- a/stratosphere/spl/source/spl_device_unique_data_service.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_device_unique_data_service.hpp" - -namespace ams::spl { - - Result DeviceUniqueDataService::DecryptDeviceUniqueDataDeprecated(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result DeviceUniqueDataService::DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { - return impl::DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptDeviceUniqueData)); - } - -} diff --git a/stratosphere/spl/source/spl_device_unique_data_service.hpp b/stratosphere/spl/source/spl_device_unique_data_service.hpp index 1b92804a6..ae907bddf 100644 --- a/stratosphere/spl/source/spl_device_unique_data_service.hpp +++ b/stratosphere/spl/source/spl_device_unique_data_service.hpp @@ -14,15 +14,23 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_crypto_service.hpp" namespace ams::spl { class DeviceUniqueDataService : public CryptoService { + public: + explicit DeviceUniqueDataService(SecureMonitorManager *manager) : CryptoService(manager) { /* ... */ } public: /* Actual commands. */ - Result DecryptDeviceUniqueDataDeprecated(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); + Result DecryptDeviceUniqueDataDeprecated(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result DecryptDeviceUniqueData(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { + return m_manager.DecryptDeviceUniqueData(dst.GetPointer(), dst.GetSize(), src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptDeviceUniqueData)); + } }; static_assert(spl::impl::IsIDeviceUniqueDataInterface); diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp deleted file mode 100644 index bf292b1c4..000000000 --- a/stratosphere/spl/source/spl_es_service.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_es_service.hpp" - -namespace ams::spl { - - Result EsService::LoadEsDeviceKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result EsService::LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { - return impl::LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreEsDeviceKey)); - } - - Result EsService::PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { - return impl::PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); - } - - Result EsService::PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation) { - return impl::PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation); - } - - Result EsService::DecryptAndStoreDrmDeviceCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { - return impl::DecryptAndStoreDrmDeviceCertKey(src.GetPointer(), src.GetSize(), access_key, key_source); - } - - Result EsService::ModularExponentiateWithDrmDeviceCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod) { - return impl::ModularExponentiateWithDrmDeviceCertKey(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize()); - } - - Result EsService::PrepareEsArchiveKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { - return impl::PrepareEsArchiveKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); - } - - Result EsService::LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { - return impl::LoadPreparedAesKey(keyslot, this, access_key); - } - -} diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index ac158caed..2f8fc05f7 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -14,21 +14,47 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_device_unique_data_service.hpp" namespace ams::spl { class EsService : public DeviceUniqueDataService { + public: + explicit EsService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ } public: /* Actual commands. */ - Result LoadEsDeviceKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); - Result PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - Result PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation); - Result DecryptAndStoreDrmDeviceCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); - Result ModularExponentiateWithDrmDeviceCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod); - Result PrepareEsArchiveKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); - Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key); + Result LoadEsDeviceKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { + return m_manager.LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreEsDeviceKey)); + } + + Result PrepareEsTitleKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { + return m_manager.PrepareEsTitleKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); + } + + Result PrepareCommonEsTitleKey(sf::Out out_access_key, KeySource key_source, u32 generation) { + return m_manager.PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation); + } + + Result DecryptAndStoreDrmDeviceCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { + return m_manager.DecryptAndStoreDrmDeviceCertKey(src.GetPointer(), src.GetSize(), access_key, key_source); + } + + Result ModularExponentiateWithDrmDeviceCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod) { + return m_manager.ModularExponentiateWithDrmDeviceCertKey(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize()); + } + + Result PrepareEsArchiveKey(sf::Out out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation) { + return m_manager.PrepareEsArchiveKey(out_access_key.GetPointer(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize(), generation); + } + + Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { + return m_manager.LoadPreparedAesKey(keyslot, this, access_key); + } }; static_assert(spl::impl::IsIEsInterface); diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp deleted file mode 100644 index d9ddd758c..000000000 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_fs_service.hpp" - -namespace ams::spl { - - Result FsService::DecryptAndStoreGcKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { - return impl::DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); - } - - Result FsService::DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { - return impl::DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreGcKey)); - } - - Result FsService::DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { - return impl::DecryptGcMessage(out_size.GetPointer(), out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize()); - } - - Result FsService::GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which) { - return impl::GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); - } - - Result FsService::LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { - return impl::LoadPreparedAesKey(keyslot, this, access_key); - } - - Result FsService::GetPackage2Hash(const sf::OutPointerBuffer &dst) { - return impl::GetPackage2Hash(dst.GetPointer(), dst.GetSize()); - } - -} diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index e4f37a87c..c0ef7c4d3 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -14,19 +14,39 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_crypto_service.hpp" namespace ams::spl { class FsService : public CryptoService { + public: + explicit FsService(SecureMonitorManager *manager) : CryptoService(manager) { /* ... */ } public: /* Actual commands. */ - Result DecryptAndStoreGcKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); - Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); - Result DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); - Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which); - Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key); - Result GetPackage2Hash(const sf::OutPointerBuffer &dst); + Result DecryptAndStoreGcKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option) { + return m_manager.DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, option); + } + + Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { + return m_manager.DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast(smc::DeviceUniqueDataMode::DecryptAndStoreGcKey)); + } + + Result DecryptGcMessage(sf::Out out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest) { + return m_manager.DecryptGcMessage(out_size.GetPointer(), out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize(), label_digest.GetPointer(), label_digest.GetSize()); + } + + Result GenerateSpecificAesKey(sf::Out out_key, KeySource key_source, u32 generation, u32 which) { + return m_manager.GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); + } + + Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key) { + return m_manager.LoadPreparedAesKey(keyslot, this, access_key); + } + + Result GetPackage2Hash(const sf::OutPointerBuffer &dst) { + return m_manager.GetPackage2Hash(dst.GetPointer(), dst.GetSize()); + } }; static_assert(spl::impl::IsIFsInterface); diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp deleted file mode 100644 index 82ce32817..000000000 --- a/stratosphere/spl/source/spl_general_service.cpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_general_service.hpp" - -namespace ams::spl { - - Result GeneralService::GetConfig(sf::Out out, u32 which) { - return impl::GetConfig(out.GetPointer(), static_cast(which)); - } - - Result GeneralService::ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) { - return impl::ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize()); - } - - Result GeneralService::SetConfig(u32 which, u64 value) { - return impl::SetConfig(static_cast(which), value); - } - - Result GeneralService::GenerateRandomBytes(const sf::OutPointerBuffer &out) { - return impl::GenerateRandomBytes(out.GetPointer(), out.GetSize()); - } - - Result GeneralService::IsDevelopment(sf::Out is_dev) { - return impl::IsDevelopment(is_dev.GetPointer()); - } - - Result GeneralService::SetBootReason(BootReasonValue boot_reason) { - return impl::SetBootReason(boot_reason); - } - - Result GeneralService::GetBootReason(sf::Out out) { - return impl::GetBootReason(out.GetPointer()); - } - -} diff --git a/stratosphere/spl/source/spl_general_service.hpp b/stratosphere/spl/source/spl_general_service.hpp index cf900f7a0..0c29e18ad 100644 --- a/stratosphere/spl/source/spl_general_service.hpp +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -15,19 +15,44 @@ */ #pragma once #include +#include "spl_secure_monitor_manager.hpp" namespace ams::spl { class GeneralService { + protected: + SecureMonitorManager &m_manager; + public: + explicit GeneralService(SecureMonitorManager *manager) : m_manager(*manager) { /* ... */ } public: /* Actual commands. */ - Result GetConfig(sf::Out out, u32 which); - Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); - Result SetConfig(u32 which, u64 value); - Result GenerateRandomBytes(const sf::OutPointerBuffer &out); - Result IsDevelopment(sf::Out is_dev); - Result SetBootReason(BootReasonValue boot_reason); - Result GetBootReason(sf::Out out); + Result GetConfig(sf::Out out, u32 key) { + return m_manager.GetConfig(out.GetPointer(), static_cast(key)); + } + + Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) { + return m_manager.ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize()); + } + + Result SetConfig(u32 key, u64 value) { + return m_manager.SetConfig(static_cast(key), value); + } + + Result GenerateRandomBytes(const sf::OutPointerBuffer &out) { + return m_manager.GenerateRandomBytes(out.GetPointer(), out.GetSize()); + } + + Result IsDevelopment(sf::Out is_dev) { + return m_manager.IsDevelopment(is_dev.GetPointer()); + } + + Result SetBootReason(BootReasonValue boot_reason) { + return m_manager.SetBootReason(boot_reason); + } + + Result GetBootReason(sf::Out out) { + return m_manager.GetBootReason(out.GetPointer()); + } }; static_assert(spl::impl::IsIGeneralInterface); diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 3129c6aa3..9618183cb 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -14,7 +14,6 @@ * along with this program. If not, see . */ #include -#include "spl_api_impl.hpp" #include "spl_random_service.hpp" #include "spl_general_service.hpp" @@ -79,12 +78,13 @@ namespace ams { class ServerManager final : public sf::hipc::ServerManager { private: sf::ExpHeapAllocator *m_allocator; + spl::SecureMonitorManager *m_secure_monitor_manager; spl::GeneralService m_general_service; sf::UnmanagedServiceObjectByPointer m_general_service_object; spl::RandomService m_random_service; sf::UnmanagedServiceObjectByPointer m_random_service_object; public: - ServerManager(sf::ExpHeapAllocator *allocator) : m_allocator(allocator), m_general_service(), m_general_service_object(std::addressof(m_general_service)), m_random_service(), m_random_service_object(std::addressof(m_random_service)) { + ServerManager(sf::ExpHeapAllocator *allocator, spl::SecureMonitorManager *manager) : m_allocator(allocator), m_secure_monitor_manager(manager), m_general_service(manager), m_general_service_object(std::addressof(m_general_service)), m_random_service(manager), m_random_service_object(std::addressof(m_random_service)) { /* ... */ } private: @@ -96,29 +96,32 @@ namespace ams { alignas(0x40) constinit u8 g_server_allocator_buffer[8_KB]; Allocator g_server_allocator; + constinit SecureMonitorManager g_secure_monitor_manager; - ServerManager g_server_manager(std::addressof(g_server_allocator)); + constinit bool g_use_new_server = false; + + ServerManager g_server_manager(std::addressof(g_server_allocator), std::addressof(g_secure_monitor_manager)); ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) { switch (port_index) { case PortIndex_General: - if (hos::GetVersion() >= hos::Version_4_0_0) { + if (g_use_new_server) { return this->AcceptImpl(server, m_general_service_object.GetShared()); } else { - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); } case PortIndex_Random: return this->AcceptImpl(server, m_random_service_object.GetShared()); case PortIndex_Crypto: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); case PortIndex_Fs: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); case PortIndex_Ssl: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); case PortIndex_Es: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); case PortIndex_Manu: - return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator)); + return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced(m_allocator, m_secure_monitor_manager)); AMS_UNREACHABLE_DEFAULT_CASE(); } } @@ -127,8 +130,10 @@ namespace ams { /* Setup server allocator. */ g_server_allocator.Attach(lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), lmem::CreateOption_None)); - /* Initialize global context. */ - spl::impl::Initialize(); + /* Initialize secure monitor manager. */ + g_secure_monitor_manager.Initialize(); + + g_use_new_server = hos::GetVersion() >= hos::Version_4_0_0; /* Create services. */ const auto fw_ver = hos::GetVersion(); diff --git a/stratosphere/spl/source/spl_manu_service.cpp b/stratosphere/spl/source/spl_manu_service.cpp deleted file mode 100644 index c204e0142..000000000 --- a/stratosphere/spl/source/spl_manu_service.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_manu_service.hpp" - -namespace ams::spl { - - Result ManuService::ReencryptDeviceUniqueData(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option) { - return impl::ReencryptDeviceUniqueData(out.GetPointer(), out.GetSize(), src.GetPointer(), src.GetSize(), access_key_dec, source_dec, access_key_enc, source_enc, option); - } - -} diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp index 3dc92a274..b1284b990 100644 --- a/stratosphere/spl/source/spl_manu_service.hpp +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -14,14 +14,19 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_device_unique_data_service.hpp" namespace ams::spl { class ManuService : public DeviceUniqueDataService { + public: + explicit ManuService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ } public: /* Actual commands. */ - Result ReencryptDeviceUniqueData(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option); + Result ReencryptDeviceUniqueData(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option) { + return m_manager.ReencryptDeviceUniqueData(out.GetPointer(), out.GetSize(), src.GetPointer(), src.GetSize(), access_key_dec, source_dec, access_key_enc, source_enc, option); + } }; static_assert(spl::impl::IsIManuInterface); diff --git a/stratosphere/spl/source/spl_random_service.cpp b/stratosphere/spl/source/spl_random_service.cpp deleted file mode 100644 index 50cdad66a..000000000 --- a/stratosphere/spl/source/spl_random_service.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_random_service.hpp" - -namespace ams::spl { - - Result RandomService::GenerateRandomBytes(const sf::OutBuffer &out) { - return impl::GenerateRandomBytes(out.GetPointer(), out.GetSize()); - } - -} diff --git a/stratosphere/spl/source/spl_random_service.hpp b/stratosphere/spl/source/spl_random_service.hpp index 7a39e0905..bd2111c68 100644 --- a/stratosphere/spl/source/spl_random_service.hpp +++ b/stratosphere/spl/source/spl_random_service.hpp @@ -15,13 +15,20 @@ */ #pragma once #include +#include "spl_secure_monitor_manager.hpp" namespace ams::spl { class RandomService final { + protected: + SecureMonitorManager &m_manager; + public: + explicit RandomService(SecureMonitorManager *manager) : m_manager(*manager) { /* ... */ } public: /* Actual commands. */ - Result GenerateRandomBytes(const sf::OutBuffer &out); + Result GenerateRandomBytes(const sf::OutBuffer &out) { + return m_manager.GenerateRandomBytes(out.GetPointer(), out.GetSize()); + } }; static_assert(spl::impl::IsIRandomInterface); diff --git a/stratosphere/spl/source/spl_secure_monitor_manager.cpp b/stratosphere/spl/source/spl_secure_monitor_manager.cpp new file mode 100644 index 000000000..15d077c95 --- /dev/null +++ b/stratosphere/spl/source/spl_secure_monitor_manager.cpp @@ -0,0 +1,190 @@ +/* + * 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_secure_monitor_manager.hpp" + +namespace ams::spl { + + void SecureMonitorManager::Initialize() { + return impl::Initialize(); + } + + Result SecureMonitorManager::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) { + return impl::ModularExponentiate(out, out_size, base, base_size, exp, exp_size, mod, mod_size); + } + + Result SecureMonitorManager::GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) { + return impl::GenerateAesKek(out_access_key, key_source, generation, option); + } + + Result SecureMonitorManager::LoadAesKey(s32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) { + R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner)); + return impl::LoadAesKey(keyslot, access_key, key_source); + } + + Result SecureMonitorManager::GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) { + return impl::GenerateAesKey(out_key, access_key, key_source); + } + + Result SecureMonitorManager::DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + return impl::DecryptDeviceUniqueData(dst, dst_size, src, src_size, access_key, key_source, option); + } + + Result SecureMonitorManager::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) { + return impl::ReencryptDeviceUniqueData(dst, dst_size, src, src_size, access_key_dec, source_dec, access_key_enc, source_enc, option); + } + + Result SecureMonitorManager::GetConfig(u64 *out, spl::ConfigItem key) { + return impl::GetConfig(out, key); + } + + Result SecureMonitorManager::SetConfig(spl::ConfigItem key, u64 value) { + return impl::SetConfig(key, value); + } + + Result SecureMonitorManager::GetPackage2Hash(void *dst, const size_t size) { + return impl::GetPackage2Hash(dst, size); + } + + Result SecureMonitorManager::GenerateRandomBytes(void *out, size_t size) { + return impl::GenerateRandomBytes(out, size); + } + + Result SecureMonitorManager::DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + return impl::DecryptAndStoreGcKey(src, src_size, access_key, key_source, option); + } + + Result SecureMonitorManager::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) { + return impl::DecryptGcMessage(out_size, dst, dst_size, base, base_size, mod, mod_size, label_digest, label_digest_size); + } + + Result SecureMonitorManager::DecryptAndStoreSslClientCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return impl::DecryptAndStoreSslClientCertKey(src, src_size, access_key, key_source); + } + + Result SecureMonitorManager::ModularExponentiateWithSslClientCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return impl::ModularExponentiateWithSslClientCertKey(out, out_size, base, base_size, mod, mod_size); + } + + Result SecureMonitorManager::DecryptAndStoreDrmDeviceCertKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return impl::DecryptAndStoreDrmDeviceCertKey(src, src_size, access_key, key_source); + } + + Result SecureMonitorManager::ModularExponentiateWithDrmDeviceCertKey(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return impl::ModularExponentiateWithDrmDeviceCertKey(out, out_size, base, base_size, mod, mod_size); + } + + Result SecureMonitorManager::IsDevelopment(bool *out) { + return impl::IsDevelopment(out); + } + + Result SecureMonitorManager::GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) { + return impl::GenerateSpecificAesKey(out_key, key_source, generation, which); + } + + Result SecureMonitorManager::DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) { + return impl::DecryptAesKey(out_key, key_source, generation, option); + } + + Result SecureMonitorManager::ComputeCtr(void *dst, size_t dst_size, s32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) { + R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner)); + return impl::ComputeCtr(dst, dst_size, keyslot, src, src_size, iv_ctr); + } + + Result SecureMonitorManager::ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *owner, const void *data, size_t size) { + R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner)); + return impl::ComputeCmac(out_cmac, keyslot, data, size); + } + + Result SecureMonitorManager::LoadEsDeviceKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + return impl::LoadEsDeviceKey(src, src_size, access_key, key_source, option); + } + + Result SecureMonitorManager::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 impl::PrepareEsTitleKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation); + } + + Result SecureMonitorManager::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 impl::PrepareEsArchiveKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation); + } + + Result SecureMonitorManager::PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) { + return impl::PrepareCommonEsTitleKey(out_access_key, key_source, generation); + } + + Result SecureMonitorManager::LoadPreparedAesKey(s32 keyslot, const void *owner, const AccessKey &access_key) { + R_TRY(this->TestAesKeySlot(nullptr, keyslot, owner)); + return impl::LoadPreparedAesKey(keyslot, access_key); + } + + Result SecureMonitorManager::AllocateAesKeySlot(s32 *out_keyslot, const void *owner) { + s32 keyslot; + R_TRY(impl::AllocateAesKeySlot(std::addressof(keyslot))); + + s32 index; + R_ABORT_UNLESS(impl::TestAesKeySlot(std::addressof(index), keyslot)); + + m_aes_keyslot_owners[index] = owner; + *out_keyslot = keyslot; + return ResultSuccess(); + } + + Result SecureMonitorManager::DeallocateAesKeySlot(s32 keyslot, const void *owner) { + s32 index; + R_TRY(this->TestAesKeySlot(std::addressof(index), keyslot, owner)); + + m_aes_keyslot_owners[index] = nullptr; + return impl::DeallocateAesKeySlot(keyslot); + } + + void SecureMonitorManager::DeallocateAesKeySlots(const void *owner) { + for (auto i = 0; i < impl::AesKeySlotCount; ++i) { + if (m_aes_keyslot_owners[i] == owner) { + m_aes_keyslot_owners[i] = nullptr; + impl::DeallocateAesKeySlot(impl::AesKeySlotMin + i); + } + } + } + + Result SecureMonitorManager::SetBootReason(BootReasonValue boot_reason) { + return impl::SetBootReason(boot_reason); + } + + Result SecureMonitorManager::GetBootReason(BootReasonValue *out) { + return impl::GetBootReason(out); + } + + os::SystemEvent *SecureMonitorManager::GetAesKeySlotAvailableEvent() { + return impl::GetAesKeySlotAvailableEvent(); + } + + Result SecureMonitorManager::TestAesKeySlot(s32 *out_index, s32 keyslot, const void *owner) { + /* Validate the keyslot (and get the index). */ + s32 index; + R_TRY(impl::TestAesKeySlot(std::addressof(index), keyslot)); + + /* Check that the keyslot is owned by the request maker. */ + R_UNLESS(m_aes_keyslot_owners[index] == owner, spl::ResultInvalidKeySlot()); + + /* Set output index. */ + if (out_index != nullptr) { + *out_index = index; + } + return ResultSuccess(); + } + + +} diff --git a/stratosphere/spl/source/spl_secure_monitor_manager.hpp b/stratosphere/spl/source/spl_secure_monitor_manager.hpp new file mode 100644 index 000000000..f54ff5da0 --- /dev/null +++ b/stratosphere/spl/source/spl_secure_monitor_manager.hpp @@ -0,0 +1,66 @@ +/* + * 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 { + + class SecureMonitorManager { + private: + const void *m_aes_keyslot_owners[impl::AesKeySlotCount]{}; + public: + constexpr SecureMonitorManager() = default; + public: + void Initialize(); + private: + Result TestAesKeySlot(s32 *out_index, s32 keyslot, const void *owner); + public: + 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 GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); + 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 DecryptDeviceUniqueData(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + 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); + Result GetConfig(u64 *out, spl::ConfigItem key); + Result SetConfig(spl::ConfigItem key, u64 value); + Result GetPackage2Hash(void *dst, const size_t size); + Result GenerateRandomBytes(void *out, size_t size); + 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 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); + 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 IsDevelopment(bool *out); + Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); + 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 *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 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 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 PrepareCommonEsTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation); + Result LoadPreparedAesKey(s32 keyslot, const void *owner, const AccessKey &access_key); + Result AllocateAesKeySlot(s32 *out_keyslot, const void *owner); + Result DeallocateAesKeySlot(s32 keyslot, const void *owner); + void DeallocateAesKeySlots(const void *owner); + Result SetBootReason(BootReasonValue boot_reason); + Result GetBootReason(BootReasonValue *out); + + os::SystemEvent *GetAesKeySlotAvailableEvent(); + }; + +} diff --git a/stratosphere/spl/source/spl_ssl_service.cpp b/stratosphere/spl/source/spl_ssl_service.cpp deleted file mode 100644 index 51ef9f3b1..000000000 --- a/stratosphere/spl/source/spl_ssl_service.cpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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_api_impl.hpp" -#include "spl_ssl_service.hpp" - -namespace ams::spl { - - Result SslService::DecryptAndStoreSslClientCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { - return impl::DecryptAndStoreSslClientCertKey(src.GetPointer(), src.GetSize(), access_key, key_source); - } - - Result SslService::ModularExponentiateWithSslClientCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod) { - return impl::ModularExponentiateWithSslClientCertKey(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize()); - } - -} diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index 3dc95f73e..650db5c79 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -14,15 +14,23 @@ * along with this program. If not, see . */ #pragma once +#include #include "spl_device_unique_data_service.hpp" namespace ams::spl { class SslService : public DeviceUniqueDataService { + public: + explicit SslService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ } public: /* Actual commands. */ - Result DecryptAndStoreSslClientCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); - Result ModularExponentiateWithSslClientCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod); + Result DecryptAndStoreSslClientCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) { + return m_manager.DecryptAndStoreSslClientCertKey(src.GetPointer(), src.GetSize(), access_key, key_source); + } + + Result ModularExponentiateWithSslClientCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod) { + return m_manager.ModularExponentiateWithSslClientCertKey(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), mod.GetPointer(), mod.GetSize()); + } }; static_assert(spl::impl::IsISslInterface);