From 9858d6fc95b2dff98cc41d52c3b89392964a5312 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 01:18:18 -0700 Subject: [PATCH 01/16] spl: Start skeletoning spl. --- stratosphere/spl/Makefile | 160 ++++++++++++++++++ .../spl/source/spl_general_service.cpp | 48 ++++++ .../spl/source/spl_general_service.hpp | 52 ++++++ stratosphere/spl/source/spl_main.cpp | 110 ++++++++++++ .../spl/source/spl_random_service.cpp | 24 +++ .../spl/source/spl_random_service.hpp | 40 +++++ .../spl/source/spl_secmon_wrapper.cpp | 80 +++++++++ .../spl/source/spl_secmon_wrapper.hpp | 53 ++++++ stratosphere/spl/source/spl_types.hpp | 122 +++++++++++++ stratosphere/spl/spl.json | 74 ++++++++ 10 files changed, 763 insertions(+) create mode 100644 stratosphere/spl/Makefile create mode 100644 stratosphere/spl/source/spl_general_service.cpp create mode 100644 stratosphere/spl/source/spl_general_service.hpp create mode 100644 stratosphere/spl/source/spl_main.cpp create mode 100644 stratosphere/spl/source/spl_random_service.cpp create mode 100644 stratosphere/spl/source/spl_random_service.hpp create mode 100644 stratosphere/spl/source/spl_secmon_wrapper.cpp create mode 100644 stratosphere/spl/source/spl_secmon_wrapper.hpp create mode 100644 stratosphere/spl/source/spl_types.hpp create mode 100644 stratosphere/spl/spl.json 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_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp new file mode 100644 index 000000000..ae0c9bf92 --- /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->secmon_wrapper->GetConfig(out.GetPointer(), static_cast(which)); +} + +Result GeneralService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { + return this->secmon_wrapper->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); +} + +Result GeneralService::SetConfig(u32 which, u64 value) { + return this->secmon_wrapper->SetConfig(static_cast(which), value); +} + +Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { + return this->secmon_wrapper->GenerateRandomBytes(out.pointer, out.num_elements); +} + +Result GeneralService::IsDevelopment(Out is_dev) { + return this->secmon_wrapper->IsDevelopment(is_dev.GetPointer()); +} + +Result GeneralService::SetBootReason(BootReasonValue boot_reason) { + return this->secmon_wrapper->SetBootReason(boot_reason); +} + +Result GeneralService::GetBootReason(Out out) { + return this->secmon_wrapper->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..9f785ee74 --- /dev/null +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -0,0 +1,52 @@ +/* + * 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 final : public IServiceObject { + private: + SecureMonitorWrapper *secmon_wrapper; + public: + GeneralService(SecureMonitorWrapper *sw) : secmon_wrapper(sw) { + /* ... */ + } + + virtual ~GeneralService() { /* ... */ } + private: + /* 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..2e2f82047 --- /dev/null +++ b/stratosphere/spl/source/spl_main.cpp @@ -0,0 +1,110 @@ +/* + * 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" + +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); }; + +int main(int argc, char **argv) +{ + consoleDebugInit(debugDevice_SVC); + + /* Create server manager. */ + static auto s_server_manager = WaitableManager(1); + + /* Create services. */ + s_server_manager.AddWaitable(new ServiceServer("csrng", 9)); + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { + s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); + /* TODO: Other services. */ + } else { + /* TODO, DeprecatedGeneralService */ + } + + /* Loop forever, servicing our services. */ + s_server_manager.Process(); + + return 0; +} + 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..0d307fcec --- /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_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp new file mode 100644 index 000000000..a2421d92f --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -0,0 +1,80 @@ +/* + * 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" + +Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { + if (result == SmcResult_Success) { + return ResultSuccess; + } + if (result < SmcResult_Max) { + return MAKERESULT(Module_Spl, static_cast(result)); + } + return ResultSplUnknownSmcResult; +} + +Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +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) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +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; +} \ 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..5c7104e4d --- /dev/null +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -0,0 +1,53 @@ +/* + * 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: + uintptr_t 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); + public: + void Initialize(); + public: + Result GetConfig(u64 *out, SplConfigItem which); + Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); + 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); +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp new file mode 100644 index 000000000..41745f74c --- /dev/null +++ b/stratosphere/spl/source/spl_types.hpp @@ -0,0 +1,122 @@ +/* + * 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, + SmcCipherMode_Cmac = 3, +}; + +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!"); + +struct AesKey { + u8 data[AES_128_KEY_SIZE]; +}; + +struct IvCtr { + u8 data[AES_128_KEY_SIZE]; +}; + +struct Cmac { + u8 data[AES_128_KEY_SIZE]; +}; + +struct AccessKey { + u8 data[AES_128_KEY_SIZE]; +}; + +struct KeySource { + u8 data[AES_128_KEY_SIZE]; +}; + +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_GetAesKeyslotEvent = 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 From 2dfa1c96d1672d5489d3f07ffdc0cea0da9f45e2 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 05:38:11 -0700 Subject: [PATCH 02/16] spl: continue implementing. --- stratosphere/spl/source/spl_ctr_drbg.cpp | 81 +++++ stratosphere/spl/source/spl_ctr_drbg.hpp | 60 ++++ stratosphere/spl/source/spl_main.cpp | 5 +- .../spl/source/spl_secmon_wrapper.cpp | 92 ++++- .../spl/source/spl_secmon_wrapper.hpp | 7 +- stratosphere/spl/source/spl_smc_wrapper.cpp | 334 ++++++++++++++++++ stratosphere/spl/source/spl_smc_wrapper.hpp | 47 +++ stratosphere/spl/source/spl_types.hpp | 12 +- 8 files changed, 628 insertions(+), 10 deletions(-) create mode 100644 stratosphere/spl/source/spl_ctr_drbg.cpp create mode 100644 stratosphere/spl/source/spl_ctr_drbg.hpp create mode 100644 stratosphere/spl/source/spl_smc_wrapper.cpp create mode 100644 stratosphere/spl/source/spl_smc_wrapper.hpp diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp new file mode 100644 index 000000000..1eadb7e4e --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_types.hpp" +#include "spl_ctr_drbg.hpp" + +void CtrDrbg::Update(const void *data) { + aes128ContextCreate(&this->aes_ctx, this->key, true); + for (size_t offset = 0; offset < sizeof(this->work[1]); offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, &this->work[1][offset], this->counter); + } + + Xor(this->work[1], data, sizeof(this->work[1])); + + std::memcpy(this->key, &this->work[1][0], sizeof(this->key)); + std::memcpy(this->counter, &this->work[1][BlockSize], sizeof(this->key)); +} + +void CtrDrbg::Initialize(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + std::memset(this->key, 0, sizeof(this->key)); + std::memset(this->counter, 0, sizeof(this->counter)); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +void CtrDrbg::Reseed(const void *seed) { + std::memcpy(this->work[0], seed, sizeof(this->work[0])); + this->Update(this->work[0]); + this->reseed_counter = 1; +} + +bool CtrDrbg::GenerateRandomBytes(void *out, size_t size) { + if (size > MaxRequestSize) { + return false; + } + + if (this->reseed_counter > ReseedInterval) { + return false; + } + + aes128ContextCreate(&this->aes_ctx, this->key, true); + u8 *cur_dst = reinterpret_cast(out); + + size_t aligned_size = (size & ~(BlockSize - 1)); + for (size_t offset = 0; offset < aligned_size; offset += BlockSize) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, cur_dst, this->counter); + cur_dst += BlockSize; + } + + if (size > aligned_size) { + IncrementCounter(this->counter); + aes128EncryptBlock(&this->aes_ctx, this->work[1], this->counter); + std::memcpy(cur_dst, this->work[1], size - aligned_size); + } + + std::memset(this->work[0], 0, sizeof(this->work[0])); + this->Update(this->work[0]); + + this->reseed_counter++; + return true; + +} \ No newline at end of file diff --git a/stratosphere/spl/source/spl_ctr_drbg.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp new file mode 100644 index 000000000..71564b582 --- /dev/null +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +/* Nintendo implements CTR_DRBG for their csrng. We will do the same. */ +class CtrDrbg { + public: + static constexpr size_t MaxRequestSize = 0x10000; + static constexpr size_t ReseedInterval = 0x7FFFFFF0; + static constexpr size_t BlockSize = AES_BLOCK_SIZE; + static constexpr size_t SeedSize = 2 * AES_BLOCK_SIZE; + private: + Aes128Context aes_ctx; + u8 counter[BlockSize]; + u8 key[BlockSize]; + u8 work[2][SeedSize]; + u32 reseed_counter; + private: + static void Xor(void *dst, const void *src, size_t size) { + const u8 *src_u8 = reinterpret_cast(src); + u8 *dst_u8 = reinterpret_cast(dst); + + for (size_t i = 0; i < size; i++) { + dst_u8[i] = src_u8[i]; + } + } + + static void IncrementCounter(void *ctr) { + u64 *ctr_64 = reinterpret_cast(ctr); + + ctr_64[1] = __builtin_bswap64(__builtin_bswap64(ctr_64[1]) + 1); + if (!ctr_64[1]) { + ctr_64[0] = __builtin_bswap64(__builtin_bswap64(ctr_64[0]) + 1); + } + } + private: + void Update(const void *data); + public: + void Initialize(const void *seed); + void Reseed(const void *seed); + bool GenerateRandomBytes(void *out, size_t size); +}; \ No newline at end of file diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 2e2f82047..a644814fb 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -89,12 +89,15 @@ static const auto MakeGeneralService = []() { return std::make_shared(1); /* Create services. */ - s_server_manager.AddWaitable(new ServiceServer("csrng", 9)); + s_server_manager.AddWaitable(new ServiceServer("csrng", 3)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_400) { s_server_manager.AddWaitable(new ServiceServer("spl:", 9)); /* TODO: Other services. */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index a2421d92f..6eeaa0904 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -18,6 +18,40 @@ #include #include "spl_secmon_wrapper.hpp" +#include "spl_smc_wrapper.hpp" +#include "spl_ctr_drbg.hpp" + +/* Globals. */ +static CtrDrbg g_drbg; +static Event g_se_event; +static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; + +void SecureMonitorWrapper::InitializeCtrDrbg() { + u8 seed[CtrDrbg::SeedSize]; + + if (SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)) != SmcResult_Success) { + std::abort(); + } + + g_drbg.Initialize(seed); +} + +void SecureMonitorWrapper::InitializeSeInterruptEvent() { + u64 irq_num; + SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); + Handle hnd; + if (R_FAILED(svcCreateInterruptEvent(&hnd, irq_num, 1))) { + std::abort(); + } + eventLoadRemote(&g_se_event, hnd, true); +} + +void SecureMonitorWrapper::Initialize() { + /* Initialize the Drbg. */ + InitializeCtrDrbg(); + /* Initialize SE interrupt event. */ + InitializeSeInterruptEvent(); +} Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { if (result == SmcResult_Success) { @@ -30,8 +64,25 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { } Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { - /* TODO */ - return ResultKernelConnectionClosed; + /* Nintendo explicitly blacklists package2 hash here, amusingly. */ + /* This is not blacklisted in safemode, but we're never in safe mode... */ + if (which == SplConfigItem_Package2Hash) { + return ResultSplInvalidArgument; + } + + SmcResult res = SmcWrapper::GetConfig(out, 1, which); + + /* Nintendo has some special handling here for hardware type/is_retail. */ + if (which == SplConfigItem_HardwareType && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + if (which == SplConfigItem_IsRetail && res == SmcResult_InvalidArgument) { + *out = 0; + res = SmcResult_Success; + } + + return ConvertToSplResult(res); } Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size) { @@ -40,13 +91,42 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base } Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { - /* TODO */ - return ResultKernelConnectionClosed; + return ConvertToSplResult(SmcWrapper::SetConfig(which, &value, 1)); +} + +Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t size) { + if (!g_drbg.GenerateRandomBytes(out, size)) { + /* We need to reseed. */ + { + u8 seed[CtrDrbg::SeedSize]; + + SmcResult res = SmcWrapper::GenerateRandomBytes(seed, sizeof(seed)); + if (res != SmcResult_Success) { + return ConvertToSplResult(res); + } + + g_drbg.Reseed(seed); + g_drbg.GenerateRandomBytes(out, size); + } + } + + return ResultSuccess; } Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { - /* TODO */ - return ResultKernelConnectionClosed; + u8 *cur_dst = reinterpret_cast(out); + + for (size_t ofs = 0; ofs < size; ofs += CtrDrbg::MaxRequestSize) { + const size_t cur_size = std::min(size - ofs, CtrDrbg::MaxRequestSize); + + Result rc = GenerateRandomBytesInternal(cur_dst, size); + if (R_FAILED(rc)) { + return rc; + } + cur_dst += cur_size; + } + + return ResultSuccess; } Result SecureMonitorWrapper::IsDevelopment(bool *out) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 5c7104e4d..820663f99 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -40,8 +40,13 @@ class SecureMonitorWrapper { return this->boot_reason_set; } static Result ConvertToSplResult(SmcResult result); + private: + static void InitializeCtrDrbg(); + static void InitializeSeInterruptEvent(); public: - void Initialize(); + static void Initialize(); + private: + Result GenerateRandomBytesInternal(void *out, size_t size); public: Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp new file mode 100644 index 000000000..97e39afc5 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +#include "spl_smc_wrapper.hpp" + +enum SmcFunctionId : u32 { + SmcFunctionId_SetConfig = 0xC3000401, + SmcFunctionId_GetConfig = 0xC3000002, + SmcFunctionId_CheckStatus = 0xC3000003, + SmcFunctionId_GetResult = 0xC3000404, + SmcFunctionId_ExpMod = 0xC3000E05, + SmcFunctionId_GenerateRandomBytes = 0xC3000006, + SmcFunctionId_GenerateAesKek = 0xC3000007, + SmcFunctionId_LoadAesKey = 0xC3000008, + SmcFunctionId_CryptAes = 0xC3000009, + SmcFunctionId_GenerateSpecificAesKey = 0xC300000A, + SmcFunctionId_ComputeCmac = 0xC300040B, + SmcFunctionId_ReEncryptRsaPrivateKey = 0xC300D60C, + SmcFunctionId_DecryptOrImportRsaPrivateKey = 0xC300100D, + + SmcFunctionId_SecureExpMod = 0xC300060F, + SmcFunctionId_UnwrapTitleKey = 0xC3000610, + SmcFunctionId_LoadTitleKey = 0xC3000011, + SmcFunctionId_UnwrapCommonTitleKey = 0xC3000012, + + /* Deprecated functions. */ + SmcFunctionId_ImportEsKey = 0xC300100C, + SmcFunctionId_DecryptRsaPrivateKey = 0xC300100D, + SmcFunctionId_ImportSecureExpModKey = 0xC300100E, +}; + +SmcResult SmcWrapper::SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SetConfig; + args.X[1] = which; + args.X[2] = 0; + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + args.X[3 + i] = value[i]; + } + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetConfig(u64 *out, size_t num_qwords, SplConfigItem which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetConfig; + args.X[1] = which; + svcCallSecureMonitor(&args); + + for (size_t i = 0; i < std::min(size_t(4), num_qwords); i++) { + out[i] = args.X[1 + i]; + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CheckStatus(SmcResult *out, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CheckStatus; + args.X[1] = op.value; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GetResult; + args.X[1] = op.value; + args.X[2] = reinterpret_cast(out_buf); + args.X[3] = out_buf_size; + svcCallSecureMonitor(&args); + + *out = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(exp); + args.X[3] = reinterpret_cast(mod); + args.X[4] = exp_size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateRandomBytes; + args.X[1] = size; + svcCallSecureMonitor(&args); + + if (args.X[0] == SmcResult_Success && (size <= sizeof(args) - sizeof(args.X[0]))) { + std::memcpy(out, &args.X[1], size); + } + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateAesKek; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = option; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadAesKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + args.X[4] = source[0]; + args.X[5] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_CryptAes; + args.X[1] = mode; + args.X[2] = iv_ctr[0]; + args.X[3] = iv_ctr[1]; + args.X[4] = src_addr; + args.X[5] = dst_addr; + args.X[6] = size; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_GenerateSpecificAesKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + args.X[4] = which; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ComputeCmac; + args.X[1] = keyslot; + args.X[2] = reinterpret_cast(data); + args.X[3] = size; + svcCallSecureMonitor(&args); + + out_mac.data64[0] = args.X[1]; + out_mac.data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ReEncryptRsaPrivateKey; + args.X[1] = reinterpret_cast(&access_key_dec); + args.X[2] = reinterpret_cast(&access_key_enc); + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = reinterpret_cast(source_dec); + args.X[7] = reinterpret_cast(source_enc); + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_SecureExpMod; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + args.X[3] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapTitleKey; + args.X[1] = reinterpret_cast(base); + args.X[2] = reinterpret_cast(mod); + std::memset(&args.X[3], 0, 4 * sizeof(args.X[3])); + std::memcpy(&args.X[3], label_digest, std::min(size_t(4 * sizeof(args.X[3])), label_digest_size)); + args.X[7] = option; + svcCallSecureMonitor(&args); + + out_op->value = args.X[1]; + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_LoadTitleKey; + args.X[1] = keyslot; + args.X[2] = access_key.data64[0]; + args.X[3] = access_key.data64[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; + args.X[1] = source[0]; + args.X[2] = source[1]; + args.X[3] = generation; + svcCallSecureMonitor(&args); + + out->data64[0] = args.X[1]; + out->data64[1] = args.X[2]; + return static_cast(args.X[0]); +} + + +/* Deprecated functions. */ +SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportEsKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + *out_size = static_cast(args.X[1]); + return static_cast(args.X[0]); +} + +SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { + SecmonArgs args; + + args.X[0] = SmcFunctionId_ImportSecureExpModKey; + args.X[1] = access_key.data64[0]; + args.X[2] = access_key.data64[1]; + args.X[3] = option; + args.X[4] = reinterpret_cast(data); + args.X[5] = size; + args.X[6] = source[0]; + args.X[7] = source[1]; + svcCallSecureMonitor(&args); + + return static_cast(args.X[0]); +} + diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp new file mode 100644 index 000000000..41a97cf40 --- /dev/null +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2018-2019 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once +#include +#include + +#include "spl_types.hpp" + +class SmcWrapper { + public: + static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); + static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); + static SmcResult CheckStatus(SmcResult *out, AsyncOperationKey op); + static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); + static SmcResult ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); + static SmcResult GenerateRandomBytes(void *out, size_t size); + static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); + static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); + static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); + static SmcResult GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which); + static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); + static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option); + static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult SecureExpMod(AsyncOperationKey *out_op, const void *base, const void *mod, u32 option); + static SmcResult UnwrapTitleKey(AsyncOperationKey *out_op, const void *base, const void *mod, const void *label_digest, size_t label_digest_size, u32 option); + static SmcResult LoadTitleKey(u32 keyslot, const AccessKey &access_key); + static SmcResult UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation); + + /* Deprecated functions. */ + static SmcResult ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); +}; diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index 41745f74c..8d8317a48 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -66,12 +66,20 @@ struct IvCtr { }; struct Cmac { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { u8 data[AES_128_KEY_SIZE]; From ccbab35debb16911cbe36270da0c61b856259d1d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 06:10:30 -0700 Subject: [PATCH 03/16] spl: finish GeneralService. --- .../spl/source/spl_secmon_wrapper.cpp | 109 +++++++++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 4 + 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 6eeaa0904..a52c49535 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -21,11 +21,21 @@ #include "spl_smc_wrapper.hpp" #include "spl_ctr_drbg.hpp" +/* Convenient. */ +constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; +constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; +constexpr u32 DeviceMapBase = 0x80000000u; + /* Globals. */ static CtrDrbg g_drbg; static Event g_se_event; + +static Handle g_se_das_hnd; +static u32 g_se_mapped_work_buffer_addr; static __attribute__((aligned(0x1000))) u8 g_work_buffer[0x1000]; +static HosMutex g_se_lock; + void SecureMonitorWrapper::InitializeCtrDrbg() { u8 seed[CtrDrbg::SeedSize]; @@ -46,11 +56,38 @@ void SecureMonitorWrapper::InitializeSeInterruptEvent() { eventLoadRemote(&g_se_event, hnd, true); } +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 = 0x80000000u + (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 event. */ InitializeSeInterruptEvent(); + /* Initialize DAS for the SE. */ + InitializeDeviceAddressSpace(); +} + +void SecureMonitorWrapper::WaitSeOperationComplete() { + eventWait(&g_se_event, U64_MAX); } Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { @@ -63,6 +100,30 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult 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; +} + 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... */ @@ -86,8 +147,52 @@ Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { } 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) { - /* TODO */ - return ResultKernelConnectionClosed; + 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 > sizeof(g_work_buffer) / 2) { + 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. */ + { + std::scoped_lock lk(g_se_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); + } + } + + std::memcpy(out, g_work_buffer, out_size); + return ResultSuccess; } Result SecureMonitorWrapper::SetConfig(SplConfigItem which, u64 value) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 820663f99..cfaf123ca 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -43,10 +43,14 @@ class SecureMonitorWrapper { private: static void InitializeCtrDrbg(); static void InitializeSeInterruptEvent(); + static void InitializeDeviceAddressSpace(); 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); public: Result GetConfig(u64 *out, SplConfigItem which); Result ExpMod(void *out, size_t out_size, const void *base, size_t base_size, const void *exp, size_t exp_size, const void *mod, size_t mod_size); From bfa84e27c13da8b1e6f84565bf613b0f0ef649f0 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 21:00:39 -0700 Subject: [PATCH 04/16] spl: implement CryptoService. --- .../spl/source/spl_crypto_service.cpp | 56 +++ .../spl/source/spl_crypto_service.hpp | 64 ++++ stratosphere/spl/source/spl_ctr_drbg.cpp | 18 +- stratosphere/spl/source/spl_ctr_drbg.hpp | 4 +- .../spl/source/spl_general_service.cpp | 14 +- .../spl/source/spl_general_service.hpp | 8 +- stratosphere/spl/source/spl_main.cpp | 5 +- .../spl/source/spl_secmon_wrapper.cpp | 327 ++++++++++++++++-- .../spl/source/spl_secmon_wrapper.hpp | 49 ++- stratosphere/spl/source/spl_smc_wrapper.cpp | 66 ++-- stratosphere/spl/source/spl_smc_wrapper.hpp | 28 +- stratosphere/spl/source/spl_types.hpp | 21 +- 12 files changed, 558 insertions(+), 102 deletions(-) create mode 100644 stratosphere/spl/source/spl_crypto_service.cpp create mode 100644 stratosphere/spl/source/spl_crypto_service.hpp diff --git a/stratosphere/spl/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp new file mode 100644 index 000000000..53c47242f --- /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..b5e025b17 --- /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 { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_ctr_drbg.cpp b/stratosphere/spl/source/spl_ctr_drbg.cpp index 1eadb7e4e..a468d2dcf 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.cpp +++ b/stratosphere/spl/source/spl_ctr_drbg.cpp @@ -26,9 +26,9 @@ void CtrDrbg::Update(const void *data) { 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)); } @@ -51,31 +51,31 @@ 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 index 71564b582..448a22eab 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.hpp +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -42,10 +42,10 @@ class CtrDrbg { 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); diff --git a/stratosphere/spl/source/spl_general_service.cpp b/stratosphere/spl/source/spl_general_service.cpp index ae0c9bf92..d83a5c5d2 100644 --- a/stratosphere/spl/source/spl_general_service.cpp +++ b/stratosphere/spl/source/spl_general_service.cpp @@ -20,29 +20,29 @@ #include "spl_general_service.hpp" Result GeneralService::GetConfig(Out out, u32 which) { - return this->secmon_wrapper->GetConfig(out.GetPointer(), static_cast(which)); + return this->GetSecureMonitorWrapper()->GetConfig(out.GetPointer(), static_cast(which)); } Result GeneralService::ExpMod(OutPointerWithClientSize out, InPointer base, InPointer exp, InPointer mod) { - return this->secmon_wrapper->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); + return this->GetSecureMonitorWrapper()->ExpMod(out.pointer, out.num_elements, base.pointer, base.num_elements, exp.pointer, exp.num_elements, mod.pointer, mod.num_elements); } Result GeneralService::SetConfig(u32 which, u64 value) { - return this->secmon_wrapper->SetConfig(static_cast(which), value); + return this->GetSecureMonitorWrapper()->SetConfig(static_cast(which), value); } Result GeneralService::GenerateRandomBytes(OutPointerWithClientSize out) { - return this->secmon_wrapper->GenerateRandomBytes(out.pointer, out.num_elements); + return this->GetSecureMonitorWrapper()->GenerateRandomBytes(out.pointer, out.num_elements); } Result GeneralService::IsDevelopment(Out is_dev) { - return this->secmon_wrapper->IsDevelopment(is_dev.GetPointer()); + return this->GetSecureMonitorWrapper()->IsDevelopment(is_dev.GetPointer()); } Result GeneralService::SetBootReason(BootReasonValue boot_reason) { - return this->secmon_wrapper->SetBootReason(boot_reason); + return this->GetSecureMonitorWrapper()->SetBootReason(boot_reason); } Result GeneralService::GetBootReason(Out out) { - return this->secmon_wrapper->GetBootReason(out.GetPointer()); + return this->GetSecureMonitorWrapper()->GetBootReason(out.GetPointer()); } diff --git a/stratosphere/spl/source/spl_general_service.hpp b/stratosphere/spl/source/spl_general_service.hpp index 9f785ee74..6d1879dac 100644 --- a/stratosphere/spl/source/spl_general_service.hpp +++ b/stratosphere/spl/source/spl_general_service.hpp @@ -21,7 +21,7 @@ #include "spl_types.hpp" #include "spl_secmon_wrapper.hpp" -class GeneralService final : public IServiceObject { +class GeneralService : public IServiceObject { private: SecureMonitorWrapper *secmon_wrapper; public: @@ -30,7 +30,11 @@ class GeneralService final : public IServiceObject { } virtual ~GeneralService() { /* ... */ } - private: + 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); diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index a644814fb..d87872bfc 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -24,6 +24,7 @@ #include "spl_random_service.hpp" #include "spl_general_service.hpp" +#include "spl_crypto_service.hpp" extern "C" { extern u32 __start__; @@ -83,8 +84,9 @@ struct SplServerOptions { static SecureMonitorWrapper s_secmon_wrapper; /* Helpers for creating services. */ -static const auto MakeRandomService = []() { return std::make_shared(&s_secmon_wrapper); }; +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); }; int main(int argc, char **argv) { @@ -100,6 +102,7 @@ int main(int argc, char **argv) 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)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index a52c49535..fae5714a1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -24,29 +24,66 @@ /* Convenient. */ constexpr size_t DeviceAddressSpaceAlignSize = 0x400000; constexpr size_t DeviceAddressSpaceAlignMask = DeviceAddressSpaceAlignSize - 1; -constexpr u32 DeviceMapBase = 0x80000000u; +constexpr u32 WorkBufferMapBase = 0x80000000u; +constexpr u32 CryptAesInMapBase = 0x90000000u; +constexpr u32 CryptAesOutMapBase = 0xC0000000u; +constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - CryptAesInMapBase); + +/* Types. */ +struct SeLinkedListEntry { + u32 num_entries; + u32 address; + u32 size; +}; + +struct SeCryptContext { + SeLinkedListEntry in; + SeLinkedListEntry out; +}; + +class DeviceAddressSpaceMapHelper { + private: + Handle das_hnd; + u64 dst_addr; + u64 src_addr; + size_t size; + u32 perm; + public: + DeviceAddressSpaceMapHelper(Handle h, u64 dst, u64 src, size_t sz, u32 p) : das_hnd(h), dst_addr(dst), src_addr(src), size(sz), perm(p) { + if (R_FAILED(svcMapDeviceAddressSpaceAligned(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr, this->perm))) { + std::abort(); + } + } + ~DeviceAddressSpaceMapHelper() { + if (R_FAILED(svcUnmapDeviceAddressSpace(this->das_hnd, CUR_PROCESS_HANDLE, this->src_addr, this->size, this->dst_addr))) { + std::abort(); + } + } +}; /* Globals. */ 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_se_lock; +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::InitializeSeInterruptEvent() { +void SecureMonitorWrapper::InitializeSeEvents() { u64 irq_num; SmcWrapper::GetConfig(&irq_num, 1, SplConfigItem_SecurityEngineIrqNumber); Handle hnd; @@ -54,6 +91,9 @@ void SecureMonitorWrapper::InitializeSeInterruptEvent() { std::abort(); } eventLoadRemote(&g_se_event, hnd, true); + + g_se_keyslot_available_event = CreateWriteOnlySystemEvent(); + g_se_keyslot_available_event->Signal(); } void SecureMonitorWrapper::InitializeDeviceAddressSpace() { @@ -67,10 +107,10 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() { 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 = 0x80000000u + (work_buffer_addr & DeviceAddressSpaceAlignMask); - + 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(); @@ -80,8 +120,8 @@ void SecureMonitorWrapper::InitializeDeviceAddressSpace() { void SecureMonitorWrapper::Initialize() { /* Initialize the Drbg. */ InitializeCtrDrbg(); - /* Initialize SE interrupt event. */ - InitializeSeInterruptEvent(); + /* Initialize SE interrupt + keyslot events. */ + InitializeSeEvents(); /* Initialize DAS for the SE. */ InitializeDeviceAddressSpace(); } @@ -102,37 +142,78 @@ Result SecureMonitorWrapper::ConvertToSplResult(SmcResult result) { 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; @@ -142,7 +223,7 @@ Result SecureMonitorWrapper::GetConfig(u64 *out, SplConfigItem which) { *out = 0; res = SmcResult_Success; } - + return ConvertToSplResult(res); } @@ -153,7 +234,7 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base u8 mod[0x100]; }; ExpModLayout *layout = reinterpret_cast(g_work_buffer); - + /* Validate sizes. */ if (base_size > sizeof(layout->base)) { return ResultSplInvalidSize; @@ -164,10 +245,10 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base if (mod_size > sizeof(layout->mod)) { return ResultSplInvalidSize; } - if (out_size > sizeof(g_work_buffer) / 2) { + 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; @@ -175,22 +256,24 @@ Result SecureMonitorWrapper::ExpMod(void *out, size_t out_size, const void *base std::memcpy(layout->base + base_ofs, base, base_size); std::memcpy(layout->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_se_lock); + 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; } @@ -204,17 +287,17 @@ Result SecureMonitorWrapper::GenerateRandomBytesInternal(void *out, size_t 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; } @@ -223,7 +306,7 @@ Result SecureMonitorWrapper::GenerateRandomBytes(void *out, size_t size) { 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; @@ -262,4 +345,190 @@ Result SecureMonitorWrapper::GetBootReason(BootReasonValue *out) { *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 = CryptAesInMapBase + (dst_addr & DeviceAddressSpaceAlignMask); + + /* Validate aligned sizes. */ + if (src_size_page_aligned > CryptAesSizeMax || dst_size_page_aligned > CryptAesSizeMax) { + return ResultSplInvalidSize; + } + + /* Helpers for mapping/unmapping. */ + DeviceAddressSpaceMapHelper in_mapper(g_se_das_hnd, src_se_map_addr, src_addr_page_aligned, src_size_page_aligned, 1); + DeviceAddressSpaceMapHelper out_mapper(g_se_das_hnd, dst_se_map_addr, dst_addr_page_aligned, dst_size_page_aligned, 2); + + /* Setup SE linked list entries. */ + SeCryptContext *crypt_ctx = reinterpret_cast(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) { + 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) { + return ResultSplInvalidKeyslot; + } + return ResultSuccess; +} + +Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { + Result rc = ValidateAesKeyslot(keyslot, owner); + if (R_FAILED(rc)) { + return rc; + } + + /* Clear the keyslot. */ + { + AccessKey access_key = {}; + KeySource key_source = {}; + + SmcWrapper::LoadAesKey(keyslot, access_key, key_source); + } + this->keyslot_owners[keyslot] = nullptr; + g_se_keyslot_available_event->Signal(); + return ResultSuccess; +} + +Result SecureMonitorWrapper::FreeAesKeyslots(const void *owner) { + for (size_t i = 0; i < GetMaxKeyslots(); i++) { + if (this->keyslot_owners[i] == owner) { + FreeAesKeyslot(i, owner); + } + } + return ResultSuccess; +} + +Handle SecureMonitorWrapper::GetAesKeyslotAvailableEventHandle() { + return g_se_keyslot_available_event->GetHandle(); } \ No newline at end of file diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index cfaf123ca..758629485 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -25,7 +25,7 @@ class SecureMonitorWrapper { static constexpr size_t MaxAesKeyslots = 6; static constexpr size_t MaxAesKeyslotsDeprecated = 4; private: - uintptr_t keyslot_owners[MaxAesKeyslots] = {}; + const void *keyslot_owners[MaxAesKeyslots] = {}; BootReasonValue boot_reason = {}; bool boot_reason_set = false; private: @@ -42,7 +42,7 @@ class SecureMonitorWrapper { static Result ConvertToSplResult(SmcResult result); private: static void InitializeCtrDrbg(); - static void InitializeSeInterruptEvent(); + static void InitializeSeEvents(); static void InitializeDeviceAddressSpace(); public: static void Initialize(); @@ -51,7 +51,10 @@ class SecureMonitorWrapper { 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); 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); @@ -59,4 +62,46 @@ class SecureMonitorWrapper { 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); + + /* 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 index 97e39afc5..8f39e87ec 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.cpp +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -123,12 +123,12 @@ SmcResult SmcWrapper::GenerateRandomBytes(void *out, size_t size) { return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option) { +SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const KeySource &source, u32 generation, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateAesKek; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; args.X[4] = option; svcCallSecureMonitor(&args); @@ -138,27 +138,27 @@ SmcResult SmcWrapper::GenerateAesKek(AccessKey *out, const u64 *source, u32 gene return static_cast(args.X[0]); } -SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source) { +SmcResult SmcWrapper::LoadAesKey(u32 keyslot, const AccessKey &access_key, const KeySource &source) { SecmonArgs args; args.X[0] = SmcFunctionId_LoadAesKey; args.X[1] = keyslot; args.X[2] = access_key.data64[0]; args.X[3] = access_key.data64[1]; - args.X[4] = source[0]; - args.X[5] = source[1]; + 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 u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { +SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr &iv_ctr, u32 dst_addr, u32 src_addr, size_t size) { SecmonArgs args; args.X[0] = SmcFunctionId_CryptAes; args.X[1] = mode; - args.X[2] = iv_ctr[0]; - args.X[3] = iv_ctr[1]; + 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; @@ -168,12 +168,12 @@ SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *i return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which) { +SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateSpecificAesKey; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; args.X[4] = which; svcCallSecureMonitor(&args); @@ -181,7 +181,7 @@ SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const u64 *source, u32 ge return static_cast(args.X[0]); } -SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size) { +SmcResult SmcWrapper::ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size) { SecmonArgs args; args.X[0] = SmcFunctionId_ComputeCmac; @@ -190,12 +190,12 @@ SmcResult SmcWrapper::ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, args.X[3] = size; svcCallSecureMonitor(&args); - out_mac.data64[0] = args.X[1]; - out_mac.data64[1] = args.X[2]; + out_mac->data64[0] = args.X[1]; + out_mac->data64[1] = args.X[2]; return static_cast(args.X[0]); } -SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option) { +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; @@ -204,14 +204,14 @@ SmcResult SmcWrapper::ReEncryptRsaPrivateKey(void *data, size_t size, const Acce 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); + args.X[6] = reinterpret_cast(&source_dec); + args.X[7] = reinterpret_cast(&source_enc); svcCallSecureMonitor(&args); return static_cast(args.X[0]); } -SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_DecryptOrImportRsaPrivateKey; @@ -220,8 +220,8 @@ SmcResult SmcWrapper::DecryptOrImportRsaPrivateKey(void *data, size_t size, cons args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + args.X[6] = source.data64[0]; + args.X[7] = source.data64[1]; svcCallSecureMonitor(&args); return static_cast(args.X[0]); @@ -267,12 +267,12 @@ SmcResult SmcWrapper::LoadTitleKey(u32 keyslot, const AccessKey &access_key) { return static_cast(args.X[0]); } -SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u32 generation) { +SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const KeySource &source, u32 generation) { SecmonArgs args; args.X[0] = SmcFunctionId_UnwrapCommonTitleKey; - args.X[1] = source[0]; - args.X[2] = source[1]; + args.X[1] = source.data64[0]; + args.X[2] = source.data64[1]; args.X[3] = generation; svcCallSecureMonitor(&args); @@ -283,7 +283,7 @@ SmcResult SmcWrapper::UnwrapCommonTitleKey(AccessKey *out, const u64 *source, u3 /* Deprecated functions. */ -SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option) { +SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_ImportEsKey; @@ -292,14 +292,14 @@ SmcResult SmcWrapper::ImportEsKey(const void *data, size_t size, const AccessKey args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + 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 u64 *source, u32 option) { +SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const KeySource &source, u32 option) { SecmonArgs args; args.X[0] = SmcFunctionId_DecryptRsaPrivateKey; @@ -308,15 +308,15 @@ SmcResult SmcWrapper::DecryptRsaPrivateKey(size_t *out_size, void *data, size_t args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + 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 u64 *source, u32 option) { +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; @@ -325,8 +325,8 @@ SmcResult SmcWrapper::ImportSecureExpModKey(const void *data, size_t size, const args.X[3] = option; args.X[4] = reinterpret_cast(data); args.X[5] = size; - args.X[6] = source[0]; - args.X[7] = source[1]; + 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 index 41a97cf40..427b4c7c7 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -21,6 +21,10 @@ #include "spl_types.hpp" class SmcWrapper { + public: + static inline u32 GetCryptAesMode(SmcCipherMode mode, u32 keyslot) { + return static_cast((mode << 4) | (keyslot & 7)); + } public: static SmcResult SetConfig(SplConfigItem which, const u64 *value, size_t num_qwords); static SmcResult GetConfig(u64 *out, size_t num_qwords, SplConfigItem which); @@ -28,20 +32,20 @@ class SmcWrapper { static SmcResult GetResult(SmcResult *out, void *out_buf, size_t out_buf_size, AsyncOperationKey op); static SmcResult ExpMod(AsyncOperationKey *out_op, const void *base, const void *exp, size_t exp_size, const void *mod); static SmcResult GenerateRandomBytes(void *out, size_t size); - static SmcResult GenerateAesKek(AccessKey *out, const u64 *source, u32 generation, u32 option); - static SmcResult LoadAesKey(u32 keyslot, const AccessKey &access_key, const u64 *source); - static SmcResult CryptAes(AsyncOperationKey *out_op, u32 mode, const u64 *iv_ctr, u32 dst_addr, u32 src_addr, size_t size); - static SmcResult GenerateSpecificAesKey(u64 *out, const u64 *source, u32 generation, u32 which); - static SmcResult ComputeCmac(Cmac &out_mac, u32 keyslot, const void *data, size_t size); - static SmcResult ReEncryptRsaPrivateKey(void *data, size_t size, const AccessKey &access_key_dec, const u64 *source_dec, const AccessKey &access_key_enc, const u64 *source_enc, u32 option); - static SmcResult DecryptOrImportRsaPrivateKey(void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + static SmcResult 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(u64 *out, const KeySource &source, u32 generation, u32 which); + static SmcResult ComputeCmac(Cmac *out_mac, u32 keyslot, const void *data, size_t size); + static SmcResult 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 u64 *source, u32 generation); - + 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 u64 *source, u32 option); - static SmcResult DecryptRsaPrivateKey(size_t *out_size, void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); - static SmcResult ImportSecureExpModKey(const void *data, size_t size, const AccessKey &access_key, const u64 *source, u32 option); + 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_types.hpp b/stratosphere/spl/source/spl_types.hpp index 8d8317a48..d7ee64dbe 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -37,7 +37,6 @@ enum SmcCipherMode : u32 { SmcCipherMode_CbcEncrypt = 0, SmcCipherMode_CbcDecrypt = 1, SmcCipherMode_Ctr = 2, - SmcCipherMode_Cmac = 3, }; enum EsKeyType : u32 { @@ -58,12 +57,20 @@ struct BootReasonValue { static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); struct AesKey { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); struct IvCtr { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); struct Cmac { union { @@ -82,8 +89,12 @@ struct AccessKey { static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { - u8 data[AES_128_KEY_SIZE]; + union { + u8 data[AES_128_KEY_SIZE]; + u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + }; }; +static_assert(alignof(AccessKey) == alignof(u8), "KeySource definition!"); enum SplServiceCmd { /* 1.0.0+ */ @@ -110,7 +121,7 @@ enum SplServiceCmd { Spl_Cmd_UnwrapCommonTitleKey = 20, Spl_Cmd_AllocateAesKeyslot = 21, Spl_Cmd_FreeAesKeyslot = 22, - Spl_Cmd_GetAesKeyslotEvent = 23, + Spl_Cmd_GetAesKeyslotAvailableEvent = 23, /* 3.0.0+ */ Spl_Cmd_SetBootReason = 24, From 9ea1a2a941362cb4f1e092ce51cf0c86dae71588 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 21:42:39 -0700 Subject: [PATCH 05/16] spl: Implement RsaService --- stratosphere/spl/source/spl_rsa_service.cpp | 24 ++++++++ stratosphere/spl/source/spl_rsa_service.hpp | 57 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 35 ++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 3 + stratosphere/spl/source/spl_types.hpp | 14 +++++ 5 files changed, 133 insertions(+) create mode 100644 stratosphere/spl/source/spl_rsa_service.cpp create mode 100644 stratosphere/spl/source/spl_rsa_service.hpp 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..c6d90185a --- /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 { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index fae5714a1..efc9eb6fd 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -29,6 +29,9 @@ 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; + /* Types. */ struct SeLinkedListEntry { u32 num_entries; @@ -520,6 +523,38 @@ Result SecureMonitorWrapper::FreeAesKeyslot(u32 keyslot, const void *owner) { 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::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 758629485..d3285195b 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -72,6 +72,9 @@ class SecureMonitorWrapper { 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); /* Helper. */ Result FreeAesKeyslots(const void *owner); diff --git a/stratosphere/spl/source/spl_types.hpp b/stratosphere/spl/source/spl_types.hpp index d7ee64dbe..78c984900 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -39,6 +39,20 @@ enum SmcCipherMode : u32 { 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, From f4a8124dc3afcbb3433ac1d603ab41ddce933024 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:10:13 -0700 Subject: [PATCH 06/16] spl: implement SslService, some of EsService --- stratosphere/spl/source/spl_es_service.cpp | 52 +++++++++ stratosphere/spl/source/spl_es_service.hpp | 69 +++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 107 ++++++++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 13 ++- stratosphere/spl/source/spl_ssl_service.cpp | 28 +++++ stratosphere/spl/source/spl_ssl_service.hpp | 60 ++++++++++ 6 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 stratosphere/spl/source/spl_es_service.cpp create mode 100644 stratosphere/spl/source/spl_es_service.hpp create mode 100644 stratosphere/spl/source/spl_ssl_service.cpp create mode 100644 stratosphere/spl/source/spl_ssl_service.hpp diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp new file mode 100644 index 000000000..20c2f8d10 --- /dev/null +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -0,0 +1,52 @@ +/* + * 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) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result EsService::UnwrapCommonTitleKey(Out out_access_key, KeySource key_source, u32 generation) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +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) { + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result EsService::LoadElicenseKey(u32 keyslot, AccessKey access_key) { + /* TODO */ + return ResultKernelConnectionClosed; +} diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp new file mode 100644 index 000000000..17f8efa0f --- /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 { + 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_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index efc9eb6fd..bcd36e479 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -555,6 +555,113 @@ Result SecureMonitorWrapper::DecryptRsaPrivateKey(void *dst, size_t dst_size, co 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]; + }; + 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]; + }; + 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::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::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index d3285195b..4b4106139 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -53,6 +53,8 @@ class SecureMonitorWrapper { 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); public: /* General. */ Result GetConfig(u64 *out, SplConfigItem which); @@ -72,10 +74,19 @@ class SecureMonitorWrapper { 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 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); + /* Helper. */ Result FreeAesKeyslots(const void *owner); Handle GetAesKeyslotAvailableEventHandle(); 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..5079ba30d --- /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 { + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + + }; +}; From 0a194cb6a6d9dd1f2d93d8c26f8150bbb354617e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:16:47 -0700 Subject: [PATCH 07/16] spl: add spl:ssl/spl:es to main, fix cmd ids --- stratosphere/spl/source/spl_main.cpp | 6 ++++++ stratosphere/spl/source/spl_ssl_service.hpp | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index d87872bfc..6fd128033 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -25,6 +25,8 @@ #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" extern "C" { extern u32 __start__; @@ -87,6 +89,8 @@ static SecureMonitorWrapper s_secmon_wrapper; 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); }; int main(int argc, char **argv) { @@ -103,6 +107,8 @@ int main(int argc, char **argv) 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)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index 5079ba30d..575109c6f 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -53,8 +53,8 @@ class SslService : public RsaService { MakeServiceCommandMeta(), MakeServiceCommandMeta(), MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; From 99106076e6a328eb87091208afa0e431f0d73f23 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 24 Apr 2019 23:42:32 -0700 Subject: [PATCH 08/16] spl: Finish implementing EsService. --- stratosphere/spl/source/spl_es_service.cpp | 12 ++-- stratosphere/spl/source/spl_main.cpp | 4 +- .../spl/source/spl_secmon_wrapper.cpp | 72 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.hpp | 8 +++ stratosphere/spl/source/spl_smc_wrapper.hpp | 3 + 5 files changed, 89 insertions(+), 10 deletions(-) diff --git a/stratosphere/spl/source/spl_es_service.cpp b/stratosphere/spl/source/spl_es_service.cpp index 20c2f8d10..cb855d05e 100644 --- a/stratosphere/spl/source/spl_es_service.cpp +++ b/stratosphere/spl/source/spl_es_service.cpp @@ -24,13 +24,11 @@ Result EsService::ImportEsKey(InPointer src, AccessKey access_key, KeySource } Result EsService::UnwrapTitleKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { - /* TODO */ - return ResultKernelConnectionClosed; + 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) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->UnwrapCommonTitleKey(out_access_key.GetPointer(), key_source, generation); } Result EsService::ImportDrmKey(InPointer src, AccessKey access_key, KeySource key_source) { @@ -42,11 +40,9 @@ Result EsService::DrmExpMod(OutPointerWithClientSize out, InPointer base } Result EsService::UnwrapElicenseKey(Out out_access_key, InPointer base, InPointer mod, InPointer label_digest, u32 generation) { - /* TODO */ - return ResultKernelConnectionClosed; + 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) { - /* TODO */ - return ResultKernelConnectionClosed; + return this->GetSecureMonitorWrapper()->LoadElicenseKey(keyslot, this, access_key); } diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 6fd128033..7bf99b4c4 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -107,8 +107,8 @@ int main(int argc, char **argv) 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:ssl", 2)); + s_server_manager.AddWaitable(new ServiceServer("spl:es", 2)); /* TODO: Other services. */ } else { /* TODO, DeprecatedGeneralService */ diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index bcd36e479..f46e5b543 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -31,6 +31,7 @@ constexpr size_t CryptAesSizeMax = static_cast(CryptAesOutMapBase - Cryp constexpr size_t RsaPrivateKeySize = 0x100; constexpr size_t RsaPrivateKeyMetaSize = 0x30; +constexpr size_t LabelDigestSizeMax = 0x20; /* Types. */ struct SeLinkedListEntry { @@ -654,6 +655,60 @@ Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const } } +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); } @@ -662,6 +717,23 @@ Result SecureMonitorWrapper::DrmExpMod(void *out, size_t out_size, const void *b 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::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::FreeAesKeyslots(const void *owner) { for (size_t i = 0; i < GetMaxKeyslots(); i++) { if (this->keyslot_owners[i] == owner) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 4b4106139..3addf70b9 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -55,6 +55,7 @@ class SecureMonitorWrapper { 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); @@ -84,8 +85,15 @@ class SecureMonitorWrapper { /* 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 LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); /* Helper. */ Result FreeAesKeyslots(const void *owner); diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp index 427b4c7c7..be7b682d7 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -25,6 +25,9 @@ class SmcWrapper { 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); From 5633444d5edd48e3582bcba2f6f2ff72e4b0609d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 00:01:47 -0700 Subject: [PATCH 09/16] spl: implement ManuService --- stratosphere/spl/source/spl_main.cpp | 13 +++-- stratosphere/spl/source/spl_manu_service.cpp | 24 ++++++++ stratosphere/spl/source/spl_manu_service.hpp | 58 +++++++++++++++++++ .../spl/source/spl_secmon_wrapper.cpp | 38 +++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 3 + 5 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 stratosphere/spl/source/spl_manu_service.cpp create mode 100644 stratosphere/spl/source/spl_manu_service.hpp diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index 7bf99b4c4..1fa653a73 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -27,6 +27,7 @@ #include "spl_crypto_service.hpp" #include "spl_ssl_service.hpp" #include "spl_es_service.hpp" +#include "spl_manu_service.hpp" extern "C" { extern u32 __start__; @@ -91,6 +92,7 @@ static const auto MakeGeneralService = []() { 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 MakeManuService = []() { return std::make_shared(&s_secmon_wrapper); }; int main(int argc, char **argv) { @@ -106,10 +108,13 @@ int main(int argc, char **argv) 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)); - /* TODO: Other services. */ + 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)); + /* TODO: spl:fs. */ + if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { + s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); + } } else { /* TODO, DeprecatedGeneralService */ } 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..e4e0a87c5 --- /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 { + 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_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index f46e5b543..83c220c00 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -558,7 +558,7 @@ Result SecureMonitorWrapper::DecryptRsaPrivateKey(void *dst, size_t dst_size, co 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]; + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; }; ImportSecureExpModKeyLayout *layout = reinterpret_cast(g_work_buffer); @@ -639,7 +639,7 @@ Result SecureMonitorWrapper::ImportEsKey(const void *src, size_t src_size, const return ImportSecureExpModKey(src, src_size, access_key, key_source, SmcDecryptOrImportMode_ImportEsKey); } else { struct ImportEsKeyLayout { - u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize]; + u8 data[RsaPrivateKeyMetaSize + 2 * RsaPrivateKeySize + 0x10]; }; ImportEsKeyLayout *layout = reinterpret_cast(g_work_buffer); @@ -734,6 +734,40 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const return ConvertToSplResult(SmcWrapper::LoadTitleKey(keyslot, access_key)); } +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) { diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 3addf70b9..d4e93ecb1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -95,6 +95,9 @@ class SecureMonitorWrapper { /* FS */ Result LoadTitleKey(u32 keyslot, const void *owner, const AccessKey &access_key); + /* 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(); From 85e8506fa8a1d411d09fb90d62dc0079925deaf3 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 00:33:10 -0700 Subject: [PATCH 10/16] spl: Implement non-Lotus FsService commands. --- stratosphere/spl/source/spl_fs_service.cpp | 44 +++++++++++++ stratosphere/spl/source/spl_fs_service.hpp | 65 +++++++++++++++++++ stratosphere/spl/source/spl_main.cpp | 6 +- .../spl/source/spl_secmon_wrapper.cpp | 20 ++++++ .../spl/source/spl_secmon_wrapper.hpp | 2 + stratosphere/spl/source/spl_smc_wrapper.cpp | 4 +- stratosphere/spl/source/spl_smc_wrapper.hpp | 2 +- 7 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 stratosphere/spl/source/spl_fs_service.cpp create mode 100644 stratosphere/spl/source/spl_fs_service.hpp diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp new file mode 100644 index 000000000..81dd84f00 --- /dev/null +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -0,0 +1,44 @@ +/* + * 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 ResultSuccess; + /* TODO */ + return ResultKernelConnectionClosed; +} + +Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + return ResultSuccess; + /* TODO */ + return ResultKernelConnectionClosed; +} + +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..4a7bd4872 --- /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 { + 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_main.cpp b/stratosphere/spl/source/spl_main.cpp index 1fa653a73..47eec447e 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -27,6 +27,7 @@ #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" extern "C" { @@ -92,6 +93,7 @@ static const auto MakeGeneralService = []() { 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); }; int main(int argc, char **argv) @@ -111,13 +113,15 @@ int main(int argc, char **argv) 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)); - /* TODO: spl:fs. */ + s_server_manager.AddWaitable(new ServiceServer("spl:fs", 2)); if (GetRuntimeFirmwareVersion() >= FirmwareVersion_500) { s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); } } else { /* TODO, DeprecatedGeneralService */ } + + RebootToRcm(); /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 83c220c00..1105ab896 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -726,6 +726,10 @@ Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, con return LoadTitleKey(keyslot, owner, access_key); } +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)) { @@ -734,6 +738,22 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const 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]; diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index d4e93ecb1..4ec4e4222 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -93,7 +93,9 @@ class SecureMonitorWrapper { Result LoadElicenseKey(u32 keyslot, const void *owner, const AccessKey &access_key); /* FS */ + 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); diff --git a/stratosphere/spl/source/spl_smc_wrapper.cpp b/stratosphere/spl/source/spl_smc_wrapper.cpp index 8f39e87ec..5845f37ce 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.cpp +++ b/stratosphere/spl/source/spl_smc_wrapper.cpp @@ -168,7 +168,7 @@ SmcResult SmcWrapper::CryptAes(AsyncOperationKey *out_op, u32 mode, const IvCtr return static_cast(args.X[0]); } -SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, u32 generation, u32 which) { +SmcResult SmcWrapper::GenerateSpecificAesKey(AesKey *out_key, const KeySource &source, u32 generation, u32 which) { SecmonArgs args; args.X[0] = SmcFunctionId_GenerateSpecificAesKey; @@ -178,6 +178,8 @@ SmcResult SmcWrapper::GenerateSpecificAesKey(u64 *out, const KeySource &source, 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]); } diff --git a/stratosphere/spl/source/spl_smc_wrapper.hpp b/stratosphere/spl/source/spl_smc_wrapper.hpp index be7b682d7..ea8798ee5 100644 --- a/stratosphere/spl/source/spl_smc_wrapper.hpp +++ b/stratosphere/spl/source/spl_smc_wrapper.hpp @@ -38,7 +38,7 @@ class SmcWrapper { 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(u64 *out, const KeySource &source, u32 generation, u32 which); + 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); From bc44e02aedaef983403b8ebda074e1cfaf6ee453 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 05:27:35 -0700 Subject: [PATCH 11/16] spl: fix vtables/other issues, now boots (not all commands work) --- stratosphere/libstratosphere | 2 +- .../spl/source/spl_crypto_service.cpp | 2 +- .../spl/source/spl_crypto_service.hpp | 34 ++++++------- stratosphere/spl/source/spl_ctr_drbg.hpp | 2 +- stratosphere/spl/source/spl_es_service.hpp | 48 +++++++++---------- stratosphere/spl/source/spl_fs_service.hpp | 42 ++++++++-------- stratosphere/spl/source/spl_main.cpp | 2 - stratosphere/spl/source/spl_manu_service.hpp | 36 +++++++------- .../spl/source/spl_random_service.hpp | 2 +- stratosphere/spl/source/spl_rsa_service.hpp | 34 ++++++------- stratosphere/spl/source/spl_ssl_service.hpp | 38 +++++++-------- stratosphere/spl/source/spl_types.hpp | 16 +++++-- 12 files changed, 131 insertions(+), 127 deletions(-) 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/source/spl_crypto_service.cpp b/stratosphere/spl/source/spl_crypto_service.cpp index 53c47242f..1e3522865 100644 --- a/stratosphere/spl/source/spl_crypto_service.cpp +++ b/stratosphere/spl/source/spl_crypto_service.cpp @@ -35,7 +35,7 @@ Result CryptoService::DecryptAesKey(Out out_key, KeySource key_source, u 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) { +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); } diff --git a/stratosphere/spl/source/spl_crypto_service.hpp b/stratosphere/spl/source/spl_crypto_service.hpp index b5e025b17..2f56df6ec 100644 --- a/stratosphere/spl/source/spl_crypto_service.hpp +++ b/stratosphere/spl/source/spl_crypto_service.hpp @@ -36,29 +36,29 @@ class CryptoService : public GeneralService { 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 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 { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + 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.hpp b/stratosphere/spl/source/spl_ctr_drbg.hpp index 448a22eab..39f82ae0a 100644 --- a/stratosphere/spl/source/spl_ctr_drbg.hpp +++ b/stratosphere/spl/source/spl_ctr_drbg.hpp @@ -39,7 +39,7 @@ class CtrDrbg { u8 *dst_u8 = reinterpret_cast(dst); for (size_t i = 0; i < size; i++) { - dst_u8[i] = src_u8[i]; + dst_u8[i] ^= src_u8[i]; } } diff --git a/stratosphere/spl/source/spl_es_service.hpp b/stratosphere/spl/source/spl_es_service.hpp index 17f8efa0f..2fba15a30 100644 --- a/stratosphere/spl/source/spl_es_service.hpp +++ b/stratosphere/spl/source/spl_es_service.hpp @@ -41,29 +41,29 @@ class EsService : public RsaService { virtual Result LoadElicenseKey(u32 keyslot, AccessKey access_key); 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(), + 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.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 4a7bd4872..359ca6401 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -39,27 +39,27 @@ class FsService : public CryptoService { virtual Result GetPackage2Hash(OutPointerWithClientSize dst); 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(), + 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_main.cpp b/stratosphere/spl/source/spl_main.cpp index 47eec447e..b97c3ee43 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -121,8 +121,6 @@ int main(int argc, char **argv) /* TODO, DeprecatedGeneralService */ } - RebootToRcm(); - /* Loop forever, servicing our services. */ s_server_manager.Process(); diff --git a/stratosphere/spl/source/spl_manu_service.hpp b/stratosphere/spl/source/spl_manu_service.hpp index e4e0a87c5..e3e911570 100644 --- a/stratosphere/spl/source/spl_manu_service.hpp +++ b/stratosphere/spl/source/spl_manu_service.hpp @@ -35,24 +35,24 @@ class ManuService : public RsaService { 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 { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + 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.hpp b/stratosphere/spl/source/spl_random_service.hpp index 0d307fcec..66261f087 100644 --- a/stratosphere/spl/source/spl_random_service.hpp +++ b/stratosphere/spl/source/spl_random_service.hpp @@ -35,6 +35,6 @@ class RandomService final : public IServiceObject { virtual Result GenerateRandomBytes(OutBuffer out); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), + MakeServiceCommandMeta(), }; }; diff --git a/stratosphere/spl/source/spl_rsa_service.hpp b/stratosphere/spl/source/spl_rsa_service.hpp index c6d90185a..6e9b2471e 100644 --- a/stratosphere/spl/source/spl_rsa_service.hpp +++ b/stratosphere/spl/source/spl_rsa_service.hpp @@ -35,23 +35,23 @@ class RsaService : public CryptoService { virtual Result DecryptRsaPrivateKey(OutPointerWithClientSize dst, InPointer src, AccessKey access_key, KeySource key_source, u32 option); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), + MakeServiceCommandMetaEx(), }; }; diff --git a/stratosphere/spl/source/spl_ssl_service.hpp b/stratosphere/spl/source/spl_ssl_service.hpp index 575109c6f..cfcdff866 100644 --- a/stratosphere/spl/source/spl_ssl_service.hpp +++ b/stratosphere/spl/source/spl_ssl_service.hpp @@ -36,25 +36,25 @@ class SslService : public RsaService { virtual Result SslExpMod(OutPointerWithClientSize out, InPointer base, InPointer mod); public: DEFINE_SERVICE_DISPATCH_TABLE { - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), - MakeServiceCommandMeta(), + 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 index 78c984900..c4cebbe2f 100644 --- a/stratosphere/spl/source/spl_types.hpp +++ b/stratosphere/spl/source/spl_types.hpp @@ -70,10 +70,11 @@ struct BootReasonValue { }; static_assert(sizeof(BootReasonValue) == sizeof(u32), "BootReasonValue definition!"); +#pragma pack(push, 1) struct AesKey { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); @@ -81,7 +82,7 @@ static_assert(alignof(AesKey) == alignof(u8), "AesKey definition!"); struct IvCtr { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); @@ -89,7 +90,7 @@ static_assert(alignof(IvCtr) == alignof(u8), "IvCtr definition!"); struct Cmac { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); @@ -97,7 +98,7 @@ static_assert(alignof(Cmac) == alignof(u8), "Cmac definition!"); struct AccessKey { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + u64 data64[AES_128_KEY_SIZE / sizeof(u64)]; }; }; static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); @@ -105,10 +106,15 @@ static_assert(alignof(AccessKey) == alignof(u8), "AccessKey definition!"); struct KeySource { union { u8 data[AES_128_KEY_SIZE]; - u8 data64[AES_128_KEY_SIZE / sizeof(u64)]; + 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+ */ From 4b8ebfa7c34d0afa393c15c4b1128f4efa0f0696 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 06:00:34 -0700 Subject: [PATCH 12/16] spl: fix CryptAesCtr (eshop games work now) --- stratosphere/spl/source/spl_secmon_wrapper.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 1105ab896..9ef73d5b1 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -425,7 +425,7 @@ Result SecureMonitorWrapper::CryptAesCtr(void *dst, size_t dst_size, u32 keyslot const u32 src_se_map_addr = CryptAesInMapBase + (src_addr_page_aligned & DeviceAddressSpaceAlignMask); const u32 dst_se_map_addr = CryptAesOutMapBase + (dst_addr_page_aligned & DeviceAddressSpaceAlignMask); const u32 src_se_addr = CryptAesInMapBase + (src_addr & DeviceAddressSpaceAlignMask); - const u32 dst_se_addr = CryptAesInMapBase + (dst_addr & DeviceAddressSpaceAlignMask); + const u32 dst_se_addr = CryptAesOutMapBase + (dst_addr & DeviceAddressSpaceAlignMask); /* Validate aligned sizes. */ if (src_size_page_aligned > CryptAesSizeMax || dst_size_page_aligned > CryptAesSizeMax) { From d984621150bb00ddba41b984719ba6227c77a1e9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 07:06:27 -0700 Subject: [PATCH 13/16] spl: Implement FsService lotus commands (gamecards work now) --- stratosphere/spl/source/spl_fs_service.cpp | 14 +-- stratosphere/spl/source/spl_fs_service.hpp | 2 +- .../spl/source/spl_secmon_wrapper.cpp | 110 +++++++++++++++++- .../spl/source/spl_secmon_wrapper.hpp | 5 + 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/stratosphere/spl/source/spl_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index 81dd84f00..eaa51d26e 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -20,15 +20,15 @@ #include "spl_fs_service.hpp" Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySource key_source, u32 option) { - return ResultSuccess; - /* TODO */ - return ResultKernelConnectionClosed; + 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 ResultSuccess; - /* TODO */ - return ResultKernelConnectionClosed; +Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { + Result rc = 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); + if (R_FAILED(rc)) { + fatalSimple(rc); + } + return rc; } Result FsService::GenerateSpecificAesKey(Out out_key, KeySource key_source, u32 generation, u32 which) { diff --git a/stratosphere/spl/source/spl_fs_service.hpp b/stratosphere/spl/source/spl_fs_service.hpp index 359ca6401..7bac28934 100644 --- a/stratosphere/spl/source/spl_fs_service.hpp +++ b/stratosphere/spl/source/spl_fs_service.hpp @@ -33,7 +33,7 @@ class FsService : public CryptoService { 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 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); diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index 9ef73d5b1..e4ca99a31 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -130,6 +130,82 @@ void SecureMonitorWrapper::Initialize() { 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); } @@ -726,6 +802,34 @@ Result SecureMonitorWrapper::LoadElicenseKey(u32 keyslot, const void *owner, con 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)); } @@ -740,16 +844,16 @@ Result SecureMonitorWrapper::LoadTitleKey(u32 keyslot, const void *owner, const 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; } diff --git a/stratosphere/spl/source/spl_secmon_wrapper.hpp b/stratosphere/spl/source/spl_secmon_wrapper.hpp index 4ec4e4222..83534ee96 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.hpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.hpp @@ -44,6 +44,9 @@ class SecureMonitorWrapper { 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: @@ -93,6 +96,8 @@ class SecureMonitorWrapper { 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); From edcfbf425405e1b6c644b4141f86755177596688 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:12:30 -0700 Subject: [PATCH 14/16] spl: Implement DeprecatedService. --- .../spl/source/spl_deprecated_service.cpp | 120 ++++++++++++++++++ .../spl/source/spl_deprecated_service.hpp | 92 ++++++++++++++ stratosphere/spl/source/spl_fs_service.cpp | 6 +- stratosphere/spl/source/spl_main.cpp | 6 +- 4 files changed, 218 insertions(+), 6 deletions(-) create mode 100644 stratosphere/spl/source/spl_deprecated_service.cpp create mode 100644 stratosphere/spl/source/spl_deprecated_service.hpp 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_fs_service.cpp b/stratosphere/spl/source/spl_fs_service.cpp index eaa51d26e..d0808e003 100644 --- a/stratosphere/spl/source/spl_fs_service.cpp +++ b/stratosphere/spl/source/spl_fs_service.cpp @@ -24,11 +24,7 @@ Result FsService::ImportLotusKey(InPointer src, AccessKey access_key, KeySou } Result FsService::DecryptLotusMessage(Out out_size, OutPointerWithClientSize out, InPointer base, InPointer mod, InPointer label_digest) { - Result rc = 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); - if (R_FAILED(rc)) { - fatalSimple(rc); - } - return rc; + 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) { diff --git a/stratosphere/spl/source/spl_main.cpp b/stratosphere/spl/source/spl_main.cpp index b97c3ee43..7cae77b4a 100644 --- a/stratosphere/spl/source/spl_main.cpp +++ b/stratosphere/spl/source/spl_main.cpp @@ -30,6 +30,8 @@ #include "spl_fs_service.hpp" #include "spl_manu_service.hpp" +#include "spl_deprecated_service.hpp" + extern "C" { extern u32 __start__; @@ -96,6 +98,8 @@ static const auto MakeEsService = []() { return std::make_shared(&s_ 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); @@ -118,7 +122,7 @@ int main(int argc, char **argv) s_server_manager.AddWaitable(new ServiceServer("spl:manu", 1)); } } else { - /* TODO, DeprecatedGeneralService */ + s_server_manager.AddWaitable(new ServiceServer("spl:", 12)); } /* Loop forever, servicing our services. */ From 51858e732ac2245588fea417fb9fb4e5d5bf4aac Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:24:25 -0700 Subject: [PATCH 15/16] fusee: embed spl. --- fusee/fusee-secondary/Makefile | 4 ++-- fusee/fusee-secondary/linker.ld | 2 ++ fusee/fusee-secondary/src/start.s | 8 ++++++++ fusee/fusee-secondary/src/stratosphere.c | 16 ++++++++++++++-- stratosphere/Makefile | 2 +- 5 files changed, 27 insertions(+), 5 deletions(-) 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) From 0d4a0348b579b39c0c9ff6245ce00705430b941e Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 11:36:23 -0700 Subject: [PATCH 16/16] spl: Loosen keyslot restrictions on 1.0.0 --- stratosphere/spl/source/spl_secmon_wrapper.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/stratosphere/spl/source/spl_secmon_wrapper.cpp b/stratosphere/spl/source/spl_secmon_wrapper.cpp index e4ca99a31..13ab731af 100644 --- a/stratosphere/spl/source/spl_secmon_wrapper.cpp +++ b/stratosphere/spl/source/spl_secmon_wrapper.cpp @@ -560,6 +560,12 @@ Result SecureMonitorWrapper::ComputeCmac(Cmac *out_cmac, u32 keyslot, const void } 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; @@ -576,13 +582,18 @@ Result SecureMonitorWrapper::ValidateAesKeyslot(u32 keyslot, const void *owner) if (keyslot >= GetMaxKeyslots()) { return ResultSplInvalidKeyslot; } - if (this->keyslot_owners[keyslot] != owner) { + 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;