From ccba70abfe5a3bbeedd85754b1396ceda94ca411 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 20 May 2020 06:03:07 -0700 Subject: [PATCH] exo2: implement SmcPrepareEsDeviceUniqueKey, SmcPrepareEsCommonTitleKey, SmcLoadPreparedAesKey --- .../program/source/smc/secmon_smc_aes.cpp | 91 ++++++++++- .../program/source/smc/secmon_smc_aes.hpp | 12 ++ .../program/source/smc/secmon_smc_es.cpp | 32 ---- .../program/source/smc/secmon_smc_handler.cpp | 7 +- .../program/source/smc/secmon_smc_rsa.cpp | 152 ++++++++++++++++++ .../program/source/smc/secmon_smc_rsa.hpp | 2 + .../libexosphere/include/exosphere/se.hpp | 1 + .../include/exosphere/se/se_oaep.hpp | 15 +- .../include/exosphere/se/se_rsa.hpp | 4 + .../libexosphere/source/se/se_execute.cpp | 19 +++ .../libexosphere/source/se/se_execute.hpp | 1 + libraries/libexosphere/source/se/se_oaep.cpp | 122 ++++++++++++++ libraries/libexosphere/source/se/se_rsa.cpp | 53 ++++++ 13 files changed, 461 insertions(+), 50 deletions(-) delete mode 100644 exosphere2/program/source/smc/secmon_smc_es.cpp rename exosphere2/program/source/smc/secmon_smc_es.hpp => libraries/libexosphere/include/exosphere/se/se_oaep.hpp (66%) create mode 100644 libraries/libexosphere/source/se/se_oaep.cpp diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 03b2b1d49..aab91f842 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -155,6 +155,15 @@ namespace ams::secmon::smc { 0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95 }; + constexpr const u8 EsCommonKeySources[EsCommonKeyType_Count][AesKeySize] = { + [EsCommonKeyType_TitleKey] = { 0x1E, 0xDC, 0x7B, 0x3B, 0x60, 0xE6, 0xB4, 0xD8, 0x78, 0xB8, 0x17, 0x15, 0x98, 0x5E, 0x62, 0x9B }, + [EsCommonKeyType_ArchiveKey] = { 0x3B, 0x78, 0xF2, 0x61, 0x0F, 0x9D, 0x5A, 0xE2, 0x7B, 0x4E, 0x45, 0xAF, 0xCB, 0x0B, 0x67, 0x4D }, + }; + + constexpr const u8 EsSealKeySource[AesKeySize] = { + 0xCB, 0xB7, 0x6E, 0x38, 0xA1, 0xCB, 0x77, 0x0F, 0xB2, 0xA5, 0xB2, 0x9D, 0xD8, 0x56, 0x9F, 0x76 + }; + constexpr const u8 SecureDataSource[AesKeySize] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; @@ -463,6 +472,51 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult LoadPreparedAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 access_key[AesKeySize]; + + const int slot = args.r[1]; + std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key)); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource)); + + /* Unseal the key. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key)); + + return SmcResult::Success; + } + + SmcResult PrepareEsCommonTitleKeyImpl(SmcArguments &args) { + /* Declare variables. */ + u8 key_source[se::AesBlockSize]; + u8 key[se::AesBlockSize]; + u8 access_key[se::AesBlockSize]; + + /* Decode arguments. */ + std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source)); + const int generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, static_cast(args.r[3]) - 1) : 0; + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + + /* Derive the key. */ + DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), EsCommonKeyType_TitleKey, generation); + + /* Prepare the aes key. */ + PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); + + /* Copy the access key to output. */ + std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key)); + + return SmcResult::Success; + } + SmcResult DecryptDeviceUniqueDataImpl(SmcArguments &args) { /* Decode arguments. */ u8 access_key[se::AesBlockSize]; @@ -530,6 +584,7 @@ namespace ams::secmon::smc { case DeviceUniqueData_ImportEsClientCertKey: ImportRsaKeyExponent(ConvertToImportRsaKey(mode), work_buffer, se::RsaSize); ImportRsaKeyModulusProvisionally(ConvertToImportRsaKey(mode), work_buffer + se::RsaSize, se::RsaSize); + CommitRsaKeyModulus(ConvertToImportRsaKey(mode)); break; AMS_UNREACHABLE_DEFAULT_CASE(); } @@ -579,8 +634,11 @@ namespace ams::secmon::smc { } SmcResult SmcLoadPreparedAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, LoadPreparedAesKeyImpl); + } + + SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, PrepareEsCommonTitleKeyImpl); } /* Device unique data functionality. */ @@ -604,6 +662,35 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* Es encryption utilities. */ + void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation) { + /* Validate pre-conditions. */ + AMS_ABORT_UNLESS(dst_size == AesKeySize); + AMS_ABORT_UNLESS(src_size == AesKeySize); + AMS_ABORT_UNLESS(0 <= type && type < EsCommonKeyType_Count); + + /* Prepare the master key for the generation. */ + const int slot = PrepareMasterKey(generation); + + /* Derive the es common key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, EsCommonKeySources[type], AesKeySize); + + /* Decrypt the input using the common key. */ + se::DecryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size); + } + + void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size) { + /* Validate pre-conditions. */ + AMS_ABORT_UNLESS(dst_size == AesKeySize); + AMS_ABORT_UNLESS(src_size == AesKeySize); + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, EsSealKeySource, sizeof(EsSealKeySource)); + + /* Seal the key. */ + se::EncryptAes128(dst, dst_size, pkg1::AesKeySlot_Smc, src, src_size); + } + /* 'Tis the last rose of summer, / Left blooming alone; */ /* Oh! who would inhabit / This bleak world alone? */ SmcResult SmcGetSecureData(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index a0ad3adb5..b502ee657 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -19,6 +19,13 @@ namespace ams::secmon::smc { + enum EsCommonKeyType { + EsCommonKeyType_TitleKey = 0, + EsCommonKeyType_ArchiveKey = 1, + + EsCommonKeyType_Count, + }; + /* General Aes functionality. */ SmcResult SmcGenerateAesKek(SmcArguments &args); SmcResult SmcLoadAesKey(SmcArguments &args); @@ -26,6 +33,7 @@ namespace ams::secmon::smc { SmcResult SmcGenerateSpecificAesKey(SmcArguments &args); SmcResult SmcComputeCmac(SmcArguments &args); SmcResult SmcLoadPreparedAesKey(SmcArguments &args); + SmcResult SmcPrepareEsCommonTitleKey(SmcArguments &args); /* Device unique data functionality. */ SmcResult SmcDecryptDeviceUniqueData(SmcArguments &args); @@ -35,6 +43,10 @@ namespace ams::secmon::smc { SmcResult SmcDecryptAndImportEsDeviceKey(SmcArguments &args); SmcResult SmcDecryptAndImportLotusKey(SmcArguments &args); + /* Es encryption utilities. */ + void DecryptWithEsCommonKey(void *dst, size_t dst_size, const void *src, size_t src_size, EsCommonKeyType type, int generation); + void PrepareEsAesKey(void *dst, size_t dst_size, const void *src, size_t src_size); + /* The last rose of summer. */ SmcResult SmcGetSecureData(SmcArguments &args); diff --git a/exosphere2/program/source/smc/secmon_smc_es.cpp b/exosphere2/program/source/smc/secmon_smc_es.cpp deleted file mode 100644 index 939e12b44..000000000 --- a/exosphere2/program/source/smc/secmon_smc_es.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2018-2020 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 "../secmon_error.hpp" -#include "secmon_smc_es.hpp" - -namespace ams::secmon::smc { - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } - - SmcResult SmcPrepareEsCommonKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; - } - -} diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index 8abe3a71a..c2577806c 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -22,7 +22,6 @@ #include "secmon_smc_carveout.hpp" #include "secmon_smc_device_unique_data.hpp" #include "secmon_smc_error.hpp" -#include "secmon_smc_es.hpp" #include "secmon_smc_info.hpp" #include "secmon_smc_memory_access.hpp" #include "secmon_smc_power_management.hpp" @@ -120,7 +119,7 @@ namespace ams::secmon::smc { { 0xC300060F, Restriction_DeviceUniqueDataNotAllowed, SmcModularExponentiateByStorageKey }, { 0xC3000610, Restriction_SafeModeNotAllowed, SmcPrepareEsDeviceUniqueKey }, { 0xC3000011, Restriction_SafeModeNotAllowed, SmcLoadPreparedAesKey }, - { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonKey } + { 0xC3000012, Restriction_SafeModeNotAllowed, SmcPrepareEsCommonTitleKey } }; constinit HandlerInfo g_kern_handlers[] = { @@ -233,8 +232,8 @@ namespace ams::secmon::smc { constinit std::atomic g_logged = 0; - constexpr int LogMin = 0x4000; - constexpr int LogMax = 0x4200; + constexpr int LogMin = 0x1000000; + constexpr int LogMax = 0x1000000; constexpr size_t LogBufSize = 0x5000; diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.cpp b/exosphere2/program/source/smc/secmon_smc_rsa.cpp index 881a9ea65..aa1e33165 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.cpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.cpp @@ -15,10 +15,158 @@ */ #include #include "../secmon_error.hpp" +#include "../secmon_key_storage.hpp" +#include "secmon_smc_aes.hpp" #include "secmon_smc_rsa.hpp" +#include "secmon_smc_se_lock.hpp" +#include "secmon_page_mapper.hpp" namespace ams::secmon::smc { + namespace { + + struct PrepareEsDeviceUniqueKeyOption { + using KeyGeneration = util::BitPack32::Field<0, 6, int>; + using Type = util::BitPack32::Field<6, 1, EsCommonKeyType>; + using Reserved = util::BitPack32::Field<7, 25, u32>; + }; + + class PrepareEsDeviceUniqueKeyAsyncArguments { + private: + int generation; + EsCommonKeyType type; + u8 label_digest[crypto::Sha256Generator::HashSize]; + public: + void Set(int gen, EsCommonKeyType t, const u8 ld[crypto::Sha256Generator::HashSize]) { + this->generation = gen; + this->type = t; + std::memcpy(this->label_digest, ld, sizeof(this->label_digest)); + } + + int GetKeyGeneration() const { return this->generation; } + EsCommonKeyType GetCommonKeyType() const { return this->type; } + void GetLabelDigest(u8 dst[crypto::Sha256Generator::HashSize]) const { std::memcpy(dst, this->label_digest, sizeof(this->label_digest)); } + }; + + class ModularExponentiateByStorageKeyAsyncArguments { + private: + u8 msg[se::RsaSize]; + public: + void Set(const void *m, size_t m_size) { + std::memcpy(this->msg, m, sizeof(this->msg)); + } + + void GetMessage(void *dst, size_t dst_size) const { std::memcpy(dst, this->msg, sizeof(this->msg)); } + }; + + constinit bool g_exp_mod_completed = false; + + constinit union { + ModularExponentiateByStorageKeyAsyncArguments modular_exponentiate_by_storage_key; + PrepareEsDeviceUniqueKeyAsyncArguments prepare_es_device_unique_key; + } g_async_arguments; + + ALWAYS_INLINE ModularExponentiateByStorageKeyAsyncArguments &GetModularExponentiateByStorageKeyAsyncArguments() { + return g_async_arguments.modular_exponentiate_by_storage_key; + } + + ALWAYS_INLINE PrepareEsDeviceUniqueKeyAsyncArguments &GetPrepareEsDeviceUniqueKeyAsyncArguments() { + return g_async_arguments.prepare_es_device_unique_key; + } + + void SecurityEngineDoneHandler() { + /* End the asynchronous operation. */ + g_exp_mod_completed = true; + EndAsyncOperation(); + } + + SmcResult PrepareEsDeviceUniqueKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + + const uintptr_t msg_address = args.r[1]; + const uintptr_t mod_address = args.r[2]; + std::memcpy(label_digest, std::addressof(args.r[3]), sizeof(label_digest)); + const util::BitPack32 option = { static_cast(args.r[7]) }; + + const auto generation = GetTargetFirmware() >= TargetFirmware_3_0_0 ? std::max(0, option.Get() - 1) : 0; + const auto type = option.Get(); + const auto reserved = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + SMC_R_UNLESS(type < EsCommonKeyType_Count, InvalidArgument); + + /* Copy the message and modulus from the user. */ + alignas(8) u8 msg[se::RsaSize]; + alignas(8) u8 mod[se::RsaSize]; + { + UserPageMapper mapper(msg_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(msg, msg_address, sizeof(msg)), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(mod, mod_address, sizeof(mod)), InvalidArgument); + } + + /* We're performing an operation, so the operation is not completed. */ + g_exp_mod_completed = false; + + /* Set the async arguments. */ + GetPrepareEsDeviceUniqueKeyAsyncArguments().Set(generation, type, label_digest); + + /* Load the es drm key into the security engine. */ + SMC_R_UNLESS(LoadRsaKey(pkg1::RsaKeySlot_Temporary, ImportRsaKey_EsDrmCert), NotInitialized); + + /* Trigger the asynchronous modular exponentiation. */ + se::ModularExponentiateAsync(pkg1::RsaKeySlot_Temporary, msg, sizeof(msg), SecurityEngineDoneHandler); + + return SmcResult::Success; + } + + SmcResult GetPrepareEsDeviceUniqueKeyResult(void *dst, size_t dst_size) { + /* Declare variables. */ + u8 key_source[se::AesBlockSize]; + u8 key[se::AesBlockSize]; + u8 access_key[se::AesBlockSize]; + + /* Validate state. */ + SMC_R_UNLESS(g_exp_mod_completed, Busy); + SMC_R_UNLESS(dst_size == sizeof(access_key), InvalidArgument); + + /* We want to relinquish our security engine lock at the end of scope. */ + ON_SCOPE_EXIT { UnlockSecurityEngine(); }; + + /* Get the async args. */ + const auto &async_args = GetPrepareEsDeviceUniqueKeyAsyncArguments(); + + /* Get the exponentiation output. */ + alignas(8) u8 msg[se::RsaSize]; + se::GetRsaResult(msg, sizeof(msg)); + + /* Decode the key. */ + { + /* Get the label digest. */ + u8 label_digest[crypto::Sha256Generator::HashSize]; + async_args.GetLabelDigest(label_digest); + + /* Decode the key source. */ + const size_t key_source_size = se::DecodeRsaOaepSha256(key_source, sizeof(key_source), msg, sizeof(msg), label_digest, sizeof(label_digest)); + SMC_R_UNLESS(key_source_size == sizeof(key_source), InvalidArgument); + } + + /* Decrypt the key. */ + DecryptWithEsCommonKey(key, sizeof(key), key_source, sizeof(key_source), async_args.GetCommonKeyType(), async_args.GetKeyGeneration()); + PrepareEsAesKey(access_key, sizeof(access_key), key, sizeof(key)); + + /* Copy the access key to output. */ + std::memcpy(dst, access_key, sizeof(access_key)); + + return SmcResult::Success; + } + + } + SmcResult SmcModularExponentiate(SmcArguments &args) { /* TODO */ return SmcResult::NotImplemented; @@ -29,4 +177,8 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args) { + return LockSecurityEngineAndInvokeAsync(args, PrepareEsDeviceUniqueKeyImpl, GetPrepareEsDeviceUniqueKeyResult); + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_rsa.hpp b/exosphere2/program/source/smc/secmon_smc_rsa.hpp index cfa4301fe..63859a94f 100644 --- a/exosphere2/program/source/smc/secmon_smc_rsa.hpp +++ b/exosphere2/program/source/smc/secmon_smc_rsa.hpp @@ -22,4 +22,6 @@ namespace ams::secmon::smc { SmcResult SmcModularExponentiate(SmcArguments &args); SmcResult SmcModularExponentiateByStorageKey(SmcArguments &args); + SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); + } diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp index c46693815..a86396ccd 100644 --- a/libraries/libexosphere/include/exosphere/se.hpp +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include diff --git a/exosphere2/program/source/smc/secmon_smc_es.hpp b/libraries/libexosphere/include/exosphere/se/se_oaep.hpp similarity index 66% rename from exosphere2/program/source/smc/secmon_smc_es.hpp rename to libraries/libexosphere/include/exosphere/se/se_oaep.hpp index a9d2d30d9..b7488bd70 100644 --- a/exosphere2/program/source/smc/secmon_smc_es.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_oaep.hpp @@ -14,19 +14,10 @@ * along with this program. If not, see . */ #pragma once -#include -#include "secmon_smc_common.hpp" +#include -namespace ams::secmon::smc { +namespace ams::se { - enum EsKeyType { - EsKeyType_TitleKey = 0, - EsKeyType_ArchiveKey = 1, - - EsKeyType_Count = 2, - }; - - SmcResult SmcPrepareEsDeviceUniqueKey(SmcArguments &args); - SmcResult SmcPrepareEsCommonKey(SmcArguments &args); + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size); } diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp index e632f67a9..c9733c5ee 100644 --- a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include namespace ams::se { @@ -27,5 +28,8 @@ namespace ams::se { void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler); + + void GetRsaResult(void *dst, size_t dst_size); } diff --git a/libraries/libexosphere/source/se/se_execute.cpp b/libraries/libexosphere/source/se/se_execute.cpp index 47252e772..4f811ee8a 100644 --- a/libraries/libexosphere/source/se/se_execute.cpp +++ b/libraries/libexosphere/source/se/se_execute.cpp @@ -131,6 +131,25 @@ namespace ams::se { std::memcpy(dst, aligned, dst_size); } + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size) { + /* Set the linked list entry. */ + LinkedListEntry src_entry; + SetLinkedListEntry(std::addressof(src_entry), src, src_size); + + /* Ensure the linked list entry data is seen correctly. */ + hw::FlushDataCache(std::addressof(src_entry), sizeof(src_entry)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the linked list addresses. */ + reg::Write(SE->SE_IN_LL_ADDR, static_cast(GetPhysicalAddress(std::addressof(src_entry)))); + + /* Start the operation. */ + StartOperation(SE, SE_OPERATION_OP_START); + + /* Ensure the operation is started. */ + EnsureOperationStarted(SE); + } + void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address) { /* Configure the linked list addresses. */ reg::Write(SE->SE_IN_LL_ADDR, in_ll_address); diff --git a/libraries/libexosphere/source/se/se_execute.hpp b/libraries/libexosphere/source/se/se_execute.hpp index 5b242c597..cc28d3ebb 100644 --- a/libraries/libexosphere/source/se/se_execute.hpp +++ b/libraries/libexosphere/source/se/se_execute.hpp @@ -23,6 +23,7 @@ namespace ams::se { void ExecuteOperation(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, void *dst, size_t dst_size, const void *src, size_t src_size); void ExecuteOperationSingleBlock(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size, const void *src, size_t src_size); + void StartInputOperation(volatile SecurityEngineRegisters *SE, const void *src, size_t src_size); void StartOperationRaw(volatile SecurityEngineRegisters *SE, SE_OPERATION_OP op, u32 out_ll_address, u32 in_ll_address); void SetDoneHandler(volatile SecurityEngineRegisters *SE, DoneHandler handler); diff --git a/libraries/libexosphere/source/se/se_oaep.cpp b/libraries/libexosphere/source/se/se_oaep.cpp new file mode 100644 index 000000000..debbeec57 --- /dev/null +++ b/libraries/libexosphere/source/se/se_oaep.cpp @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2018-2020 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 "se_execute.hpp" + +namespace ams::se { + + /* NOTE: This implementation is mostly copy/pasted from crypto::impl::RsaOaepImpl. */ + + namespace { + + constexpr inline size_t HashSize = sizeof(Sha256Hash); + + constexpr inline u8 HeadMagic = 0x00; + + void ApplyMGF1(u8 *dst, size_t dst_size, const void *src, size_t src_size) { + /* Check our pre-conditions. */ + AMS_ABORT_UNLESS(src_size <= RsaSize - (1 + HashSize)); + + /* Create a buffer. */ + util::AlignedBuffer buf; + u32 counter = 0; + + while (dst_size > 0) { + /* Setup the current hash buffer. */ + const size_t cur_size = std::min(HashSize, dst_size); + std::memcpy(static_cast(buf), src, src_size); + { + u32 counter_be; + util::StoreBigEndian(std::addressof(counter_be), counter++); + std::memcpy(static_cast(buf) + src_size, std::addressof(counter_be), sizeof(counter_be)); + } + + /* Ensure se sees correct data. */ + hw::FlushDataCache(buf, src_size + sizeof(u32)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + Sha256Hash hash; + se::CalculateSha256(std::addressof(hash), buf, src_size + sizeof(u32)); + + /* Mask the current output. */ + const u8 *mask = hash.bytes; + for (size_t i = 0; i < cur_size; ++i) { + *(dst++) ^= *(mask++); + } + + /* Advance. */ + dst_size -= cur_size; + } + } + + } + + size_t DecodeRsaOaepSha256(void *dst, size_t dst_size, void *src, size_t src_size, const void *label_digest, size_t label_digest_size) { + /* Check our preconditions. */ + AMS_ABORT_UNLESS(src_size == RsaSize); + AMS_ABORT_UNLESS(label_digest_size == HashSize); + + /* Get a byte-readable copy of the input. */ + u8 *buf = static_cast(src); + + /* Validate sanity byte. */ + bool is_valid = buf[0] == HeadMagic; + + /* Decrypt seed and masked db. */ + size_t db_len = src_size - HashSize - 1; + u8 *seed = buf + 1; + u8 *db = seed + HashSize; + ApplyMGF1(seed, HashSize, db, db_len); + ApplyMGF1(db, db_len, seed, HashSize); + + /* Check the label digest. */ + is_valid &= crypto::IsSameBytes(label_digest, db, HashSize); + + /* Skip past the label digest. */ + db += HashSize; + db_len -= HashSize; + + /* Verify that DB is of the form 0000...0001 < message > */ + s32 msg_ofs = 0; + { + int looking_for_one = 1; + int invalid_db_padding = 0; + int is_zero; + int is_one; + for (size_t i = 0; i < db_len; /* ... */) { + is_zero = (db[i] == 0); + is_one = (db[i] == 1); + msg_ofs += (looking_for_one & is_one) * (static_cast(++i)); + looking_for_one &= ~is_one; + invalid_db_padding |= (looking_for_one & ~is_zero); + } + + is_valid &= (invalid_db_padding == 0); + } + + /* If we're invalid, return zero size. */ + const size_t valid_msg_size = db_len - msg_ofs; + const size_t msg_size = std::min(dst_size, static_cast(is_valid) * valid_msg_size); + + /* Copy to output. */ + std::memcpy(dst, db + msg_ofs, msg_size); + + /* Return copied size. */ + return msg_size; + } + +} diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp index 99ec5e90a..9fc473931 100644 --- a/libraries/libexosphere/source/se/se_rsa.cpp +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -67,6 +67,10 @@ namespace ams::se { } } + void WaitForInputReadComplete(volatile SecurityEngineRegisters *SE) { + while (reg::HasValue(SE->SE_INT_STATUS, SE_REG_BITS_ENUM(INT_STATUS_IN_DONE, CLEAR))) { /* ... */ } + } + } void ClearRsaKeySlot(int slot) { @@ -174,4 +178,53 @@ namespace ams::se { GetRsaResult(SE, dst, dst_size); } + void ModularExponentiateAsync(int slot, const void *src, size_t src_size, DoneHandler handler) { + /* Validate the slot and size. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(src_size <= RsaSize); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Create a work buffer. */ + u8 work[RsaSize]; + util::ClearMemory(work, sizeof(work)); + + /* Copy the input into the work buffer (reversing endianness). */ + const u8 *src_u8 = static_cast(src); + for (size_t i = 0; i < src_size; ++i) { + work[src_size - 1 - i] = src_u8[i]; + } + + /* Flush the work buffer to ensure the SE sees correct results. */ + hw::FlushDataCache(work, sizeof(work)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the engine to perform RSA encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG)); + + /* Configure the engine to use the keyslot and correct modulus/exp sizes. */ + const auto &info = g_rsa_key_infos[slot]; + reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot)); + reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val); + reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val); + + /* Set the done handler. */ + SetDoneHandler(SE, handler); + + /* Trigger the input operation. */ + StartInputOperation(SE, work, src_size); + + /* Wait for input to be read by the se. */ + WaitForInputReadComplete(SE); + } + + void GetRsaResult(void *dst, size_t dst_size) { + GetRsaResult(GetRegisters(), dst, dst_size); + } + }