diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp new file mode 100644 index 000000000..1eadb7e4e --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_types.hpp" +#include "spl_ctr_drbg.hpp" + +void CtrDrbg::Update(const void *data) { + aes128ContextCreate(&this->aes_ctx, this->key, true); + for (size_t offset = 0; offset < sizeof(this->work[1]); offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter); + } + + Xor(this->work[1], data, sizeof(this->work[1])); + + std::memcpy(this->key, &this->work[1][0], sizeof(this->key)); + std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key)); +} + +void CtrDrbg::Initialize(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + std::memset(this->key, 0, sizeof(this->key)); + std::memset(this->counter, 0, sizeof(this->counter)); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +void CtrDrbg::Reseed(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) { + if (size > MaxRequestSize) { + return false; + } + + if (this->reseed_counter > ReseedInterval) { + return false; + } + + aes128ContextCreate(&this->aes_ctx, this->key, true); + u8 *cur_dst = reinterpret_cast(out); + + size_t aligned_size = (size & ~(BlockSize - 1)); + for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter); + cur_dst += BlockSize; + } + + if (size > aligned_size) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter); + std::memcpy(cur_dst, this->work[1], size - aligned_size); + } + + std::memset(this->work[0], 0, sizeof(this->work[0])); + this->Update(this->work[0]); + + this->reseed_counter++; + return true; + +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp new file mode 100644 index 000000000..71564b582 --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +/* 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 aes_ctx; + u8 counter[BlockSize]; + u8 key[BlockSize]; + u8 work[2][SeedSize]; + u32 reseed_counter; + private: + static void Xor(void *dst, const void *src, size_t size) { + const u8 *src_u8 = reinterpret_cast(src); + u8 *dst_u8 = reinterpret_cast(dst); + + for (size_t i = 0; i < size; i++) { + dst_u8[i] = src_u8[i]; + } + } + + static void IncrementCounter(void *ctr) { + u64 *ctr_64 = reinterpret_cast(ctr); + + ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); + if (!ctr_64[1]) { + ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); + } + } + private: + void Update(const void *data); + public: + void Initialize(const void *seed); + void Reseed(const void *seed); + bool GenerateRandomBytes(void *out, size_t size); +}; \ No newline at end of file diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 2e2f82047..a644814fb 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -89,12 +89,15 @@ static const auto MakeGeneralService = []() { return std::make_shared(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("csrng", 9)); + s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); /* TODO: Other services. */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index a2421d92f..6eeaa0904 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -18,6 +18,40 @@ #include #include "spl_secmon_wrapper.hpp" +#include "spl_smc_wrapper.hpp" +#include "spl_ctr_drbg.hpp" + +/* Globals. */ +static CtrDrbg g_drbg; +static Event g_se_event; +static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; + +void SecureMonitorWrapper::InitializeCtrDrbg() { + u8 seed[CtrDrbg::SeedSize]; + + if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { + std::abort(); + } + + g_drbg.Initialize(seed); +} + +void SecureMonitorWrapper::InitializeSeInterruptEvent() { + u64 irq_num; + SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); + Handle hnd; + if (R_FAILED(svcCreateInterruptEvent(&hnd, irq_num, 1))) { + std::abort(); + } + eventLoadRemote(&g_se_event, hnd, true); +} + +void SecureMonitorWrapper::Initialize() { + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + /* Initialize SE interrupt event. */ + InitializeSeInterruptEvent(); +} Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { if (result == SmcResult_Success) { @@ -30,8 +64,25 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { } Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Nintendo explicitly blacklists package2 hash here, amusingly. */ + /* This is not blacklisted in safemode, but we're never in safe mode... */ + if (which == SplConfigItem_Package2Hash) { + return ResultSplInvalidArgument; + } + + SmcResult res = SmcWrapper::GetConfig(out, 1, which); + + /* Nintendo has some special handling here for hardware type/is_retail. */ + if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + if (which == SplConfigItem_IsRetail && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + + return ConvertToSplResult(res); } Result SecureMonitorWrapper::ExpMod(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) { @@ -40,13 +91,42 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base } Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { - /* TODO */ - return ResultKernelConnectionClosed; + return ConvertToSplResult(SmcWrapper::SetConfig(which, &value, 1)); +} + +Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t size) { + if (!g_drbg.GenerateRandomBytes(out, size)) { + /* We need to reseed. */ + { + u8 seed[CtrDrbg::SeedSize]; + + SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + g_drbg.Reseed(seed); + g_drbg.GenerateRandomBytes(out, size); + } + } + + return ResultSuccess; } Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { - /* TODO */ - return ResultKernelConnectionClosed; + u8 *cur_dst = reinterpret_cast(out); + + for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { + const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); + + Result rc = GenerateRandomBytesInternal(cur_dst, size); + if (R_FAILED(rc)) { + return rc; + } + cur_dst += cur_size; + } + + return ResultSuccess; } Result SecureMonitorWrapper::IsDevelopment(bool *out) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 5c7104e4d..820663f99 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -40,8 +40,13 @@ class SecureMonitorWrapper { return this->boot_reason_set; } static Result ConvertToSplResult(SmcResult result); + private: + static void InitializeCtrDrbg(); + static void InitializeSeInterruptEvent(); public: - void Initialize(); + static void Initialize(); + private: + Result GenerateRandomBytesInternal(void *out, size_t size); public: Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(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); diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp new file mode 100644 index 000000000..97e39afc5 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_smc_wrapper.hpp" + +enum SmcFunctionId : u32 { + SmcFunctionId_SetConfig = 0xC3000401, + SmcFunctionId_GetConfig = 0xC3000002, + SmcFunctionId_CheckStatus = 0xC3000003, + SmcFunctionId_GetResult = 0xC3000404, + SmcFunctionId_ExpMod = 0xC3000E05, + SmcFunctionId_GenerateRandomBytes = 0xC3000006, + SmcFunctionId_GenerateAesKek = 0xC3000007, + SmcFunctionId_LoadAesKey = 0xC3000008, + SmcFunctionId_CryptAes = 0xC3000009, + SmcFunctionId_GenerateSpecificAesKey = 0xC300000A, + SmcFunctionId_ComputeCmac = 0xC300040B, + SmcFunctionId_ReEncryptRsaPrivateKey = 0xC300D60C, + SmcFunctionId_DecryptOrImportRsaPrivateKey = 0xC300100D, + + SmcFunctionId_SecureExpMod = 0xC300060F, + SmcFunctionId_UnwrapTitleKey = 0xC3000610, + SmcFunctionId_LoadTitleKey = 0xC3000011, + SmcFunctionId_UnwrapCommonTitleKey = 0xC3000012, + + /* Deprecated functions. */ + SmcFunctionId_ImportEsKey = 0xC300100C, + SmcFunctionId_DecryptRsaPrivateKey = 0xC300100D, + SmcFunctionId_ImportSecureExpModKey = 0xC300100E, +}; + +SmcResult SmcWrapper::SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SetConfig; + args.X[1] = which; + args.X[2] = 0; + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + args.X[3 + i] = value[i]; + } + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetConfig(u64 *out, size_t num_qwords, SplConfigItem which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetConfig; + args.X[1] = which; + svcCallSecureMonitor(&args); + + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + out[i] = args.X[1 + i]; + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CheckStatus(SmcResult *out, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CheckStatus; + args.X[1] = op.value; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetResult; + args.X[1] = op.value; + args.X[2] = reinterpret_cast(out_buf); + args.X[3] = out_buf_size; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(exp); + args.X[3] = reinterpret_cast(mod); + args.X[4] = exp_size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateRandomBytes; + args.X[1] = size; + svcCallSecureMonitor(&args); + + if (args.X[0] == SmcResult_Success && (size <= sizeof(args) - sizeof(args.X[0]))) { + std::memcpy(out, &args.X[1], size); + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateAesKek; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = option; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadAesKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + args.X[4] = source[0]; + args.X[5] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CryptAes; + args.X[1] = mode; + args.X[2] = iv_ctr[0]; + args.X[3] = iv_ctr[1]; + args.X[4] = src_addr; + args.X[5] = dst_addr; + args.X[6] = size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateSpecificAesKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = which; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ComputeCmac; + args.X[1] = keyslot; + args.X[2] = reinterpret_cast(data); + args.X[3] = size; + svcCallSecureMonitor(&args); + + out_mac.data64[0] = args.X[1]; + out_mac.data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey; + args.X[1] = reinterpret_cast(&access_key_dec); + args.X[2] = reinterpret_cast(&access_key_enc); + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = reinterpret_cast(source_dec); + args.X[7] = reinterpret_cast(source_enc); + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SecureExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + args.X[3] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapTitleKey; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + std::memset(&args.X[3], 0, 4 * sizeof(args.X[3])); + std::memcpy(&args.X[3], label_digest, std::min(size_t(4 * sizeof(args.X[3])), label_digest_size)); + args.X[7] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadTitleKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + + +/* Deprecated functions. */ +SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportEsKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + *out_size = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportSecureExpModKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp new file mode 100644 index 000000000..41a97cf40 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +class SmcWrapper { + public: + static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); + static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); + static SmcResult CheckStatus(SmcResult *out, AsyncOperationKey op); + static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); + static SmcResult ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); + static SmcResult GenerateRandomBytes(void *out, size_t size); + static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); + static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); + static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + static SmcResult GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which); + static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); + static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option); + static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option); + static SmcResult UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option); + static SmcResult LoadTitleKey(u32 keyslot, const AccessKey &access_key); + static SmcResult UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation); + + /* Deprecated functions. */ + static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index 41745f74c..8d8317a48 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -66,12 +66,20 @@ struct IvCtr { }; struct Cmac { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { u8 data[AES_128_KEY_SIZE];