From e11fad6598c8676432d2d6b5dc4e33510cbd39ab Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Tue, 12 May 2020 00:32:09 -0700 Subject: [PATCH] exo2: implement through boot config load/validate --- .../program/source/boot/secmon_boot.hpp | 6 + .../source/boot/secmon_boot_config.cpp | 16 +- .../source/boot/secmon_boot_functions.cpp | 81 +++++++++ .../source/boot/secmon_boot_functions.hpp | 7 + .../program/source/boot/secmon_boot_rsa.cpp | 159 ++++++++++++++++++ .../program/source/boot/secmon_main.cpp | 22 ++- libraries/libexosphere/include/exosphere.hpp | 1 + .../libexosphere/include/exosphere/br.hpp | 19 +++ .../include/exosphere/br/br_types.hpp | 30 ++++ .../exosphere/br/impl/br_erista_types.hpp | 21 +++ .../exosphere/br/impl/br_mariko_types.hpp | 21 +++ .../libexosphere/include/exosphere/fuse.hpp | 2 + .../libexosphere/include/exosphere/pkg1.hpp | 1 + .../include/exosphere/pkg1/pkg1_api.hpp | 25 +++ .../exosphere/pkg1/pkg1_boot_config.hpp | 6 +- .../libexosphere/include/exosphere/se.hpp | 1 + .../include/exosphere/se/se_hash.hpp | 30 ++++ .../include/exosphere/se/se_rsa.hpp | 2 + ...ecmon_configuration_context.arch.arm64.hpp | 4 + .../exosphere/secmon/secmon_memory_layout.hpp | 8 +- .../include/exosphere/tegra/tegra_sysctr0.hpp | 19 ++- .../libexosphere/source/fuse/fuse_api.cpp | 26 ++- .../libexosphere/source/pkg1/pkg1_api.cpp | 40 +++++ libraries/libexosphere/source/se/se_hash.cpp | 72 ++++++++ .../libexosphere/source/se/se_registers.hpp | 42 ++--- libraries/libexosphere/source/se/se_rsa.cpp | 76 +++++++-- 26 files changed, 688 insertions(+), 49 deletions(-) create mode 100644 exosphere2/program/source/boot/secmon_boot_rsa.cpp create mode 100644 libraries/libexosphere/include/exosphere/br.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/br_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp create mode 100644 libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp create mode 100644 libraries/libexosphere/include/exosphere/se/se_hash.hpp create mode 100644 libraries/libexosphere/source/pkg1/pkg1_api.cpp create mode 100644 libraries/libexosphere/source/se/se_hash.cpp diff --git a/exosphere2/program/source/boot/secmon_boot.hpp b/exosphere2/program/source/boot/secmon_boot.hpp index dca6d0091..d82c67cac 100644 --- a/exosphere2/program/source/boot/secmon_boot.hpp +++ b/exosphere2/program/source/boot/secmon_boot.hpp @@ -22,4 +22,10 @@ namespace ams::secmon::boot { void InitializeColdBoot(); + bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size); + bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size); + + bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size); + bool VerifyBootConfigEcid(const pkg1::BootConfig &bc); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_config.cpp b/exosphere2/program/source/boot/secmon_boot_config.cpp index ad0649c3e..c4448624a 100644 --- a/exosphere2/program/source/boot/secmon_boot_config.cpp +++ b/exosphere2/program/source/boot/secmon_boot_config.cpp @@ -14,9 +14,21 @@ * along with this program. If not, see . */ #include +#include "secmon_boot.hpp" namespace ams::secmon::boot { - /* TODO */ + bool VerifyBootConfigSignature(pkg1::BootConfig &bc, const void *mod, size_t mod_size) { + return VerifySignature(std::addressof(bc.signature), sizeof(bc.signature), mod, mod_size, std::addressof(bc.signed_data), sizeof(bc.signed_data)); + } -} \ No newline at end of file + bool VerifyBootConfigEcid(const pkg1::BootConfig &bc) { + /* Get the ecid. */ + br::BootEcid ecid; + fuse::GetEcid(std::addressof(ecid)); + + /* Verify it matches. */ + return crypto::IsSameBytes(std::addressof(ecid), bc.signed_data.ecid, sizeof(ecid)); + } + +} diff --git a/exosphere2/program/source/boot/secmon_boot_functions.cpp b/exosphere2/program/source/boot/secmon_boot_functions.cpp index 3095f2f77..a9488e44e 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.cpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.cpp @@ -14,13 +14,94 @@ * along with this program. If not, see . */ #include +#include "secmon_boot.hpp" #include "secmon_boot_functions.hpp" namespace ams::secmon::boot { + namespace { + + constexpr inline uintptr_t SYSCTR0 = MemoryRegionVirtualDeviceSysCtr0.GetAddress(); + + constinit const u8 BootConfigRsaPublicModulus[se::RsaSize] = { + 0xB5, 0x96, 0x87, 0x31, 0x39, 0xAA, 0xBB, 0x3C, 0x28, 0xF3, 0xF0, 0x65, 0xF1, 0x50, 0x70, 0x64, + 0xE6, 0x6C, 0x97, 0x50, 0xCD, 0xA6, 0xEE, 0xEA, 0xC3, 0x8F, 0xE6, 0xB5, 0x81, 0x54, 0x65, 0x33, + 0x1B, 0x88, 0x4B, 0xCE, 0x9F, 0x53, 0xDF, 0xE4, 0xF6, 0xAD, 0xC3, 0x78, 0xD7, 0x3C, 0xD1, 0xDB, + 0x27, 0x21, 0xA0, 0x24, 0x30, 0x2D, 0x98, 0x41, 0xA8, 0xDF, 0x50, 0x7D, 0xAB, 0xCE, 0x00, 0xD9, + 0xCB, 0xAC, 0x8F, 0x37, 0xF5, 0x53, 0xE4, 0x97, 0x1F, 0x13, 0x3C, 0x19, 0xFF, 0x05, 0xA7, 0x3B, + 0xF6, 0xF4, 0x01, 0xDE, 0xF0, 0xC3, 0x77, 0x7B, 0x83, 0xBA, 0xAF, 0x99, 0x30, 0x94, 0x87, 0x25, + 0x4E, 0x54, 0x42, 0x3F, 0xAC, 0x27, 0xF9, 0xCC, 0x87, 0xDD, 0xAE, 0xF2, 0x54, 0xF3, 0x97, 0x49, + 0xF4, 0xB0, 0xF8, 0x6D, 0xDA, 0x60, 0xE0, 0xFD, 0x57, 0xAE, 0x55, 0xA9, 0x76, 0xEA, 0x80, 0x24, + 0xA0, 0x04, 0x7D, 0xBE, 0xD1, 0x81, 0xD3, 0x0C, 0x95, 0xCF, 0xB7, 0xE0, 0x2D, 0x21, 0x21, 0xFF, + 0x97, 0x1E, 0xB3, 0xD7, 0x9F, 0xBB, 0x33, 0x0C, 0x23, 0xC5, 0x88, 0x4A, 0x33, 0xB9, 0xC9, 0x4E, + 0x1E, 0x65, 0x51, 0x45, 0xDE, 0xF9, 0x64, 0x7C, 0xF0, 0xBF, 0x11, 0xB4, 0x93, 0x8D, 0x5D, 0xC6, + 0xAB, 0x37, 0x9E, 0xE9, 0x39, 0xC1, 0xC8, 0xDB, 0xB9, 0xFE, 0x45, 0xCE, 0x7B, 0xDD, 0x72, 0xD9, + 0x6F, 0x68, 0x13, 0xC0, 0x4B, 0xBA, 0x00, 0xF4, 0x1E, 0x89, 0x71, 0x91, 0x26, 0xA6, 0x46, 0x12, + 0xDF, 0x29, 0x6B, 0xC2, 0x5A, 0x53, 0xAF, 0xB9, 0x5B, 0xFD, 0x13, 0x9F, 0xD1, 0x8A, 0x7C, 0xB5, + 0x04, 0xFD, 0x69, 0xEA, 0x23, 0xB4, 0x6D, 0x16, 0x21, 0x98, 0x54, 0xB4, 0xDF, 0xE6, 0xAB, 0x93, + 0x36, 0xB6, 0xD2, 0x43, 0xCF, 0x2B, 0x98, 0x1D, 0x45, 0xC9, 0xBB, 0x20, 0x42, 0xB1, 0x9D, 0x1D + }; + + } + void ClearIram() { /* Clear the boot code image from where it was loaded in IRAM. */ util::ClearMemory(MemoryRegionPhysicalIramBootCodeImage.GetPointer(), MemoryRegionPhysicalIramBootCodeImage.GetSize()); } + void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state) { + /* Check NX Bootloader's state once per microsecond until it's advanced enough. */ + while (params.bootloader_state < state) { + util::WaitMicroSeconds(1); + } + } + + void LoadBootConfig(const void *src) { + pkg1::BootConfig * const dst = secmon::impl::GetBootConfigStorage(); + + if (pkg1::IsProduction()) { + std::memset(dst, 0, sizeof(*dst)); + } else { + hw::FlushDataCache(src, sizeof(*dst)); + hw::DataSynchronizationBarrierInnerShareable(); + std::memcpy(dst, src, sizeof(*dst)); + } + } + + void VerifyOrClearBootConfig() { + /* On production hardware, the boot config is already cleared. */ + if (pkg1::IsProduction()) { + return; + } + + pkg1::BootConfig * const bc = secmon::impl::GetBootConfigStorage(); + + /* Determine if the bc is valid for the device. */ + bool valid_for_device = false; + { + const bool valid_signature = secmon::boot::VerifyBootConfigSignature(*bc, BootConfigRsaPublicModulus, util::size(BootConfigRsaPublicModulus)); + if (valid_signature) { + valid_for_device = secmon::boot::VerifyBootConfigEcid(*bc); + } + } + + /* If the boot config is not valid for the device, clear its signed data. */ + if (!valid_for_device) { + util::ClearMemory(std::addressof(bc->signed_data), sizeof(bc->signed_data)); + } + } + + void EnableTsc(u64 initial_tsc_value) { + /* Write the initial value to the CNTCV registers. */ + const u32 lo = static_cast(initial_tsc_value >> 0); + const u32 hi = static_cast(initial_tsc_value >> 32); + + reg::Write(SYSCTR0 + SYSCTR0_CNTCV0, lo); + reg::Write(SYSCTR0 + SYSCTR0_CNTCV1, hi); + + /* Configure the system counter control register. */ + reg::Write(SYSCTR0 + SYSCTR0_CNTCR, SYSCTR0_REG_BITS_ENUM(CNTCR_HDBG, ENABLE), + SYSCTR0_REG_BITS_ENUM(CNTCR_EN, ENABLE)); + } + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_functions.hpp b/exosphere2/program/source/boot/secmon_boot_functions.hpp index 5ec201add..83ac091db 100644 --- a/exosphere2/program/source/boot/secmon_boot_functions.hpp +++ b/exosphere2/program/source/boot/secmon_boot_functions.hpp @@ -20,4 +20,11 @@ namespace ams::secmon::boot { void ClearIram(); + void WaitForNxBootloader(const pkg1::SecureMonitorParameters ¶ms, pkg1::BootloaderState state); + + void LoadBootConfig(const void *src); + void VerifyOrClearBootConfig(); + + void EnableTsc(u64 initial_tsc_value); + } \ No newline at end of file diff --git a/exosphere2/program/source/boot/secmon_boot_rsa.cpp b/exosphere2/program/source/boot/secmon_boot_rsa.cpp new file mode 100644 index 000000000..344196c05 --- /dev/null +++ b/exosphere2/program/source/boot/secmon_boot_rsa.cpp @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "secmon_boot.hpp" + +namespace ams::secmon::boot { + + namespace { + + constinit const u8 RsaPublicKeyExponent[] = { + 0x00, 0x01, 0x00, 0x01, + }; + + constexpr inline u8 TailMagic = 0xBC; + + bool VerifyRsaPssSha256(const u8 *sig, const void *msg, size_t msg_size) { + /* Define constants. */ + constexpr int EmBits = 2047; + constexpr int EmLen = util::DivideUp(EmBits, BITSIZEOF(u8)); + constexpr int SaltLen = 0x20; + constexpr int HashLen = se::Sha256HashSize; + + /* Define a work buffer. */ + u8 work[EmLen]; + ON_SCOPE_EXIT { util::ClearMemory(work, sizeof(work)); }; + + /* Calculate the message hash, first flushing cache to ensure SE sees correct data. */ + se::Sha256Hash msg_hash; + hw::FlushDataCache(msg, msg_size); + hw::DataSynchronizationBarrierInnerShareable(); + se::CalculateSha256(std::addressof(msg_hash), msg, msg_size); + + /* Verify the tail magic. */ + bool is_valid = sig[EmLen - 1] == TailMagic; + + /* Determine extents of masked db and h. */ + const u8 *masked_db = std::addressof(sig[0]); + const u8 *h = std::addressof(sig[EmLen - HashLen - 1]); + + /* Verify the extra bits are zero. */ + is_valid &= (masked_db[0] >> (BITSIZEOF(u8) - (BITSIZEOF(u8) * EmLen - EmBits))) == 0; + + /* Calculate the db mask. */ + { + constexpr int MaskLen = EmLen - HashLen - 1; + constexpr int HashIters = util::DivideUp(MaskLen, HashLen); + + u8 mgf1_buf[sizeof(u32) + HashLen]; + + std::memcpy(std::addressof(mgf1_buf[0]), h, HashLen); + std::memset(std::addressof(mgf1_buf[HashLen]), 0, sizeof(u32)); + + for (int i = 0; i < HashIters; ++i) { + /* Set the counter for this iteration. */ + mgf1_buf[sizeof(mgf1_buf) - 1] = i; + + /* Calculate the sha256 to the appropriate place in the work buffer. */ + auto *mgf1_dst = reinterpret_cast(std::addressof(work[HashLen * i])); + + hw::FlushDataCache(mgf1_buf, sizeof(mgf1_buf)); + hw::DataSynchronizationBarrierInnerShareable(); + se::CalculateSha256(mgf1_dst, mgf1_buf, sizeof(mgf1_buf)); + } + } + + /* Decrypt masked db using the mask we just generated. */ + for (int i = 0; i < EmLen - HashLen - 1; ++i) { + work[i] ^= masked_db[i]; + } + + /* Mask out the top bits. */ + u8 *db = work; + db[0] &= 0xFF >> (BITSIZEOF(u8) * EmLen - EmBits); + + /* Verify that DB is of the form 0000...0001 */ + constexpr int DbLen = EmLen - HashLen - 1; + int salt_ofs = 0; + { + int looking_for_one = 1; + int invalid_db_padding = 0; + int is_zero; + int is_one; + for (size_t i = 0; i < DbLen; /* ... */) { + is_zero = (db[i] == 0); + is_one = (db[i] == 1); + salt_ofs += (looking_for_one & is_one) * (static_cast(++i)); + looking_for_one &= ~is_one; + invalid_db_padding |= (looking_for_one & ~is_zero); + } + + is_valid &= (invalid_db_padding == 0); + } + + /* Verify salt. */ + is_valid &= (DbLen - salt_ofs) == SaltLen; + + /* Setup the message to verify. */ + const u8 *salt = std::addressof(db[DbLen - SaltLen]); + u8 verif_msg[8 + HashLen + SaltLen]; + ON_SCOPE_EXIT { util::ClearMemory(verif_msg, sizeof(verif_msg)); }; + + util::ClearMemory(std::addressof(verif_msg[0]), 8); + std::memcpy(std::addressof(verif_msg[8]), std::addressof(msg_hash), HashLen); + std::memcpy(std::addressof(verif_msg[8 + HashLen]), salt, SaltLen); + + /* Verify the final hash. */ + return VerifyHash(h, reinterpret_cast(std::addressof(verif_msg[0])), sizeof(verif_msg)); + } + + bool VerifyRsaPssSha256(int slot, void *sig, size_t sig_size, const void *msg, size_t msg_size) { + /* Exponentiate the signature, using the signature as the destination buffer. */ + se::ModularExponentiate(sig, sig_size, slot, sig, sig_size); + + /* Verify the pss padding. */ + return VerifyRsaPssSha256(static_cast(sig), msg, msg_size); + } + + } + + bool VerifySignature(void *sig, size_t sig_size, const void *mod, size_t mod_size, const void *msg, size_t msg_size) { + /* Load the public key into a temporary keyslot. */ + const int slot = pkg1::RsaKeySlot_Temporary; + se::SetRsaKey(slot, mod, mod_size, RsaPublicKeyExponent, util::size(RsaPublicKeyExponent)); + + return VerifyRsaPssSha256(slot, sig, sig_size, msg, msg_size); + } + + bool VerifyHash(const void *hash, uintptr_t msg, size_t msg_size) { + /* Zero-sized messages are always valid. */ + if (msg_size == 0) { + return true; + } + + /* Ensure that the SE sees correct data for the message. */ + hw::FlushDataCache(reinterpret_cast(msg), msg_size); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Calculate the hash. */ + se::Sha256Hash calc_hash; + se::CalculateSha256(std::addressof(calc_hash), reinterpret_cast(msg), msg_size); + + /* Verify the result. */ + return crypto::IsSameBytes(std::addressof(calc_hash), hash, sizeof(calc_hash)); + } + +} diff --git a/exosphere2/program/source/boot/secmon_main.cpp b/exosphere2/program/source/boot/secmon_main.cpp index 9818f7ef7..93b9f9c93 100644 --- a/exosphere2/program/source/boot/secmon_main.cpp +++ b/exosphere2/program/source/boot/secmon_main.cpp @@ -26,7 +26,7 @@ namespace ams::secmon { /* Set library register addresses. */ /* actmon::SetRegisterAddress(MemoryRegionVirtualDeviceActivityMonitor.GetAddress()); */ clkrst::SetRegisterAddress(MemoryRegionVirtualDeviceClkRst.GetAddress()); - /* flowctrl::SetRegisterAddress(); */ + flow::SetRegisterAddress(MemoryRegionVirtualDeviceFlowController.GetAddress()); fuse::SetRegisterAddress(MemoryRegionVirtualDeviceFuses.GetAddress()); gic::SetRegisterAddress(MemoryRegionVirtualDeviceGicDistributor.GetAddress(), MemoryRegionVirtualDeviceGicCpuInterface.GetAddress()); i2c::SetRegisterAddress(i2c::Port_1, MemoryRegionVirtualDeviceI2c1.GetAddress()); @@ -69,6 +69,26 @@ namespace ams::secmon { /* Initialize the random cache. */ secmon::smc::FillRandomCache(); } + + /* Wait for NX Bootloader to finish loading the BootConfig. */ + secmon::boot::WaitForNxBootloader(secmon_params, pkg1::BootloaderState_LoadedBootConfig); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Load the bootconfig. */ + secmon::boot::LoadBootConfig(MemoryRegionPhysicalIramBootConfig.GetPointer()); + + /* Verify or clear the boot config. */ + secmon::boot::VerifyOrClearBootConfig(); + + /* Get the boot config. */ + const auto &bc = secmon::GetBootConfig(); + + /* Set the tsc value by the boot config. */ + { + constexpr u64 TscMask = (static_cast(1) << 55) - 1; + + secmon::boot::EnableTsc(bc.data.GetInitialTscValue() & TscMask); + } } } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere.hpp b/libraries/libexosphere/include/exosphere.hpp index c92a382d9..b320a52e5 100644 --- a/libraries/libexosphere/include/exosphere.hpp +++ b/libraries/libexosphere/include/exosphere.hpp @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libexosphere/include/exosphere/br.hpp b/libraries/libexosphere/include/exosphere/br.hpp new file mode 100644 index 000000000..089713a08 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br.hpp @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include diff --git a/libraries/libexosphere/include/exosphere/br/br_types.hpp b/libraries/libexosphere/include/exosphere/br/br_types.hpp new file mode 100644 index 000000000..382010755 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/br_types.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +#include +#include + +namespace ams::br { + + struct BootEcid { + u32 ecid[4]; + }; + static_assert(util::is_pod::value); + static_assert(sizeof(BootEcid) == 0x10); + +} diff --git a/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp b/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp new file mode 100644 index 000000000..ee26f5b9e --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/impl/br_erista_types.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::br::erista { + +} diff --git a/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp b/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp new file mode 100644 index 000000000..8210479c2 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/br/impl/br_mariko_types.hpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::br::mariko { + +} diff --git a/libraries/libexosphere/include/exosphere/fuse.hpp b/libraries/libexosphere/include/exosphere/fuse.hpp index 3df44b8a3..55efdb334 100644 --- a/libraries/libexosphere/include/exosphere/fuse.hpp +++ b/libraries/libexosphere/include/exosphere/fuse.hpp @@ -15,6 +15,7 @@ */ #pragma once #include +#include #include namespace ams::fuse { @@ -45,5 +46,6 @@ namespace ams::fuse { HardwareType GetHardwareType(); HardwareState GetHardwareState(); pmic::Regulator GetRegulator(); + void GetEcid(br::BootEcid *out); } \ No newline at end of file diff --git a/libraries/libexosphere/include/exosphere/pkg1.hpp b/libraries/libexosphere/include/exosphere/pkg1.hpp index d7dbad6af..90a61cb17 100644 --- a/libraries/libexosphere/include/exosphere/pkg1.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1.hpp @@ -20,3 +20,4 @@ #include #include #include +#include diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp new file mode 100644 index 000000000..632a122ac --- /dev/null +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_api.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::pkg1 { + + bool IsProduction(); + bool IsProductionForVersionCheck(); + bool IsProductionForPublicKey(); + +} diff --git a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp index da73f0112..8cef0c823 100644 --- a/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp +++ b/libraries/libexosphere/include/exosphere/pkg1/pkg1_boot_config.hpp @@ -90,9 +90,13 @@ namespace ams::pkg1 { return static_cast(this->flags0[3]); } - bool IsTscInitialValueValid() const { + constexpr bool IsInitialTscValueValid() const { return (this->flags0[4] & (1 << 0)) != 0; } + + constexpr u64 GetInitialTscValue() const { + return this->IsInitialTscValueValid() ? this->initial_tsc_value : 0; + } }; static_assert(util::is_pod::value); static_assert(sizeof(BootConfigData) == 0x200); diff --git a/libraries/libexosphere/include/exosphere/se.hpp b/libraries/libexosphere/include/exosphere/se.hpp index ad9abd632..c46693815 100644 --- a/libraries/libexosphere/include/exosphere/se.hpp +++ b/libraries/libexosphere/include/exosphere/se.hpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libexosphere/include/exosphere/se/se_hash.hpp b/libraries/libexosphere/include/exosphere/se/se_hash.hpp new file mode 100644 index 000000000..934ced609 --- /dev/null +++ b/libraries/libexosphere/include/exosphere/se/se_hash.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include + +namespace ams::se { + + constexpr inline int Sha256HashSize = crypto::Sha256Generator::HashSize; + + union Sha256Hash { + u8 bytes[Sha256HashSize / sizeof( u8)]; + u32 words[Sha256HashSize / sizeof(u32)]; + }; + + void CalculateSha256(Sha256Hash *dst, const void *src, size_t src_size); + +} diff --git a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp index e16b99c66..e632f67a9 100644 --- a/libraries/libexosphere/include/exosphere/se/se_rsa.hpp +++ b/libraries/libexosphere/include/exosphere/se/se_rsa.hpp @@ -26,4 +26,6 @@ namespace ams::se { void SetRsaKey(int slot, const void *mod, size_t mod_size, const void *exp, size_t exp_size); + void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size); + } diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp index 7f094a14b..bf8515650 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_configuration_context.arch.arm64.hpp @@ -70,6 +70,10 @@ namespace ams::secmon { GetConfigurationContext().secmon_cfg.key_generation = generation; } + ALWAYS_INLINE pkg1::BootConfig *GetBootConfigStorage() { + return std::addressof(GetConfigurationContext().boot_config); + } + } ALWAYS_INLINE const ConfigurationContext &GetConfigurationContext() { diff --git a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp index c40368a3c..1e25108c5 100644 --- a/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp +++ b/libraries/libexosphere/include/exosphere/secmon/secmon_memory_layout.hpp @@ -271,7 +271,9 @@ namespace ams::secmon { static_assert(MemoryRegionVirtual.Contains(MemoryRegionVirtualTzramL2L3PageTable)); static_assert(MemoryRegionPhysicalTzramNonVolatile.Contains(MemoryRegionPhysicalTzramL2L3PageTable)); - constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(0x7C010800, 0xD800); - constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(0x40032000, 0xC000); + constexpr inline const MemoryRegion MemoryRegionPhysicalTzramFullProgramImage = MemoryRegion(UINT64_C(0x7C010800), 0xD800); + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootCodeImage = MemoryRegion(UINT64_C(0x40032000), 0xC000); -} \ No newline at end of file + constexpr inline const MemoryRegion MemoryRegionPhysicalIramBootConfig = MemoryRegion(UINT64_C(0x4003F800), 0x400); + +} diff --git a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp index a191641f9..d7e7fd497 100644 --- a/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp +++ b/libraries/libexosphere/include/exosphere/tegra/tegra_sysctr0.hpp @@ -16,6 +16,9 @@ #pragma once #include +#define SYSCTR0_CNTCR (0x00C) +#define SYSCTR0_CNTCV0 (0x008) +#define SYSCTR0_CNTCV1 (0x00C) #define SYSCTR0_CNTFID0 (0x020) #define SYSCTR0_CNTFID1 (0x024) @@ -33,4 +36,18 @@ #define SYSCTR0_COUNTERID10 (0xFF8) #define SYSCTR0_COUNTERID11 (0xFFC) -#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n \ No newline at end of file +#define SYSCTR0_COUNTERID(n) SYSCTR0_COUNTERID##n + +#define SYSCTR0_REG_BITS_MASK(NAME) REG_NAMED_BITS_MASK (SYSCTR0, NAME) +#define SYSCTR0_REG_BITS_VALUE(NAME, VALUE) REG_NAMED_BITS_VALUE (SYSCTR0, NAME, VALUE) +#define SYSCTR0_REG_BITS_ENUM(NAME, ENUM) REG_NAMED_BITS_ENUM (SYSCTR0, NAME, ENUM) +#define SYSCTR0_REG_BITS_ENUM_SEL(NAME, __COND__, TRUE_ENUM, FALSE_ENUM) REG_NAMED_BITS_ENUM_SEL(SYSCTR0, NAME, __COND__, TRUE_ENUM, FALSE_ENUM) + +#define DEFINE_SYSCTR0_REG(NAME, __OFFSET__, __WIDTH__) REG_DEFINE_NAMED_REG (SYSCTR0, NAME, __OFFSET__, __WIDTH__) +#define DEFINE_SYSCTR0_REG_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE) REG_DEFINE_NAMED_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE) +#define DEFINE_SYSCTR0_REG_TWO_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE) REG_DEFINE_NAMED_TWO_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE) +#define DEFINE_SYSCTR0_REG_THREE_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) REG_DEFINE_NAMED_THREE_BIT_ENUM(SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN) +#define DEFINE_SYSCTR0_REG_FOUR_BIT_ENUM(NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) REG_DEFINE_NAMED_FOUR_BIT_ENUM (SYSCTR0, NAME, __OFFSET__, ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, ELEVEN, TWELVE, THIRTEEN, FOURTEEN, FIFTEEN) + +DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_EN, 0, DISABLE, ENABLE); +DEFINE_SYSCTR0_REG_BIT_ENUM(CNTCR_HDBG, 1, DISABLE, ENABLE); \ No newline at end of file diff --git a/libraries/libexosphere/source/fuse/fuse_api.cpp b/libraries/libexosphere/source/fuse/fuse_api.cpp index 73f48494d..32d491393 100644 --- a/libraries/libexosphere/source/fuse/fuse_api.cpp +++ b/libraries/libexosphere/source/fuse/fuse_api.cpp @@ -118,4 +118,28 @@ namespace ams::fuse { return pmic::Regulator_Erista_Max77621; } -} \ No newline at end of file + void GetEcid(br::BootEcid *out) { + /* Get the registers. */ + const volatile auto &chip = GetChipRegisters(); + + /* Read the ecid components. */ + const u32 vendor = reg::Read(chip.FUSE_OPT_VENDOR_CODE); + const u32 fab = reg::Read(chip.FUSE_OPT_FAB_CODE); + const u32 lot0 = reg::Read(chip.FUSE_OPT_LOT_CODE_0); + const u32 lot1 = reg::Read(chip.FUSE_OPT_LOT_CODE_1); + const u32 wafer = reg::Read(chip.FUSE_OPT_WAFER_ID); + const u32 x_coord = reg::Read(chip.FUSE_OPT_X_COORDINATE); + const u32 y_coord = reg::Read(chip.FUSE_OPT_Y_COORDINATE); + const u32 reserved = reg::Read(chip.FUSE_OPT_OPS_RESERVED); + + /* Clear the output. */ + util::ClearMemory(out, sizeof(*out)); + + /* Copy the component bits. */ + out->ecid[0] = static_cast((lot1 << 30) | (wafer << 24) | (x_coord << 15) | (y_coord << 6) | (reserved)); + out->ecid[1] = static_cast((lot0 << 26) | (lot1 >> 2)); + out->ecid[2] = static_cast((fab << 26) | (lot0 >> 6)); + out->ecid[3] = static_cast(vendor); + } + +} diff --git a/libraries/libexosphere/source/pkg1/pkg1_api.cpp b/libraries/libexosphere/source/pkg1/pkg1_api.cpp new file mode 100644 index 000000000..035b4d087 --- /dev/null +++ b/libraries/libexosphere/source/pkg1/pkg1_api.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::pkg1 { + + namespace { + + bool IsProductionImpl() { + return fuse::GetHardwareState() != fuse::HardwareState_Development; + } + + } + + bool IsProduction() { + return IsProductionImpl(); + } + + bool IsProductionForVersionCheck() { + return IsProductionImpl(); + } + + bool IsProductionForPublicKey() { + return IsProductionImpl(); + } + +} diff --git a/libraries/libexosphere/source/se/se_hash.cpp b/libraries/libexosphere/source/se/se_hash.cpp new file mode 100644 index 000000000..2ef38deb2 --- /dev/null +++ b/libraries/libexosphere/source/se/se_hash.cpp @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include +#include "se_execute.hpp" + +namespace ams::se { + + namespace { + + void SetMessageSize(volatile SecurityEngineRegisters *SE, size_t src_size) { + /* Set the message size. */ + reg::Write(SE->SE_SHA_MSG_LENGTH[0], src_size * BITSIZEOF(u8)); + reg::Write(SE->SE_SHA_MSG_LENGTH[1], 0); + reg::Write(SE->SE_SHA_MSG_LENGTH[2], 0); + reg::Write(SE->SE_SHA_MSG_LENGTH[3], 0); + + /* Set the message remaining size. */ + reg::Write(SE->SE_SHA_MSG_LEFT[0], src_size * BITSIZEOF(u8)); + reg::Write(SE->SE_SHA_MSG_LEFT[1], 0); + reg::Write(SE->SE_SHA_MSG_LEFT[2], 0); + reg::Write(SE->SE_SHA_MSG_LEFT[3], 0); + } + + void GetHashResult(volatile SecurityEngineRegisters *SE, void *dst, size_t dst_size) { + /* Copy out the words. */ + const int num_words = dst_size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + const u32 word = reg::Read(SE->SE_HASH_RESULT[i]); + util::StoreBigEndian(static_cast(dst) + i, word); + } + } + + } + + void CalculateSha256(Sha256Hash *dst, const void *src, size_t src_size) { + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Configure the engine to perform SHA256 "encryption". */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, SHA256), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, SHA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, HASH_REG)); + + /* Begin a hardware hash operation. */ + reg::Write(SE->SE_SHA_CONFIG, SE_REG_BITS_VALUE(SHA_CONFIG_HW_INIT_HASH, 1)); + + /* Set the message size. */ + SetMessageSize(SE, src_size); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, src, src_size); + + /* Get the result. */ + GetHashResult(SE, dst, sizeof(*dst)); + } + +} diff --git a/libraries/libexosphere/source/se/se_registers.hpp b/libraries/libexosphere/source/se/se_registers.hpp index d76e0571c..2a5e70352 100644 --- a/libraries/libexosphere/source/se/se_registers.hpp +++ b/libraries/libexosphere/source/se/se_registers.hpp @@ -133,32 +133,12 @@ namespace ams::se { DEFINE_SE_REG_BIT_ENUM_WITH_SW_CLEAR(INT_STATUS_ERR_STAT, 16); /* SE_CONFIG */ - DEFINE_SE_REG(CONFIG_DST, 2, 3); - DEFINE_SE_REG(CONFIG_DEC_ALG, 8, 4); - DEFINE_SE_REG(CONFIG_ENC_ALG, 12, 4); DEFINE_SE_REG(CONFIG_DEC_MODE, 16, 8); DEFINE_SE_REG(CONFIG_ENC_MODE, 24, 8); - enum SE_CONFIG_DST { - SE_CONFIG_DST_MEMORY = 0, - SE_CONFIG_DST_HASH_REG = 1, - SE_CONFIG_DST_KEYTABLE = 2, - SE_CONFIG_DST_SRK = 3, - SE_CONFIG_DST_RSA_REG = 4, - }; - - enum SE_CONFIG_DEC_ALG { - SE_CONFIG_DEC_ALG_NOP = 0, - SE_CONFIG_DEC_ALG_AES_DEC = 1, - }; - - enum SE_CONFIG_ENC_ALG { - SE_CONFIG_ENC_ALG_NOP = 0, - SE_CONFIG_ENC_ALG_AES_ENC = 1, - SE_CONFIG_ENC_ALG_RNG = 2, - SE_CONFIG_ENC_ALG_SHA = 3, - SE_CONFIG_ENC_ALG_RSA = 4, - }; + DEFINE_SE_REG_THREE_BIT_ENUM(CONFIG_DST, 2, MEMORY, HASH_REG, KEYTABLE, SRK, RSA_REG, RESERVED5, RESERVED6, RESERVED7); + DEFINE_SE_REG_FOUR_BIT_ENUM(CONFIG_DEC_ALG, 8, NOP, AES_DEC, RESERVED2, RESERVED3, RESERVED4, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); + DEFINE_SE_REG_FOUR_BIT_ENUM(CONFIG_ENC_ALG, 12, NOP, AES_ENC, RNG, SHA, RSA, RESERVED5, RESERVED6, RESERVED7, RESERVED8, RESERVED9, RESERVED10, RESERVED11, RESERVED12, RESERVED13, RESERVED14, RESERVED15); enum SE_CONFIG_DEC_MODE { SE_CONFIG_DEC_MODE_AESMODE_KEY128 = 0, @@ -171,13 +151,16 @@ namespace ams::se { SE_CONFIG_ENC_MODE_AESMODE_KEY192 = 1, SE_CONFIG_ENC_MODE_AESMODE_KEY256 = 2, - SE_CONFIG_ENC_MODE_AESMODE_SHA1 = 1, - SE_CONFIG_ENC_MODE_AESMODE_SHA224 = 4, - SE_CONFIG_ENC_MODE_AESMODE_SHA256 = 5, - SE_CONFIG_ENC_MODE_AESMODE_SHA384 = 6, - SE_CONFIG_ENC_MODE_AESMODE_SHA512 = 7, + SE_CONFIG_ENC_MODE_SHA1 = 1, + SE_CONFIG_ENC_MODE_SHA224 = 4, + SE_CONFIG_ENC_MODE_SHA256 = 5, + SE_CONFIG_ENC_MODE_SHA384 = 6, + SE_CONFIG_ENC_MODE_SHA512 = 7, }; + /* SE_SHA_CONFIG */ + DEFINE_SE_REG(SHA_CONFIG_HW_INIT_HASH, 0, 1); + /* SE_CRYPTO_KEYTABLE_ADDR */ DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_WORD, 0, 4); @@ -208,6 +191,9 @@ namespace ams::se { DEFINE_SE_REG(CRYPTO_KEYTABLE_ADDR_KEYIV_KEY_SLOT, 4, 4); + /* SE_RSA_CONFIG */ + DEFINE_SE_REG(RSA_CONFIG_KEY_SLOT, 24, 1); + /* SE_RSA_KEYTABLE_ADDR */ DEFINE_SE_REG(RSA_KEYTABLE_ADDR_WORD_ADDR, 0, 6); DEFINE_SE_REG_BIT_ENUM(RSA_KEYTABLE_ADDR_EXPMOD_SEL, 6, EXPONENT, MODULUS); diff --git a/libraries/libexosphere/source/se/se_rsa.cpp b/libraries/libexosphere/source/se/se_rsa.cpp index d39a040dc..99ec5e90a 100644 --- a/libraries/libexosphere/source/se/se_rsa.cpp +++ b/libraries/libexosphere/source/se/se_rsa.cpp @@ -27,10 +27,7 @@ namespace ams::se { constinit RsaKeyInfo g_rsa_key_infos[RsaKeySlotCount] = {}; - void ClearRsaKeySlot(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { - /* Get the engine. */ - auto *SE = GetRegisters(); - + void ClearRsaKeySlot(volatile SecurityEngineRegisters *SE, int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod) { constexpr int NumWords = se::RsaSize / sizeof(u32); for (int i = 0; i < NumWords; ++i) { /* Select the keyslot word. */ @@ -44,10 +41,7 @@ namespace ams::se { } } - void SetRsaKey(int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { - /* Get the engine. */ - auto *SE = GetRegisters(); - + void SetRsaKey(volatile SecurityEngineRegisters *SE, int slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL expmod, const void *key, size_t key_size) { const int num_words = key_size / sizeof(u32); for (int i = 0; i < num_words; ++i) { /* Select the keyslot word. */ @@ -64,6 +58,15 @@ namespace ams::se { } } + void GetRsaResult(volatile SecurityEngineRegisters *SE, void *dst, size_t size) { + /* Copy out the words. */ + const int num_words = size / sizeof(u32); + for (int i = 0; i < num_words; ++i) { + const u32 word = reg::Read(SE->SE_RSA_OUTPUT[i]); + util::StoreBigEndian(static_cast(dst) + num_words - 1 - i, word); + } + } + } void ClearRsaKeySlot(int slot) { @@ -73,11 +76,14 @@ namespace ams::se { /* Clear the info. */ g_rsa_key_infos[slot] = {}; + /* Get the engine. */ + auto *SE = GetRegisters(); + /* Clear the modulus. */ - ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); + ClearRsaKeySlot(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS); /* Clear the exponent. */ - ClearRsaKeySlot(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); + ClearRsaKeySlot(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT); } void LockRsaKeySlot(int slot, u32 flags) { @@ -117,9 +123,55 @@ namespace ams::se { info.modulus_size_val = (mod_size / 64) - 1; info.exponent_size_val = (exp_size / 4); + /* Get the engine. */ + auto *SE = GetRegisters(); + /* Set the modulus and exponent. */ - SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); - SetRsaKey(slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + SetRsaKey(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_MODULUS, mod, mod_size); + SetRsaKey(SE, slot, SE_RSA_KEYTABLE_ADDR_EXPMOD_SEL_EXPONENT, exp, exp_size); + } + + void ModularExponentiate(void *dst, size_t dst_size, int slot, const void *src, size_t src_size) { + /* Validate the slot and sizes. */ + AMS_ABORT_UNLESS(0 <= slot && slot < RsaKeySlotCount); + AMS_ABORT_UNLESS(src_size <= RsaSize); + AMS_ABORT_UNLESS(dst_size <= RsaSize); + + /* Get the engine. */ + auto *SE = GetRegisters(); + + /* Create a work buffer. */ + u8 work[RsaSize]; + util::ClearMemory(work, sizeof(work)); + + /* Copy the input into the work buffer (reversing endianness). */ + const u8 *src_u8 = static_cast(src); + for (size_t i = 0; i < src_size; ++i) { + work[src_size - 1 - i] = src_u8[i]; + } + + /* Flush the work buffer to ensure the SE sees correct results. */ + hw::FlushDataCache(work, sizeof(work)); + hw::DataSynchronizationBarrierInnerShareable(); + + /* Configure the engine to perform RSA encryption. */ + reg::Write(SE->SE_CONFIG, SE_REG_BITS_ENUM(CONFIG_ENC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_DEC_MODE, AESMODE_KEY128), + SE_REG_BITS_ENUM(CONFIG_ENC_ALG, RSA), + SE_REG_BITS_ENUM(CONFIG_DEC_ALG, NOP), + SE_REG_BITS_ENUM(CONFIG_DST, RSA_REG)); + + /* Configure the engine to use the keyslot and correct modulus/exp sizes. */ + const auto &info = g_rsa_key_infos[slot]; + reg::Write(SE->SE_RSA_CONFIG, SE_REG_BITS_VALUE(RSA_CONFIG_KEY_SLOT, slot)); + reg::Write(SE->SE_RSA_KEY_SIZE, info.modulus_size_val); + reg::Write(SE->SE_RSA_EXP_SIZE, info.exponent_size_val); + + /* Execute the operation. */ + ExecuteOperation(SE, SE_OPERATION_OP_START, nullptr, 0, work, src_size); + + /* Copy out the result. */ + GetRsaResult(SE, dst, dst_size); } }