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];