spl: refactor for accuracy/move into libstrat

This commit is contained in:
Michael Scire 2021-10-10 12:57:24 -07:00
parent 4758dfa933
commit d8a36e39f2
40 changed files with 1898 additions and 1732 deletions

View file

@ -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_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_Ctr: se::ComputeAes128CtrAsync(output_address, slot, input_address, size, iv, sizeof(iv), SecurityEngineDoneHandler); break;
case CipherMode_Cmac: case CipherMode_Cmac:
return SmcResult::NotImplemented; return SmcResult::NotSupported;
default: default:
return SmcResult::InvalidArgument; return SmcResult::InvalidArgument;
} }
@ -765,8 +765,8 @@ namespace ams::secmon::smc {
const auto which = static_cast<SecureData>(args.r[1]); const auto which = static_cast<SecureData>(args.r[1]);
/* Validate arguments/conditions. */ /* Validate arguments/conditions. */
SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented); SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotSupported);
SMC_R_UNLESS(which < SecureData_Count, NotImplemented); SMC_R_UNLESS(which < SecureData_Count, NotSupported);
/* Use a temporary buffer. */ /* Use a temporary buffer. */
u8 secure_data[AesKeySize]; u8 secure_data[AesKeySize];

View file

@ -20,7 +20,7 @@ namespace ams::secmon::smc {
enum class SmcResult : u32 { enum class SmcResult : u32 {
Success = 0, Success = 0,
NotImplemented = 1, NotSupported = 1,
InvalidArgument = 2, InvalidArgument = 2,
Busy = 3, Busy = 3,
NoAsyncOperation = 4, NoAsyncOperation = 4,

View file

@ -345,7 +345,7 @@ namespace ams::secmon::smc {
PerformUserShutDown(); PerformUserShutDown();
} }
} else /* if (soc_type == fuse::SocType_Mariko) */ { } else /* if (soc_type == fuse::SocType_Mariko) */ {
return SmcResult::NotImplemented; return SmcResult::NotSupported;
} }
break; break;
case ConfigItem::ExospherePayloadAddress: case ConfigItem::ExospherePayloadAddress:
@ -389,7 +389,7 @@ namespace ams::secmon::smc {
/* Validate arguments. */ /* Validate arguments. */
/* NOTE: In the future, configuration for non-NAND storage may be implemented. */ /* 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); SMC_R_UNLESS(user_offset + 2 * sizeof(EmummcFilePath) <= 4_KB, InvalidArgument);
/* Get the emummc config. */ /* Get the emummc config. */

View file

@ -70,7 +70,7 @@ namespace ams::secmon::smc {
SmcResult SmcWriteAddress(SmcArguments &args) { SmcResult SmcWriteAddress(SmcArguments &args) {
/* NOTE: This smc was deprecated in Atmosphère 0.13.0. */ /* NOTE: This smc was deprecated in Atmosphère 0.13.0. */
AMS_UNUSED(args); AMS_UNUSED(args);
return SmcResult::NotImplemented; return SmcResult::NotSupported;
} }
} }

View file

@ -18,7 +18,8 @@
#include <stratosphere/spl/spl_types.hpp> #include <stratosphere/spl/spl_types.hpp>
#include <stratosphere/spl/spl_api.hpp> #include <stratosphere/spl/spl_api.hpp>
#include <stratosphere/spl/smc/spl_smc.hpp> #include <stratosphere/spl/smc/spl_secure_monitor_api.hpp>
#include <stratosphere/spl/impl/spl_api_impl.hpp>
#include <stratosphere/spl/impl/spl_random_interface.hpp> #include <stratosphere/spl/impl/spl_random_interface.hpp>
#include <stratosphere/spl/impl/spl_deprecated_general_interface.hpp> #include <stratosphere/spl/impl/spl_deprecated_general_interface.hpp>
#include <stratosphere/spl/impl/spl_general_interface.hpp> #include <stratosphere/spl/impl/spl_general_interface.hpp>

View file

@ -13,32 +13,47 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere/spl/spl_types.hpp>
namespace ams::spl::impl { namespace ams::spl::impl {
constexpr inline s32 AesKeySlotMin = 16;
constexpr inline s32 AesKeySlotCount = 9;
constexpr inline s32 AesKeySlotMax = AesKeySlotMin + AesKeySlotCount - 1;
/* Initialization. */ /* Initialization. */
void Initialize(); void Initialize();
/* General. */ /* 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 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 GenerateRandomBytes(void *out, size_t size);
Result IsDevelopment(bool *out); Result IsDevelopment(bool *out);
Result SetBootReason(BootReasonValue boot_reason); Result SetBootReason(BootReasonValue boot_reason);
Result GetBootReason(BootReasonValue *out); 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. */ /* Crypto. */
Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option); 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 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 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 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 *owner, const void *data, size_t size); Result ComputeCmac(Cmac *out_cmac, s32 keyslot, const void *data, size_t size);
Result AllocateAesKeySlot(s32 *out_keyslot, const void *owner);
Result DeallocateAesKeySlot(s32 keyslot, const void *owner); Result AllocateAesKeySlot(s32 *out_keyslot);
Result DeallocateAesKeySlot(s32 keyslot);
Result TestAesKeySlot(s32 *out_index, s32 keyslot);
os::SystemEvent *GetAesKeySlotAvailableEvent();
/* RSA. */ /* 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); 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 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 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 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 */ /* FS */
Result DecryptAndStoreGcKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); 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 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 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); Result GetPackage2Hash(void *dst, const size_t size);
/* Manu. */ /* 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); 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();
} }

View file

@ -20,24 +20,24 @@
namespace ams::spl::smc { namespace ams::spl::smc {
/* Helpers for converting arguments. */ /* Helpers for converting arguments. */
inline u32 GetComputeAesMode(CipherMode mode, u32 keyslot) { constexpr ALWAYS_INLINE u32 GetComputeAesMode(CipherMode mode, u32 keyslot) {
return static_cast<u32>((static_cast<u32>(mode) << 4) | (keyslot & 7)); return static_cast<u32>((static_cast<u32>(mode) << 4) | (keyslot & 7));
} }
inline u32 GetPrepareEsDeviceUniqueKeyOption(EsCommonKeyType type, u32 generation) { constexpr ALWAYS_INLINE u32 GetPrepareEsDeviceUniqueKeyOption(EsDeviceUniqueKeyType type, u32 generation) {
return static_cast<u32>((static_cast<u32>(type) << 6) | (generation & 0x3F)); return static_cast<u32>((static_cast<u32>(type) << 6) | (generation & 0x3F));
} }
/* Functions. */ /* Functions. */
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);
Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem which); Result GetConfig(u64 *out, size_t num_qwords, spl::ConfigItem key);
Result GetResult(Result *out, AsyncOperationKey op); Result GetResult(Result *out, AsyncOperationKey op);
Result GetResultData(Result *out, void *out_buf, size_t out_buf_size, 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 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 GenerateRandomBytes(void *out, size_t size);
Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option); Result GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
Result LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source); 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 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 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); 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); Result AtmosphereGetEmummcConfig(void *out_config, void *out_paths, u32 storage_id);
/* Helpers. */ /* Helpers. */
inline Result SetConfig(spl::ConfigItem which, const u64 *value, size_t num_qwords) { ALWAYS_INLINE Result SetConfig(spl::ConfigItem key, const u64 *value, size_t num_qwords) {
return SetConfig(which, nullptr, value, 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) { ALWAYS_INLINE Result SetConfig(spl::ConfigItem key, const u64 value) {
return SetConfig(which, std::addressof(value), 1); return SetConfig(key, std::addressof(value), 1);
} }
} }

View file

@ -54,7 +54,7 @@ namespace ams::spl {
enum class Result { enum class Result {
Success = 0, Success = 0,
NotImplemented = 1, NotSupported = 1,
InvalidArgument = 2, InvalidArgument = 2,
InProgress = 3, InProgress = 3,
NoAsyncOperation = 4, NoAsyncOperation = 4,
@ -69,7 +69,7 @@ namespace ams::spl {
/* Convert to the list of known SecureMonitorErrors. */ /* Convert to the list of known SecureMonitorErrors. */
const auto converted = R_MAKE_NAMESPACE_RESULT(::ams::spl, static_cast<u32>(smc_result)); const auto converted = R_MAKE_NAMESPACE_RESULT(::ams::spl, static_cast<u32>(smc_result));
R_UNLESS(spl::ResultSecureMonitorError::Includes(converted), spl::ResultUnknownSecureMonitorError()); R_UNLESS(spl::ResultSecureMonitorError::Includes(converted), spl::ResultUnexpectedSecureMonitorResult());
/* Return the error. */ /* Return the error. */
return converted; return converted;
@ -95,7 +95,7 @@ namespace ams::spl {
DrmDeviceCert = 2, DrmDeviceCert = 2,
}; };
enum class EsCommonKeyType { enum class EsDeviceUniqueKeyType {
TitleKey = 0, TitleKey = 0,
ArchiveKey = 1, 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 { enum class HardwareType {
Icosa = 0, Icosa = 0,
Copper = 1, Copper = 1,
@ -168,40 +171,40 @@ namespace ams::spl {
struct AesKey { struct AesKey {
union { union {
u8 data[AES_128_KEY_SIZE]; u8 data[AesKeySize];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; u64 data64[AesKeySize / sizeof(u64)];
}; };
}; };
static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!");
struct IvCtr { struct IvCtr {
union { union {
u8 data[AES_128_KEY_SIZE]; u8 data[AesKeySize];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; u64 data64[AesKeySize / sizeof(u64)];
}; };
}; };
static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!");
struct Cmac { struct Cmac {
union { union {
u8 data[AES_128_KEY_SIZE]; u8 data[AesKeySize];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; u64 data64[AesKeySize / sizeof(u64)];
}; };
}; };
static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!");
struct AccessKey { struct AccessKey {
union { union {
u8 data[AES_128_KEY_SIZE]; u8 data[AesKeySize];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; u64 data64[AesKeySize / sizeof(u64)];
}; };
}; };
static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!");
struct KeySource { struct KeySource {
union { union {
u8 data[AES_128_KEY_SIZE]; u8 data[AesKeySize];
u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; u64 data64[AesKeySize / sizeof(u64)];
}; };
}; };
static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!");

View file

@ -14,34 +14,32 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include <stratosphere/spl.hpp>
#include <stratosphere/spl/smc/spl_smc.hpp>
namespace ams::exosphere { namespace ams::exosphere {
ApiInfo GetApiInfo() { ApiInfo GetApiInfo() {
u64 exosphere_cfg; u64 exosphere_cfg;
if (spl::smc::GetConfig(std::addressof(exosphere_cfg), 1, spl::ConfigItem::ExosphereApiVersion) != spl::smc::Result::Success) { if (R_FAILED(spl::impl::GetConfig(std::addressof(exosphere_cfg), spl::ConfigItem::ExosphereApiVersion))) {
R_ABORT_UNLESS(ResultNotPresent()); R_ABORT_UNLESS(exosphere::ResultNotPresent());
} }
return ApiInfo{ util::BitPack64{exosphere_cfg} }; return ApiInfo{ util::BitPack64{exosphere_cfg} };
} }
void ForceRebootToRcm() { 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() { 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() { 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() { 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) { void CopyToIram(uintptr_t iram_dst, const void *dram_src, size_t size) {
@ -67,19 +65,21 @@ namespace ams::exosphere {
} }
bool IsRcmBugPatched() { bool IsRcmBugPatched() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereHasRcmBugPatch); return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereHasRcmBugPatch);
} }
bool ShouldBlankProdInfo() { bool ShouldBlankProdInfo() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereBlankProdInfo); return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereBlankProdInfo);
} }
bool ShouldAllowWritesToProdInfo() { bool ShouldAllowWritesToProdInfo() {
return GetBooleanConfigItem(spl::ConfigItem::ExosphereAllowCalWrites); return spl::impl::GetConfigBool(spl::ConfigItem::ExosphereAllowCalWrites);
} }
u64 GetDeviceId() { 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;
} }
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<crypto::AesEncryptor128, AesKeySize, false>;
/* 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<size_t>(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<AesKeySlotCacheEntry> 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<Drbg> g_drbg;
constinit os::InterruptName g_interrupt_name;
constinit os::InterruptEventType g_interrupt;
constinit util::TypedStorage<os::SystemEvent> 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<os::InterruptName>(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<uintptr_t>(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<DecryptAesLayout *>(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<DecryptAndStoreDeviceUniqueKeyLayout *>(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<smc::DeviceUniqueDataMode>(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<ModularExponentiateWithStorageKeyLayout *>(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<PrepareEsDeviceUniqueKeyLayout *>(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<u64>(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<ModularExponentiateLayout *>(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<u8 *>(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<uintptr_t>(src);
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(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<SeCryptContext *>(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<DecryptDeviceUniqueDataLayout *>(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<smc::DeviceUniqueDataMode>(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<u32>(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<LoadEsDeviceKeyLayout *>(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<u32>(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<u32>(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<ReencryptDeviceUniqueDataLayout *>(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);
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
namespace ams::spl::impl {
constexpr inline int BitsPerByte = BITSIZEOF(u8);
/* Nintendo implements CTR_DRBG for their csrng. We will do the same. */
template<typename BlockCipher, size_t KeySize, bool UseDerivation>
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<const u8 *>(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<const u8 *>(src);
u8 *dst_u8 = static_cast<u8 *>(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<u8 *>(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<u8 *>(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<u8 *>(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;
}
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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);
}
};
}

View file

@ -16,47 +16,47 @@
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
namespace ams::spl { namespace ams::spl::impl {
class KeySlotCacheEntry : public util::IntrusiveListBaseNode<KeySlotCacheEntry> { class AesKeySlotCacheEntry : public util::IntrusiveListBaseNode<AesKeySlotCacheEntry> {
NON_COPYABLE(KeySlotCacheEntry); NON_COPYABLE(AesKeySlotCacheEntry);
NON_MOVEABLE(KeySlotCacheEntry); NON_MOVEABLE(AesKeySlotCacheEntry);
private: private:
friend class KeySlotCache; friend class AesKeySlotCache;
public: public:
static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize; static constexpr size_t KeySize = crypto::AesDecryptor128::KeySize;
private: private:
const s32 m_slot_index; const s32 m_aes_keyslot_index;
s32 m_virtual_slot; s32 m_virtual_aes_keyslot;
public: 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 { 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) { void SetVirtualAesKeySlot(s32 virtual_slot) {
m_virtual_slot = virtual_slot; m_virtual_aes_keyslot = virtual_slot;
} }
void ClearVirtualSlot() { void ClearVirtualAesKeySlot() {
m_virtual_slot = -1; m_virtual_aes_keyslot = -1;
} }
}; };
class KeySlotCache { class AesKeySlotCache {
NON_COPYABLE(KeySlotCache); NON_COPYABLE(AesKeySlotCache);
NON_MOVEABLE(KeySlotCache); NON_MOVEABLE(AesKeySlotCache);
private: private:
using KeySlotCacheEntryList = util::IntrusiveListBaseTraits<KeySlotCacheEntry>::ListType; using AesKeySlotCacheEntryList = util::IntrusiveListBaseTraits<AesKeySlotCacheEntry>::ListType;
private: private:
KeySlotCacheEntryList m_mru_list; AesKeySlotCacheEntryList m_mru_list;
public: public:
constexpr KeySlotCache() : m_mru_list() { /* ... */ } constexpr AesKeySlotCache() : m_mru_list() { /* ... */ }
s32 Allocate(s32 virtual_slot) { s32 Allocate(s32 virtual_slot) {
return this->AllocateFromLru(virtual_slot); return this->AllocateFromLru(virtual_slot);
@ -65,7 +65,7 @@ namespace ams::spl {
bool Find(s32 *out, s32 virtual_slot) { bool Find(s32 *out, s32 virtual_slot) {
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) {
if (it->Contains(virtual_slot)) { if (it->Contains(virtual_slot)) {
*out = it->GetPhysicalKeySlotIndex(); *out = it->GetPhysicalAesKeySlotIndex();
this->UpdateMru(it); this->UpdateMru(it);
return true; return true;
@ -78,8 +78,8 @@ namespace ams::spl {
bool Release(s32 *out, s32 virtual_slot) { bool Release(s32 *out, s32 virtual_slot) {
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) {
if (it->Contains(virtual_slot)) { if (it->Contains(virtual_slot)) {
*out = it->GetPhysicalKeySlotIndex(); *out = it->GetPhysicalAesKeySlotIndex();
it->ClearVirtualSlot(); it->ClearVirtualAesKeySlot();
this->UpdateLru(it); this->UpdateLru(it);
return true; return true;
@ -91,13 +91,13 @@ namespace ams::spl {
bool FindPhysical(s32 physical_slot) { bool FindPhysical(s32 physical_slot) {
for (auto it = m_mru_list.begin(); it != m_mru_list.end(); ++it) { 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); this->UpdateMru(it);
if (it->GetVirtualKeySlotIndex() == physical_slot) { if (it->GetVirtualAesKeySlotIndex() == physical_slot) {
return true; return true;
} else { } else {
it->SetVirtualSlot(physical_slot); it->SetVirtualAesKeySlot(physical_slot);
return false; return false;
} }
} }
@ -105,7 +105,7 @@ namespace ams::spl {
AMS_ABORT(); AMS_ABORT();
} }
void AddEntry(KeySlotCacheEntry *entry) { void AddEntry(AesKeySlotCacheEntry *entry) {
m_mru_list.push_front(*entry); m_mru_list.push_front(*entry);
} }
private: private:
@ -113,22 +113,22 @@ namespace ams::spl {
AMS_ASSERT(!m_mru_list.empty()); AMS_ASSERT(!m_mru_list.empty());
auto it = m_mru_list.rbegin(); auto it = m_mru_list.rbegin();
it->SetVirtualSlot(virtual_slot); it->SetVirtualAesKeySlot(virtual_slot);
auto *entry = std::addressof(*it); auto *entry = std::addressof(*it);
m_mru_list.pop_back(); m_mru_list.pop_back();
m_mru_list.push_front(*entry); 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); auto *entry = std::addressof(*it);
m_mru_list.erase(it); m_mru_list.erase(it);
m_mru_list.push_front(*entry); m_mru_list.push_front(*entry);
} }
void UpdateLru(KeySlotCacheEntryList::iterator it) { void UpdateLru(AesKeySlotCacheEntryList::iterator it) {
auto *entry = std::addressof(*it); auto *entry = std::addressof(*it);
m_mru_list.erase(it); m_mru_list.erase(it);
m_mru_list.push_back(*entry); m_mru_list.push_back(*entry);

View file

@ -17,25 +17,27 @@
namespace ams::spl::smc { 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; svc::SecureMonitorArguments args;
args.r[0] = static_cast<u64>(FunctionId::SetConfig); args.r[0] = static_cast<u64>(FunctionId::SetConfig);
args.r[1] = static_cast<u64>(which); args.r[1] = static_cast<u64>(key);
args.r[2] = reinterpret_cast<u64>(address); args.r[2] = reinterpret_cast<u64>(sign);
for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) { for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) {
args.r[3 + i] = value[i]; args.r[3 + i] = value[i];
} }
svc::CallSecureMonitor(std::addressof(args)); svc::CallSecureMonitor(std::addressof(args));
out_op->value = args.r[1];
return static_cast<Result>(args.r[0]); return static_cast<Result>(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; svc::SecureMonitorArguments args;
args.r[0] = static_cast<u64>(FunctionId::GetConfig); args.r[0] = static_cast<u64>(FunctionId::GetConfig);
args.r[1] = static_cast<u64>(which); args.r[1] = static_cast<u64>(key);
svc::CallSecureMonitor(std::addressof(args)); svc::CallSecureMonitor(std::addressof(args));
for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) { for (size_t i = 0; i < std::min(static_cast<size_t>(4), num_qwords); i++) {
@ -124,7 +126,7 @@ namespace ams::spl::smc {
return static_cast<Result>(args.r[0]); return static_cast<Result>(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; svc::SecureMonitorArguments args;
args.r[0] = static_cast<u64>(FunctionId::ComputeAes); args.r[0] = static_cast<u64>(FunctionId::ComputeAes);

View file

@ -29,9 +29,9 @@ namespace ams::spl {
Manu Manu
}; };
os::SdkMutex g_mutex; constinit os::SdkMutex g_mutex;
s32 g_initialize_count = 0; constinit s32 g_initialize_count = 0;
InitializeMode g_initialize_mode = InitializeMode::None; constinit InitializeMode g_initialize_mode = InitializeMode::None;
Result AllocateAesKeySlotImpl(s32 *out) { Result AllocateAesKeySlotImpl(s32 *out) {
return serviceDispatchOut(splCryptoGetServiceSession(), 21, *out); return serviceDispatchOut(splCryptoGetServiceSession(), 21, *out);
@ -63,7 +63,7 @@ namespace ams::spl {
auto is_event_initialized = false; auto is_event_initialized = false;
while (true) { while (true) {
R_TRY_CATCH(static_cast<::ams::Result>(f())) { R_TRY_CATCH(static_cast<::ams::Result>(f())) {
R_CATCH(spl::ResultOutOfKeySlots) { R_CATCH(spl::ResultNoAvailableKeySlot) {
if (!is_event_initialized) { if (!is_event_initialized) {
GetAesKeySlotAvailableEvent(std::addressof(event)); GetAesKeySlotAvailableEvent(std::addressof(event));
is_event_initialized = true; is_event_initialized = true;

View file

@ -22,7 +22,7 @@ namespace ams::spl {
R_DEFINE_NAMESPACE_RESULT_MODULE(26); R_DEFINE_NAMESPACE_RESULT_MODULE(26);
R_DEFINE_ERROR_RANGE(SecureMonitorError, 0, 99); 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(SecureMonitorInvalidArgument, 2);
R_DEFINE_ERROR_RESULT(SecureMonitorBusy, 3); R_DEFINE_ERROR_RESULT(SecureMonitorBusy, 3);
R_DEFINE_ERROR_RESULT(SecureMonitorNoAsyncOperation, 4); R_DEFINE_ERROR_RESULT(SecureMonitorNoAsyncOperation, 4);
@ -30,14 +30,14 @@ namespace ams::spl {
R_DEFINE_ERROR_RESULT(SecureMonitorNotPermitted, 6); R_DEFINE_ERROR_RESULT(SecureMonitorNotPermitted, 6);
R_DEFINE_ERROR_RESULT(SecureMonitorNotInitialized, 7); R_DEFINE_ERROR_RESULT(SecureMonitorNotInitialized, 7);
R_DEFINE_ERROR_RESULT(InvalidSize, 100); R_DEFINE_ERROR_RESULT(InvalidBufferSize, 100);
R_DEFINE_ERROR_RESULT(UnknownSecureMonitorError, 101); R_DEFINE_ERROR_RESULT(UnexpectedSecureMonitorResult, 101);
R_DEFINE_ERROR_RESULT(DecryptionFailed, 102); R_DEFINE_ERROR_RESULT(DecryptionFailed, 102);
R_DEFINE_ERROR_RESULT(InvalidDeviceUniqueDataType, 103);
R_DEFINE_ERROR_RESULT(OutOfKeySlots, 104); R_DEFINE_ERROR_RESULT(NoAvailableKeySlot, 104);
R_DEFINE_ERROR_RESULT(InvalidKeySlot, 105); R_DEFINE_ERROR_RESULT(InvalidKeySlot, 105);
R_DEFINE_ERROR_RESULT(BootReasonAlreadySet, 106); R_DEFINE_ERROR_RESULT(BootReasonAlreadyInitialized, 106);
R_DEFINE_ERROR_RESULT(BootReasonNotSet, 107); R_DEFINE_ERROR_RESULT(BootReasonNotInitialized, 107);
R_DEFINE_ERROR_RESULT(InvalidArgument, 108); R_DEFINE_ERROR_RESULT(InvalidArgument, 108);
} }

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<size_t>(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<KeySlotCacheEntry> 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<u64>(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<DecryptAesBlockLayout *>(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<DecryptAndStoreDeviceUniqueKeyLayout *>(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<smc::DeviceUniqueDataMode>(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<ModularExponentiateWithStorageKeyLayout *>(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<PrepareEsDeviceUniqueKeyLayout *>(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<u64>(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<ModularExponentiateLayout *>(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<u8 *>(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<uintptr_t>(src);
const uintptr_t dst_addr = reinterpret_cast<uintptr_t>(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<SeCryptContext *>(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<void *>(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<DecryptDeviceUniqueDataLayout *>(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<smc::DeviceUniqueDataMode>(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<u32>(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<LoadEsDeviceKeyLayout *>(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<u32>(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<u32>(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<ReencryptDeviceUniqueDataLayout *>(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));
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<AccessKey> 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<AesKey> out_key, AccessKey access_key, KeySource key_source) {
return impl::GenerateAesKey(out_key.GetPointer(), access_key, key_source);
}
Result CryptoService::DecryptAesKey(sf::Out<AesKey> 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<Cmac> 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<s32> 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();
}
}

View file

@ -14,24 +14,57 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_general_service.hpp" #include "spl_general_service.hpp"
namespace ams::spl { namespace ams::spl {
class CryptoService : public GeneralService { class CryptoService : public GeneralService {
public: public:
virtual ~CryptoService(); explicit CryptoService(SecureMonitorManager *manager) : GeneralService(manager) { /* ... */ }
public:
virtual ~CryptoService(){
/* Free any keyslots this service is using. */
m_manager.DeallocateAesKeySlots(this);
}
public: public:
/* Actual commands. */ /* Actual commands. */
Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option); Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option) {
Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source); return m_manager.GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option);
Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source); }
Result DecryptAesKey(sf::Out<AesKey> 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 LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) {
Result ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf); return m_manager.LoadAesKey(keyslot, this, access_key, key_source);
Result AllocateAesKeySlot(sf::Out<s32> out_keyslot); }
Result DeallocateAesKeySlot(s32 keyslot);
Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd); Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source) {
return m_manager.GenerateAesKey(out_key.GetPointer(), access_key, key_source);
}
Result DecryptAesKey(sf::Out<AesKey> 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<Cmac> 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<s32> 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<CryptoService>); static_assert(spl::impl::IsICryptoInterface<CryptoService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u8 *>(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;
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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<const u8 *>(src);
u8 *dst_u8 = reinterpret_cast<u8 *>(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<u64 *>(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);
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u64> out, u32 which) {
return impl::GetConfig(out.GetPointer(), static_cast<spl::ConfigItem>(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<AccessKey> 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<AesKey> 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<spl::ConfigItem>(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<u32> 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<bool> is_dev) {
return impl::IsDevelopment(is_dev.GetPointer());
}
Result DeprecatedService::GenerateSpecificAesKey(sf::Out<AesKey> 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<AesKey> 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<Cmac> 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<AccessKey> 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<AccessKey> 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<AccessKey> out_access_key, KeySource key_source) {
return impl::PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, 0);
}
Result DeprecatedService::PrepareCommonEsTitleKey(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation) {
return impl::PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation);
}
Result DeprecatedService::AllocateAesKeySlot(sf::Out<s32> 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<BootReasonValue> out) {
return impl::GetBootReason(out.GetPointer());
}
}

View file

@ -15,41 +15,130 @@
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "spl_secure_monitor_manager.hpp"
namespace ams::spl { namespace ams::spl {
class DeprecatedService { class DeprecatedService {
protected:
SecureMonitorManager &m_manager;
public: 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: public:
/* Actual commands. */ /* Actual commands. */
Result GetConfig(sf::Out<u64> out, u32 which); Result GetConfig(sf::Out<u64> out, u32 which) {
Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); return m_manager.GetConfig(out.GetPointer(), static_cast<spl::ConfigItem>(which));
Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option); }
Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source);
Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source); Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) {
Result SetConfig(u32 which, u64 value); return m_manager.ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize());
Result GenerateRandomBytes(const sf::OutPointerBuffer &out); }
Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option);
Result DecryptGcMessage(sf::Out<u32> out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); Result GenerateAesKek(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option) {
Result IsDevelopment(sf::Out<bool> is_dev); return m_manager.GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option);
Result GenerateSpecificAesKey(sf::Out<AesKey> 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<AesKey> out_key, KeySource key_source, u32 generation, u32 option); Result LoadAesKey(s32 keyslot, AccessKey access_key, KeySource key_source) {
Result ComputeCtrDeprecated(const sf::OutBuffer &out_buf, s32 keyslot, const sf::InBuffer &in_buf, IvCtr iv_ctr); return m_manager.LoadAesKey(keyslot, this, access_key, key_source);
Result ComputeCtr(const sf::OutNonSecureBuffer &out_buf, s32 keyslot, const sf::InNonSecureBuffer &in_buf, IvCtr iv_ctr); }
Result ComputeCmac(sf::Out<Cmac> out_cmac, s32 keyslot, const sf::InPointerBuffer &in_buf);
Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); Result GenerateAesKey(sf::Out<AesKey> out_key, AccessKey access_key, KeySource key_source) {
Result PrepareEsTitleKeyDeprecated(sf::Out<AccessKey> out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); return m_manager.GenerateAesKey(out_key.GetPointer(), access_key, key_source);
Result PrepareEsTitleKey(sf::Out<AccessKey> 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<AccessKey> out_access_key, KeySource key_source); Result SetConfig(u32 which, u64 value) {
Result PrepareCommonEsTitleKey(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation); return m_manager.SetConfig(static_cast<spl::ConfigItem>(which), value);
Result AllocateAesKeySlot(sf::Out<s32> out_keyslot); }
Result DeallocateAesKeySlot(s32 keyslot);
Result GetAesKeySlotAvailableEvent(sf::OutCopyHandle out_hnd); Result GenerateRandomBytes(const sf::OutPointerBuffer &out) {
Result SetBootReason(BootReasonValue boot_reason); return m_manager.GenerateRandomBytes(out.GetPointer(), out.GetSize());
Result GetBootReason(sf::Out<BootReasonValue> out); }
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<u32> 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<bool> is_dev) {
return m_manager.IsDevelopment(is_dev.GetPointer());
}
Result GenerateSpecificAesKey(sf::Out<AesKey> 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<AesKey> 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<Cmac> 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<AccessKey> 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<AccessKey> 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<AccessKey> out_access_key, KeySource key_source) {
return m_manager.PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, 0);
}
Result PrepareCommonEsTitleKey(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation) {
return m_manager.PrepareCommonEsTitleKey(out_access_key.GetPointer(), key_source, generation);
}
Result AllocateAesKeySlot(sf::Out<s32> 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<BootReasonValue> out) {
return m_manager.GetBootReason(out.GetPointer());
}
}; };
static_assert(spl::impl::IsIDeprecatedGeneralInterface<DeprecatedService>); static_assert(spl::impl::IsIDeprecatedGeneralInterface<DeprecatedService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u32>(smc::DeviceUniqueDataMode::DecryptDeviceUniqueData));
}
}

View file

@ -14,15 +14,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_crypto_service.hpp" #include "spl_crypto_service.hpp"
namespace ams::spl { namespace ams::spl {
class DeviceUniqueDataService : public CryptoService { class DeviceUniqueDataService : public CryptoService {
public:
explicit DeviceUniqueDataService(SecureMonitorManager *manager) : CryptoService(manager) { /* ... */ }
public: public:
/* Actual commands. */ /* Actual commands. */
Result DecryptDeviceUniqueDataDeprecated(const sf::OutPointerBuffer &dst, const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); 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); 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<u32>(smc::DeviceUniqueDataMode::DecryptDeviceUniqueData));
}
}; };
static_assert(spl::impl::IsIDeviceUniqueDataInterface<DeviceUniqueDataService>); static_assert(spl::impl::IsIDeviceUniqueDataInterface<DeviceUniqueDataService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreEsDeviceKey));
}
Result EsService::PrepareEsTitleKey(sf::Out<AccessKey> 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<AccessKey> 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<AccessKey> 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);
}
}

View file

@ -14,21 +14,47 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_device_unique_data_service.hpp" #include "spl_device_unique_data_service.hpp"
namespace ams::spl { namespace ams::spl {
class EsService : public DeviceUniqueDataService { class EsService : public DeviceUniqueDataService {
public:
explicit EsService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ }
public: public:
/* Actual commands. */ /* Actual commands. */
Result LoadEsDeviceKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); 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); return m_manager.LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, option);
Result PrepareEsTitleKey(sf::Out<AccessKey> out_access_key, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest, u32 generation); }
Result PrepareCommonEsTitleKey(sf::Out<AccessKey> out_access_key, KeySource key_source, u32 generation);
Result DecryptAndStoreDrmDeviceCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); Result LoadEsDeviceKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) {
Result ModularExponentiateWithDrmDeviceCertKey(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod); return m_manager.LoadEsDeviceKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreEsDeviceKey));
Result PrepareEsArchiveKey(sf::Out<AccessKey> 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 PrepareEsTitleKey(sf::Out<AccessKey> 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<AccessKey> 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<AccessKey> 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<EsService>); static_assert(spl::impl::IsIEsInterface<EsService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreGcKey));
}
Result FsService::DecryptGcMessage(sf::Out<u32> 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<AesKey> 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());
}
}

View file

@ -14,19 +14,39 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_crypto_service.hpp" #include "spl_crypto_service.hpp"
namespace ams::spl { namespace ams::spl {
class FsService : public CryptoService { class FsService : public CryptoService {
public:
explicit FsService(SecureMonitorManager *manager) : CryptoService(manager) { /* ... */ }
public: public:
/* Actual commands. */ /* Actual commands. */
Result DecryptAndStoreGcKeyDeprecated(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source, u32 option); 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); return m_manager.DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, option);
Result DecryptGcMessage(sf::Out<u32> out_size, const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &mod, const sf::InPointerBuffer &label_digest); }
Result GenerateSpecificAesKey(sf::Out<AesKey> out_key, KeySource key_source, u32 generation, u32 which);
Result LoadPreparedAesKey(s32 keyslot, AccessKey access_key); Result DecryptAndStoreGcKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source) {
Result GetPackage2Hash(const sf::OutPointerBuffer &dst); return m_manager.DecryptAndStoreGcKey(src.GetPointer(), src.GetSize(), access_key, key_source, static_cast<u32>(smc::DeviceUniqueDataMode::DecryptAndStoreGcKey));
}
Result DecryptGcMessage(sf::Out<u32> 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<AesKey> 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<FsService>); static_assert(spl::impl::IsIFsInterface<FsService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#include "spl_api_impl.hpp"
#include "spl_general_service.hpp"
namespace ams::spl {
Result GeneralService::GetConfig(sf::Out<u64> out, u32 which) {
return impl::GetConfig(out.GetPointer(), static_cast<spl::ConfigItem>(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<spl::ConfigItem>(which), value);
}
Result GeneralService::GenerateRandomBytes(const sf::OutPointerBuffer &out) {
return impl::GenerateRandomBytes(out.GetPointer(), out.GetSize());
}
Result GeneralService::IsDevelopment(sf::Out<bool> is_dev) {
return impl::IsDevelopment(is_dev.GetPointer());
}
Result GeneralService::SetBootReason(BootReasonValue boot_reason) {
return impl::SetBootReason(boot_reason);
}
Result GeneralService::GetBootReason(sf::Out<BootReasonValue> out) {
return impl::GetBootReason(out.GetPointer());
}
}

View file

@ -15,19 +15,44 @@
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "spl_secure_monitor_manager.hpp"
namespace ams::spl { namespace ams::spl {
class GeneralService { class GeneralService {
protected:
SecureMonitorManager &m_manager;
public:
explicit GeneralService(SecureMonitorManager *manager) : m_manager(*manager) { /* ... */ }
public: public:
/* Actual commands. */ /* Actual commands. */
Result GetConfig(sf::Out<u64> out, u32 which); Result GetConfig(sf::Out<u64> out, u32 key) {
Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod); return m_manager.GetConfig(out.GetPointer(), static_cast<spl::ConfigItem>(key));
Result SetConfig(u32 which, u64 value); }
Result GenerateRandomBytes(const sf::OutPointerBuffer &out);
Result IsDevelopment(sf::Out<bool> is_dev); Result ModularExponentiate(const sf::OutPointerBuffer &out, const sf::InPointerBuffer &base, const sf::InPointerBuffer &exp, const sf::InPointerBuffer &mod) {
Result SetBootReason(BootReasonValue boot_reason); return m_manager.ModularExponentiate(out.GetPointer(), out.GetSize(), base.GetPointer(), base.GetSize(), exp.GetPointer(), exp.GetSize(), mod.GetPointer(), mod.GetSize());
Result GetBootReason(sf::Out<BootReasonValue> out); }
Result SetConfig(u32 key, u64 value) {
return m_manager.SetConfig(static_cast<spl::ConfigItem>(key), value);
}
Result GenerateRandomBytes(const sf::OutPointerBuffer &out) {
return m_manager.GenerateRandomBytes(out.GetPointer(), out.GetSize());
}
Result IsDevelopment(sf::Out<bool> is_dev) {
return m_manager.IsDevelopment(is_dev.GetPointer());
}
Result SetBootReason(BootReasonValue boot_reason) {
return m_manager.SetBootReason(boot_reason);
}
Result GetBootReason(sf::Out<BootReasonValue> out) {
return m_manager.GetBootReason(out.GetPointer());
}
}; };
static_assert(spl::impl::IsIGeneralInterface<GeneralService>); static_assert(spl::impl::IsIGeneralInterface<GeneralService>);

View file

@ -14,7 +14,6 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "spl_api_impl.hpp"
#include "spl_random_service.hpp" #include "spl_random_service.hpp"
#include "spl_general_service.hpp" #include "spl_general_service.hpp"
@ -79,12 +78,13 @@ namespace ams {
class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, SplServerOptions, NumSessions> { class ServerManager final : public sf::hipc::ServerManager<PortIndex_Count, SplServerOptions, NumSessions> {
private: private:
sf::ExpHeapAllocator *m_allocator; sf::ExpHeapAllocator *m_allocator;
spl::SecureMonitorManager *m_secure_monitor_manager;
spl::GeneralService m_general_service; spl::GeneralService m_general_service;
sf::UnmanagedServiceObjectByPointer<spl::impl::IGeneralInterface, spl::GeneralService> m_general_service_object; sf::UnmanagedServiceObjectByPointer<spl::impl::IGeneralInterface, spl::GeneralService> m_general_service_object;
spl::RandomService m_random_service; spl::RandomService m_random_service;
sf::UnmanagedServiceObjectByPointer<spl::impl::IRandomInterface, spl::RandomService> m_random_service_object; sf::UnmanagedServiceObjectByPointer<spl::impl::IRandomInterface, spl::RandomService> m_random_service_object;
public: 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: private:
@ -96,29 +96,32 @@ namespace ams {
alignas(0x40) constinit u8 g_server_allocator_buffer[8_KB]; alignas(0x40) constinit u8 g_server_allocator_buffer[8_KB];
Allocator g_server_allocator; 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) { ams::Result ServerManager::OnNeedsToAccept(int port_index, Server *server) {
switch (port_index) { switch (port_index) {
case PortIndex_General: 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()); return this->AcceptImpl(server, m_general_service_object.GetShared());
} else { } else {
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IDeprecatedGeneralInterface, spl::DeprecatedService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IDeprecatedGeneralInterface, spl::DeprecatedService>(m_allocator, m_secure_monitor_manager));
} }
case PortIndex_Random: case PortIndex_Random:
return this->AcceptImpl(server, m_random_service_object.GetShared()); return this->AcceptImpl(server, m_random_service_object.GetShared());
case PortIndex_Crypto: case PortIndex_Crypto:
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::ICryptoInterface, spl::CryptoService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::ICryptoInterface, spl::CryptoService>(m_allocator, m_secure_monitor_manager));
case PortIndex_Fs: case PortIndex_Fs:
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IFsInterface, spl::FsService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IFsInterface, spl::FsService>(m_allocator, m_secure_monitor_manager));
case PortIndex_Ssl: case PortIndex_Ssl:
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::ISslInterface, spl::SslService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::ISslInterface, spl::SslService>(m_allocator, m_secure_monitor_manager));
case PortIndex_Es: case PortIndex_Es:
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IEsInterface, spl::EsService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IEsInterface, spl::EsService>(m_allocator, m_secure_monitor_manager));
case PortIndex_Manu: case PortIndex_Manu:
return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IManuInterface, spl::ManuService>(m_allocator)); return this->AcceptImpl(server, ObjectFactory::CreateSharedEmplaced<spl::impl::IManuInterface, spl::ManuService>(m_allocator, m_secure_monitor_manager));
AMS_UNREACHABLE_DEFAULT_CASE(); AMS_UNREACHABLE_DEFAULT_CASE();
} }
} }
@ -127,8 +130,10 @@ namespace ams {
/* Setup server allocator. */ /* Setup server allocator. */
g_server_allocator.Attach(lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), lmem::CreateOption_None)); g_server_allocator.Attach(lmem::CreateExpHeap(g_server_allocator_buffer, sizeof(g_server_allocator_buffer), lmem::CreateOption_None));
/* Initialize global context. */ /* Initialize secure monitor manager. */
spl::impl::Initialize(); g_secure_monitor_manager.Initialize();
g_use_new_server = hos::GetVersion() >= hos::Version_4_0_0;
/* Create services. */ /* Create services. */
const auto fw_ver = hos::GetVersion(); const auto fw_ver = hos::GetVersion();

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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);
}
}

View file

@ -14,14 +14,19 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_device_unique_data_service.hpp" #include "spl_device_unique_data_service.hpp"
namespace ams::spl { namespace ams::spl {
class ManuService : public DeviceUniqueDataService { class ManuService : public DeviceUniqueDataService {
public:
explicit ManuService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ }
public: public:
/* Actual commands. */ /* 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<ManuService>); static_assert(spl::impl::IsIManuInterface<ManuService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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());
}
}

View file

@ -15,13 +15,20 @@
*/ */
#pragma once #pragma once
#include <stratosphere.hpp> #include <stratosphere.hpp>
#include "spl_secure_monitor_manager.hpp"
namespace ams::spl { namespace ams::spl {
class RandomService final { class RandomService final {
protected:
SecureMonitorManager &m_manager;
public:
explicit RandomService(SecureMonitorManager *manager) : m_manager(*manager) { /* ... */ }
public: public:
/* Actual commands. */ /* 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<RandomService>); static_assert(spl::impl::IsIRandomInterface<RandomService>);

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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();
}
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stratosphere.hpp>
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();
};
}

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include <stratosphere.hpp>
#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());
}
}

View file

@ -14,15 +14,23 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <stratosphere.hpp>
#include "spl_device_unique_data_service.hpp" #include "spl_device_unique_data_service.hpp"
namespace ams::spl { namespace ams::spl {
class SslService : public DeviceUniqueDataService { class SslService : public DeviceUniqueDataService {
public:
explicit SslService(SecureMonitorManager *manager) : DeviceUniqueDataService(manager) { /* ... */ }
public: public:
/* Actual commands. */ /* Actual commands. */
Result DecryptAndStoreSslClientCertKey(const sf::InPointerBuffer &src, AccessKey access_key, KeySource key_source); 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); 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<SslService>); static_assert(spl::impl::IsISslInterface<SslService>);