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 {