spl: implement CryptoService.

This commit is contained in:
Michael Scire 2019-04-24 21:00:39 -07:00
parent ccbab35deb
commit bfa84e27c1
12 changed files with 558 additions and 102 deletions

View file

@ -0,0 +1,56 @@
/*
* 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_crypto_service.hpp"
Result CryptoService::GenerateAesKek(Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option) {
return this->GetSecureMonitorWrapper()->GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option);
}
Result CryptoService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) {
return this->GetSecureMonitorWrapper()->LoadAesKey(keyslot, this, access_key, key_source);
}
Result CryptoService::GenerateAesKey(Out<AesKey> out_key, AccessKey access_key, KeySource key_source) {
return this->GetSecureMonitorWrapper()->GenerateAesKey(out_key.GetPointer(), access_key, key_source);
}
Result CryptoService::DecryptAesKey(Out<AesKey> out_key, KeySource key_source, u32 generation, u32 option) {
return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option);
}
Result CryptoService::CryptAesCtr(OutBuffer<u8> out_buf, u32 keyslot, InBuffer<u8> in_buf, IvCtr iv_ctr) {
return this->GetSecureMonitorWrapper()->CryptAesCtr(out_buf.buffer, out_buf.num_elements, keyslot, this, in_buf.buffer, in_buf.num_elements, iv_ctr);
}
Result CryptoService::ComputeCmac(Out<Cmac> out_cmac, u32 keyslot, InPointer<u8> in_buf) {
return this->GetSecureMonitorWrapper()->ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.pointer, in_buf.num_elements);
}
Result CryptoService::AllocateAesKeyslot(Out<u32> out_keyslot) {
return this->GetSecureMonitorWrapper()->AllocateAesKeyslot(out_keyslot.GetPointer(), this);
}
Result CryptoService::FreeAesKeyslot(u32 keyslot) {
return this->GetSecureMonitorWrapper()->FreeAesKeyslot(keyslot, this);
}
void CryptoService::GetAesKeyslotAvailableEvent(Out<CopiedHandle> out_hnd) {
out_hnd.SetValue(this->GetSecureMonitorWrapper()->GetAesKeyslotAvailableEventHandle());
}

View file

@ -0,0 +1,64 @@
/*
* 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"
#include "spl_general_service.hpp"
class CryptoService : public GeneralService {
public:
CryptoService(SecureMonitorWrapper *sw) : GeneralService(sw) {
/* ... */
}
virtual ~CryptoService() {
this->GetSecureMonitorWrapper()->FreeAesKeyslots(this);
}
protected:
/* Actual commands. */
virtual Result GenerateAesKek(Out<AccessKey> out_access_key, KeySource key_source, u32 generation, u32 option);
virtual Result LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source);
virtual Result GenerateAesKey(Out<AesKey> out_key, AccessKey access_key, KeySource key_source);
virtual Result DecryptAesKey(Out<AesKey> out_key, KeySource key_source, u32 generation, u32 option);
virtual Result CryptAesCtr(OutBuffer<u8> out_buf, u32 keyslot, InBuffer<u8> in_buf, IvCtr iv_ctr);
virtual Result ComputeCmac(Out<Cmac> out_cmac, u32 keyslot, InPointer<u8> in_buf);
virtual Result AllocateAesKeyslot(Out<u32> out_keyslot);
virtual Result FreeAesKeyslot(u32 keyslot);
virtual void GetAesKeyslotAvailableEvent(Out<CopiedHandle> out_hnd);
public:
DEFINE_SERVICE_DISPATCH_TABLE {
MakeServiceCommandMeta<Spl_Cmd_GetConfig, &CryptoService::GetConfig>(),
MakeServiceCommandMeta<Spl_Cmd_ExpMod, &CryptoService::ExpMod>(),
MakeServiceCommandMeta<Spl_Cmd_SetConfig, &CryptoService::SetConfig>(),
MakeServiceCommandMeta<Spl_Cmd_GenerateRandomBytes, &CryptoService::GenerateRandomBytes>(),
MakeServiceCommandMeta<Spl_Cmd_IsDevelopment, &CryptoService::IsDevelopment>(),
MakeServiceCommandMeta<Spl_Cmd_SetBootReason, &CryptoService::SetBootReason, FirmwareVersion_300>(),
MakeServiceCommandMeta<Spl_Cmd_GetBootReason, &CryptoService::GetBootReason, FirmwareVersion_300>(),
MakeServiceCommandMeta<Spl_Cmd_GenerateAesKek, &CryptoService::GenerateAesKek>(),
MakeServiceCommandMeta<Spl_Cmd_LoadAesKey, &CryptoService::LoadAesKey>(),
MakeServiceCommandMeta<Spl_Cmd_GenerateAesKey, &CryptoService::GenerateAesKey>(),
MakeServiceCommandMeta<Spl_Cmd_DecryptAesKey, &CryptoService::DecryptAesKey>(),
MakeServiceCommandMeta<Spl_Cmd_CryptAesCtr, &CryptoService::CryptAesCtr>(),
MakeServiceCommandMeta<Spl_Cmd_ComputeCmac, &CryptoService::ComputeCmac>(),
MakeServiceCommandMeta<Spl_Cmd_AllocateAesKeyslot, &CryptoService::AllocateAesKeyslot, FirmwareVersion_200>(),
MakeServiceCommandMeta<Spl_Cmd_FreeAesKeyslot, &CryptoService::FreeAesKeyslot, FirmwareVersion_200>(),
MakeServiceCommandMeta<Spl_Cmd_GetAesKeyslotAvailableEvent, &CryptoService::GetAesKeyslotAvailableEvent, FirmwareVersion_200>(),
};
};

View file

@ -26,9 +26,9 @@ void CtrDrbg::Update(const void *data) {
IncrementCounter(this->counter); IncrementCounter(this->counter);
aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter); aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter);
} }
Xor(this->work[1], data, sizeof(this->work[1])); Xor(this->work[1], data, sizeof(this->work[1]));
std::memcpy(this->key, &this->work[1][0], sizeof(this->key)); std::memcpy(this->key, &this->work[1][0], sizeof(this->key));
std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key)); std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key));
} }
@ -51,31 +51,31 @@ bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) {
if (size > MaxRequestSize) { if (size > MaxRequestSize) {
return false; return false;
} }
if (this->reseed_counter > ReseedInterval) { if (this->reseed_counter > ReseedInterval) {
return false; return false;
} }
aes128ContextCreate(&this->aes_ctx, this->key, true); aes128ContextCreate(&this->aes_ctx, this->key, true);
u8 *cur_dst = reinterpret_cast<u8 *>(out); u8 *cur_dst = reinterpret_cast<u8 *>(out);
size_t aligned_size = (size & ~(BlockSize - 1)); size_t aligned_size = (size & ~(BlockSize - 1));
for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { for (size_t offset = 0; offset < aligned_size; offset += BlockSize) {
IncrementCounter(this->counter); IncrementCounter(this->counter);
aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter); aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter);
cur_dst += BlockSize; cur_dst += BlockSize;
} }
if (size > aligned_size) { if (size > aligned_size) {
IncrementCounter(this->counter); IncrementCounter(this->counter);
aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter); aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter);
std::memcpy(cur_dst, this->work[1], size - aligned_size); std::memcpy(cur_dst, this->work[1], size - aligned_size);
} }
std::memset(this->work[0], 0, sizeof(this->work[0])); std::memset(this->work[0], 0, sizeof(this->work[0]));
this->Update(this->work[0]); this->Update(this->work[0]);
this->reseed_counter++; this->reseed_counter++;
return true; return true;
} }

View file

@ -42,10 +42,10 @@ class CtrDrbg {
dst_u8[i] = src_u8[i]; dst_u8[i] = src_u8[i];
} }
} }
static void IncrementCounter(void *ctr) { static void IncrementCounter(void *ctr) {
u64 *ctr_64 = reinterpret_cast<u64 *>(ctr); u64 *ctr_64 = reinterpret_cast<u64 *>(ctr);
ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1);
if (!ctr_64[1]) { if (!ctr_64[1]) {
ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1);

View file

@ -20,29 +20,29 @@
#include "spl_general_service.hpp" #include "spl_general_service.hpp"
Result GeneralService::GetConfig(Out<u64> out, u32 which) { Result GeneralService::GetConfig(Out<u64> out, u32 which) {
return this->secmon_wrapper->GetConfig(out.GetPointer(), static_cast<SplConfigItem>(which)); return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast<SplConfigItem>(which));
} }
Result GeneralService::ExpMod(OutPointerWithClientSize<u8> out, InPointer<u8> base, InPointer<u8> exp, InPointer<u8> mod) { Result GeneralService::ExpMod(OutPointerWithClientSize<u8> out, InPointer<u8> base, InPointer<u8> exp, InPointer<u8> mod) {
return this->secmon_wrapper->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); return this->GetSecureMonitorWrapper()->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements);
} }
Result GeneralService::SetConfig(u32 which, u64 value) { Result GeneralService::SetConfig(u32 which, u64 value) {
return this->secmon_wrapper->SetConfig(static_cast<SplConfigItem>(which), value); return this->GetSecureMonitorWrapper()->SetConfig(static_cast<SplConfigItem>(which), value);
} }
Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize<u8> out) { Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize<u8> out) {
return this->secmon_wrapper->GenerateRandomBytes(out.pointer, out.num_elements); return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements);
} }
Result GeneralService::IsDevelopment(Out<bool> is_dev) { Result GeneralService::IsDevelopment(Out<bool> is_dev) {
return this->secmon_wrapper->IsDevelopment(is_dev.GetPointer()); return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer());
} }
Result GeneralService::SetBootReason(BootReasonValue boot_reason) { Result GeneralService::SetBootReason(BootReasonValue boot_reason) {
return this->secmon_wrapper->SetBootReason(boot_reason); return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason);
} }
Result GeneralService::GetBootReason(Out<BootReasonValue> out) { Result GeneralService::GetBootReason(Out<BootReasonValue> out) {
return this->secmon_wrapper->GetBootReason(out.GetPointer()); return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer());
} }

View file

@ -21,7 +21,7 @@
#include "spl_types.hpp" #include "spl_types.hpp"
#include "spl_secmon_wrapper.hpp" #include "spl_secmon_wrapper.hpp"
class GeneralService final : public IServiceObject { class GeneralService : public IServiceObject {
private: private:
SecureMonitorWrapper *secmon_wrapper; SecureMonitorWrapper *secmon_wrapper;
public: public:
@ -30,7 +30,11 @@ class GeneralService final : public IServiceObject {
} }
virtual ~GeneralService() { /* ... */ } virtual ~GeneralService() { /* ... */ }
private: protected:
SecureMonitorWrapper *GetSecureMonitorWrapper() const {
return this->secmon_wrapper;
}
protected:
/* Actual commands. */ /* Actual commands. */
virtual Result GetConfig(Out<u64> out, u32 which); virtual Result GetConfig(Out<u64> out, u32 which);
virtual Result ExpMod(OutPointerWithClientSize<u8> out, InPointer<u8> base, InPointer<u8> exp, InPointer<u8> mod); virtual Result ExpMod(OutPointerWithClientSize<u8> out, InPointer<u8> base, InPointer<u8> exp, InPointer<u8> mod);

View file

@ -24,6 +24,7 @@
#include "spl_random_service.hpp" #include "spl_random_service.hpp"
#include "spl_general_service.hpp" #include "spl_general_service.hpp"
#include "spl_crypto_service.hpp"
extern "C" { extern "C" {
extern u32 __start__; extern u32 __start__;
@ -83,8 +84,9 @@ struct SplServerOptions {
static SecureMonitorWrapper s_secmon_wrapper; static SecureMonitorWrapper s_secmon_wrapper;
/* Helpers for creating services. */ /* Helpers for creating services. */
static const auto MakeRandomService = []() { return std::make_shared<RandomService>(&s_secmon_wrapper); }; static const auto MakeRandomService = []() { return std::make_shared<RandomService>(&s_secmon_wrapper); };
static const auto MakeGeneralService = []() { return std::make_shared<GeneralService>(&s_secmon_wrapper); }; static const auto MakeGeneralService = []() { return std::make_shared<GeneralService>(&s_secmon_wrapper); };
static const auto MakeCryptoService = []() { return std::make_shared<CryptoService>(&s_secmon_wrapper); };
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@ -100,6 +102,7 @@ int main(int argc, char **argv)
s_server_manager.AddWaitable(new ServiceServer<RandomService, +MakeRandomService>("csrng", 3)); 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));
s_server_manager.AddWaitable(new ServiceServer<GeneralService, +MakeCryptoService>("spl:mig", 6));
/* TODO: Other services. */ /* TODO: Other services. */
} else { } else {
/* TODO, DeprecatedGeneralService */ /* TODO, DeprecatedGeneralService */

View file

@ -24,29 +24,66 @@
/* Convenient. */ /* Convenient. */
constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; constexpr size_t DeviceAddressSpaceAlignSize = 0x400000;
constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1;
constexpr u32 DeviceMapBase = 0x80000000u; constexpr u32 WorkBufferMapBase = 0x80000000u;
constexpr u32 CryptAesInMapBase = 0x90000000u;
constexpr u32 CryptAesOutMapBase = 0xC0000000u;
constexpr size_t CryptAesSizeMax = static_cast<size_t>(CryptAesOutMapBase - CryptAesInMapBase);
/* Types. */
struct SeLinkedListEntry {
u32 num_entries;
u32 address;
u32 size;
};
struct SeCryptContext {
SeLinkedListEntry in;
SeLinkedListEntry out;
};
class DeviceAddressSpaceMapHelper {
private:
Handle das_hnd;
u64 dst_addr;
u64 src_addr;
size_t size;
u32 perm;
public:
DeviceAddressSpaceMapHelper(Handle h, u64 dst, u64 src, size_t sz, u32 p) : das_hnd(h), dst_addr(dst), src_addr(src), size(sz), perm(p) {
if (R_FAILED(svcMapDeviceAddressSpaceAligned(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr, this->perm))) {
std::abort();
}
}
~DeviceAddressSpaceMapHelper() {
if (R_FAILED(svcUnmapDeviceAddressSpace(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr))) {
std::abort();
}
}
};
/* Globals. */ /* Globals. */
static CtrDrbg g_drbg; static CtrDrbg g_drbg;
static Event g_se_event; static Event g_se_event;
static IEvent *g_se_keyslot_available_event = nullptr;
static Handle g_se_das_hnd; static Handle g_se_das_hnd;
static u32 g_se_mapped_work_buffer_addr; static u32 g_se_mapped_work_buffer_addr;
static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000];
constexpr size_t MaxWorkBufferSize = sizeof(g_work_buffer) / 2;
static HosMutex g_se_lock; static HosMutex g_async_op_lock;
void SecureMonitorWrapper::InitializeCtrDrbg() { void SecureMonitorWrapper::InitializeCtrDrbg() {
u8 seed[CtrDrbg::SeedSize]; u8 seed[CtrDrbg::SeedSize];
if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) {
std::abort(); std::abort();
} }
g_drbg.Initialize(seed); g_drbg.Initialize(seed);
} }
void SecureMonitorWrapper::InitializeSeInterruptEvent() { void SecureMonitorWrapper::InitializeSeEvents() {
u64 irq_num; u64 irq_num;
SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber);
Handle hnd; Handle hnd;
@ -54,6 +91,9 @@ void SecureMonitorWrapper::InitializeSeInterruptEvent() {
std::abort(); std::abort();
} }
eventLoadRemote(&g_se_event, hnd, true); eventLoadRemote(&g_se_event, hnd, true);
g_se_keyslot_available_event = CreateWriteOnlySystemEvent();
g_se_keyslot_available_event->Signal();
} }
void SecureMonitorWrapper::InitializeDeviceAddressSpace() { void SecureMonitorWrapper::InitializeDeviceAddressSpace() {
@ -67,10 +107,10 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() {
if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_SE, g_se_das_hnd))) { if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_SE, g_se_das_hnd))) {
std::abort(); std::abort();
} }
const u64 work_buffer_addr = reinterpret_cast<u64>(g_work_buffer); const u64 work_buffer_addr = reinterpret_cast<u64>(g_work_buffer);
g_se_mapped_work_buffer_addr = 0x80000000u + (work_buffer_addr & DeviceAddressSpaceAlignMask); g_se_mapped_work_buffer_addr = WorkBufferMapBase + (work_buffer_addr & DeviceAddressSpaceAlignMask);
/* Map the work buffer for the SE. */ /* Map the work buffer for the SE. */
if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_se_das_hnd, CUR_PROCESS_HANDLE, work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, 3))) { if (R_FAILED(svcMapDeviceAddressSpaceAligned(g_se_das_hnd, CUR_PROCESS_HANDLE, work_buffer_addr, sizeof(g_work_buffer), g_se_mapped_work_buffer_addr, 3))) {
std::abort(); std::abort();
@ -80,8 +120,8 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() {
void SecureMonitorWrapper::Initialize() { void SecureMonitorWrapper::Initialize() {
/* Initialize the Drbg. */ /* Initialize the Drbg. */
InitializeCtrDrbg(); InitializeCtrDrbg();
/* Initialize SE interrupt event. */ /* Initialize SE interrupt + keyslot events. */
InitializeSeInterruptEvent(); InitializeSeEvents();
/* Initialize DAS for the SE. */ /* Initialize DAS for the SE. */
InitializeDeviceAddressSpace(); InitializeDeviceAddressSpace();
} }
@ -102,37 +142,78 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) {
SmcResult SecureMonitorWrapper::WaitCheckStatus(AsyncOperationKey op_key) { SmcResult SecureMonitorWrapper::WaitCheckStatus(AsyncOperationKey op_key) {
WaitSeOperationComplete(); WaitSeOperationComplete();
SmcResult op_res; SmcResult op_res;
SmcResult res = SmcWrapper::CheckStatus(&op_res, op_key); SmcResult res = SmcWrapper::CheckStatus(&op_res, op_key);
if (res != SmcResult_Success) { if (res != SmcResult_Success) {
return res; return res;
} }
return op_res; return op_res;
} }
SmcResult SecureMonitorWrapper::WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key) { SmcResult SecureMonitorWrapper::WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key) {
WaitSeOperationComplete(); WaitSeOperationComplete();
SmcResult op_res; SmcResult op_res;
SmcResult res = SmcWrapper::GetResult(&op_res, out_buf, out_buf_size, op_key); SmcResult res = SmcWrapper::GetResult(&op_res, out_buf, out_buf_size, op_key);
if (res != SmcResult_Success) { if (res != SmcResult_Success) {
return res; return res;
} }
return op_res; return op_res;
} }
SmcResult SecureMonitorWrapper::DecryptAesBlock(u32 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));
armDCacheFlush(layout, sizeof(*layout));
{
std::scoped_lock<HosMutex> lk(g_async_op_lock);
AsyncOperationKey op_key;
const IvCtr iv_ctr = {};
const u32 mode = SmcWrapper::GetCryptAesMode(SmcCipherMode_CbcDecrypt, keyslot);
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);
SmcResult res = SmcWrapper::CryptAes(&op_key, mode, iv_ctr, dst_ll_addr, src_ll_addr, sizeof(layout->in_block));
if (res != SmcResult_Success) {
return res;
}
if ((res = WaitCheckStatus(op_key)) != SmcResult_Success) {
return res;
}
}
armDCacheFlush(layout, sizeof(*layout));
std::memcpy(dst, layout->out_block, sizeof(layout->out_block));
return SmcResult_Success;
}
Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) {
/* Nintendo explicitly blacklists package2 hash here, amusingly. */ /* Nintendo explicitly blacklists package2 hash here, amusingly. */
/* This is not blacklisted in safemode, but we're never in safe mode... */ /* This is not blacklisted in safemode, but we're never in safe mode... */
if (which == SplConfigItem_Package2Hash) { if (which == SplConfigItem_Package2Hash) {
return ResultSplInvalidArgument; return ResultSplInvalidArgument;
} }
SmcResult res = SmcWrapper::GetConfig(out, 1, which); SmcResult res = SmcWrapper::GetConfig(out, 1, which);
/* Nintendo has some special handling here for hardware type/is_retail. */ /* Nintendo has some special handling here for hardware type/is_retail. */
if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) { if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) {
*out = 0; *out = 0;
@ -142,7 +223,7 @@ Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) {
*out = 0; *out = 0;
res = SmcResult_Success; res = SmcResult_Success;
} }
return ConvertToSplResult(res); return ConvertToSplResult(res);
} }
@ -153,7 +234,7 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base
u8 mod[0x100]; u8 mod[0x100];
}; };
ExpModLayout *layout = reinterpret_cast<ExpModLayout *>(g_work_buffer); ExpModLayout *layout = reinterpret_cast<ExpModLayout *>(g_work_buffer);
/* Validate sizes. */ /* Validate sizes. */
if (base_size > sizeof(layout->base)) { if (base_size > sizeof(layout->base)) {
return ResultSplInvalidSize; return ResultSplInvalidSize;
@ -164,10 +245,10 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base
if (mod_size > sizeof(layout->mod)) { if (mod_size > sizeof(layout->mod)) {
return ResultSplInvalidSize; return ResultSplInvalidSize;
} }
if (out_size > sizeof(g_work_buffer) / 2) { if (out_size > MaxWorkBufferSize) {
return ResultSplInvalidSize; return ResultSplInvalidSize;
} }
/* Copy data into work buffer. */ /* Copy data into work buffer. */
const size_t base_ofs = sizeof(layout->base) - base_size; const size_t base_ofs = sizeof(layout->base) - base_size;
const size_t mod_ofs = sizeof(layout->mod) - mod_size; const size_t mod_ofs = sizeof(layout->mod) - mod_size;
@ -175,22 +256,24 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base
std::memcpy(layout->base + base_ofs, base, base_size); std::memcpy(layout->base + base_ofs, base, base_size);
std::memcpy(layout->exp, exp, exp_size); std::memcpy(layout->exp, exp, exp_size);
std::memcpy(layout->mod + mod_ofs, mod, mod_size); std::memcpy(layout->mod + mod_ofs, mod, mod_size);
/* Do exp mod operation. */ /* Do exp mod operation. */
armDCacheFlush(layout, sizeof(*layout));
{ {
std::scoped_lock<HosMutex> lk(g_se_lock); std::scoped_lock<HosMutex> lk(g_async_op_lock);
AsyncOperationKey op_key; AsyncOperationKey op_key;
SmcResult res = SmcWrapper::ExpMod(&op_key, layout->base, layout->exp, exp_size, layout->mod); SmcResult res = SmcWrapper::ExpMod(&op_key, layout->base, layout->exp, exp_size, layout->mod);
if (res != SmcResult_Success) { if (res != SmcResult_Success) {
return ConvertToSplResult(res); return ConvertToSplResult(res);
} }
if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) {
return ConvertToSplResult(res); return ConvertToSplResult(res);
} }
} }
armDCacheFlush(g_work_buffer, sizeof(out_size));
std::memcpy(out, g_work_buffer, out_size); std::memcpy(out, g_work_buffer, out_size);
return ResultSuccess; return ResultSuccess;
} }
@ -204,17 +287,17 @@ Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t size)
/* We need to reseed. */ /* We need to reseed. */
{ {
u8 seed[CtrDrbg::SeedSize]; u8 seed[CtrDrbg::SeedSize];
SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)); SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed));
if (res != SmcResult_Success) { if (res != SmcResult_Success) {
return ConvertToSplResult(res); return ConvertToSplResult(res);
} }
g_drbg.Reseed(seed); g_drbg.Reseed(seed);
g_drbg.GenerateRandomBytes(out, size); g_drbg.GenerateRandomBytes(out, size);
} }
} }
return ResultSuccess; return ResultSuccess;
} }
@ -223,7 +306,7 @@ Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) {
for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) {
const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize);
Result rc = GenerateRandomBytesInternal(cur_dst, size); Result rc = GenerateRandomBytesInternal(cur_dst, size);
if (R_FAILED(rc)) { if (R_FAILED(rc)) {
return rc; return rc;
@ -262,4 +345,190 @@ Result SecureMonitorWrapper::GetBootReason(BootReasonValue *out) {
*out = GetBootReason(); *out = GetBootReason();
return ResultSuccess; return ResultSuccess;
}
Result SecureMonitorWrapper::GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option) {
return ConvertToSplResult(SmcWrapper::GenerateAesKek(out_access_key, key_source, generation, option));
}
Result SecureMonitorWrapper::LoadAesKey(u32 keyslot, const void *owner, const AccessKey &access_key, const KeySource &key_source) {
Result rc = ValidateAesKeyslot(keyslot, owner);
if (R_FAILED(rc)) {
return rc;
}
return ConvertToSplResult(SmcWrapper::LoadAesKey(keyslot, access_key, key_source));
}
Result SecureMonitorWrapper::GenerateAesKey(AesKey *out_key, const AccessKey &access_key, const KeySource &key_source) {
Result rc;
SmcResult smc_rc;
static const 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(this);
if (R_FAILED((rc = keyslot_holder.Allocate()))) {
return rc;
}
smc_rc = SmcWrapper::LoadAesKey(keyslot_holder.GetKeyslot(), access_key, s_generate_aes_key_source);
if (smc_rc == SmcResult_Success) {
smc_rc = DecryptAesBlock(keyslot_holder.GetKeyslot(), out_key, &key_source);
}
return ConvertToSplResult(smc_rc);
}
Result SecureMonitorWrapper::DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option) {
Result rc;
static const 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;
if (R_FAILED((rc = GenerateAesKek(&access_key, s_decrypt_aes_key_source, generation, option)))) {
return rc;
}
return GenerateAesKey(out_key, access_key, key_source);
}
Result SecureMonitorWrapper::CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr) {
Result rc = ValidateAesKeyslot(keyslot, owner);
if (R_FAILED(rc)) {
return rc;
}
/* Succeed immediately if there's nothing to crypt. */
if (src_size == 0) {
return ResultSuccess;
}
/* Validate sizes. */
if (src_size > dst_size || src_size % AES_BLOCK_SIZE != 0) {
return ResultSplInvalidSize;
}
/* 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 = src_addr & ~0xFFFul;
const uintptr_t dst_addr_page_aligned = dst_addr & ~0xFFFul;
const size_t src_size_page_aligned = ((src_addr + src_size + 0xFFFul) & ~0xFFFul) - src_addr_page_aligned;
const size_t dst_size_page_aligned = ((dst_addr + dst_size + 0xFFFul) & ~0xFFFul) - dst_addr_page_aligned;
const u32 src_se_map_addr = CryptAesInMapBase + (src_addr_page_aligned & DeviceAddressSpaceAlignMask);
const u32 dst_se_map_addr = CryptAesOutMapBase + (dst_addr_page_aligned & DeviceAddressSpaceAlignMask);
const u32 src_se_addr = CryptAesInMapBase + (src_addr & DeviceAddressSpaceAlignMask);
const u32 dst_se_addr = CryptAesInMapBase + (dst_addr & DeviceAddressSpaceAlignMask);
/* Validate aligned sizes. */
if (src_size_page_aligned > CryptAesSizeMax || dst_size_page_aligned > CryptAesSizeMax) {
return ResultSplInvalidSize;
}
/* Helpers for mapping/unmapping. */
DeviceAddressSpaceMapHelper in_mapper(g_se_das_hnd, src_se_map_addr, src_addr_page_aligned, src_size_page_aligned, 1);
DeviceAddressSpaceMapHelper out_mapper(g_se_das_hnd, dst_se_map_addr, dst_addr_page_aligned, dst_size_page_aligned, 2);
/* 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;
armDCacheFlush(crypt_ctx, sizeof(*crypt_ctx));
armDCacheFlush(const_cast<void *>(src), src_size);
armDCacheFlush(dst, dst_size);
{
std::scoped_lock<HosMutex> lk(g_async_op_lock);
AsyncOperationKey op_key;
const u32 mode = SmcWrapper::GetCryptAesMode(SmcCipherMode_Ctr, keyslot);
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);
SmcResult res = SmcWrapper::CryptAes(&op_key, mode, iv_ctr, dst_ll_addr, src_ll_addr, src_size);
if (res != SmcResult_Success) {
return ConvertToSplResult(res);
}
if ((res = WaitCheckStatus(op_key)) != SmcResult_Success) {
return ConvertToSplResult(res);
}
}
armDCacheFlush(dst, dst_size);
return ResultSuccess;
}
Result SecureMonitorWrapper::ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size) {
Result rc = ValidateAesKeyslot(keyslot, owner);
if (R_FAILED(rc)) {
return rc;
}
if (size > MaxWorkBufferSize) {
return ResultSplInvalidSize;
}
std::memcpy(g_work_buffer, data, size);
return ConvertToSplResult(SmcWrapper::ComputeCmac(out_cmac, keyslot, g_work_buffer, size));
}
Result SecureMonitorWrapper::AllocateAesKeyslot(u32 *out_keyslot, const void *owner) {
for (size_t i = 0; i < GetMaxKeyslots(); i++) {
if (this->keyslot_owners[i] == 0) {
this->keyslot_owners[i] = owner;
*out_keyslot = static_cast<u32>(i);
return ResultSuccess;
}
}
g_se_keyslot_available_event->Clear();
return ResultSplOutOfKeyslots;
}
Result SecureMonitorWrapper::ValidateAesKeyslot(u32 keyslot, const void *owner) {
if (keyslot >= GetMaxKeyslots()) {
return ResultSplInvalidKeyslot;
}
if (this->keyslot_owners[keyslot] != owner) {
return ResultSplInvalidKeyslot;
}
return ResultSuccess;
}
Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) {
Result rc = ValidateAesKeyslot(keyslot, owner);
if (R_FAILED(rc)) {
return rc;
}
/* Clear the keyslot. */
{
AccessKey access_key = {};
KeySource key_source = {};
SmcWrapper::LoadAesKey(keyslot, access_key, key_source);
}
this->keyslot_owners[keyslot] = nullptr;
g_se_keyslot_available_event->Signal();
return ResultSuccess;
}
Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) {
for (size_t i = 0; i < GetMaxKeyslots(); i++) {
if (this->keyslot_owners[i] == owner) {
FreeAesKeyslot(i, owner);
}
}
return ResultSuccess;
}
Handle SecureMonitorWrapper::GetAesKeyslotAvailableEventHandle() {
return g_se_keyslot_available_event->GetHandle();
} }

View file

@ -25,7 +25,7 @@ class SecureMonitorWrapper {
static constexpr size_t MaxAesKeyslots = 6; static constexpr size_t MaxAesKeyslots = 6;
static constexpr size_t MaxAesKeyslotsDeprecated = 4; static constexpr size_t MaxAesKeyslotsDeprecated = 4;
private: private:
uintptr_t keyslot_owners[MaxAesKeyslots] = {}; const void *keyslot_owners[MaxAesKeyslots] = {};
BootReasonValue boot_reason = {}; BootReasonValue boot_reason = {};
bool boot_reason_set = false; bool boot_reason_set = false;
private: private:
@ -42,7 +42,7 @@ class SecureMonitorWrapper {
static Result ConvertToSplResult(SmcResult result); static Result ConvertToSplResult(SmcResult result);
private: private:
static void InitializeCtrDrbg(); static void InitializeCtrDrbg();
static void InitializeSeInterruptEvent(); static void InitializeSeEvents();
static void InitializeDeviceAddressSpace(); static void InitializeDeviceAddressSpace();
public: public:
static void Initialize(); static void Initialize();
@ -51,7 +51,10 @@ class SecureMonitorWrapper {
void WaitSeOperationComplete(); void WaitSeOperationComplete();
SmcResult WaitCheckStatus(AsyncOperationKey op_key); SmcResult WaitCheckStatus(AsyncOperationKey op_key);
SmcResult WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key); SmcResult WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key);
Result ValidateAesKeyslot(u32 keyslot, const void *owner);
SmcResult DecryptAesBlock(u32 keyslot, void *dst, const void *src);
public: public:
/* General. */
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);
Result SetConfig(SplConfigItem which, u64 value); Result SetConfig(SplConfigItem which, u64 value);
@ -59,4 +62,46 @@ class SecureMonitorWrapper {
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);
/* Crypto. */
Result GenerateAesKek(AccessKey *out_access_key, const KeySource &key_source, u32 generation, u32 option);
Result LoadAesKey(u32 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 DecryptAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 option);
Result CryptAesCtr(void *dst, size_t dst_size, u32 keyslot, const void *owner, const void *src, size_t src_size, const IvCtr &iv_ctr);
Result ComputeCmac(Cmac *out_cmac, u32 keyslot, const void *owner, const void *data, size_t size);
Result AllocateAesKeyslot(u32 *out_keyslot, const void *owner);
Result FreeAesKeyslot(u32 keyslot, const void *owner);
/* Helper. */
Result FreeAesKeyslots(const void *owner);
Handle GetAesKeyslotAvailableEventHandle();
private:
class ScopedAesKeyslot {
private:
SecureMonitorWrapper *secmon_wrapper;
u32 slot;
bool has_slot;
public:
ScopedAesKeyslot(SecureMonitorWrapper *sw) : secmon_wrapper(sw), slot(0), has_slot(false) {
/* ... */
}
~ScopedAesKeyslot() {
if (has_slot) {
this->secmon_wrapper->FreeAesKeyslot(slot, this);
}
}
u32 GetKeyslot() const {
return this->slot;
}
Result Allocate() {
Result rc = this->secmon_wrapper->AllocateAesKeyslot(&this->slot, this);
if (R_SUCCEEDED(rc)) {
this->has_slot = true;
}
return rc;
}
};
}; };

View file

@ -123,12 +123,12 @@ SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) {
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_GenerateAesKek; args.X[0] = SmcFunctionId_GenerateAesKek;
args.X[1] = source[0]; args.X[1] = source.data64[0];
args.X[2] = source[1]; args.X[2] = source.data64[1];
args.X[3] = generation; args.X[3] = generation;
args.X[4] = option; args.X[4] = option;
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
@ -138,27 +138,27 @@ SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 gene
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_LoadAesKey; args.X[0] = SmcFunctionId_LoadAesKey;
args.X[1] = keyslot; args.X[1] = keyslot;
args.X[2] = access_key.data64[0]; args.X[2] = access_key.data64[0];
args.X[3] = access_key.data64[1]; args.X[3] = access_key.data64[1];
args.X[4] = source[0]; args.X[4] = source.data64[0];
args.X[5] = source[1]; args.X[5] = source.data64[1];
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
return static_cast<SmcResult>(args.X[0]); 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) { SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_CryptAes; args.X[0] = SmcFunctionId_CryptAes;
args.X[1] = mode; args.X[1] = mode;
args.X[2] = iv_ctr[0]; args.X[2] = iv_ctr.data64[0];
args.X[3] = iv_ctr[1]; args.X[3] = iv_ctr.data64[1];
args.X[4] = src_addr; args.X[4] = src_addr;
args.X[5] = dst_addr; args.X[5] = dst_addr;
args.X[6] = size; args.X[6] = size;
@ -168,12 +168,12 @@ SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *i
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_GenerateSpecificAesKey; args.X[0] = SmcFunctionId_GenerateSpecificAesKey;
args.X[1] = source[0]; args.X[1] = source.data64[0];
args.X[2] = source[1]; args.X[2] = source.data64[1];
args.X[3] = generation; args.X[3] = generation;
args.X[4] = which; args.X[4] = which;
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
@ -181,7 +181,7 @@ SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 ge
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { SmcResult SmcWrapper::ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_ComputeCmac; args.X[0] = SmcFunctionId_ComputeCmac;
@ -190,12 +190,12 @@ SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data,
args.X[3] = size; args.X[3] = size;
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
out_mac.data64[0] = args.X[1]; out_mac->data64[0] = args.X[1];
out_mac.data64[1] = args.X[2]; out_mac->data64[1] = args.X[2];
return static_cast<SmcResult>(args.X[0]); 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) { SmcResult SmcWrapper::ReEncryptRsaPrivateKey(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) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey; args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey;
@ -204,14 +204,14 @@ SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const Acce
args.X[3] = option; args.X[3] = option;
args.X[4] = reinterpret_cast<u64>(data); args.X[4] = reinterpret_cast<u64>(data);
args.X[5] = size; args.X[5] = size;
args.X[6] = reinterpret_cast<u64>(source_dec); args.X[6] = reinterpret_cast<u64>(&source_dec);
args.X[7] = reinterpret_cast<u64>(source_enc); args.X[7] = reinterpret_cast<u64>(&source_enc);
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey;
@ -220,8 +220,8 @@ SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, cons
args.X[3] = option; args.X[3] = option;
args.X[4] = reinterpret_cast<u64>(data); args.X[4] = reinterpret_cast<u64>(data);
args.X[5] = size; args.X[5] = size;
args.X[6] = source[0]; args.X[6] = source.data64[0];
args.X[7] = source[1]; args.X[7] = source.data64[1];
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
@ -267,12 +267,12 @@ SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) {
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);
} }
SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; args.X[0] = SmcFunctionId_UnwrapCommonTitleKey;
args.X[1] = source[0]; args.X[1] = source.data64[0];
args.X[2] = source[1]; args.X[2] = source.data64[1];
args.X[3] = generation; args.X[3] = generation;
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
@ -283,7 +283,7 @@ SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u3
/* Deprecated functions. */ /* Deprecated functions. */
SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_ImportEsKey; args.X[0] = SmcFunctionId_ImportEsKey;
@ -292,14 +292,14 @@ SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey
args.X[3] = option; args.X[3] = option;
args.X[4] = reinterpret_cast<u64>(data); args.X[4] = reinterpret_cast<u64>(data);
args.X[5] = size; args.X[5] = size;
args.X[6] = source[0]; args.X[6] = source.data64[0];
args.X[7] = source[1]; args.X[7] = source.data64[1];
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
return static_cast<SmcResult>(args.X[0]); 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) { SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; args.X[0] = SmcFunctionId_DecryptRsaPrivateKey;
@ -308,15 +308,15 @@ SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t
args.X[3] = option; args.X[3] = option;
args.X[4] = reinterpret_cast<u64>(data); args.X[4] = reinterpret_cast<u64>(data);
args.X[5] = size; args.X[5] = size;
args.X[6] = source[0]; args.X[6] = source.data64[0];
args.X[7] = source[1]; args.X[7] = source.data64[1];
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
*out_size = static_cast<size_t>(args.X[1]); *out_size = static_cast<size_t>(args.X[1]);
return static_cast<SmcResult>(args.X[0]); 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) { SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) {
SecmonArgs args; SecmonArgs args;
args.X[0] = SmcFunctionId_ImportSecureExpModKey; args.X[0] = SmcFunctionId_ImportSecureExpModKey;
@ -325,8 +325,8 @@ SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const
args.X[3] = option; args.X[3] = option;
args.X[4] = reinterpret_cast<u64>(data); args.X[4] = reinterpret_cast<u64>(data);
args.X[5] = size; args.X[5] = size;
args.X[6] = source[0]; args.X[6] = source.data64[0];
args.X[7] = source[1]; args.X[7] = source.data64[1];
svcCallSecureMonitor(&args); svcCallSecureMonitor(&args);
return static_cast<SmcResult>(args.X[0]); return static_cast<SmcResult>(args.X[0]);

View file

@ -21,6 +21,10 @@
#include "spl_types.hpp" #include "spl_types.hpp"
class SmcWrapper { class SmcWrapper {
public:
static inline u32 GetCryptAesMode(SmcCipherMode mode, u32 keyslot) {
return static_cast<u32>((mode << 4) | (keyslot & 7));
}
public: public:
static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); 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 GetConfig(u64 *out, size_t num_qwords, SplConfigItem which);
@ -28,20 +32,20 @@ class SmcWrapper {
static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, 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 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 GenerateRandomBytes(void *out, size_t size);
static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); static SmcResult GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option);
static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source);
static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &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 GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which);
static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); 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 ReEncryptRsaPrivateKey(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);
static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
static SmcResult SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, 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 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 LoadTitleKey(u32 keyslot, const AccessKey &access_key);
static SmcResult UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation); static SmcResult UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation);
/* Deprecated functions. */ /* Deprecated functions. */
static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &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 DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option);
static SmcResult ImportSecureExpModKey(const 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 KeySource &source, u32 option);
}; };

View file

@ -37,7 +37,6 @@ enum SmcCipherMode : u32 {
SmcCipherMode_CbcEncrypt = 0, SmcCipherMode_CbcEncrypt = 0,
SmcCipherMode_CbcDecrypt = 1, SmcCipherMode_CbcDecrypt = 1,
SmcCipherMode_Ctr = 2, SmcCipherMode_Ctr = 2,
SmcCipherMode_Cmac = 3,
}; };
enum EsKeyType : u32 { enum EsKeyType : u32 {
@ -58,12 +57,20 @@ struct BootReasonValue {
static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!");
struct AesKey { struct AesKey {
u8 data[AES_128_KEY_SIZE]; union {
u8 data[AES_128_KEY_SIZE];
u8 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
}; };
static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!");
struct IvCtr { struct IvCtr {
u8 data[AES_128_KEY_SIZE]; union {
u8 data[AES_128_KEY_SIZE];
u8 data64[AES_128_KEY_SIZE / sizeof(u64)];
};
}; };
static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!");
struct Cmac { struct Cmac {
union { union {
@ -82,8 +89,12 @@ struct AccessKey {
static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!");
struct KeySource { struct KeySource {
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), "KeySource definition!");
enum SplServiceCmd { enum SplServiceCmd {
/* 1.0.0+ */ /* 1.0.0+ */
@ -110,7 +121,7 @@ enum SplServiceCmd {
Spl_Cmd_UnwrapCommonTitleKey = 20, Spl_Cmd_UnwrapCommonTitleKey = 20,
Spl_Cmd_AllocateAesKeyslot = 21, Spl_Cmd_AllocateAesKeyslot = 21,
Spl_Cmd_FreeAesKeyslot = 22, Spl_Cmd_FreeAesKeyslot = 22,
Spl_Cmd_GetAesKeyslotEvent = 23, Spl_Cmd_GetAesKeyslotAvailableEvent = 23,
/* 3.0.0+ */ /* 3.0.0+ */
Spl_Cmd_SetBootReason = 24, Spl_Cmd_SetBootReason = 24,