diff --git a/fusee/fusee-secondary/Makefile b/fusee/fusee-secondary/Makefile index d4a0c2c91..8b3157330 100644 --- a/fusee/fusee-secondary/Makefile +++ b/fusee/fusee-secondary/Makefile @@ -84,7 +84,7 @@ ifneq ($(BUILD),$(notdir $(CURDIR))) export OUTPUT := $(CURDIR)/$(TARGET) export TOPDIR := $(CURDIR) -export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/ams_mitm +export KIPDIRS := $(AMS)/stratosphere/loader $(AMS)/stratosphere/pm $(AMS)/stratosphere/sm $(AMS)/stratosphere/boot $(AMS)/stratosphere/spl $(AMS)/stratosphere/ams_mitm export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ $(foreach dir,$(DATA),$(CURDIR)/$(dir)) \ $(AMS)/exosphere $(AMS)/exosphere/lp0fw $(AMS)/exosphere/rebootstub \ @@ -96,7 +96,7 @@ export DEPSDIR := $(CURDIR)/$(BUILD) CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) -KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip boot_100.kip boot_200.kip +KIPFILES := loader.kip pm.kip sm.kip ams_mitm.kip spl.kip boot_100.kip boot_200.kip BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) fusee-primary.bin \ exosphere.bin lp0fw.bin rebootstub.bin thermosphere.bin splash_screen.bmp \ sept-primary.bin sept-secondary.enc \ diff --git a/fusee/fusee-secondary/linker.ld b/fusee/fusee-secondary/linker.ld index a6328675a..0c8578714 100644 --- a/fusee/fusee-secondary/linker.ld +++ b/fusee/fusee-secondary/linker.ld @@ -242,6 +242,8 @@ SECTIONS PROVIDE(__sept_secondary_enc_size__ = sept_secondary_enc_end - sept_secondary_enc); PROVIDE(__sm_kip_start__ = sm_kip - __start__); PROVIDE(__sm_kip_size__ = sm_kip_end - sm_kip); + PROVIDE(__spl_kip_start__ = spl_kip - __start__); + PROVIDE(__spl_kip_size__ = spl_kip_end - spl_kip); PROVIDE(__splash_screen_bmp_start__ = splash_screen_bmp - __start__); PROVIDE(__splash_screen_bmp_size__ = splash_screen_bmp_end - splash_screen_bmp); PROVIDE(__thermosphere_bin_start__ = thermosphere_bin - __start__); diff --git a/fusee/fusee-secondary/src/start.s b/fusee/fusee-secondary/src/start.s index f0826c8e8..1d3572545 100644 --- a/fusee/fusee-secondary/src/start.s +++ b/fusee/fusee-secondary/src/start.s @@ -191,6 +191,14 @@ _content_headers: .asciz "sm" .align 5 +/* spl content header */ +.word __spl_kip_start__ +.word __spl_kip_size__ +.word CONTENT_TYPE_KIP +.word 0xCCCCCCCC +.asciz "spl" +.align 5 + /* splash_screen content header */ .word __splash_screen_bmp_start__ .word __splash_screen_bmp_size__ diff --git a/fusee/fusee-secondary/src/stratosphere.c b/fusee/fusee-secondary/src/stratosphere.c index e11a2b312..d782e3b09 100644 --- a/fusee/fusee-secondary/src/stratosphere.c +++ b/fusee/fusee-secondary/src/stratosphere.c @@ -35,6 +35,7 @@ #include "ams_mitm_kip.h" #include "boot_100_kip.h" #include "boot_200_kip.h" +#include "spl_kip.h" #undef u8 #undef u32 @@ -45,12 +46,13 @@ static bool g_stratosphere_loader_enabled = true; static bool g_stratosphere_sm_enabled = true; static bool g_stratosphere_pm_enabled = true; static bool g_stratosphere_ams_mitm_enabled = true; +static bool g_stratosphere_spl_enabled = true; static bool g_stratosphere_boot_enabled = false; extern const uint8_t boot_100_kip[], boot_200_kip[]; -extern const uint8_t loader_kip[], pm_kip[], sm_kip[], ams_mitm_kip[]; +extern const uint8_t loader_kip[], pm_kip[], sm_kip[], spl_kip[], ams_mitm_kip[]; extern const uint32_t boot_100_kip_size, boot_200_kip_size; -extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, ams_mitm_kip_size; +extern const uint32_t loader_kip_size, pm_kip_size, sm_kip_size, spl_kip_size, ams_mitm_kip_size; /* GCC doesn't consider the size as const... we have to write it ourselves. */ @@ -90,6 +92,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) { num_processes++; } + if (g_stratosphere_spl_enabled) { + size += spl_kip_size; + num_processes++; + } + if (g_stratosphere_ams_mitm_enabled) { size += ams_mitm_kip_size; num_processes++; @@ -129,6 +136,11 @@ ini1_header_t *stratosphere_get_ini1(uint32_t target_firmware) { data += sm_kip_size; } + if (g_stratosphere_spl_enabled) { + memcpy(data, spl_kip, spl_kip_size); + data += spl_kip_size; + } + if (g_stratosphere_ams_mitm_enabled) { memcpy(data, ams_mitm_kip, ams_mitm_kip_size); data += ams_mitm_kip_size; diff --git a/stratosphere/Makefile b/stratosphere/Makefile index 76ce0d97a..da374f808 100644 --- a/stratosphere/Makefile +++ b/stratosphere/Makefile @@ -1,4 +1,4 @@ -MODULES := loader pm sm boot ams_mitm eclct.stub ro creport fatal dmnt +MODULES := loader pm sm boot ams_mitm spl eclct.stub ro creport fatal dmnt SUBFOLDERS := libstratosphere $(MODULES) diff --git a/stratosphere/libstratosphere b/stratosphere/libstratosphere index 9e34dbe7e..63fc847f8 160000 --- a/stratosphere/libstratosphere +++ b/stratosphere/libstratosphere @@ -1 +1 @@ -Subproject commit 9e34dbe7e2689eadae205f34d494696b5ea6a3f5 +Subproject commit 63fc847f8ae43b173a9031071eebb76a1961c41c diff --git a/stratosphere/spl/Makefile b/stratosphere/spl/Makefile new file mode 100644 index 000000000..4ebc8f6d9 --- /dev/null +++ b/stratosphere/spl/Makefile @@ -0,0 +1,160 @@ +#--------------------------------------------------------------------------------- +.SUFFIXES: +#--------------------------------------------------------------------------------- + +ifeq ($(strip $(DEVKITPRO)),) +$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") +endif + +TOPDIR ?= $(CURDIR) +include $(DEVKITPRO)/libnx/switch_rules + +AMSBRANCH := $(shell git symbolic-ref --short HEAD) +AMSREV := $(AMSBRANCH)-$(shell git rev-parse --short HEAD) + +ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) + AMSREV := $(AMSREV)-dirty +endif + +#--------------------------------------------------------------------------------- +# TARGET is the name of the output +# BUILD is the directory where object files & intermediate files will be placed +# SOURCES is a list of directories containing source code +# DATA is a list of directories containing data files +# INCLUDES is a list of directories containing header files +# EXEFS_SRC is the optional input directory containing data copied into exefs, if anything this normally should only contain "main.npdm". +#--------------------------------------------------------------------------------- +TARGET := $(notdir $(CURDIR)) +BUILD := build +SOURCES := source +DATA := data +INCLUDES := include ../../common/include +EXEFS_SRC := exefs_src + +DEFINES := -DDISABLE_IPC -DATMOSPHERE_GIT_BRANCH=\"$(AMSBRANCH)\" -DATMOSPHERE_GIT_REV=\"$(AMSREV)\" + +#--------------------------------------------------------------------------------- +# options for code generation +#--------------------------------------------------------------------------------- +ARCH := -march=armv8-a -mtune=cortex-a57 -mtp=soft -fPIE + +CFLAGS := -g -Wall -O2 -ffunction-sections \ + $(ARCH) $(DEFINES) + +CFLAGS += $(INCLUDE) -D__SWITCH__ + +CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions -std=gnu++17 + +ASFLAGS := -g $(ARCH) +LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) + +LIBS := -lstratosphere -lnx + +#--------------------------------------------------------------------------------- +# list of directories containing libraries, this must be the top level containing +# include and lib +#--------------------------------------------------------------------------------- +LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/../libstratosphere + + +#--------------------------------------------------------------------------------- +# no real need to edit anything past this point unless you need to add additional +# rules for different file extensions +#--------------------------------------------------------------------------------- +ifneq ($(BUILD),$(notdir $(CURDIR))) +#--------------------------------------------------------------------------------- + +export OUTPUT := $(CURDIR)/$(TARGET) +export TOPDIR := $(CURDIR) + +export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ + $(foreach dir,$(DATA),$(CURDIR)/$(dir)) + +export DEPSDIR := $(CURDIR)/$(BUILD) + +CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) +CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) +SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) +BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) + +#--------------------------------------------------------------------------------- +# use CXX for linking C++ projects, CC for standard C +#--------------------------------------------------------------------------------- +ifeq ($(strip $(CPPFILES)),) +#--------------------------------------------------------------------------------- + export LD := $(CC) +#--------------------------------------------------------------------------------- +else +#--------------------------------------------------------------------------------- + export LD := $(CXX) +#--------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------- + +export OFILES := $(addsuffix .o,$(BINFILES)) \ + $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) + +export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ + $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ + -I$(CURDIR)/$(BUILD) + +export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) + +export BUILD_EXEFS_SRC := $(TOPDIR)/$(EXEFS_SRC) + +ifeq ($(strip $(CONFIG_JSON)),) + jsons := $(wildcard *.json) + ifneq (,$(findstring $(TARGET).json,$(jsons))) + export APP_JSON := $(TOPDIR)/$(TARGET).json + else + ifneq (,$(findstring config.json,$(jsons))) + export APP_JSON := $(TOPDIR)/config.json + endif + endif +else + export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) +endif + +.PHONY: $(BUILD) clean all + +#--------------------------------------------------------------------------------- +all: $(BUILD) + +$(BUILD): + @[ -d $@ ] || mkdir -p $@ + @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile + +#--------------------------------------------------------------------------------- +clean: + @echo clean ... + @rm -fr $(BUILD) $(TARGET).kip $(TARGET).elf + + +#--------------------------------------------------------------------------------- +else +.PHONY: all + +DEPENDS := $(OFILES:.o=.d) + +#--------------------------------------------------------------------------------- +# main targets +#--------------------------------------------------------------------------------- +all : $(OUTPUT).kip + +$(OUTPUT).kip : $(OUTPUT).elf + +$(OUTPUT).elf : $(OFILES) + +#--------------------------------------------------------------------------------- +# you need a rule like this for each extension you use as binary data +#--------------------------------------------------------------------------------- +%.bin.o : %.bin +#--------------------------------------------------------------------------------- + @echo $(notdir $<) + @$(bin2o) + +-include $(DEPENDS) + +#--------------------------------------------------------------------------------------- +endif +#--------------------------------------------------------------------------------------- diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp new file mode 100644 index 000000000..1e3522865 --- /dev/null +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -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 . + */ + +#include +#include + +#include "spl_crypto_service.hpp" + +Result CryptoService::GenerateAesKek(Out 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 out_key, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->GenerateAesKey(out_key.GetPointer(), access_key, key_source); +} + +Result CryptoService::DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option); +} + +Result CryptoService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer 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 out_cmac, u32 keyslot, InPointer in_buf) { + return this->GetSecureMonitorWrapper()->ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.pointer, in_buf.num_elements); +} + +Result CryptoService::AllocateAesKeyslot(Out 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 out_hnd) { + out_hnd.SetValue(this->GetSecureMonitorWrapper()->GetAesKeyslotAvailableEventHandle()); +} diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp new file mode 100644 index 000000000..2f56df6ec --- /dev/null +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -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 . + */ + +#pragma once +#include +#include + +#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 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 out_key, AccessKey access_key, KeySource key_source); + virtual Result DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option); + virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf); + virtual Result AllocateAesKeyslot(Out out_keyslot); + virtual Result FreeAesKeyslot(u32 keyslot); + virtual void GetAesKeyslotAvailableEvent(Out out_hnd); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp new file mode 100644 index 000000000..a468d2dcf --- /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..39f82ae0a --- /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_deprecated_service.cpp b/stratosphere/spl/source/spl_deprecated_service.cpp new file mode 100644 index 000000000..12484385b --- /dev/null +++ b/stratosphere/spl/source/spl_deprecated_service.cpp @@ -0,0 +1,120 @@ +/* + * 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_deprecated_service.hpp" + +Result DeprecatedService::GetConfig(Out out, u32 which) { + return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result DeprecatedService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { + 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 DeprecatedService::GenerateAesKek(Out out_access_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->GenerateAesKek(out_access_key.GetPointer(), key_source, generation, option); +} + +Result DeprecatedService::LoadAesKey(u32 keyslot, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->LoadAesKey(keyslot, this, access_key, key_source); +} + +Result DeprecatedService::GenerateAesKey(Out out_key, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->GenerateAesKey(out_key.GetPointer(), access_key, key_source); +} + +Result DeprecatedService::SetConfig(u32 which, u64 value) { + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); +} + +Result DeprecatedService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result DeprecatedService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportLotusKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + return this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); +} + +Result DeprecatedService::IsDevelopment(Out is_dev) { + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); +} + +Result DeprecatedService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { + return this->GetSecureMonitorWrapper()->GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); +} + +Result DeprecatedService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptAesKey(out_key.GetPointer(), key_source, generation, option); +} + +Result DeprecatedService::CryptAesCtrDeprecated(OutBuffer out_buf, u32 keyslot, InBuffer 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 DeprecatedService::CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer 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 DeprecatedService::ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf) { + return this->GetSecureMonitorWrapper()->ComputeCmac(out_cmac.GetPointer(), keyslot, this, in_buf.pointer, in_buf.num_elements); +} + +Result DeprecatedService::ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportEsKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result DeprecatedService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapTitleKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); +} + +Result DeprecatedService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + return this->GetSecureMonitorWrapper()->LoadTitleKey(keyslot, this, access_key); +} + +Result DeprecatedService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); +} + +Result DeprecatedService::AllocateAesKeyslot(Out out_keyslot) { + return this->GetSecureMonitorWrapper()->AllocateAesKeyslot(out_keyslot.GetPointer(), this); +} + +Result DeprecatedService::FreeAesKeyslot(u32 keyslot) { + return this->GetSecureMonitorWrapper()->FreeAesKeyslot(keyslot, this); +} + +void DeprecatedService::GetAesKeyslotAvailableEvent(Out out_hnd) { + out_hnd.SetValue(this->GetSecureMonitorWrapper()->GetAesKeyslotAvailableEventHandle()); +} + +Result DeprecatedService::SetBootReason(BootReasonValue boot_reason) { + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); +} + +Result DeprecatedService::GetBootReason(Out out) { + return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer()); +} diff --git a/stratosphere/spl/source/spl_deprecated_service.hpp b/stratosphere/spl/source/spl_deprecated_service.hpp new file mode 100644 index 000000000..cd52d2d0d --- /dev/null +++ b/stratosphere/spl/source/spl_deprecated_service.hpp @@ -0,0 +1,92 @@ +/* + * 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" +#include "spl_secmon_wrapper.hpp" + +class DeprecatedService : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + DeprecatedService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~DeprecatedService() { /* ... */ } + protected: + SecureMonitorWrapper *GetSecureMonitorWrapper() const { + return this->secmon_wrapper; + } + protected: + /* Actual commands. */ + virtual Result GetConfig(Out out, u32 which); + virtual Result ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod); + virtual Result GenerateAesKek(Out 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 out_key, AccessKey access_key, KeySource key_source); + virtual Result SetConfig(u32 which, u64 value); + virtual Result GenerateRandomBytes(OutPointerWithClientSize out); + virtual Result ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); + virtual Result IsDevelopment(Out is_dev); + virtual Result GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which); + virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptAesKey(Out out_key, KeySource key_source, u32 generation, u32 option); + virtual Result CryptAesCtrDeprecated(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result CryptAesCtr(OutBuffer out_buf, u32 keyslot, InBuffer in_buf, IvCtr iv_ctr); + virtual Result ComputeCmac(Out out_cmac, u32 keyslot, InPointer in_buf); + virtual Result ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation); + virtual Result AllocateAesKeyslot(Out out_keyslot); + virtual Result FreeAesKeyslot(u32 keyslot); + virtual void GetAesKeyslotAvailableEvent(Out out_hnd); + virtual Result SetBootReason(BootReasonValue boot_reason); + virtual Result GetBootReason(Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp new file mode 100644 index 000000000..cb855d05e --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -0,0 +1,48 @@ +/* + * 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_es_service.hpp" + +Result EsService::ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportEsKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result EsService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapTitleKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); +} + +Result EsService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); +} + +Result EsService::ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->ImportDrmKey(src.pointer, src.num_elements, access_key, key_source); +} + +Result EsService::DrmExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod) { + return this->GetSecureMonitorWrapper()->DrmExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements); +} + +Result EsService::UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { + return this->GetSecureMonitorWrapper()->UnwrapElicenseKey(out_access_key.GetPointer(), base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements, generation); +} + +Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { + return this->GetSecureMonitorWrapper()->LoadElicenseKey(keyslot, this, access_key); +} diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp new file mode 100644 index 000000000..2fba15a30 --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -0,0 +1,69 @@ +/* + * 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" +#include "spl_rsa_service.hpp" + +class EsService : public RsaService { + public: + EsService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~EsService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportEsKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation); + virtual Result ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source); + virtual Result DrmExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); + virtual Result UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation); + virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + }; +}; diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp new file mode 100644 index 000000000..d0808e003 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -0,0 +1,40 @@ +/* + * 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_fs_service.hpp" + +Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->ImportLotusKey(src.pointer, src.num_elements, access_key, key_source, option); +} + +Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + return this->GetSecureMonitorWrapper()->DecryptLotusMessage(out_size.GetPointer(), out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements, label_digest.pointer, label_digest.num_elements); +} + +Result FsService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { + return this->GetSecureMonitorWrapper()->GenerateSpecificAesKey(out_key.GetPointer(), key_source, generation, which); +} + +Result FsService::LoadTitleKey(u32 keyslot, AccessKey access_key) { + return this->GetSecureMonitorWrapper()->LoadTitleKey(keyslot, this, access_key); +} + +Result FsService::GetPackage2Hash(OutPointerWithClientSize dst) { + return this->GetSecureMonitorWrapper()->GetPackage2Hash(dst.pointer, dst.num_elements); +} diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp new file mode 100644 index 000000000..7bac28934 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -0,0 +1,65 @@ +/* + * 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" +#include "spl_crypto_service.hpp" + +class FsService : public CryptoService { + public: + FsService(SecureMonitorWrapper *sw) : CryptoService(sw) { + /* ... */ + } + + virtual ~FsService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option); + virtual Result DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest); + virtual Result GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which); + virtual Result LoadTitleKey(u32 keyslot, AccessKey access_key); + virtual Result GetPackage2Hash(OutPointerWithClientSize dst); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp new file mode 100644 index 000000000..d83a5c5d2 --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.cpp @@ -0,0 +1,48 @@ +/* + * 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_general_service.hpp" + +Result GeneralService::GetConfig(Out out, u32 which) { + return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result GeneralService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { + 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) { + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); +} + +Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result GeneralService::IsDevelopment(Out is_dev) { + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); +} + +Result GeneralService::SetBootReason(BootReasonValue boot_reason) { + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); +} + +Result GeneralService::GetBootReason(Out out) { + return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer()); +} diff --git a/stratosphere/spl/source/spl_general_service.hpp b/stratosphere/spl/source/spl_general_service.hpp new file mode 100644 index 000000000..6d1879dac --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -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 . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" +#include "spl_secmon_wrapper.hpp" + +class GeneralService : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + GeneralService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~GeneralService() { /* ... */ } + protected: + SecureMonitorWrapper *GetSecureMonitorWrapper() const { + return this->secmon_wrapper; + } + protected: + /* Actual commands. */ + virtual Result GetConfig(Out out, u32 which); + virtual Result ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod); + virtual Result SetConfig(u32 which, u64 value); + virtual Result GenerateRandomBytes(OutPointerWithClientSize out); + virtual Result IsDevelopment(Out is_dev); + virtual Result SetBootReason(BootReasonValue boot_reason); + virtual Result GetBootReason(Out out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp new file mode 100644 index 000000000..7cae77b4a --- /dev/null +++ b/stratosphere/spl/source/spl_main.cpp @@ -0,0 +1,133 @@ +/* + * 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 +#include + +#include +#include + +#include "spl_random_service.hpp" +#include "spl_general_service.hpp" +#include "spl_crypto_service.hpp" +#include "spl_ssl_service.hpp" +#include "spl_es_service.hpp" +#include "spl_fs_service.hpp" +#include "spl_manu_service.hpp" + +#include "spl_deprecated_service.hpp" + +extern "C" { + extern u32 __start__; + + u32 __nx_applet_type = AppletType_None; + + #define INNER_HEAP_SIZE 0x28000 + size_t nx_inner_heap_size = INNER_HEAP_SIZE; + char nx_inner_heap[INNER_HEAP_SIZE]; + + void __libnx_initheap(void); + void __appInit(void); + void __appExit(void); + + /* Exception handling. */ + alignas(16) u8 __nx_exception_stack[0x1000]; + u64 __nx_exception_stack_size = sizeof(__nx_exception_stack); + void __libnx_exception_handler(ThreadExceptionDump *ctx); + u64 __stratosphere_title_id = TitleId_Spl; + void __libstratosphere_exception_handler(AtmosphereFatalErrorContext *ctx); +} + +void __libnx_exception_handler(ThreadExceptionDump *ctx) { + StratosphereCrashHandler(ctx); +} + + +void __libnx_initheap(void) { + void* addr = nx_inner_heap; + size_t size = nx_inner_heap_size; + + /* Newlib */ + extern char* fake_heap_start; + extern char* fake_heap_end; + + fake_heap_start = (char*)addr; + fake_heap_end = (char*)addr + size; +} + +void __appInit(void) { + SetFirmwareVersionForLibnx(); + + /* SPL doesn't really access any services... */ +} + +void __appExit(void) { + /* SPL doesn't really access any services... */ +} + +struct SplServerOptions { + static constexpr size_t PointerBufferSize = 0x800; + static constexpr size_t MaxDomains = 0; + static constexpr size_t MaxDomainObjects = 0; +}; + +/* Single secure monitor wrapper singleton. */ +static SecureMonitorWrapper s_secmon_wrapper; + +/* Helpers for creating services. */ +static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeGeneralService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeCryptoService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeSslService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeEsService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeFsService = []() { return std::make_shared(&s_secmon_wrapper); }; +static const auto MakeManuService = []() { return std::make_shared(&s_secmon_wrapper); }; + +static const auto MakeDeprecatedService = []() { return std::make_shared(&s_secmon_wrapper); }; + +int main(int argc, char **argv) +{ + consoleDebugInit(debugDevice_SVC); + + /* Initialize global context. */ + SecureMonitorWrapper::Initialize(); + + /* Create server manager. */ + static auto s_server_manager = WaitableManager(1); + + /* Create services. */ + s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { + s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); + s_server_manager.AddWaitable(new ServiceServer("spl:mig", 6)); + s_server_manager.AddWaitable(new ServiceServer("spl:ssl", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:fs", 2)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); + } + } else { + s_server_manager.AddWaitable(new ServiceServer("spl:", 12)); + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + return 0; +} + diff --git a/stratosphere/spl/source/spl_manu_service.cpp b/stratosphere/spl/source/spl_manu_service.cpp new file mode 100644 index 000000000..b78b7e192 --- /dev/null +++ b/stratosphere/spl/source/spl_manu_service.cpp @@ -0,0 +1,24 @@ +/* + * 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_manu_service.hpp" + +Result ManuService::ReEncryptRsaPrivateKey(OutPointerWithClientSize out, InPointer src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option) { + return this->GetSecureMonitorWrapper()->ReEncryptRsaPrivateKey(out.pointer, out.num_elements, src.pointer, src.num_elements, access_key_dec, source_dec, access_key_enc, source_enc, option); +} diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp new file mode 100644 index 000000000..e3e911570 --- /dev/null +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -0,0 +1,58 @@ +/* + * 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" +#include "spl_rsa_service.hpp" + +class ManuService : public RsaService { + public: + ManuService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~ManuService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ReEncryptRsaPrivateKey(OutPointerWithClientSize out, InPointer src, AccessKey access_key_dec, KeySource source_dec, AccessKey access_key_enc, KeySource source_enc, u32 option); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_random_service.cpp b/stratosphere/spl/source/spl_random_service.cpp new file mode 100644 index 000000000..a7b0cd6d3 --- /dev/null +++ b/stratosphere/spl/source/spl_random_service.cpp @@ -0,0 +1,24 @@ +/* + * 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_random_service.hpp" + +Result RandomService::GenerateRandomBytes(OutBuffer out) { + return this->secmon_wrapper->GenerateRandomBytes(out.buffer, out.num_elements); +} diff --git a/stratosphere/spl/source/spl_random_service.hpp b/stratosphere/spl/source/spl_random_service.hpp new file mode 100644 index 000000000..66261f087 --- /dev/null +++ b/stratosphere/spl/source/spl_random_service.hpp @@ -0,0 +1,40 @@ +/* + * 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" +#include "spl_secmon_wrapper.hpp" + +class RandomService final : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + RandomService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~RandomService() { /* ... */ } + private: + /* Actual commands. */ + virtual Result GenerateRandomBytes(OutBuffer out); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMeta(), + }; +}; diff --git a/stratosphere/spl/source/spl_rsa_service.cpp b/stratosphere/spl/source/spl_rsa_service.cpp new file mode 100644 index 000000000..23849b9de --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_service.cpp @@ -0,0 +1,24 @@ +/* + * 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_rsa_service.hpp" + +Result RsaService::DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option) { + return this->GetSecureMonitorWrapper()->DecryptRsaPrivateKey(dst.pointer, dst.num_elements, src.pointer, src.num_elements, access_key, key_source, option); +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp new file mode 100644 index 000000000..6e9b2471e --- /dev/null +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -0,0 +1,57 @@ +/* + * 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" +#include "spl_crypto_service.hpp" + +class RsaService : public CryptoService { + public: + RsaService(SecureMonitorWrapper *sw) : CryptoService(sw) { + /* ... */ + } + + virtual ~RsaService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp new file mode 100644 index 000000000..13ab731af --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -0,0 +1,917 @@ +/* + * 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_secmon_wrapper.hpp" +#include "spl_smc_wrapper.hpp" +#include "spl_ctr_drbg.hpp" + +/* Convenient. */ +constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; +constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; +constexpr u32 WorkBufferMapBase = 0x80000000u; +constexpr u32 CryptAesInMapBase = 0x90000000u; +constexpr u32 CryptAesOutMapBase = 0xC0000000u; +constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - CryptAesInMapBase); + +constexpr size_t RsaPrivateKeySize = 0x100; +constexpr size_t RsaPrivateKeyMetaSize = 0x30; +constexpr size_t LabelDigestSizeMax = 0x20; + +/* 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. */ +static CtrDrbg g_drbg; +static Event g_se_event; +static IEvent *g_se_keyslot_available_event = nullptr; + +static Handle g_se_das_hnd; +static u32 g_se_mapped_work_buffer_addr; +static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; +constexpr size_t MaxWorkBufferSize = sizeof(g_work_buffer) / 2; + +static HosMutex g_async_op_lock; + +void SecureMonitorWrapper::InitializeCtrDrbg() { + u8 seed[CtrDrbg::SeedSize]; + + if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { + std::abort(); + } + + g_drbg.Initialize(seed); +} + +void SecureMonitorWrapper::InitializeSeEvents() { + 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); + + g_se_keyslot_available_event = CreateWriteOnlySystemEvent(); + g_se_keyslot_available_event->Signal(); +} + +void SecureMonitorWrapper::InitializeDeviceAddressSpace() { + constexpr u64 DeviceName_SE = 29; + + /* Create Address Space. */ + if (R_FAILED(svcCreateDeviceAddressSpace(&g_se_das_hnd, 0, (1ul << 32)))) { + std::abort(); + } + /* Attach it to the SE. */ + if (R_FAILED(svcAttachDeviceAddressSpace(DeviceName_SE, g_se_das_hnd))) { + std::abort(); + } + + const u64 work_buffer_addr = reinterpret_cast(g_work_buffer); + g_se_mapped_work_buffer_addr = WorkBufferMapBase + (work_buffer_addr & DeviceAddressSpaceAlignMask); + + /* 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))) { + std::abort(); + } +} + +void SecureMonitorWrapper::Initialize() { + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + /* Initialize SE interrupt + keyslot events. */ + InitializeSeEvents(); + /* Initialize DAS for the SE. */ + InitializeDeviceAddressSpace(); +} + +void SecureMonitorWrapper::CalcMgf1AndXor(void *dst, size_t dst_size, const void *src, size_t src_size) { + uint8_t *dst_u8 = reinterpret_cast(dst); + + u32 ctr = 0; + while (dst_size > 0) { + size_t cur_size = SHA256_HASH_SIZE; + if (cur_size > dst_size) { + cur_size = dst_size; + } + dst_size -= cur_size; + + u32 ctr_be = __builtin_bswap32(ctr++); + u8 hash[SHA256_HASH_SIZE]; + { + Sha256Context ctx; + sha256ContextCreate(&ctx); + sha256ContextUpdate(&ctx, src, src_size); + sha256ContextUpdate(&ctx, &ctr_be, sizeof(ctr_be)); + sha256ContextGetHash(&ctx, hash); + } + + for (size_t i = 0; i < cur_size; i++) { + *(dst_u8++) ^= hash[i]; + } + } +} + +size_t SecureMonitorWrapper::DecodeRsaOaep(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size) { + /* Very basic validation. */ + if (dst_size == 0 || src_size != 0x100 || label_digest_size != SHA256_HASH_SIZE) { + return 0; + } + + u8 block[0x100]; + std::memcpy(block, src, sizeof(block)); + + /* First, validate byte 0 == 0, and unmask DB. */ + int invalid = block[0]; + u8 *salt = block + 1; + u8 *db = salt + SHA256_HASH_SIZE; + CalcMgf1AndXor(salt, SHA256_HASH_SIZE, db, src_size - (1 + SHA256_HASH_SIZE)); + CalcMgf1AndXor(db, src_size - (1 + SHA256_HASH_SIZE), salt, SHA256_HASH_SIZE); + + /* Validate label digest. */ + for (size_t i = 0; i < SHA256_HASH_SIZE; i++) { + invalid |= db[i] ^ reinterpret_cast(label_digest)[i]; + } + + /* Locate message after 00...0001 padding. */ + const u8 *padded_msg = db + SHA256_HASH_SIZE; + size_t padded_msg_size = src_size - (1 + 2 * SHA256_HASH_SIZE); + size_t msg_ind = 0; + int not_found = 1; + int wrong_padding = 0; + size_t i = 0; + while (i < padded_msg_size) { + int zero = (padded_msg[i] == 0); + int one = (padded_msg[i] == 1); + msg_ind += static_cast(not_found & one) * (++i); + not_found &= ~one; + wrong_padding |= (not_found & ~zero); + } + + if (invalid | not_found | wrong_padding) { + return 0; + } + + /* Copy message out. */ + size_t msg_size = padded_msg_size - msg_ind; + if (msg_size > dst_size) { + return 0; + } + std::memcpy(dst, padded_msg + msg_ind, msg_size); + return msg_size; +} + +void SecureMonitorWrapper::WaitSeOperationComplete() { + eventWait(&g_se_event, U64_MAX); +} + +Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { + if (result == SmcResult_Success) { + return ResultSuccess; + } + if (result < SmcResult_Max) { + return MAKERESULT(Module_Spl, static_cast(result)); + } + return ResultSplUnknownSmcResult; +} + +SmcResult SecureMonitorWrapper::WaitCheckStatus(AsyncOperationKey op_key) { + WaitSeOperationComplete(); + + SmcResult op_res; + SmcResult res = SmcWrapper::CheckStatus(&op_res, op_key); + if (res != SmcResult_Success) { + return res; + } + + return op_res; +} + +SmcResult SecureMonitorWrapper::WaitGetResult(void *out_buf, size_t out_buf_size, AsyncOperationKey op_key) { + WaitSeOperationComplete(); + + SmcResult op_res; + SmcResult res = SmcWrapper::GetResult(&op_res, out_buf, out_buf_size, op_key); + if (res != SmcResult_Success) { + return 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(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 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) { + /* 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) { + struct ExpModLayout { + u8 base[0x100]; + u8 exp[0x100]; + u8 mod[0x100]; + }; + ExpModLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (exp_size > sizeof(layout->exp)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (out_size > MaxWorkBufferSize) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->exp, exp, exp_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::ExpMod(&op_key, layout->base, layout->exp, exp_size, layout->mod); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(g_work_buffer, sizeof(out_size)); + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess; +} + +Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { + 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) { + 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) { + u64 is_retail; + Result rc = this->GetConfig(&is_retail, SplConfigItem_IsRetail); + if (R_FAILED(rc)) { + return rc; + } + + *out = (is_retail == 0); + return ResultSuccess; +} + +Result SecureMonitorWrapper::SetBootReason(BootReasonValue boot_reason) { + if (this->IsBootReasonSet()) { + return ResultSplBootReasonAlreadySet; + } + + this->boot_reason = boot_reason; + this->boot_reason_set = true; + return ResultSuccess; +} + +Result SecureMonitorWrapper::GetBootReason(BootReasonValue *out) { + if (!this->IsBootReasonSet()) { + return ResultSplBootReasonNotSet; + } + + *out = GetBootReason(); + 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(src); + const uintptr_t dst_addr = reinterpret_cast(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 = CryptAesOutMapBase + (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(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(src), src_size); + armDCacheFlush(dst, dst_size); + { + std::scoped_lock 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) { + if (GetRuntimeFirmwareVersion() <= FirmwareVersion_100) { + /* On 1.0.0, keyslots were kind of a wild west. */ + *out_keyslot = 0; + return ResultSuccess; + } + + for (size_t i = 0; i < GetMaxKeyslots(); i++) { + if (this->keyslot_owners[i] == 0) { + this->keyslot_owners[i] = owner; + *out_keyslot = static_cast(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 && GetRuntimeFirmwareVersion() > FirmwareVersion_100) { + return ResultSplInvalidKeyslot; + } + return ResultSuccess; +} + +Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { + if (GetRuntimeFirmwareVersion() <= FirmwareVersion_100) { + /* On 1.0.0, keyslots were kind of a wild west. */ + return ResultSuccess; + } + + 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::DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct DecryptRsaPrivateKeyLayout { + u8 data[RsaPrivateKeySize + RsaPrivateKeyMetaSize]; + }; + DecryptRsaPrivateKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size < RsaPrivateKeyMetaSize || src_size > sizeof(DecryptRsaPrivateKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout->data, src, src_size); + armDCacheFlush(layout, sizeof(*layout)); + + SmcResult smc_res; + size_t copy_size = 0; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + copy_size = std::min(dst_size, src_size - RsaPrivateKeyMetaSize); + smc_res = SmcWrapper::DecryptOrImportRsaPrivateKey(layout->data, src_size, access_key, key_source, SmcDecryptOrImportMode_DecryptRsaPrivateKey); + } else { + smc_res = SmcWrapper::DecryptRsaPrivateKey(©_size, layout->data, src_size, access_key, key_source, option); + copy_size = std::min(dst_size, copy_size); + } + + armDCacheFlush(layout, sizeof(*layout)); + if (smc_res == SmcResult_Success) { + std::memcpy(dst, layout->data, copy_size); + } + + return ConvertToSplResult(smc_res); +} + +Result SecureMonitorWrapper::ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + struct ImportSecureExpModKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; + }; + ImportSecureExpModKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size > sizeof(ImportSecureExpModKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + + armDCacheFlush(layout, sizeof(*layout)); + SmcResult smc_res; + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + smc_res = SmcWrapper::DecryptOrImportRsaPrivateKey(layout->data, src_size, access_key, key_source, option); + } else { + smc_res = SmcWrapper::ImportSecureExpModKey(layout->data, src_size, access_key, key_source, option); + } + + return ConvertToSplResult(smc_res); +} + +Result SecureMonitorWrapper::SecureExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, u32 option) { + struct SecureExpModLayout { + u8 base[0x100]; + u8 mod[0x100]; + }; + SecureExpModLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (out_size > MaxWorkBufferSize) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::SecureExpMod(&op_key, layout->base, layout->mod, option); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, out_size, op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(g_work_buffer, sizeof(out_size)); + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess; +} + +Result SecureMonitorWrapper::ImportSslKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportSslKey); +} + +Result SecureMonitorWrapper::SslExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return SecureExpMod(out, out_size, base, base_size, mod, mod_size, SmcSecureExpModMode_Ssl); +} + +Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportEsKey); + } else { + struct ImportEsKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; + }; + ImportEsKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size > sizeof(ImportEsKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + + armDCacheFlush(layout, sizeof(*layout)); + return ConvertToSplResult(SmcWrapper::ImportEsKey(layout->data, src_size, access_key, key_source, option)); + } +} + +Result SecureMonitorWrapper::UnwrapEsRsaOaepWrappedKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation, EsKeyType type) { + struct UnwrapEsKeyLayout { + u8 base[0x100]; + u8 mod[0x100]; + }; + UnwrapEsKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate sizes. */ + if (base_size > sizeof(layout->base)) { + return ResultSplInvalidSize; + } + if (mod_size > sizeof(layout->mod)) { + return ResultSplInvalidSize; + } + if (label_digest_size > LabelDigestSizeMax) { + return ResultSplInvalidSize; + } + + /* Copy data into work buffer. */ + const size_t base_ofs = sizeof(layout->base) - base_size; + const size_t mod_ofs = sizeof(layout->mod) - mod_size; + std::memset(layout, 0, sizeof(*layout)); + std::memcpy(layout->base + base_ofs, base, base_size); + std::memcpy(layout->mod + mod_ofs, mod, mod_size); + + /* Do exp mod operation. */ + armDCacheFlush(layout, sizeof(*layout)); + { + std::scoped_lock lk(g_async_op_lock); + AsyncOperationKey op_key; + + SmcResult res = SmcWrapper::UnwrapTitleKey(&op_key, layout->base, layout->mod, label_digest, label_digest_size, SmcWrapper::GetUnwrapEsKeyOption(type, generation)); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + if ((res = WaitGetResult(g_work_buffer, sizeof(*out_access_key), op_key)) != SmcResult_Success) { + return ConvertToSplResult(res); + } + } + armDCacheFlush(g_work_buffer, sizeof(*out_access_key)); + + std::memcpy(out_access_key, g_work_buffer, sizeof(*out_access_key)); + return ResultSuccess; +} + +Result SecureMonitorWrapper::UnwrapTitleKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) { + return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, EsKeyType_TitleKey); +} + +Result SecureMonitorWrapper::UnwrapCommonTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation) { + return ConvertToSplResult(SmcWrapper::UnwrapCommonTitleKey(out_access_key, key_source, generation)); +} + +Result SecureMonitorWrapper::ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source) { + return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportDrmKey); +} + +Result SecureMonitorWrapper::DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size) { + return SecureExpMod(out, out_size, base, base_size, mod, mod_size, SmcSecureExpModMode_Drm); +} + +Result SecureMonitorWrapper::UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation) { + return UnwrapEsRsaOaepWrappedKey(out_access_key, base, base_size, mod, mod_size, label_digest, label_digest_size, generation, EsKeyType_ElicenseKey); +} + +Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + /* Right now, this is just literally the same function as LoadTitleKey in N's impl. */ + return LoadTitleKey(keyslot, owner, access_key); +} + +Result SecureMonitorWrapper::ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option) { + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + option = SmcDecryptOrImportMode_ImportLotusKey; + } + return ImportSecureExpModKey(src, src_size, access_key, key_source, option); +} + +Result SecureMonitorWrapper::DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size) { + /* Validate sizes. */ + if (dst_size > MaxWorkBufferSize || label_digest_size != LabelDigestSizeMax) { + return ResultSplInvalidSize; + } + + /* Nintendo doesn't check this result code, but we will. */ + Result rc = SecureExpMod(g_work_buffer, 0x100, base, base_size, mod, mod_size, SmcSecureExpModMode_Lotus); + if (R_FAILED(rc)) { + return rc; + } + + size_t data_size = DecodeRsaOaep(dst, dst_size, label_digest, label_digest_size, g_work_buffer, 0x100); + if (data_size == 0) { + return ResultSplDecryptionFailed; + } + + *out_size = static_cast(data_size); + return ResultSuccess; +} + +Result SecureMonitorWrapper::GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which) { + return ConvertToSplResult(SmcWrapper::GenerateSpecificAesKey(out_key, key_source, generation, which)); +} + +Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + return ConvertToSplResult(SmcWrapper::LoadTitleKey(keyslot, access_key)); +} + +Result SecureMonitorWrapper::GetPackage2Hash(void *dst, const size_t size) { + u64 hash[4]; + + if (size < sizeof(hash)) { + return ResultSplInvalidSize; + } + + SmcResult smc_res; + if ((smc_res = SmcWrapper::GetConfig(hash, 4, SplConfigItem_Package2Hash)) != SmcResult_Success) { + return ConvertToSplResult(smc_res); + } + + std::memcpy(dst, hash, sizeof(hash)); + return ResultSuccess; +} + +Result SecureMonitorWrapper::ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option) { + struct ReEncryptRsaPrivateKeyLayout { + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; + AccessKey access_key_dec; + KeySource source_dec; + AccessKey access_key_enc; + KeySource source_enc; + }; + ReEncryptRsaPrivateKeyLayout *layout = reinterpret_cast(g_work_buffer); + + /* Validate size. */ + if (src_size < RsaPrivateKeyMetaSize || src_size > sizeof(ReEncryptRsaPrivateKeyLayout)) { + return ResultSplInvalidSize; + } + + std::memcpy(layout, src, src_size); + layout->access_key_dec = access_key_dec; + layout->source_dec = source_dec; + layout->access_key_enc = access_key_enc; + layout->source_enc = source_enc; + + armDCacheFlush(layout, sizeof(*layout)); + + SmcResult smc_res = SmcWrapper::ReEncryptRsaPrivateKey(layout->data, src_size, layout->access_key_dec, layout->source_dec, layout->access_key_enc, layout->source_enc, option); + if (smc_res == SmcResult_Success) { + size_t copy_size = std::min(dst_size, src_size); + armDCacheFlush(layout, copy_size); + std::memcpy(dst, layout->data, copy_size); + } + + return ConvertToSplResult(smc_res); + +} + +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(); +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp new file mode 100644 index 000000000..83534ee96 --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -0,0 +1,139 @@ +/* + * 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 SecureMonitorWrapper { + public: + static constexpr size_t MaxAesKeyslots = 6; + static constexpr size_t MaxAesKeyslotsDeprecated = 4; + private: + const void *keyslot_owners[MaxAesKeyslots] = {}; + BootReasonValue boot_reason = {}; + bool boot_reason_set = false; + private: + static size_t GetMaxKeyslots() { + return (GetRuntimeFirmwareVersion() >= FirmwareVersion_600) ? MaxAesKeyslots : MaxAesKeyslotsDeprecated; + } + private: + BootReasonValue GetBootReason() const { + return this->boot_reason; + } + bool IsBootReasonSet() const { + return this->boot_reason_set; + } + static Result ConvertToSplResult(SmcResult result); + private: + static void InitializeCtrDrbg(); + static void InitializeSeEvents(); + static void InitializeDeviceAddressSpace(); + + static void CalcMgf1AndXor(void *dst, size_t dst_size, const void *src, size_t src_size); + static size_t DecodeRsaOaep(void *dst, size_t dst_size, const void *label_digest, size_t label_digest_size, const void *src, size_t src_size); + public: + static void Initialize(); + private: + Result GenerateRandomBytesInternal(void *out, size_t size); + void WaitSeOperationComplete(); + SmcResult WaitCheckStatus(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); + Result ImportSecureExpModKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result SecureExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size, u32 option); + Result UnwrapEsRsaOaepWrappedKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation, EsKeyType type); + public: + /* General. */ + 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 SetConfig(SplConfigItem which, u64 value); + Result GenerateRandomBytes(void *out, size_t size); + Result IsDevelopment(bool *out); + Result SetBootReason(BootReasonValue boot_reason); + 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); + + /* RSA. */ + Result DecryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + + /* SSL */ + Result ImportSslKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result SslExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + + /* ES */ + Result ImportEsKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result UnwrapTitleKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); + Result UnwrapCommonTitleKey(AccessKey *out_access_key, const KeySource &key_source, u32 generation); + Result ImportDrmKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source); + Result DrmExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *mod, size_t mod_size); + Result UnwrapElicenseKey(AccessKey *out_access_key, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size, u32 generation); + Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); + + /* FS */ + Result ImportLotusKey(const void *src, size_t src_size, const AccessKey &access_key, const KeySource &key_source, u32 option); + Result DecryptLotusMessage(u32 *out_size, void *dst, size_t dst_size, const void *base, size_t base_size, const void *mod, size_t mod_size, const void *label_digest, size_t label_digest_size); + Result GenerateSpecificAesKey(AesKey *out_key, const KeySource &key_source, u32 generation, u32 which); + Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + Result GetPackage2Hash(void *dst, const size_t size); + + /* Manu. */ + Result ReEncryptRsaPrivateKey(void *dst, size_t dst_size, const void *src, size_t src_size, const AccessKey &access_key_dec, const KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &source_enc, u32 option); + + /* Helper. */ + Result 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; + } + }; +}; diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp new file mode 100644 index 000000000..5845f37ce --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -0,0 +1,336 @@ +/* + * 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 KeySource &source, u32 generation, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateAesKek; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[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 KeySource &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.data64[0]; + args.X[5] = source.data64[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &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.data64[0]; + args.X[3] = iv_ctr.data64[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(AesKey *out_key, const KeySource &source, u32 generation, u32 which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateSpecificAesKey; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; + args.X[3] = generation; + args.X[4] = which; + svcCallSecureMonitor(&args); + + out_key->data64[0] = args.X[1]; + out_key->data64[1] = args.X[2]; + 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 KeySource &source_dec, const AccessKey &access_key_enc, const KeySource &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 KeySource &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.data64[0]; + args.X[7] = source.data64[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 KeySource &source, u32 generation) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[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 KeySource &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.data64[0]; + args.X[7] = source.data64[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 KeySource &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.data64[0]; + args.X[7] = source.data64[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 KeySource &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.data64[0]; + args.X[7] = source.data64[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..ea8798ee5 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -0,0 +1,54 @@ +/* + * 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 inline u32 GetCryptAesMode(SmcCipherMode mode, u32 keyslot) { + return static_cast((mode << 4) | (keyslot & 7)); + } + static inline u32 GetUnwrapEsKeyOption(EsKeyType type, u32 generation) { + return static_cast((type << 6) | (generation & 0x3F)); + } + 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 KeySource &source, u32 generation, u32 option); + static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source); + static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + static SmcResult GenerateSpecificAesKey(AesKey *out_key, const KeySource &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 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 KeySource &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 KeySource &source, u32 generation); + + /* Deprecated functions. */ + 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 KeySource &source, u32 option); + static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option); +}; diff --git a/stratosphere/spl/source/spl_ssl_service.cpp b/stratosphere/spl/source/spl_ssl_service.cpp new file mode 100644 index 000000000..2572968f1 --- /dev/null +++ b/stratosphere/spl/source/spl_ssl_service.cpp @@ -0,0 +1,28 @@ +/* + * 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_ssl_service.hpp" + +Result SslService::ImportSslKey(InPointer src, AccessKey access_key, KeySource key_source) { + return this->GetSecureMonitorWrapper()->ImportSslKey(src.pointer, src.num_elements, access_key, key_source); +} + +Result SslService::SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod) { + return this->GetSecureMonitorWrapper()->SslExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, mod.pointer, mod.num_elements); +} diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp new file mode 100644 index 000000000..cfcdff866 --- /dev/null +++ b/stratosphere/spl/source/spl_ssl_service.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" +#include "spl_rsa_service.hpp" + +class SslService : public RsaService { + public: + SslService(SecureMonitorWrapper *sw) : RsaService(sw) { + /* ... */ + } + + virtual ~SslService() { + /* ... */ + } + protected: + /* Actual commands. */ + virtual Result ImportSslKey(InPointer src, AccessKey access_key, KeySource key_source); + virtual Result SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); + public: + DEFINE_SERVICE_DISPATCH_TABLE { + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + + }; +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp new file mode 100644 index 000000000..c4cebbe2f --- /dev/null +++ b/stratosphere/spl/source/spl_types.hpp @@ -0,0 +1,161 @@ +/* + * 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 +#include +#include + +enum SmcResult : u32 { + SmcResult_Success = 0, + SmcResult_NotImplemented = 1, + SmcResult_InvalidArgument = 2, + SmcResult_InProgress = 3, + SmcResult_NoAsyncOperation = 4, + SmcResult_InvalidAsyncOperation = 5, + SmcResult_Blacklisted = 6, + + SmcResult_Max = 99, +}; + +enum SmcCipherMode : u32 { + SmcCipherMode_CbcEncrypt = 0, + SmcCipherMode_CbcDecrypt = 1, + SmcCipherMode_Ctr = 2, +}; + +enum SmcDecryptOrImportMode : u32 { + SmcDecryptOrImportMode_DecryptRsaPrivateKey = 0, + SmcDecryptOrImportMode_ImportLotusKey = 1, + SmcDecryptOrImportMode_ImportEsKey = 2, + SmcDecryptOrImportMode_ImportSslKey = 3, + SmcDecryptOrImportMode_ImportDrmKey = 4, +}; + +enum SmcSecureExpModMode : u32 { + SmcSecureExpModMode_Lotus = 0, + SmcSecureExpModMode_Ssl = 1, + SmcSecureExpModMode_Drm = 2, +}; + +enum EsKeyType : u32 { + EsKeyType_TitleKey = 0, + EsKeyType_ElicenseKey = 1, +}; + +struct AsyncOperationKey { + u64 value; +}; + +struct BootReasonValue { + u8 power_intr; + u8 rtc_intr; + u8 _0x3; + u8 boot_reason; +}; +static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); + +#pragma pack(push, 1) +struct AesKey { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); + +struct IvCtr { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); + +struct Cmac { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); + +struct AccessKey { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); + +struct KeySource { + union { + u8 data[AES_128_KEY_SIZE]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; +}; +static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); +#pragma pack(pop) + +enum CsrngCmd { + Csrng_Cmd_GenerateRandomBytes = 0, +}; + +enum SplServiceCmd { + /* 1.0.0+ */ + Spl_Cmd_GetConfig = 0, + Spl_Cmd_ExpMod = 1, + Spl_Cmd_GenerateAesKek = 2, + Spl_Cmd_LoadAesKey = 3, + Spl_Cmd_GenerateAesKey = 4, + Spl_Cmd_SetConfig = 5, + Spl_Cmd_GenerateRandomBytes = 7, + Spl_Cmd_ImportLotusKey = 9, + Spl_Cmd_DecryptLotusMessage = 10, + Spl_Cmd_IsDevelopment = 11, + Spl_Cmd_GenerateSpecificAesKey = 12, + Spl_Cmd_DecryptRsaPrivateKey = 13, + Spl_Cmd_DecryptAesKey = 14, + Spl_Cmd_CryptAesCtr = 15, + Spl_Cmd_ComputeCmac = 16, + Spl_Cmd_ImportEsKey = 17, + Spl_Cmd_UnwrapTitleKey = 18, + Spl_Cmd_LoadTitleKey = 19, + + /* 2.0.0+ */ + Spl_Cmd_UnwrapCommonTitleKey = 20, + Spl_Cmd_AllocateAesKeyslot = 21, + Spl_Cmd_FreeAesKeyslot = 22, + Spl_Cmd_GetAesKeyslotAvailableEvent = 23, + + /* 3.0.0+ */ + Spl_Cmd_SetBootReason = 24, + Spl_Cmd_GetBootReason = 25, + + /* 5.0.0+ */ + Spl_Cmd_ImportSslKey = 26, + Spl_Cmd_SslExpMod = 27, + Spl_Cmd_ImportDrmKey = 28, + Spl_Cmd_DrmExpMod = 29, + Spl_Cmd_ReEncryptRsaPrivateKey = 30, + Spl_Cmd_GetPackage2Hash = 31, + + /* 6.0.0+ */ + Spl_Cmd_UnwrapElicenseKey = 31, /* re-used command id :( */ + Spl_Cmd_LoadElicenseKey = 32, +}; diff --git a/stratosphere/spl/spl.json b/stratosphere/spl/spl.json new file mode 100644 index 000000000..022b06cc1 --- /dev/null +++ b/stratosphere/spl/spl.json @@ -0,0 +1,74 @@ +{ + "name": "spl", + "title_id": "0x0100000000000028", + "main_thread_stack_size": "0x00002000", + "main_thread_priority": 27, + "default_cpu_id": 3, + "process_category": 1, + "kernel_capabilities": [{ + "type": "handle_table_size", + "value": 128 + }, { + "type": "irq_pair", + "value": [44, null] + }, { + "type": "syscalls", + "value": { + "svcSetHeapSize": "0x01", + "svcSetMemoryPermission": "0x02", + "svcSetMemoryAttribute": "0x03", + "svcMapMemory": "0x04", + "svcUnmapMemory": "0x05", + "svcQueryMemory": "0x06", + "svcExitProcess": "0x07", + "svcCreateThread": "0x08", + "svcStartThread": "0x09", + "svcExitThread": "0x0a", + "svcSleepThread": "0x0b", + "svcGetThreadPriority": "0x0c", + "svcSetThreadPriority": "0x0d", + "svcGetThreadCoreMask": "0x0e", + "svcSetThreadCoreMask": "0x0f", + "svcGetCurrentProcessorNumber": "0x10", + "svcSignalEvent": "0x11", + "svcClearEvent": "0x12", + "svcMapSharedMemory": "0x13", + "svcUnmapSharedMemory": "0x14", + "svcCreateTransferMemory": "0x15", + "svcCloseHandle": "0x16", + "svcResetSignal": "0x17", + "svcWaitSynchronization": "0x18", + "svcCancelSynchronization": "0x19", + "svcArbitrateLock": "0x1a", + "svcArbitrateUnlock": "0x1b", + "svcWaitProcessWideKeyAtomic": "0x1c", + "svcSignalProcessWideKey": "0x1d", + "svcGetSystemTick": "0x1e", + "svcConnectToNamedPort": "0x1f", + "svcSendSyncRequestLight": "0x20", + "svcSendSyncRequest": "0x21", + "svcSendSyncRequestWithUserBuffer": "0x22", + "svcSendAsyncRequestWithUserBuffer": "0x23", + "svcGetProcessId": "0x24", + "svcGetThreadId": "0x25", + "svcBreak": "0x26", + "svcOutputDebugString": "0x27", + "svcReturnFromException": "0x28", + "svcGetInfo": "0x29", + "svcWaitForAddress": "0x34", + "svcSignalToAddress": "0x35", + "svcCreateSession": "0x40", + "svcAcceptSession": "0x41", + "svcReplyAndReceiveLight": "0x42", + "svcReplyAndReceive": "0x43", + "svcReplyAndReceiveWithUserBuffer": "0x44", + "svcCreateEvent": "0x45", + "svcCreateInterruptEvent": "0x53", + "svcCreateDeviceAddressSpace": "0x56", + "svcAttachDeviceAddressSpace": "0x57", + "svcMapDeviceAddressSpaceAligned": "0x5a", + "svcUnmapDeviceAddressSpace": "0x5c", + "svcCallSecureMonitor": "0x7f" + } + }] +} \ No newline at end of file