From aa50944568fb363d9736443321d5c94802181334 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 15 May 2020 17:42:04 -0700 Subject: [PATCH] exo2: tentative (read: bugged) SmcComputeCmac, SmcGenerateSpecificAesKey, SmcGetSecureData --- .../program/source/smc/secmon_smc_aes.cpp | 173 +++++++++++++++++- .../program/source/smc/secmon_smc_aes.hpp | 2 + .../program/source/smc/secmon_smc_handler.cpp | 16 +- .../libexosphere/include/exosphere/fuse.hpp | 5 + .../include/exosphere/se/se_aes.hpp | 3 + .../libexosphere/source/fuse/fuse_api.cpp | 5 + libraries/libexosphere/source/se/se_aes.cpp | 119 +++++++++++- 7 files changed, 316 insertions(+), 7 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index c004091ef..b007c8b12 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -19,12 +19,14 @@ #include "../secmon_misc.hpp" #include "secmon_smc_aes.hpp" #include "secmon_smc_se_lock.hpp" +#include "secmon_user_page_mapper.hpp" namespace ams::secmon::smc { namespace { - constexpr inline auto AesKeySize = se::AesBlockSize; + constexpr inline auto AesKeySize = se::AesBlockSize; + constexpr inline size_t CmacSizeMax = 4_KB; enum SealKey { SealKey_LoadAesKey = 0, @@ -54,6 +56,22 @@ namespace ams::secmon::smc { CipherMode_Cmac = 3, }; + enum SpecificAesKey { + SpecificAesKey_CalibrationEncryption0 = 0, + SpecificAesKey_CalibrationEncryption1 = 1, + + SpecificAesKey_Count, + }; + + enum SecureData { + SecureData_Calibration = 0, + SecureData_SafeMode = 1, + SecureData_UserSystemProperEncryption = 2, + SecureData_UserSystem = 3, + + SecureData_Count, + }; + struct GenerateAesKekOption { using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>; using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>; @@ -93,6 +111,54 @@ namespace ams::secmon::smc { [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, }; + constexpr const u8 CalibrationKeySource[AesKeySize] = { + 0xE2, 0xD6, 0xB8, 0x7A, 0x11, 0x9C, 0xB8, 0x80, 0xE8, 0x22, 0x88, 0x8A, 0x46, 0xFB, 0xA1, 0x95 + }; + + constexpr const u8 SecureDataSource[AesKeySize] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + constexpr const u8 SecureDataCounters[][AesKeySize] = { + [SecureData_Calibration] = { 0x3C, 0xD5, 0x92, 0xEC, 0x68, 0x31, 0x4A, 0x06, 0xD4, 0x1B, 0x0C, 0xD9, 0xF6, 0x2E, 0xD9, 0xE9 }, + [SecureData_SafeMode] = { 0x50, 0x81, 0xCF, 0x77, 0x18, 0x11, 0xD7, 0x0D, 0x13, 0x29, 0x60, 0xED, 0x4B, 0x21, 0x3E, 0xFC }, + [SecureData_UserSystemProperEncryption] = { 0x98, 0xCB, 0x4C, 0xEB, 0x15, 0xF1, 0x4A, 0x5A, 0x7A, 0x86, 0xB6, 0xF1, 0x94, 0x66, 0xF4, 0x9D }, + }; + + constexpr const u8 SecureDataTweaks[][AesKeySize] = { + [SecureData_Calibration] = { 0xAC, 0xCA, 0x9A, 0xCA, 0xFF, 0x2E, 0xB9, 0x22, 0xCC, 0x1F, 0x4F, 0xAD, 0xDD, 0x77, 0x21, 0x1E }, + [SecureData_SafeMode] = { 0x6E, 0xF8, 0x2A, 0x1A, 0xE0, 0x4F, 0xC3, 0x20, 0x08, 0x7B, 0xBA, 0x50, 0xC0, 0xCD, 0x7B, 0x39 }, + [SecureData_UserSystemProperEncryption] = { 0x6D, 0x02, 0x56, 0x2D, 0xF4, 0x3D, 0x0A, 0x15, 0xB1, 0x34, 0x5C, 0xC2, 0x84, 0x4C, 0xD4, 0x28 }, + }; + + constexpr const u8 *GetSecureDataCounter(SecureData which) { + switch (which) { + case SecureData_Calibration: + return SecureDataCounters[SecureData_Calibration]; + case SecureData_SafeMode: + return SecureDataCounters[SecureData_SafeMode]; + case SecureData_UserSystem: + case SecureData_UserSystemProperEncryption: + return SecureDataCounters[SecureData_UserSystemProperEncryption]; + default: + return nullptr; + } + } + + constexpr const u8 *GetSecureDataTweak(SecureData which) { + switch (which) { + case SecureData_Calibration: + return SecureDataTweaks[SecureData_Calibration]; + case SecureData_SafeMode: + return SecureDataTweaks[SecureData_SafeMode]; + case SecureData_UserSystem: + case SecureData_UserSystemProperEncryption: + return SecureDataTweaks[SecureData_UserSystemProperEncryption]; + default: + return nullptr; + } + } + constexpr uintptr_t LinkedListAddressMinimum = secmon::MemoryRegionDram.GetAddress(); constexpr size_t LinkedListAddressRangeSize = 4_MB - 2_KB; constexpr uintptr_t LinkedListAddressMaximum = LinkedListAddressMinimum + LinkedListAddressRangeSize; @@ -152,6 +218,20 @@ namespace ams::secmon::smc { return Slot; } + void GetSecureDataImpl(u8 *dst, SecureData which, bool tweak) { + /* Compute the appropriate AES-CTR. */ + se::ComputeAes128Ctr(dst, AesKeySize, pkg1::AesKeySlot_Device, SecureDataSource, AesKeySize, GetSecureDataCounter(which), AesKeySize); + + /* Tweak, if we should. */ + if (tweak) { + const u8 * const tweak = GetSecureDataTweak(which); + + for (size_t i = 0; i < AesKeySize; ++i) { + dst[i] ^= tweak[i]; + } + } + } + SmcResult GenerateAesKekImpl(SmcArguments &args) { /* Decode arguments. */ u8 kek_source[AesKeySize]; @@ -281,6 +361,85 @@ namespace ams::secmon::smc { return SmcResult::Success; } + SmcResult GenerateSpecificAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 key_source[AesKeySize]; + std::memcpy(key_source, std::addressof(args.r[1]), sizeof(key_source)); + + const int generation = GetTargetFirmware() >= TargetFirmware_4_0_0 ? std::max(static_cast(args.r[3]) - 1, pkg1::KeyGeneration_1_0_0) : pkg1::KeyGeneration_1_0_0; + const auto which = static_cast(args.r[4]); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(which < SpecificAesKey_Count, InvalidArgument); + + /* Generate the specific aes key. */ + u8 output_key[AesKeySize]; + if (fuse::GetPatchVersion() >= fuse::PatchVersion_Odnx02A2) { + const int slot = PrepareDeviceMasterKey(generation); + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, CalibrationKeySource, sizeof(CalibrationKeySource)); + se::DecryptAes128(output_key, sizeof(output_key), pkg1::AesKeySlot_Smc, key_source, sizeof(key_source)); + } else { + GetSecureDataImpl(output_key, SecureData_Calibration, which == SpecificAesKey_CalibrationEncryption1); + } + + /* Copy the key to output. */ + std::memcpy(std::addressof(args.r[1]), output_key, sizeof(output_key)); + return SmcResult::Success; + } + + SmcResult ComputeCmacImpl(SmcArguments &args) { + /* Decode arguments. */ + const int slot = args.r[1]; + const uintptr_t data_address = args.r[2]; + const uintptr_t data_size = args.r[3]; + + /* Declare buffer for user data. */ + alignas(8) u8 user_data[CmacSizeMax]; + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + SMC_R_UNLESS(data_size <= sizeof(user_data), InvalidArgument); + + /* Map the user data, and copy to stack. */ + { + UserPageMapper mapper(data_address); + SMC_R_UNLESS(mapper.Map(), InvalidArgument); + SMC_R_UNLESS(mapper.CopyFromUser(user_data, data_address, data_size), InvalidArgument); + } + + /* Ensure the SE sees consistent data. */ + hw::FlushDataCache(user_data, data_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Compute the mac. */ + { + u8 mac[se::AesBlockSize]; + se::ComputeAes128Cmac(mac, sizeof(mac), slot, user_data, data_size); + + std::memcpy(std::addressof(args.r[1]), mac, sizeof(mac)); + } + + return SmcResult::Success; + } + + SmcResult GetSecureDataImpl(SmcArguments &args) { + /* Decode arguments. */ + const auto which = static_cast(args.r[1]); + + /* Validate arguments/conditions. */ + SMC_R_UNLESS(fuse::GetPatchVersion() < fuse::PatchVersion_Odnx02A2, NotImplemented); + SMC_R_UNLESS(which < SecureData_Count, NotImplemented); + + /* Use a temporary buffer. */ + u8 secure_data[AesKeySize]; + GetSecureDataImpl(secure_data, which, false); + + /* Copy out. */ + std::memcpy(std::addressof(args.r[1]), secure_data, sizeof(secure_data)); + return SmcResult::Success; + } + } SmcResult SmcGenerateAesKek(SmcArguments &args) { @@ -296,13 +455,11 @@ namespace ams::secmon::smc { } SmcResult SmcGenerateSpecificAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, GenerateSpecificAesKeyImpl); } SmcResult SmcComputeCmac(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, ComputeCmacImpl); } SmcResult SmcLoadPreparedAesKey(SmcArguments &args) { @@ -310,4 +467,10 @@ namespace ams::secmon::smc { return SmcResult::NotImplemented; } + /* 'Tis the last rose of summer, / Left blooming alone; */ + /* Oh! who would inhabit / This bleak world alone? */ + SmcResult SmcGetSecureData(SmcArguments &args) { + return LockSecurityEngineAndInvoke(args, GetSecureDataImpl); + } + } diff --git a/exosphere2/program/source/smc/secmon_smc_aes.hpp b/exosphere2/program/source/smc/secmon_smc_aes.hpp index 8aa55fd81..067b4bf6b 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.hpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.hpp @@ -26,4 +26,6 @@ namespace ams::secmon::smc { SmcResult SmcComputeCmac(SmcArguments &args); SmcResult SmcLoadPreparedAesKey(SmcArguments &args); + SmcResult SmcGetSecureData(SmcArguments &args); + } diff --git a/exosphere2/program/source/smc/secmon_smc_handler.cpp b/exosphere2/program/source/smc/secmon_smc_handler.cpp index ddea4311e..9d9b92ea6 100644 --- a/exosphere2/program/source/smc/secmon_smc_handler.cpp +++ b/exosphere2/program/source/smc/secmon_smc_handler.cpp @@ -140,7 +140,11 @@ namespace ams::secmon::smc { { 0xF0000201, Restriction_None, SmcIramCopy }, { 0xF0000002, Restriction_None, SmcReadWriteRegister }, { 0xF0000003, Restriction_None, SmcWriteAddress }, - { 0xF0000003, Restriction_None, SmcGetEmummcConfig }, + { 0xF0000404, Restriction_None, SmcGetEmummcConfig }, + }; + + constexpr const HandlerInfo GetSecureDataHandlerInfo = { + 0x67891234, Restriction_None, SmcGetSecureData }; constinit HandlerTable g_handler_tables[] = { @@ -163,6 +167,11 @@ namespace ams::secmon::smc { InvalidSmcError(id); } + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return g_handler_tables[HandlerType_User]; + } + /* Check if we're a user SMC. */ if (type == HandlerType_User) { /* Nintendo uses OEM SMCs. */ @@ -181,6 +190,11 @@ namespace ams::secmon::smc { } const HandlerInfo &GetHandlerInfo(const HandlerTable &table, u64 id) { + /* Provide support for legacy SmcGetSecureData. */ + if (id == GetSecureDataHandlerInfo.function_id) { + return GetSecureDataHandlerInfo; + } + /* Get and check the index. */ const auto index = util::BitPack64{id}.Get(); if (AMS_UNLIKELY(index >= table.count)) { diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index e6ff9ebb9..eef79fed7 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -43,6 +43,10 @@ namespace ams::fuse { HardwareState_Undefined = 2, }; + enum PatchVersion { + PatchVersion_Odnx02A2 = (SocType_Erista << 12) | 0x07F, + }; + enum DramId { DramId_IcosaSamsung4GB = 0, DramId_IcosaHynix4GB = 1, @@ -96,6 +100,7 @@ namespace ams::fuse { HardwareType GetHardwareType(); HardwareState GetHardwareState(); u64 GetDeviceId(); + PatchVersion GetPatchVersion(); QuestState GetQuestState(); pmic::Regulator GetRegulator(); int GetDeviceUniqueKeyGeneration(); diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index b78820416..942f9155d 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -35,6 +35,9 @@ namespace ams::se { void ComputeAes128Ctr(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, const void *iv, size_t iv_size); + void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void DecryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); void ComputeAes128CtrAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler); diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index c01286bfe..ce83e77c3 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -281,6 +281,11 @@ namespace ams::fuse { } } + PatchVersion GetPatchVersion() { + const auto patch_version = reg::Read(GetChipRegisters().FUSE_SOC_SPEEDO_1_CALIB); + return static_cast(static_cast(GetSocType() << 12) | patch_version); + } + QuestState GetQuestState() { return static_cast(util::BitPack32{GetOdmWord(4)}.Get()); } diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index ed0ec0e91..e1c8286ce 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -49,6 +49,14 @@ namespace ams::se { SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, BOTTOM), SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, DISABLE)); + constexpr inline u32 AesConfigCmac = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_VCTRAM_SEL, INIT_AESOUT), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_INPUT_SEL, MEMORY), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_XOR_POS, TOP), + SE_REG_BITS_ENUM (CRYPTO_CONFIG_HASH_ENB, ENABLE)); + constexpr inline u32 AesConfigCbcEncrypt = reg::Encode(SE_REG_BITS_VALUE(CRYPTO_CONFIG_CTR_CNTN, 0), SE_REG_BITS_ENUM (CRYPTO_CONFIG_KEYSCH_BYPASS, DISABLE), SE_REG_BITS_ENUM (CRYPTO_CONFIG_IV_SELECT, ORIGINAL), @@ -117,7 +125,7 @@ namespace ams::se { SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); - /* Set the key word. */ + /* Set the iv word. */ SE->SE_CRYPTO_KEYTABLE_DATA = *(iv_u32++); } } @@ -168,6 +176,107 @@ namespace ams::se { ExecuteOperationSingleBlock(SE, dst, dst_size, src, src_size); } + void ExpandSubkey(u8 *subkey) { + /* Shift everything left one bit. */ + u8 prev = 0; + for (int i = AesBlockSize - 1; i >= 0; --i) { + const u8 top = (subkey[i] >> 7); + subkey[i] = ((subkey[i] << 1) | top); + prev = top; + } + + /* And xor with Rb if necessary. */ + if (prev != 0) { + subkey[AesBlockSize - 1] ^= 0x87; + } + } + + void GetCmacResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) { + const int num_words = dst_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + reg::Write(static_cast(dst) + i, reg::Read(SE->SE_HASH_RESULT[i])); + } + } + + void ComputeAesCmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size, AesMode mode) { + /* Validate input. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Determine mac extents. */ + const int num_blocks = util::DivideUp(src_size, AesBlockSize); + const size_t last_block_size = (src_size == 0) ? 0 : (src_size - ((num_blocks - 1) * AesBlockSize)); + + /* Create subkey. */ + u8 subkey[AesBlockSize]; + { + /* Encrypt zeroes. */ + std::memset(subkey, 0, sizeof(subkey)); + EncryptAes(subkey, sizeof(subkey), slot, subkey, sizeof(subkey), mode); + + /* Expand. */ + ExpandSubkey(subkey); + + /* Account for last block. */ + if (last_block_size) { + ExpandSubkey(subkey); + } + } + + /* Configure for AES-CMAC. */ + SetConfig(SE, true, SE_CONFIG_DST_HASH_REG); + SetAesConfig(SE, slot, true, AesConfigCmac); + UpdateAesMode(SE, mode); + + /* Set the IV to zero. */ + for (int i = 0; i < 4; ++i) { + /* Select the keyslot. */ + reg::Write(SE->SE_CRYPTO_KEYTABLE_ADDR, SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, slot), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_KEYIV_SEL, IV), + SE_REG_BITS_ENUM (CRYPTO_KEYTABLE_ADDR_KEYIV_IV_SEL, ORIGINAL_IV), + SE_REG_BITS_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_WORD, i)); + + /* Set the iv word. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + + /* Handle blocks before the last. */ + if (num_blocks > 1) { + SetBlockCount(SE, num_blocks - 1); + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, src, src_size); + reg::ReadWrite(SE->SE_CRYPTO_CONFIG, SE_REG_BITS_ENUM(CRYPTO_CONFIG_IV_SELECT, UPDATED)); + } + + /* Handle the last block. */ + { + SetBlockCount(SE, 1); + + /* Create the last block. */ + u8 last_block[AesBlockSize]; + if (last_block_size < sizeof(last_block)) { + std::memset(last_block, 0, sizeof(last_block)); + last_block[last_block_size] = 0x80; + } + std::memcpy(last_block, static_cast(src) + src_size - last_block_size, last_block_size); + + /* Xor with the subkey. */ + for (size_t i = 0; i < AesBlockSize; ++i) { + last_block[i] ^= subkey[i]; + } + + /* Ensure the SE sees correct data. */ + hw::FlushDataCache(last_block, sizeof(last_block)); + hw::DataSynchronizationBarrierInnerShareable(); + + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, last_block, sizeof(last_block)); + } + + /* Get the output. */ + GetCmacResult(SE, dst, dst_size); + } + void ComputeAes128Async(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, DoneHandler handler, u32 config, bool encrypt, volatile SecurityEngineRegisters *SE) { /* If nothing to decrypt, succeed. */ if (size == 0) { return; } @@ -328,6 +437,14 @@ namespace ams::se { } } + void ComputeAes128Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes128); + } + + void ComputeAes256Cmac(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + return ComputeAesCmac(dst, dst_size, slot, src, src_size, AesMode_Aes256); + } + void EncryptAes128CbcAsync(u32 out_ll_address, int slot, u32 in_ll_address, u32 size, const void *iv, size_t iv_size, DoneHandler handler) { /* Validate the iv. */ AMS_ABORT_UNLESS(iv_size == AesBlockSize);