From bf546d5fb3b33b59a39c36cdf9684f0749982d21 Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Fri, 15 May 2020 11:10:28 -0700 Subject: [PATCH] exo2: implement SmcGenerateAesKek, SmcLoadAesKey --- .../program/source/smc/secmon_smc_aes.cpp | 188 +++++++++++++++++- .../program/source/smc/secmon_smc_common.hpp | 6 + .../smc/secmon_smc_power_management.cpp | 8 +- .../program/source/smc/secmon_smc_random.cpp | 11 +- .../source/smc/secmon_smc_register_access.cpp | 41 ++-- .../program/source/smc/secmon_smc_se_lock.cpp | 38 ++-- .../exosphere/pkg1/pkg1_key_generation.hpp | 8 + .../exosphere/pkg1/pkg1_se_key_slots.hpp | 4 + 8 files changed, 239 insertions(+), 65 deletions(-) diff --git a/exosphere2/program/source/smc/secmon_smc_aes.cpp b/exosphere2/program/source/smc/secmon_smc_aes.cpp index 2edd1d812..e517e611e 100644 --- a/exosphere2/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere2/program/source/smc/secmon_smc_aes.cpp @@ -15,18 +15,198 @@ */ #include #include "../secmon_error.hpp" +#include "../secmon_key_storage.hpp" +#include "../secmon_misc.hpp" #include "secmon_smc_aes.hpp" +#include "secmon_smc_se_lock.hpp" namespace ams::secmon::smc { + namespace { + + constexpr inline auto AesKeySize = se::AesBlockSize; + + enum SealKey { + SealKey_LoadAesKey = 0, + SealKey_DecryptDeviceUniqueData = 1, + SealKey_LoadLotusKey = 2, + SealKey_LoadEsDeviceKey = 3, + SealKey_ReencryptDeviceUniqueData = 4, + SealKey_LoadSslKey = 5, + SealKey_LoadEsClientCertKey = 6, + + SealKey_Count, + }; + + enum KeyType { + KeyType_Default = 0, + KeyType_NormalOnly = 1, + KeyType_RecoveryOnly = 2, + KeyType_NormalAndRecovery = 3, + + KeyType_Count, + }; + + struct GenerateAesKekOption { + using IsDeviceUnique = util::BitPack32::Field<0, 1, bool>; + using KeyTypeIndex = util::BitPack32::Field<1, 4, KeyType>; + using SealKeyIndex = util::BitPack32::Field<5, 3, SealKey>; + using Reserved = util::BitPack32::Field<8, 24, u32>; + }; + + constexpr const u8 SealKeySources[SealKey_Count][AesKeySize] = { + [SealKey_LoadAesKey] = { 0xF4, 0x0C, 0x16, 0x26, 0x0D, 0x46, 0x3B, 0xE0, 0x8C, 0x6A, 0x56, 0xE5, 0x82, 0xD4, 0x1B, 0xF6 }, + [SealKey_DecryptDeviceUniqueData] = { 0x7F, 0x54, 0x2C, 0x98, 0x1E, 0x54, 0x18, 0x3B, 0xBA, 0x63, 0xBD, 0x4C, 0x13, 0x5B, 0xF1, 0x06 }, + [SealKey_LoadLotusKey] = { 0xC7, 0x3F, 0x73, 0x60, 0xB7, 0xB9, 0x9D, 0x74, 0x0A, 0xF8, 0x35, 0x60, 0x1A, 0x18, 0x74, 0x63 }, + [SealKey_LoadEsDeviceKey] = { 0x0E, 0xE0, 0xC4, 0x33, 0x82, 0x66, 0xE8, 0x08, 0x39, 0x13, 0x41, 0x7D, 0x04, 0x64, 0x2B, 0x6D }, + [SealKey_ReencryptDeviceUniqueData] = { 0xE1, 0xA8, 0xAA, 0x6A, 0x2D, 0x9C, 0xDE, 0x43, 0x0C, 0xDE, 0xC6, 0x17, 0xF6, 0xC7, 0xF1, 0xDE }, + [SealKey_LoadSslKey] = { 0x74, 0x20, 0xF6, 0x46, 0x77, 0xB0, 0x59, 0x2C, 0xE8, 0x1B, 0x58, 0x64, 0x47, 0x41, 0x37, 0xD9 }, + [SealKey_LoadEsClientCertKey] = { 0xAA, 0x19, 0x0F, 0xFA, 0x4C, 0x30, 0x3B, 0x2E, 0xE6, 0xD8, 0x9A, 0xCF, 0xE5, 0x3F, 0xB3, 0x4B }, + }; + + constexpr const u8 KeyTypeSources[KeyType_Count][AesKeySize] = { + [KeyType_Default] = { 0x4D, 0x87, 0x09, 0x86, 0xC4, 0x5D, 0x20, 0x72, 0x2F, 0xBA, 0x10, 0x53, 0xDA, 0x92, 0xE8, 0xA9 }, + [KeyType_NormalOnly] = { 0x25, 0x03, 0x31, 0xFB, 0x25, 0x26, 0x0B, 0x79, 0x8C, 0x80, 0xD2, 0x69, 0x98, 0xE2, 0x22, 0x77 }, + [KeyType_RecoveryOnly] = { 0x76, 0x14, 0x1D, 0x34, 0x93, 0x2D, 0xE1, 0x84, 0x24, 0x7B, 0x66, 0x65, 0x55, 0x04, 0x65, 0x81 }, + [KeyType_NormalAndRecovery] = { 0xAF, 0x3D, 0xB7, 0xF3, 0x08, 0xA2, 0xD8, 0xA2, 0x08, 0xCA, 0x18, 0xA8, 0x69, 0x46, 0xC9, 0x0B }, + }; + + constexpr const u8 SealKeyMasks[SealKey_Count][AesKeySize] = { + [SealKey_LoadAesKey] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, + [SealKey_DecryptDeviceUniqueData] = { 0xA2, 0xAB, 0xBF, 0x9C, 0x92, 0x2F, 0xBB, 0xE3, 0x78, 0x79, 0x9B, 0xC0, 0xCC, 0xEA, 0xA5, 0x74 }, + [SealKey_LoadLotusKey] = { 0x57, 0xE2, 0xD9, 0x45, 0xE4, 0x92, 0xF4, 0xFD, 0xC3, 0xF9, 0x86, 0x38, 0x89, 0x78, 0x9F, 0x3C }, + [SealKey_LoadEsDeviceKey] = { 0xE5, 0x4D, 0x9A, 0x02, 0xF0, 0x4F, 0x5F, 0xA8, 0xAD, 0x76, 0x0A, 0xF6, 0x32, 0x95, 0x59, 0xBB }, + [SealKey_ReencryptDeviceUniqueData] = { 0x59, 0xD9, 0x31, 0xF4, 0xA7, 0x97, 0xB8, 0x14, 0x40, 0xD6, 0xA2, 0x60, 0x2B, 0xED, 0x15, 0x31 }, + [SealKey_LoadSslKey] = { 0xFD, 0x6A, 0x25, 0xE5, 0xD8, 0x38, 0x7F, 0x91, 0x49, 0xDA, 0xF8, 0x59, 0xA8, 0x28, 0xE6, 0x75 }, + [SealKey_LoadEsClientCertKey] = { 0x89, 0x96, 0x43, 0x9A, 0x7C, 0xD5, 0x59, 0x55, 0x24, 0xD5, 0x24, 0x18, 0xAB, 0x6C, 0x04, 0x61 }, + }; + + int PrepareMasterKey(int generation) { + if (generation == GetKeyGeneration()) { + return pkg1::AesKeySlot_Master; + } + + constexpr int Slot = pkg1::AesKeySlot_Smc; + LoadMasterKey(Slot, generation); + + return Slot; + } + + int PrepareDeviceMasterKey(int generation) { + if (generation == pkg1::KeyGeneration_1_0_0) { + return pkg1::AesKeySlot_Device; + } + if (generation == GetKeyGeneration()) { + return pkg1::AesKeySlot_DeviceMaster; + } + + constexpr int Slot = pkg1::AesKeySlot_Smc; + LoadDeviceMasterKey(Slot, generation); + + return Slot; + } + + SmcResult GenerateAesKekImpl(SmcArguments &args) { + /* Decode arguments. */ + u8 kek_source[AesKeySize]; + std::memcpy(kek_source, std::addressof(args.r[1]), AesKeySize); + + const int generation = std::min(args.r[3] - 1, pkg1::KeyGeneration_1_0_0); + + const util::BitPack32 option = { static_cast(args.r[4]) }; + const bool is_device_unique = option.Get(); + const auto key_type = option.Get(); + const auto seal_key = option.Get(); + const u32 reserved = option.Get(); + + /* Validate arguments. */ + SMC_R_UNLESS(reserved == 0, InvalidArgument); + + if (is_device_unique) { + SMC_R_UNLESS(pkg1::IsValidDeviceUniqueKeyGeneration(generation), InvalidArgument); + } else { + SMC_R_UNLESS(pkg1::IsValidKeyGeneration(generation), InvalidArgument); + SMC_R_UNLESS(generation <= GetKeyGeneration(), InvalidArgument); + } + + SMC_R_UNLESS(0 <= key_type && key_type < KeyType_Count, InvalidArgument); + SMC_R_UNLESS(0 <= seal_key && seal_key < SealKey_Count, InvalidArgument); + + switch (key_type) { + case KeyType_NormalOnly: SMC_R_UNLESS(!IsRecoveryBoot(), InvalidArgument); break; + case KeyType_RecoveryOnly: SMC_R_UNLESS( IsRecoveryBoot(), InvalidArgument); break; + default: break; + } + + /* Declare temporary data storage. */ + u8 static_source[AesKeySize]; + u8 generated_key[AesKeySize]; + u8 access_key[AesKeySize]; + + /* Derive the static source. */ + for (size_t i = 0; i < sizeof(static_source); ++i) { + static_source[i] = KeyTypeSources[key_type][i] ^ SealKeyMasks[seal_key][i]; + } + + /* Get the seal key source. */ + const u8 * const seal_key_source = SealKeySources[seal_key]; + + /* Get the key slot. */ + const int slot = is_device_unique ? PrepareDeviceMasterKey(generation) : PrepareMasterKey(generation); + + /* Derive a static generation kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, slot, static_source, sizeof(static_source)); + + /* Decrypt the input with the static generation kek. */ + se::DecryptAes128(generated_key, sizeof(generated_key), pkg1::AesKeySlot_Smc, kek_source, sizeof(kek_source)); + + /* Generate the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, seal_key_source, AesKeySize); + + /* Seal the generated key. */ + se::EncryptAes128(access_key, sizeof(access_key), pkg1::AesKeySlot_Smc, generated_key, sizeof(generated_key)); + + /* Copy the access key out. */ + std::memcpy(std::addressof(args.r[1]), access_key, sizeof(access_key)); + return SmcResult::Success; + } + + SmcResult LoadAesKeyImpl(SmcArguments &args) { + /* Decode arguments. */ + const int slot = args.r[1]; + + u8 access_key[AesKeySize]; + std::memcpy(access_key, std::addressof(args.r[2]), sizeof(access_key)); + + u8 key_source[AesKeySize]; + std::memcpy(key_source, std::addressof(args.r[4]), sizeof(key_source)); + + /* Validate arguments. */ + SMC_R_UNLESS(pkg1::IsUserAesKeySlot(slot), InvalidArgument); + + /* Get the seal key source. */ + constexpr const u8 * const SealKeySource = SealKeySources[SealKey_LoadAesKey]; + + /* Derive the seal key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_RandomForUserWrap, SealKeySource, AesKeySize); + + /* Unseal the access key. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_Smc, pkg1::AesKeySlot_Smc, access_key, sizeof(access_key)); + + /* Derive the key. */ + se::SetEncryptedAesKey128(slot, pkg1::AesKeySlot_Smc, key_source, sizeof(key_source)); + + return SmcResult::Success; + } + + } + SmcResult SmcGenerateAesKek(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, GenerateAesKekImpl); } SmcResult SmcLoadAesKey(SmcArguments &args) { - /* TODO */ - return SmcResult::NotImplemented; + return LockSecurityEngineAndInvoke(args, LoadAesKeyImpl); } SmcResult SmcComputeAes(SmcArguments &args) { diff --git a/exosphere2/program/source/smc/secmon_smc_common.hpp b/exosphere2/program/source/smc/secmon_smc_common.hpp index 861acca26..a03658e61 100644 --- a/exosphere2/program/source/smc/secmon_smc_common.hpp +++ b/exosphere2/program/source/smc/secmon_smc_common.hpp @@ -35,6 +35,12 @@ namespace ams::secmon::smc { PsciAlreadyOn = static_cast(-4), }; + #define SMC_R_SUCCEEEDED(res) (res == SmcResult::Success) + #define SMC_R_FAILED(res) (res != SmcResult::Success) + + #define SMC_R_TRY(res_expr) ({ const auto _tmp_r_try_rc = (res_expr); if (SMC_R_FAILED(_tmp_r_try_rc)) { return _tmp_r_try_rc; } }) + #define SMC_R_UNLESS(cond, RES) ({ if (!(cond)) { return SmcResult::RES; }}) + struct SmcArguments { u64 r[8]; }; diff --git a/exosphere2/program/source/smc/secmon_smc_power_management.cpp b/exosphere2/program/source/smc/secmon_smc_power_management.cpp index 7ccd6ef77..92cbd3491 100644 --- a/exosphere2/program/source/smc/secmon_smc_power_management.cpp +++ b/exosphere2/program/source/smc/secmon_smc_power_management.cpp @@ -104,14 +104,10 @@ namespace ams::secmon::smc { SmcResult SmcPowerOnCpu(SmcArguments &args) { /* Get and validate the core to power on. */ const int which_core = args.r[1]; - if (!(0 <= which_core && which_core < NumCores)) { - return SmcResult::PsciInvalidParameters; - } + SMC_R_UNLESS(0 <= which_core && which_core < NumCores, PsciInvalidParameters); /* Ensure the core isn't already on. */ - if (IsCoreOn(which_core)) { - return SmcResult::PsciAlreadyOn; - } + SMC_R_UNLESS(!IsCoreOn(which_core), PsciAlreadyOn); /* Save the entry context. */ SetEntryContext(which_core, args.r[2], args.r[3]); diff --git a/exosphere2/program/source/smc/secmon_smc_random.cpp b/exosphere2/program/source/smc/secmon_smc_random.cpp index d057d27d5..6b3099a6d 100644 --- a/exosphere2/program/source/smc/secmon_smc_random.cpp +++ b/exosphere2/program/source/smc/secmon_smc_random.cpp @@ -26,10 +26,7 @@ namespace ams::secmon::smc { SmcResult GenerateRandomBytesImpl(SmcArguments &args) { /* Validate the input size. */ const size_t size = args.r[1]; - - if (size > MaxRandomBytes) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); /* Create a buffer that the se can generate bytes into. */ util::AlignedBuffer buffer; @@ -69,11 +66,7 @@ namespace ams::secmon::smc { } else { /* Otherwise, we'll retrieve some bytes from the cache. */ const size_t size = args.r[1]; - - /* Validate the input size. */ - if (size > MaxRandomBytes) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(size <= MaxRandomBytes, InvalidArgument); /* Get random bytes from the cache. */ GetRandomFromCache(std::addressof(args.r[1]), size); diff --git a/exosphere2/program/source/smc/secmon_smc_register_access.cpp b/exosphere2/program/source/smc/secmon_smc_register_access.cpp index b9e8b4d63..cdf834353 100644 --- a/exosphere2/program/source/smc/secmon_smc_register_access.cpp +++ b/exosphere2/program/source/smc/secmon_smc_register_access.cpp @@ -150,39 +150,34 @@ namespace ams::secmon::smc { const u32 value = args.r[3]; /* Validate that the address is aligned. */ - if (!util::IsAligned(address, alignof(u32))) { - return SmcResult::InvalidArgument; - } + SMC_R_UNLESS(util::IsAligned(address, alignof(u32)), InvalidArgument); /* Find the access table. */ const AccessTableEntry * const entry = GetAccessTableEntry(address); - /* If we have no table, don't perform the write. */ - if (entry == nullptr) { + /* If we have a table, perform the write. */ + if (entry != nullptr) { + /* Get the address to read or write. */ + const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); + u32 out = 0; + + if (mask != ~static_cast(0)) { + out = reg::Read(virtual_address); + } + if (mask != static_cast(0)) { + reg::Write(virtual_address, (out & ~mask) | (value & mask)); + } + + args.r[1] = out; + } else { /* For no clearly discernable reason, SmcReadWriteRegister returns success despite not doing the read/write */ /* when accessing the SMMU controls for the BPMP and for APB-DMA. */ /* This is "probably" to fuck with hackers who got access to the SMC and are trying to get control of the */ /* BPMP to exploit jamais vu, deja vu, or other related DMA/wake-from-sleep vulnerabilities. */ - constexpr uintptr_t MC = MemoryRegionVirtualDeviceMemoryController.GetAddress(); - if (address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)) { - return SmcResult::Success; - } - - return SmcResult::InvalidArgument; + constexpr uintptr_t MC = MemoryRegionPhysicalDeviceMemoryController.GetAddress(); + SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument); } - /* Get the address to read or write. */ - const uintptr_t virtual_address = entry->virtual_address + (address - entry->address); - u32 out = 0; - - if (mask != ~static_cast(0)) { - out = reg::Read(virtual_address); - } - if (mask != static_cast(0)) { - reg::Write(virtual_address, (out & ~mask) | (value & mask)); - } - - args.r[1] = out; return SmcResult::Success; } diff --git a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp index 63b662282..b8ce15ab2 100644 --- a/exosphere2/program/source/smc/secmon_smc_se_lock.cpp +++ b/exosphere2/program/source/smc/secmon_smc_se_lock.cpp @@ -39,40 +39,32 @@ namespace ams::secmon::smc { } SmcResult LockSecurityEngineAndInvoke(SmcArguments &args, SmcHandler impl) { - /* Try to lock the SE. */ - if (!TryLockSecurityEngine()) { - return SmcResult::Busy; - } + /* Try to lock the security engine. */ + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); ON_SCOPE_EXIT { UnlockSecurityEngine(); }; return impl(args); } SmcResult LockSecurityEngineAndInvokeAsync(SmcArguments &args, SmcHandler impl, GetResultHandler result_handler) { - SmcResult result = SmcResult::Busy; - /* Try to lock the security engine. */ - if (TryLockSecurityEngine()) { - /* Try to start an async operation. */ - if (const u64 async_key = BeginAsyncOperation(result_handler); async_key != InvalidAsyncKey) { - /* Invoke the operation. */ - result = impl(args); + SMC_R_UNLESS(TryLockSecurityEngine(), Busy); + auto se_guard = SCOPE_GUARD { UnlockSecurityEngine(); }; - /* If the operation was successful, return the key. */ - if (result == SmcResult::Success) { - args.r[1] = async_key; - return SmcResult::Success; - } + /* Try to start an async operation. */ + const u64 async_key = BeginAsyncOperation(result_handler); + SMC_R_UNLESS(async_key != InvalidAsyncKey, Busy); + auto async_guard = SCOPE_GUARD { CancelAsyncOperation(async_key); }; - /* Otherwise, cancel the async operation. */ - CancelAsyncOperation(async_key); - } + /* Try to invoke the operation. */ + SMC_R_TRY(impl(args)); - /* We failed to invoke the async op, so unlock the security engine. */ - UnlockSecurityEngine(); - } + /* We succeeded! Cancel our guards, and return the async key to our caller. */ + async_guard.Cancel(); + se_guard.Cancel(); - return result; + args.r[1] = async_key; + return SmcResult::Success; } } diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp index 711f36ba4..5e58876f1 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_key_generation.hpp @@ -44,4 +44,12 @@ namespace ams::pkg1 { constexpr inline const int OldMasterKeyCount = KeyGeneration_Count - 1; constexpr inline const int OldDeviceMasterKeyCount = KeyGeneration_Count - KeyGeneration_4_0_0; + constexpr bool IsValidDeviceUniqueKeyGeneration(int generation) { + return generation == KeyGeneration_1_0_0 || (KeyGeneration_4_0_0 <= generation && generation <= KeyGeneration_Current); + } + + constexpr bool IsValidKeyGeneration(int generation) { + return KeyGeneration_Min <= generation && generation <= KeyGeneration_Current; + } + } diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp index 614aa5a38..e7c7074f8 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -53,4 +53,8 @@ namespace ams::pkg1 { RsaKeySlot_PrivateKey = 1, }; + constexpr bool IsUserAesKeySlot(int slot) { + return AesKeySlot_UserStart <= slot && slot < AesKeySlot_UserEnd; + } + }