diff --git a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp index 8e1829849..ae12326a1 100644 --- a/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp +++ b/libraries/libmesosphere/include/mesosphere/board/nintendo/nx/kern_k_system_control.hpp @@ -51,9 +51,7 @@ namespace ams::kern::board::nintendo::nx { static NOINLINE u32 GetCreateProcessMemoryPool(); /* Randomness. */ - static void GenerateRandom(u64 *dst, size_t count); - static u64 GenerateRandomRange(u64 min, u64 max); - static u64 GenerateRandomU64(); + static void GenerateRandomBytesForUninitialized(void *dst, size_t size); /* Privileged Access. */ static void ReadWriteRegisterPrivileged(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp index 3d7ea5310..cc8f0f317 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_k_system_control_base.hpp @@ -33,17 +33,30 @@ namespace ams::kern { class KResourceLimit; + struct CtrDrbgContext { + u8 v[16]; + u8 key[16]; + u8 seed[32]; + u8 temp[32]; + u32 reseed_counter; + u8 output[128]; + u32 remaining; + }; + class KSystemControlBase { public: /* This can be overridden as needed. */ static constexpr size_t SecureAppletMemorySize = 0; protected: - /* Nintendo uses std::mt19937_t for randomness. */ - /* To save space (and because mt19337_t isn't secure anyway), */ - /* We will use TinyMT. */ - static constinit inline bool s_uninitialized_random_generator{true}; - static constinit inline util::TinyMT s_random_generator{util::ConstantInitialize}; - static constinit inline KSpinLock s_random_lock; + static constinit inline bool s_uninitialized_random_generator{true}; + static constinit inline CtrDrbgContext s_random_generator{}; + static constinit inline KSpinLock s_random_lock; + private: + /* Randomness helpers. */ + static void InitializeRandomGenerator(const void *seed, size_t size); + static void ReseedRandomGeneratorInternal(); + static u32 GenerateRandomU32(); + static u64 GenerateRandomU64Internal(); public: class Init { private: @@ -66,7 +79,7 @@ namespace ams::kern { static u64 GenerateRandomRange(u64 min, u64 max); }; protected: - static NOINLINE void InitializePhase1Base(u64 seed); + static NOINLINE void InitializePhase1Base(const void *seed, size_t size); public: /* Initialization. */ static NOINLINE void ConfigureKTargetSystem(); @@ -78,6 +91,10 @@ namespace ams::kern { static void GenerateRandom(u64 *dst, size_t count); static u64 GenerateRandomRange(u64 min, u64 max); static u64 GenerateRandomU64(); + static void ReseedRandomGenerator(); + + /* Obtains random bytes while the generator is not yet initialized. Board-provided. */ + static void GenerateRandomBytesForUninitialized(void *dst, size_t size); /* Register access Access. */ static Result ReadWriteRegister(u32 *out, ams::svc::PhysicalAddress address, u32 mask, u32 value); diff --git a/libraries/libmesosphere/libmesosphere.mk b/libraries/libmesosphere/libmesosphere.mk index 2ab310a25..7998d5f78 100644 --- a/libraries/libmesosphere/libmesosphere.mk +++ b/libraries/libmesosphere/libmesosphere.mk @@ -108,6 +108,8 @@ $(OFILES_SRC) : $(HFILES_BIN) kern_libc_generic.o: CFLAGS += -fno-builtin +crypto_aes_impl.arch.arm64.o: CXXFLAGS := $(filter-out -mgeneral-regs-only,$(CXXFLAGS)) + #--------------------------------------------------------------------------------- %_bin.h %.bin.o : %.bin #--------------------------------------------------------------------------------- diff --git a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp index bb7581c83..2cc6f0f81 100644 --- a/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp +++ b/libraries/libmesosphere/source/board/nintendo/nx/kern_k_system_control.cpp @@ -74,12 +74,6 @@ namespace ams::kern::board::nintendo::nx { return value; } - ALWAYS_INLINE u64 GenerateRandomU64FromSmc() { - u64 value; - smc::GenerateRandomBytes(std::addressof(value), sizeof(value)); - return value; - } - ALWAYS_INLINE u64 GetConfigU64(smc::ConfigItem which) { u64 value; smc::GetConfig(&value, 1, which); @@ -477,9 +471,9 @@ namespace ams::kern::board::nintendo::nx { /* Initialize random and resource limit. */ { - u64 seed; - smc::GenerateRandomBytes(std::addressof(seed), sizeof(seed)); - KSystemControlBase::InitializePhase1Base(seed); + u8 seed[32]; + smc::GenerateRandomBytes(seed, sizeof(seed)); + KSystemControlBase::InitializePhase1Base(seed, sizeof(seed)); } /* Configure the Kernel Carveout region. */ @@ -524,32 +518,8 @@ namespace ams::kern::board::nintendo::nx { } /* Randomness. */ - void KSystemControl::GenerateRandom(u64 *dst, size_t count) { - MESOSPHERE_INIT_ABORT_UNLESS(count <= 7); - smc::GenerateRandomBytes(dst, count * sizeof(u64)); - } - - u64 KSystemControl::GenerateRandomRange(u64 min, u64 max) { - KScopedInterruptDisable intr_disable; - KScopedSpinLock lk(s_random_lock); - - - if (AMS_LIKELY(!s_uninitialized_random_generator)) { - return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); }); - } else { - return KSystemControlBase::GenerateUniformRange(min, max, GenerateRandomU64FromSmc); - } - } - - u64 KSystemControl::GenerateRandomU64() { - KScopedInterruptDisable intr_disable; - KScopedSpinLock lk(s_random_lock); - - if (AMS_LIKELY(!s_uninitialized_random_generator)) { - return s_random_generator.GenerateRandomU64(); - } else { - return GenerateRandomU64FromSmc(); - } + void KSystemControl::GenerateRandomBytesForUninitialized(void *dst, size_t size) { + smc::GenerateRandomBytes(dst, size); } void KSystemControl::SleepSystem() { diff --git a/libraries/libmesosphere/source/kern_k_process.cpp b/libraries/libmesosphere/source/kern_k_process.cpp index ad49b783b..88694cfea 100644 --- a/libraries/libmesosphere/source/kern_k_process.cpp +++ b/libraries/libmesosphere/source/kern_k_process.cpp @@ -264,6 +264,11 @@ namespace ams::kern { m_is_default_application_system_resource = false; m_is_immortal = immortal; + /* Reseed the random generator on application launch. */ + if (params.flags & ams::svc::CreateProcessFlag_IsApplication) { + KSystemControl::ReseedRandomGenerator(); + } + /* Setup our system resource. */ if (const size_t system_resource_num_pages = params.system_resource_num_pages; system_resource_num_pages != 0) { /* Create a secure system resource. */ @@ -332,6 +337,11 @@ namespace ams::kern { m_is_default_application_system_resource = false; m_is_immortal = false; + /* Reseed the random generator on application launch. */ + if (params.flags & ams::svc::CreateProcessFlag_IsApplication) { + KSystemControl::ReseedRandomGenerator(); + } + /* Get the memory sizes. */ const size_t code_num_pages = params.code_num_pages; const size_t system_resource_num_pages = params.system_resource_num_pages; diff --git a/libraries/libmesosphere/source/kern_k_system_control_base.cpp b/libraries/libmesosphere/source/kern_k_system_control_base.cpp index 38c507486..ffe172846 100644 --- a/libraries/libmesosphere/source/kern_k_system_control_base.cpp +++ b/libraries/libmesosphere/source/kern_k_system_control_base.cpp @@ -14,6 +14,7 @@ * along with this program. If not, see . */ #include +#include #if defined(ATMOSPHERE_ARCH_ARM64) #include #endif @@ -27,6 +28,55 @@ namespace ams::kern { } + namespace { + + /* The kernel is otherwise FP/SIMD-free, but AES instructions require the NEON registers. */ + class KScopedFpuEnable { + private: + u64 m_saved_cpacr_el1; + public: + ALWAYS_INLINE KScopedFpuEnable() : m_saved_cpacr_el1(cpu::GetCpacrEl1()) { + cpu::SetCpacrEl1(m_saved_cpacr_el1 | 0x100000); + cpu::InstructionMemoryBarrier(); + } + + ALWAYS_INLINE ~KScopedFpuEnable() { + cpu::SetCpacrEl1(m_saved_cpacr_el1); + cpu::InstructionMemoryBarrier(); + } + }; + + ALWAYS_INLINE void CtrDrbgIncrementCounter(u8 *v) { + for (size_t i = 16; i > 0; --i) { + if ((++v[i - 1]) != 0) { + break; + } + } + } + + void CtrDrbgUpdate(CtrDrbgContext &ctx, const u8 *data) { + /* Initialize the block cipher with the current key. */ + ams::crypto::impl::AesImpl<16> aes; + aes.Initialize(ctx.key, sizeof(ctx.key), true); + + /* Generate two blocks of new key/counter material. */ + CtrDrbgIncrementCounter(ctx.v); + aes.EncryptBlock(ctx.temp + 0, 16, ctx.v, 16); + CtrDrbgIncrementCounter(ctx.v); + aes.EncryptBlock(ctx.temp + 16, 16, ctx.v, 16); + + /* Mix in the provided data. */ + for (size_t i = 0; i < sizeof(ctx.temp); ++i) { + ctx.temp[i] ^= data[i]; + } + + /* Update the key and counter. */ + std::memcpy(ctx.key, ctx.temp, sizeof(ctx.key)); + std::memcpy(ctx.v, ctx.temp + sizeof(ctx.key), sizeof(ctx.v)); + } + + } + /* Initialization. */ size_t KSystemControlBase::Init::GetRealMemorySize() { return ams::kern::MainMemorySize; @@ -102,25 +152,13 @@ namespace ams::kern { /* Randomness for Initialization. */ void KSystemControlBase::Init::GenerateRandom(u64 *dst, size_t count) { - if (AMS_UNLIKELY(s_uninitialized_random_generator)) { - const u64 seed = KHardwareTimer::GetTick(); - s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); - s_uninitialized_random_generator = false; - } - for (size_t i = 0; i < count; ++i) { - dst[i] = s_random_generator.GenerateRandomU64(); + dst[i] = KSystemControlBase::GenerateRandomU64(); } } u64 KSystemControlBase::Init::GenerateRandomRange(u64 min, u64 max) { - if (AMS_UNLIKELY(s_uninitialized_random_generator)) { - const u64 seed = KHardwareTimer::GetTick(); - s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); - s_uninitialized_random_generator = false; - } - - return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); }); + return KSystemControlBase::GenerateRandomRange(min, max); } /* System Initialization. */ @@ -135,15 +173,17 @@ namespace ams::kern { } /* Initialize random and resource limit. */ - KSystemControlBase::InitializePhase1Base(KHardwareTimer::GetTick()); + u64 seed[4]; + for (size_t i = 0; i < util::size(seed); ++i) { + seed[i] = KHardwareTimer::GetTick(); + } + KSystemControlBase::InitializePhase1Base(seed, sizeof(seed)); } - void KSystemControlBase::InitializePhase1Base(u64 seed) { - /* Initialize the rng, if we somehow haven't already. */ - if (AMS_UNLIKELY(s_uninitialized_random_generator)) { - s_random_generator.Initialize(reinterpret_cast(std::addressof(seed)), sizeof(seed) / sizeof(u32)); - s_uninitialized_random_generator = false; - } + void KSystemControlBase::InitializePhase1Base(const void *seed, size_t size) { + /* Initialize the random generator. */ + KSystemControlBase::InitializeRandomGenerator(seed, size); + s_uninitialized_random_generator = false; /* Initialize debug logging. */ KDebugLog::Initialize(); @@ -213,13 +253,97 @@ namespace ams::kern { R_THROW(svc::ResultNotImplemented()); } + /* Randomness helpers. Assume the caller holds the random lock. */ + void KSystemControlBase::InitializeRandomGenerator(const void *seed, size_t size) { + MESOSPHERE_ASSERT(size == sizeof(s_random_generator.seed)); + + KScopedFpuEnable fp_enabled; + auto &ctx = s_random_generator; + + /* Zero the counter and key, then derive the initial state from the seed. */ + std::memset(ctx.v, 0, sizeof(ctx.v)); + std::memset(ctx.key, 0, sizeof(ctx.key)); + std::memcpy(ctx.seed, seed, sizeof(ctx.seed)); + + CtrDrbgUpdate(ctx, ctx.seed); + + ctx.reseed_counter = 1; + ctx.remaining = 0; + } + + void KSystemControlBase::ReseedRandomGeneratorInternal() { + KScopedFpuEnable fp_enabled; + auto &ctx = s_random_generator; + + /* Pull fresh entropy, then derive a new state from it. */ + KSystemControl::GenerateRandomBytesForUninitialized(ctx.seed, sizeof(ctx.seed)); + CtrDrbgUpdate(ctx, ctx.seed); + + ctx.remaining = 0; + ctx.reseed_counter = 1; + } + + u32 KSystemControlBase::GenerateRandomU32() { + auto &ctx = s_random_generator; + + if (ctx.remaining == 0) { + KScopedFpuEnable fp_enabled; + + /* Reseed if we've exhausted this generation's entropy, otherwise use zero additional input. */ + if (ctx.reseed_counter <= 0x7FFFFFF0) { + std::memset(ctx.seed, 0, sizeof(ctx.seed)); + } else { + KSystemControl::GenerateRandomBytesForUninitialized(ctx.seed, sizeof(ctx.seed)); + CtrDrbgUpdate(ctx, ctx.seed); + ctx.remaining = 0; + std::memset(ctx.seed, 0, sizeof(ctx.seed)); + ctx.reseed_counter = 1; + } + + /* Generate eight blocks of output. */ + ams::crypto::impl::AesImpl<16> aes; + aes.Initialize(ctx.key, sizeof(ctx.key), true); + for (size_t i = 0; i < 8; ++i) { + CtrDrbgIncrementCounter(ctx.v); + aes.EncryptBlock(ctx.output + 16 * i, 16, ctx.v, 16); + } + + /* Derive the next state. */ + CtrDrbgUpdate(ctx, ctx.seed); + + ++ctx.reseed_counter; + ctx.remaining = 32; + } + + /* Consume a word from the output buffer, zeroizing it in place for backtracking resistance. */ + const size_t index = 32 - ctx.remaining; + u32 value; + std::memcpy(std::addressof(value), ctx.output + 4 * index, sizeof(value)); + std::memset(ctx.output + 4 * index, 0, sizeof(value)); + --ctx.remaining; + return value; + } + + u64 KSystemControlBase::GenerateRandomU64Internal() { + /* Fall back to the board's raw source while the generator is uninitialized. */ + if (AMS_UNLIKELY(s_uninitialized_random_generator)) { + u64 value; + KSystemControl::GenerateRandomBytesForUninitialized(std::addressof(value), sizeof(value)); + return value; + } + + const u64 high = KSystemControlBase::GenerateRandomU32(); + const u64 low = KSystemControlBase::GenerateRandomU32(); + return (high << 32) | low; + } + /* Randomness. */ void KSystemControlBase::GenerateRandom(u64 *dst, size_t count) { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(s_random_lock); for (size_t i = 0; i < count; ++i) { - dst[i] = s_random_generator.GenerateRandomU64(); + dst[i] = KSystemControlBase::GenerateRandomU64Internal(); } } @@ -227,14 +351,46 @@ namespace ams::kern { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(s_random_lock); - return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return s_random_generator.GenerateRandomU64(); }); + /* While uninitialized, fall back to a raw single-draw modulo. */ + if (AMS_UNLIKELY(s_uninitialized_random_generator)) { + u64 value; + KSystemControl::GenerateRandomBytesForUninitialized(std::addressof(value), sizeof(value)); + + const u64 difference = max - min; + if (difference == std::numeric_limits::max()) { + return value; + } + + return min + (value % (difference + 1)); + } + + return KSystemControlBase::GenerateUniformRange(min, max, []() ALWAYS_INLINE_LAMBDA -> u64 { return KSystemControlBase::GenerateRandomU64Internal(); }); } u64 KSystemControlBase::GenerateRandomU64() { KScopedInterruptDisable intr_disable; KScopedSpinLock lk(s_random_lock); - return s_random_generator.GenerateRandomU64(); + return KSystemControlBase::GenerateRandomU64Internal(); + } + + void KSystemControlBase::ReseedRandomGenerator() { + /* This must be unreachable while the generator is uninitialized. */ + MESOSPHERE_ABORT_UNLESS(!s_uninitialized_random_generator); + + KScopedInterruptDisable intr_disable; + KScopedSpinLock lk(s_random_lock); + + KSystemControlBase::ReseedRandomGeneratorInternal(); + } + + void KSystemControlBase::GenerateRandomBytesForUninitialized(void *dst, size_t size) { + /* The default board has no secure monitor; fall back to the hardware timer. */ + u8 *dst_8 = static_cast(dst); + for (size_t offset = 0; offset < size; offset += sizeof(u64)) { + const u64 tick = KHardwareTimer::GetTick(); + std::memcpy(dst_8 + offset, std::addressof(tick), std::min(sizeof(tick), size - offset)); + } } void KSystemControlBase::SleepSystem() { diff --git a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp index 066687771..42e72d7d6 100644 --- a/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp +++ b/libraries/libvapours/include/vapours/crypto/impl/crypto_aes_impl.hpp @@ -33,7 +33,7 @@ namespace ams::crypto::impl { #ifdef ATMOSPHERE_IS_EXOSPHERE int m_slot; #endif - #ifdef ATMOSPHERE_IS_STRATOSPHERE + #if defined(ATMOSPHERE_IS_STRATOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) u32 m_round_keys[RoundKeySize / sizeof(u32)]; #endif public: @@ -43,7 +43,7 @@ namespace ams::crypto::impl { void EncryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; void DecryptBlock(void *dst, size_t dst_size, const void *src, size_t src_size) const; - #ifdef ATMOSPHERE_IS_STRATOSPHERE + #if defined(ATMOSPHERE_IS_STRATOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) const u8 *GetRoundKey() const { return reinterpret_cast(m_round_keys); } diff --git a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp index 35fdf4fa2..c07a20f2e 100644 --- a/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp +++ b/libraries/libvapours/source/crypto/impl/crypto_aes_impl.arch.arm64.cpp @@ -14,13 +14,16 @@ * along with this program. If not, see . */ #include -#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#if defined(ATMOSPHERE_IS_MESOSPHERE) +#include +#endif +#if defined(ATMOSPHERE_IS_STRATOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) #include #endif namespace ams::crypto::impl { -#if defined(ATMOSPHERE_IS_STRATOSPHERE) +#if defined(ATMOSPHERE_IS_STRATOSPHERE) || defined(ATMOSPHERE_IS_MESOSPHERE) namespace {