mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-11-09 22:56:35 +00:00
spl: continue implementing.
This commit is contained in:
parent
9858d6fc95
commit
2dfa1c96d1
8 changed files with 628 additions and 10 deletions
81
stratosphere/spl/source/spl_ctr_drbg.cpp
Normal file
81
stratosphere/spl/source/spl_ctr_drbg.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#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<u8 *>(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;
|
||||||
|
|
||||||
|
}
|
60
stratosphere/spl/source/spl_ctr_drbg.hpp
Normal file
60
stratosphere/spl/source/spl_ctr_drbg.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#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<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);
|
||||||
|
};
|
|
@ -89,12 +89,15 @@ static const auto MakeGeneralService = []() { return std::make_shared<GeneralSer
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
consoleDebugInit(debugDevice_SVC);
|
consoleDebugInit(debugDevice_SVC);
|
||||||
|
|
||||||
|
/* Initialize global context. */
|
||||||
|
SecureMonitorWrapper::Initialize();
|
||||||
|
|
||||||
/* Create server manager. */
|
/* Create server manager. */
|
||||||
static auto s_server_manager = WaitableManager<SplServerOptions>(1);
|
static auto s_server_manager = WaitableManager<SplServerOptions>(1);
|
||||||
|
|
||||||
/* Create services. */
|
/* Create services. */
|
||||||
s_server_manager.AddWaitable(new ServiceServer<RandomService, +MakeRandomService>("csrng", 9));
|
s_server_manager.AddWaitable(new ServiceServer<RandomService, +MakeRandomService>("csrng", 3));
|
||||||
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) {
|
if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) {
|
||||||
s_server_manager.AddWaitable(new ServiceServer<GeneralService, +MakeGeneralService>("spl:", 9));
|
s_server_manager.AddWaitable(new ServiceServer<GeneralService, +MakeGeneralService>("spl:", 9));
|
||||||
/* TODO: Other services. */
|
/* TODO: Other services. */
|
||||||
|
|
|
@ -18,6 +18,40 @@
|
||||||
#include <stratosphere.hpp>
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
#include "spl_secmon_wrapper.hpp"
|
#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) {
|
Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) {
|
||||||
if (result == SmcResult_Success) {
|
if (result == SmcResult_Success) {
|
||||||
|
@ -30,8 +64,25 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) {
|
Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) {
|
||||||
/* TODO */
|
/* Nintendo explicitly blacklists package2 hash here, amusingly. */
|
||||||
return ResultKernelConnectionClosed;
|
/* 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) {
|
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) {
|
Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) {
|
||||||
/* TODO */
|
return ConvertToSplResult(SmcWrapper::SetConfig(which, &value, 1));
|
||||||
return ResultKernelConnectionClosed;
|
}
|
||||||
|
|
||||||
|
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) {
|
Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) {
|
||||||
/* TODO */
|
u8 *cur_dst = reinterpret_cast<u8 *>(out);
|
||||||
return ResultKernelConnectionClosed;
|
|
||||||
|
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) {
|
Result SecureMonitorWrapper::IsDevelopment(bool *out) {
|
||||||
|
|
|
@ -40,8 +40,13 @@ class SecureMonitorWrapper {
|
||||||
return this->boot_reason_set;
|
return this->boot_reason_set;
|
||||||
}
|
}
|
||||||
static Result ConvertToSplResult(SmcResult result);
|
static Result ConvertToSplResult(SmcResult result);
|
||||||
|
private:
|
||||||
|
static void InitializeCtrDrbg();
|
||||||
|
static void InitializeSeInterruptEvent();
|
||||||
public:
|
public:
|
||||||
void Initialize();
|
static void Initialize();
|
||||||
|
private:
|
||||||
|
Result GenerateRandomBytesInternal(void *out, size_t size);
|
||||||
public:
|
public:
|
||||||
Result GetConfig(u64 *out, SplConfigItem which);
|
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);
|
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);
|
||||||
|
|
334
stratosphere/spl/source/spl_smc_wrapper.cpp
Normal file
334
stratosphere/spl/source/spl_smc_wrapper.cpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#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<SmcResult>(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<SmcResult>(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<SmcResult>(args.X[1]);
|
||||||
|
return static_cast<SmcResult>(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<u64>(out_buf);
|
||||||
|
args.X[3] = out_buf_size;
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
*out = static_cast<SmcResult>(args.X[1]);
|
||||||
|
return static_cast<SmcResult>(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<u64>(base);
|
||||||
|
args.X[2] = reinterpret_cast<u64>(exp);
|
||||||
|
args.X[3] = reinterpret_cast<u64>(mod);
|
||||||
|
args.X[4] = exp_size;
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
out_op->value = args.X[1];
|
||||||
|
return static_cast<SmcResult>(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<SmcResult>(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<SmcResult>(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<SmcResult>(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<SmcResult>(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<SmcResult>(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<u64>(data);
|
||||||
|
args.X[3] = size;
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
out_mac.data64[0] = args.X[1];
|
||||||
|
out_mac.data64[1] = args.X[2];
|
||||||
|
return static_cast<SmcResult>(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<u64>(&access_key_dec);
|
||||||
|
args.X[2] = reinterpret_cast<u64>(&access_key_enc);
|
||||||
|
args.X[3] = option;
|
||||||
|
args.X[4] = reinterpret_cast<u64>(data);
|
||||||
|
args.X[5] = size;
|
||||||
|
args.X[6] = reinterpret_cast<u64>(source_dec);
|
||||||
|
args.X[7] = reinterpret_cast<u64>(source_enc);
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(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<u64>(data);
|
||||||
|
args.X[5] = size;
|
||||||
|
args.X[6] = source[0];
|
||||||
|
args.X[7] = source[1];
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(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<u64>(base);
|
||||||
|
args.X[2] = reinterpret_cast<u64>(mod);
|
||||||
|
args.X[3] = option;
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
out_op->value = args.X[1];
|
||||||
|
return static_cast<SmcResult>(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<u64>(base);
|
||||||
|
args.X[2] = reinterpret_cast<u64>(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<SmcResult>(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<SmcResult>(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<SmcResult>(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<u64>(data);
|
||||||
|
args.X[5] = size;
|
||||||
|
args.X[6] = source[0];
|
||||||
|
args.X[7] = source[1];
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(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<u64>(data);
|
||||||
|
args.X[5] = size;
|
||||||
|
args.X[6] = source[0];
|
||||||
|
args.X[7] = source[1];
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
*out_size = static_cast<size_t>(args.X[1]);
|
||||||
|
return static_cast<SmcResult>(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<u64>(data);
|
||||||
|
args.X[5] = size;
|
||||||
|
args.X[6] = source[0];
|
||||||
|
args.X[7] = source[1];
|
||||||
|
svcCallSecureMonitor(&args);
|
||||||
|
|
||||||
|
return static_cast<SmcResult>(args.X[0]);
|
||||||
|
}
|
||||||
|
|
47
stratosphere/spl/source/spl_smc_wrapper.hpp
Normal file
47
stratosphere/spl/source/spl_smc_wrapper.hpp
Normal file
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <switch.h>
|
||||||
|
#include <stratosphere.hpp>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
};
|
|
@ -66,12 +66,20 @@ struct IvCtr {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Cmac {
|
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 {
|
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 {
|
struct KeySource {
|
||||||
u8 data[AES_128_KEY_SIZE];
|
u8 data[AES_128_KEY_SIZE];
|
||||||
|
|
Loading…
Reference in a new issue