From d984621150bb00ddba41b984719ba6227c77a1e9 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Thu, 25 Apr 2019 07:06:27 -0700 Subject: [PATCH] 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);