diff --git a/exosphere/program/source/boot/secmon_boot_setup.cpp b/exosphere/program/source/boot/secmon_boot_setup.cpp index 11237594b..7b8511c3a 100644 --- a/exosphere/program/source/boot/secmon_boot_setup.cpp +++ b/exosphere/program/source/boot/secmon_boot_setup.cpp @@ -68,12 +68,56 @@ namespace ams::secmon::boot { /* to the warmboot key? To be decided during the process of implementing ams-on-mariko support. */ } + constinit const u8 DeviceMasterKeySourceKekSource[se::AesBlockSize] = { + 0x0C, 0x91, 0x09, 0xDB, 0x93, 0x93, 0x07, 0x81, 0x07, 0x3C, 0xC4, 0x16, 0x22, 0x7C, 0x6C, 0x28 + }; + /* This function derives the master kek and device keys using the tsec root key. */ - /* NOTE: Exosphere does not use this in practice, and expects the bootloader to set up keys already. */ - /* NOTE: This function is currently not implemented. If implemented, it will only be a reference implementation. */ - [[maybe_unused]] - void DeriveMasterKekAndDeviceKey() { - /* TODO: Decide whether to implement this. */ + void DeriveMasterKekAndDeviceKeyErista(bool is_prod) { + /* NOTE: Exosphere does not use this in practice, and expects the bootloader to set up keys already. */ + /* NOTE: This function is currently not implemented. If implemented, it will only be a reference implementation. */ + if constexpr (false) { + /* TODO: Consider implementing this as a reference. */ + } + } + + constinit const u8 MarikoMasterKekSourceProd[se::AesBlockSize] = { + 0x0E, 0x44, 0x0C, 0xED, 0xB4, 0x36, 0xC0, 0x3F, 0xAA, 0x1D, 0xAE, 0xBF, 0x62, 0xB1, 0x09, 0x82 + }; + + constinit const u8 MarikoMasterKekSourceDev[se::AesBlockSize] = { + 0xF9, 0x37, 0xCF, 0x9A, 0xBD, 0x86, 0xBB, 0xA9, 0x9C, 0x9E, 0x03, 0xC4, 0xFC, 0xBC, 0x3B, 0xCE + }; + + void DeriveMasterKekAndDeviceKeyMariko(bool is_prod) { + /* Clear all keyslots other than KEK and SBK in SE1. */ + for (int i = 0; i < pkg1::AesKeySlot_Count; ++i) { + if (i != pkg1::AesKeySlot_MarikoKek && i != pkg1::AesKeySlot_SecureBoot) { + se::ClearAesKeySlot(i); + } + } + + /* Clear all keyslots in SE2. */ + for (int i = 0; i < pkg1::AesKeySlot_Count; ++i) { + se::ClearAesKeySlot2(i); + } + + /* Derive the master kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_MasterKek, pkg1::AesKeySlot_MarikoKek, is_prod ? MarikoMasterKekSourceProd : MarikoMasterKekSourceDev, se::AesBlockSize); + + /* Derive the device master key source kek. */ + se::SetEncryptedAesKey128(pkg1::AesKeySlot_DeviceMasterKeySourceKekMariko, pkg1::AesKeySlot_SecureBoot, DeviceMasterKeySourceKekSource, se::AesBlockSize); + + /* Clear the KEK, now that we're done using it. */ + se::ClearAesKeySlot(pkg1::AesKeySlot_MarikoKek); + } + + void DeriveMasterKekAndDeviceKey(bool is_prod) { + if (GetSocType() == fuse::SocType_Mariko) { + DeriveMasterKekAndDeviceKeyMariko(is_prod); + } else /* if (GetSocType() == fuse::SocType_Erista) */ { + DeriveMasterKekAndDeviceKeyErista(is_prod); + } } void SetupRandomKey(int slot, se::KeySlotLockFlags flags) { @@ -219,7 +263,7 @@ namespace ams::secmon::boot { const int current_generation = secmon::GetKeyGeneration(); /* Get the kek slot. */ - const int kek_slot = fuse::GetSocType() == fuse::SocType_Mariko ? pkg1::AesKeySlot_DeviceMasterKeySourceKekMariko : pkg1::AesKeySlot_DeviceMasterKeySourceKekErista; + const int kek_slot = GetSocType() == fuse::SocType_Mariko ? pkg1::AesKeySlot_DeviceMasterKeySourceKekMariko : pkg1::AesKeySlot_DeviceMasterKeySourceKekErista; /* Iterate for all generations. */ for (int i = 0; i < pkg1::OldDeviceMasterKeyCount; ++i) { @@ -251,10 +295,7 @@ namespace ams::secmon::boot { se::LockAesKeySlot(pkg1::AesKeySlot_DeviceMasterKeySourceKekMariko, se::KeySlotLockFlags_AllLockKek); } - void DeriveAllKeys() { - /* Determine whether we're prod. */ - const bool is_prod = IsProduction(); - + void DeriveAllKeys(bool is_prod) { /* Get the ephemeral work block. */ u8 * const work_block = se::GetEphemeralWorkBlock(); ON_SCOPE_EXIT { util::ClearMemory(work_block, se::AesBlockSize); }; @@ -303,16 +344,18 @@ namespace ams::secmon::boot { /* Initialize the rng. */ se::InitializeRandom(); + /* Determine whether we're production. */ + const bool is_prod = IsProduction(); + /* Derive the master kek and device key. */ - if constexpr (false) { - DeriveMasterKekAndDeviceKey(); - } + /* NOTE: This is a no-op on erista, because fusee will have set up keys. */ + DeriveMasterKekAndDeviceKey(is_prod); /* Lock the device key as only usable as a kek. */ se::LockAesKeySlot(pkg1::AesKeySlot_Device, se::KeySlotLockFlags_AllLockKek); /* Derive all keys. */ - DeriveAllKeys(); + DeriveAllKeys(is_prod); } } @@ -351,6 +394,9 @@ namespace ams::secmon::boot { /* Set the security engine to Per Key Secure. */ se::SetPerKeySecure(); + /* Set the security engine to Context Save Secure. */ + se::SetContextSaveSecure(); + /* Setup the PMC registers. */ SetupPmcRegisters(); diff --git a/exosphere/program/source/smc/secmon_smc_aes.cpp b/exosphere/program/source/smc/secmon_smc_aes.cpp index 60912e78a..158f249ae 100644 --- a/exosphere/program/source/smc/secmon_smc_aes.cpp +++ b/exosphere/program/source/smc/secmon_smc_aes.cpp @@ -257,7 +257,7 @@ namespace ams::secmon::smc { } int PrepareDeviceMasterKey(int generation) { - if (generation == pkg1::KeyGeneration_1_0_0) { + if (generation == pkg1::KeyGeneration_1_0_0 && GetSocType() == fuse::SocType_Erista) { return pkg1::AesKeySlot_Device; } if (generation == GetKeyGeneration()) { 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 260722d8d..659691397 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_se_key_slots.hpp @@ -37,7 +37,8 @@ namespace ams::pkg1 { AesKeySlot_Master = 13, AesKeySlot_Device = 15, - AesKeySlot_SecmonEnd = 16, + AesKeySlot_Count = 16, + AesKeySlot_SecmonEnd = AesKeySlot_Count, /* Used only during boot. */ AesKeySlot_Tsec = 12, @@ -48,6 +49,10 @@ namespace ams::pkg1 { AesKeySlot_DeviceMasterKeySourceKekErista = 10, AesKeySlot_MasterKek = 13, AesKeySlot_DeviceMasterKeySourceKekMariko = 14, + + /* Mariko only keyslots, used during boot. */ + AesKeySlot_MarikoKek = 12, + AesKeySlot_MarikoBek = 13, }; enum RsaKeySlot { diff --git a/libraries/libexosphere/include/exosphere/se/se_aes.hpp b/libraries/libexosphere/include/exosphere/se/se_aes.hpp index d3f18d20c..b5a828648 100644 --- a/libraries/libexosphere/include/exosphere/se/se_aes.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_aes.hpp @@ -26,6 +26,9 @@ namespace ams::se { void ClearAesKeyIv(int slot); void LockAesKeySlot(int slot, u32 flags); + /* NOTE: This is Nintendo's API, but if we actually want to use SE2 we should use a different one. */ + void ClearAesKeySlot2(int slot); + void SetAesKey(int slot, const void *key, size_t key_size); void SetEncryptedAesKey128(int dst_slot, int kek_slot, const void *key, size_t key_size); diff --git a/libraries/libexosphere/include/exosphere/se/se_management.hpp b/libraries/libexosphere/include/exosphere/se/se_management.hpp index 2c57646bd..843de190c 100644 --- a/libraries/libexosphere/include/exosphere/se/se_management.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_management.hpp @@ -25,6 +25,7 @@ namespace ams::se { void SetSecure(bool secure); void SetTzramSecure(); void SetPerKeySecure(); + void SetContextSaveSecure(); void Lockout(); diff --git a/libraries/libexosphere/source/se/se_aes.cpp b/libraries/libexosphere/source/se/se_aes.cpp index 00105f71d..f5950552c 100644 --- a/libraries/libexosphere/source/se/se_aes.cpp +++ b/libraries/libexosphere/source/se/se_aes.cpp @@ -362,22 +362,29 @@ namespace ams::se { StartOperationRaw(SE, SE_OPERATION_OP_START, out_ll_address, in_ll_address); } + void ClearAesKeySlot(volatile SecurityEngineRegisters *SE, int slot) { + /* Validate the key slot. */ + AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + + for (int i = 0; i < 16; ++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_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, i)); + + /* Write the data. */ + SE->SE_CRYPTO_KEYTABLE_DATA = 0; + } + } + } void ClearAesKeySlot(int slot) { - /* Validate the key slot. */ - AMS_ABORT_UNLESS(0 <= slot && slot < AesKeySlotCount); + /* Clear the slot in SE1. */ + ClearAesKeySlot(GetRegisters(), slot); + } - /* Get the engine. */ - auto *SE = GetRegisters(); - - for (int i = 0; i < 16; ++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_VALUE(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, i)); - - /* Write the data. */ - SE->SE_CRYPTO_KEYTABLE_DATA = 0; - } + void ClearAesKeySlot2(int slot) { + /* Clear the slot in SE2. */ + ClearAesKeySlot(GetRegisters2(), slot); } void ClearAesKeyIv(int slot) { diff --git a/libraries/libexosphere/source/se/se_management.cpp b/libraries/libexosphere/source/se/se_management.cpp index 7b4bfe37b..fe5c20de4 100644 --- a/libraries/libexosphere/source/se/se_management.cpp +++ b/libraries/libexosphere/source/se/se_management.cpp @@ -24,6 +24,18 @@ namespace ams::se { constinit uintptr_t g_register2_address = secmon::MemoryRegionPhysicalDeviceSecurityEngine2.GetAddress(); constinit DoneHandler g_done_handler = nullptr; + void SetSecure(volatile SecurityEngineRegisters *SE, bool secure) { + /* Set the security software setting. */ + if (secure) { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); + } else { + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, NONSECURE)); + } + + /* Read the status register to force an update. */ + reg::Read(SE->SE_SE_SECURITY); + } + } volatile SecurityEngineRegisters *GetRegisters() { @@ -45,17 +57,13 @@ namespace ams::se { } void SetSecure(bool secure) { - auto *SE = GetRegisters(); + /* Set security for SE1. */ + SetSecure(GetRegisters(), secure); - /* Set the security software setting. */ - if (secure) { - reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, SECURE)); - } else { - reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_SOFT_SETTING, NONSECURE)); + /* If SE2 is present, set security for SE2. */ + if (fuse::GetSocType() == fuse::SocType_Mariko) { + SetSecure(GetRegisters2(), secure); } - - /* Read the status register to force an update. */ - reg::Read(SE->SE_SE_SECURITY); } void SetTzramSecure() { @@ -72,6 +80,18 @@ namespace ams::se { reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_PERKEY_SETTING, SECURE)); } + + void SetContextSaveSecure() { + /* Context save lock to trustzone secure is only available on mariko. */ + if (fuse::GetSocType() == fuse::SocType_Mariko) { + auto *SE = GetRegisters(); + auto *SE2 = GetRegisters2(); + + reg::ReadWrite(SE->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_CTX_SAVE_TZ_LOCK, SECURE)); + reg::ReadWrite(SE2->SE_SE_SECURITY, SE_REG_BITS_ENUM(SECURITY_CTX_SAVE_TZ_LOCK, SECURE)); + } + } + void Lockout() { auto *SE = GetRegisters(); diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp index 5cffa954f..189767aec 100644 --- a/libraries/libexosphere/source/se/se_registers.hpp +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -106,10 +106,12 @@ namespace ams::se { DEFINE_SE_REG_BIT_ENUM(STATUS_MEM_INTERFACE, 2, IDLE, BUSY); /* SE_SECURITY */ - DEFINE_SE_REG_BIT_ENUM(SECURITY_HARD_SETTING, 0, SECURE, NONSECURE); - DEFINE_SE_REG_BIT_ENUM(SECURITY_ENG_DIS, 1, DISABLE, ENABLE); - DEFINE_SE_REG_BIT_ENUM(SECURITY_PERKEY_SETTING, 2, SECURE, NONSECURE); - DEFINE_SE_REG_BIT_ENUM(SECURITY_SOFT_SETTING, 16, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_HARD_SETTING, 0, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_ENG_DIS, 1, DISABLE, ENABLE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_PERKEY_SETTING, 2, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_CTX_SAVE_TZ_LOCK, 4, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_CTX_TZ_LOCK_SOFT, 5, SECURE, NONSECURE); + DEFINE_SE_REG_BIT_ENUM(SECURITY_SOFT_SETTING, 16, SECURE, NONSECURE); /* SE_TZRAM_SECURITY */ DEFINE_SE_REG(TZRAM_SETTING, 0, BITSIZEOF(u32)); diff --git a/libraries/libexosphere/source/se/se_rng.cpp b/libraries/libexosphere/source/se/se_rng.cpp index 4cb099bde..70eea576e 100644 --- a/libraries/libexosphere/source/se/se_rng.cpp +++ b/libraries/libexosphere/source/se/se_rng.cpp @@ -44,31 +44,50 @@ namespace ams::se { reg::Write(SE->SE_RNG_CONFIG, SE_REG_BITS_ENUM(RNG_CONFIG_SRC, ENTROPY), SE_REG_BITS_VALUE(RNG_CONFIG_MODE, mode)); } - } + void InitializeRandom(volatile SecurityEngineRegisters *SE) { + /* Lock the entropy source. */ + reg::Write(SE->SE_RNG_SRC_CONFIG, SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, ENABLE), + SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, ENABLE)); - void InitializeRandom() { - /* Get the engine. */ - auto *SE = GetRegisters(); + /* Set the reseed interval to force a reseed every 70000 blocks. */ + SE->SE_RNG_RESEED_INTERVAL = RngReseedInterval; - /* Lock the entropy source. */ - reg::Write(SE->SE_RNG_SRC_CONFIG, SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE, ENABLE), - SE_REG_BITS_ENUM(RNG_SRC_CONFIG_RO_ENTROPY_SOURCE_LOCK, ENABLE)); + /* Initialize the DRBG. */ + { + u8 dummy_buf[AesBlockSize]; - /* Set the reseed interval to force a reseed every 70000 blocks. */ - SE->SE_RNG_RESEED_INTERVAL = RngReseedInterval; + /* Configure the engine to force drbg instantiation by writing random to memory. */ + ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_FORCE_INSTANTIATION); - /* Initialize the DRBG. */ - { - u8 dummy_buf[AesBlockSize]; + /* Configure to do a single RNG block operation to trigger DRBG init. */ + SE->SE_CRYPTO_LAST_BLOCK = 0; - /* Configure the engine to force drbg instantiation by writing random to memory. */ - ConfigRng(SE, SE_CONFIG_DST_MEMORY, SE_RNG_CONFIG_MODE_FORCE_INSTANTIATION); + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, dummy_buf, sizeof(dummy_buf), nullptr, 0); + } + } - /* Configure to do a single RNG block operation to trigger DRBG init. */ + void GenerateSrk(volatile SecurityEngineRegisters *SE) { + /* Configure the RNG to output to SRK and force a reseed. */ + ConfigRng(SE, SE_CONFIG_DST_SRK, SE_RNG_CONFIG_MODE_FORCE_RESEED); + + /* Configure a single block operation. */ SE->SE_CRYPTO_LAST_BLOCK = 0; /* Execute the operation. */ - ExecuteOperation(SE, SE_OPERATION_OP_START, dummy_buf, sizeof(dummy_buf), nullptr, 0); + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + } + + } + + void InitializeRandom() { + /* Initialize random for SE1. */ + InitializeRandom(GetRegisters()); + + /* If we have SE2, initialize random for SE2. */ + /* NOTE: Nintendo's implementation of this is incorrect. */ + if (fuse::GetSocType() == fuse::SocType_Mariko) { + InitializeRandom(GetRegisters2()); } } @@ -130,17 +149,14 @@ namespace ams::se { } void GenerateSrk() { - /* Get the engine. */ - auto *SE = GetRegisters(); + /* Generate SRK for SE1. */ + GenerateSrk(GetRegisters()); - /* Configure the RNG to output to SRK and force a reseed. */ - ConfigRng(SE, SE_CONFIG_DST_SRK, SE_RNG_CONFIG_MODE_FORCE_RESEED); - - /* Configure a single block operation. */ - SE->SE_CRYPTO_LAST_BLOCK = 0; - - /* Execute the operation. */ - ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, nullptr, 0); + /* If we have SE2, generate SRK for SE2. */ + /* NOTE: Nintendo's implementation of this is incorrect. */ + if (fuse::GetSocType() == fuse::SocType_Mariko) { + GenerateSrk(GetRegisters2()); + } } }