mirror of
https://github.com/Atmosphere-NX/Atmosphere
synced 2024-12-22 20:31:14 +00:00
exo2: implement SmcGenerateAesKek, SmcLoadAesKey
This commit is contained in:
parent
864b6085a8
commit
bf546d5fb3
8 changed files with 239 additions and 65 deletions
|
@ -15,18 +15,198 @@
|
|||
*/
|
||||
#include <exosphere.hpp>
|
||||
#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<int>(args.r[3] - 1, pkg1::KeyGeneration_1_0_0);
|
||||
|
||||
const util::BitPack32 option = { static_cast<u32>(args.r[4]) };
|
||||
const bool is_device_unique = option.Get<GenerateAesKekOption::IsDeviceUnique>();
|
||||
const auto key_type = option.Get<GenerateAesKekOption::KeyTypeIndex>();
|
||||
const auto seal_key = option.Get<GenerateAesKekOption::SealKeyIndex>();
|
||||
const u32 reserved = option.Get<GenerateAesKekOption::Reserved>();
|
||||
|
||||
/* 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) {
|
||||
|
|
|
@ -35,6 +35,12 @@ namespace ams::secmon::smc {
|
|||
PsciAlreadyOn = static_cast<u32>(-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];
|
||||
};
|
||||
|
|
|
@ -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]);
|
||||
|
|
|
@ -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<hw::DataCacheLineSize, MaxRandomBytes> 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);
|
||||
|
|
|
@ -150,27 +150,13 @@ 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) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
@ -183,6 +169,15 @@ namespace ams::secmon::smc {
|
|||
}
|
||||
|
||||
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 = MemoryRegionPhysicalDeviceMemoryController.GetAddress();
|
||||
SMC_R_UNLESS((address == (MC + MC_SMMU_AVPC_ASID) || address == (MC + MC_SMMU_PPCS1_ASID)), InvalidArgument);
|
||||
}
|
||||
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
|
|
|
@ -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(); };
|
||||
|
||||
/* 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); };
|
||||
|
||||
/* Try to invoke the operation. */
|
||||
SMC_R_TRY(impl(args));
|
||||
|
||||
/* We succeeded! Cancel our guards, and return the async key to our caller. */
|
||||
async_guard.Cancel();
|
||||
se_guard.Cancel();
|
||||
|
||||
/* If the operation was successful, return the key. */
|
||||
if (result == SmcResult::Success) {
|
||||
args.r[1] = async_key;
|
||||
return SmcResult::Success;
|
||||
}
|
||||
|
||||
/* Otherwise, cancel the async operation. */
|
||||
CancelAsyncOperation(async_key);
|
||||
}
|
||||
|
||||
/* We failed to invoke the async op, so unlock the security engine. */
|
||||
UnlockSecurityEngine();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -53,4 +53,8 @@ namespace ams::pkg1 {
|
|||
RsaKeySlot_PrivateKey = 1,
|
||||
};
|
||||
|
||||
constexpr bool IsUserAesKeySlot(int slot) {
|
||||
return AesKeySlot_UserStart <= slot && slot < AesKeySlot_UserEnd;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue