From 49fc2acc9fc82023dc8ba6f02839718b5baee942 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 --- libexosphere/include/exosphere/fuse.hpp | 5 + libexosphere/include/exosphere/se/se_aes.hpp | 3 + libexosphere/source/fuse/fuse_api.cpp | 5 + libexosphere/source/se/se_aes.cpp | 119 ++++++++++++++++++- 4 files changed, 131 insertions(+), 1 deletion(-) diff --git a/libexosphere/include/exosphere/fuse.hpp b/libexosphere/include/exosphere/fuse.hpp index e6ff9ebb..eef79fed 100644 --- a/libexosphere/include/exosphere/fuse.hpp +++ b/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/libexosphere/include/exosphere/se/se_aes.hpp b/libexosphere/include/exosphere/se/se_aes.hpp index b7882041..942f9155 100644 --- a/libexosphere/include/exosphere/se/se_aes.hpp +++ b/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/libexosphere/source/fuse/fuse_api.cpp b/libexosphere/source/fuse/fuse_api.cpp index c01286bf..ce83e77c 100644 --- a/libexosphere/source/fuse/fuse_api.cpp +++ b/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/libexosphere/source/se/se_aes.cpp b/libexosphere/source/se/se_aes.cpp index ed0ec0e9..e1c8286c 100644 --- a/libexosphere/source/se/se_aes.cpp +++ b/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);