mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-11-10 06:01:52 +00:00
smcGetRandomFor{User,Priv} Implementations.
This commit is contained in:
parent
34667a65f9
commit
cec055a44b
4 changed files with 132 additions and 1 deletions
71
exosphere/randomcache.c
Normal file
71
exosphere/randomcache.c
Normal file
|
@ -0,0 +1,71 @@
|
|||
#include <stdint.h>
|
||||
|
||||
#include "utils.h"
|
||||
#include "randomcache.h"
|
||||
#include "se.h"
|
||||
|
||||
/* TrustZone maintains a cache of random for the kernel. */
|
||||
/* So that requests can still be serviced even when a */
|
||||
/* usermode SMC is in progress. */
|
||||
|
||||
volatile uint8_t g_random_cache[0x400];
|
||||
volatile unsigned int g_random_cache_low = 0;
|
||||
volatile unsigned int g_random_cache_high = 0x3FF;
|
||||
|
||||
|
||||
void randomcache_refill_segment(unsigned int offset, unsigned int size) {
|
||||
if (offset + size >= 0x400) {
|
||||
size = 0x400 - offset;
|
||||
}
|
||||
|
||||
flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]);
|
||||
se_generate_random(KEYSLOT_SWITCH_RNGKEY, &g_random_cache[offset], size);
|
||||
flush_dcache_range(&g_random_cache[offset], &g_random_cache[offset + size]);
|
||||
|
||||
}
|
||||
|
||||
void randomcache_init(void) {
|
||||
randomcache_refill_segment(0, 0x400);
|
||||
g_random_cache_low = 0;
|
||||
g_random_cache_high = 0x3FF;
|
||||
}
|
||||
|
||||
void randomcache_refill(void) {
|
||||
unsigned int high_plus_one = (g_random_cache_high + 1) & 0x3FF;
|
||||
if (g_random_cache_low != high_plus_one) {
|
||||
/* Only refill if there's data to refill. */
|
||||
if (g_random_cache_low < high_plus_one) {
|
||||
/* NOTE: There is a bug in official code that causes this to not work properly. */
|
||||
/* In particular, official code checks whether high_plus_one == 0x400. */
|
||||
/* However, because high_plus_one is &= 0x3FF'd, this can never be the case. */
|
||||
/* We will implement according to Nintendo's intention, and not include their bug. */
|
||||
/* This should have no impact on actual observable results, anyway, since this data is random anyway... */
|
||||
|
||||
if (g_random_cache_high != 0x3FF) { /* This is if (true) in Nintendo's code due to the above bug. */
|
||||
randomcache_refill_segment(high_plus_one, 0x400 - high_plus_one);
|
||||
g_random_cache_high = (g_random_cache_high + 0x400 - high_plus_one) & 0x3FF;
|
||||
}
|
||||
|
||||
if (g_random_cache_low > 0) {
|
||||
randomcache_refill_segment(0, g_random_cache_low);
|
||||
g_random_cache_high = (g_random_cache_high + g_random_cache_low) & 0x3FF;
|
||||
}
|
||||
} else { /* g_random_cache_low > high_plus_one */
|
||||
randomcache_refill_segment(high_plus_one, g_random_cache_low - high_plus_one);
|
||||
g_random_cache_high - g_random_cache_low - 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void randomcache_getbytes(void *dst, size_t num_bytes) {
|
||||
unsigned int low = g_random_cache_low;
|
||||
|
||||
memcpy(dst, &g_random_cache[low], num_bytes);
|
||||
|
||||
unsigned int new_low = low + num_bytes;
|
||||
if (new_low + 0x38 > 0x3FF) {
|
||||
new_low = 0;
|
||||
}
|
||||
|
||||
g_random_cache_low = new_low;
|
||||
}
|
13
exosphere/randomcache.h
Normal file
13
exosphere/randomcache.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
#ifndef EXOSPHERE_RANDOM_CACHE_H
|
||||
#define EXOSPHERE_RANDOM_CACHE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* This method must be called on startup. */
|
||||
void randomcache_init(void);
|
||||
void randomcache_refill(void);
|
||||
|
||||
void randomcache_getbytes(void *dst, size_t num_bytes);
|
||||
|
||||
|
||||
#endif
|
|
@ -279,6 +279,10 @@ uint32_t smc_exp_mod(smc_args_t *args) {
|
|||
return smc_wrapper_async(args, user_exp_mod, smc_exp_mod_get_result);
|
||||
}
|
||||
|
||||
uint32_t smc_get_random_bytes_for_user(smc_args_t *args) {
|
||||
return smc_wrapper_sync(args, user_get_random_bytes);
|
||||
}
|
||||
|
||||
uint32_t smc_generate_aes_kek(smc_args_t *args) {
|
||||
return smc_wrapper_sync(args, user_generate_aes_kek);
|
||||
}
|
||||
|
@ -368,4 +372,31 @@ uint32_t cpu_suspend_wrapper(smc_args_t *args) {
|
|||
|
||||
uint32_t smc_cpu_suspend(smc_args_t *args) {
|
||||
return smc_wrapper_sync(args, cpu_suspend_wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t smc_get_random_bytes_for_priv(smc_args_t *args) {
|
||||
/* This is an interesting SMC. */
|
||||
/* The kernel must NEVER be unable to get random bytes, if it needs them */
|
||||
/* As such: */
|
||||
|
||||
uint32_t result;
|
||||
|
||||
/* TODO: Make atomic. */
|
||||
if (g_is_smc_in_progress == 0) {
|
||||
g_is_smc_in_progress = 1;
|
||||
/* If the kernel isn't denied service by a usermode SMC, generate fresh random bytes. */
|
||||
result = user_get_random_bytes(args);
|
||||
/* Also, refill our cache while we have the chance in case we get denied later. */
|
||||
randomcache_refill();
|
||||
g_is_smc_in_progress = 0;
|
||||
} else {
|
||||
if (args->X[1] > 0x38) {
|
||||
return 2;
|
||||
}
|
||||
/* Retrieve bytes from the cache. */
|
||||
size_t num_bytes = (size_t)args->X[1];
|
||||
randomcache_getbytes(&args->X[1], num_bytes);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,22 @@ uint32_t user_exp_mod(smc_args_t *args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
uint32_t user_get_random_bytes(smc_args_t *args) {
|
||||
uint8_t random_bytes[0x40];
|
||||
if (args->X[1] > 0x38) {
|
||||
return 2;
|
||||
}
|
||||
|
||||
size_t size = (size_t)args->X[1];
|
||||
|
||||
flush_dcache_range(random_bytes, random_bytes + size);
|
||||
se_generate_random(KEYSLOT_SWITCH_RNGKEY, random_bytes, size);
|
||||
flush_dcache_range(random_bytes, random_bytes + size);
|
||||
|
||||
memcpy(&args->X[1], random_bytes, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t user_generate_aes_kek(smc_args_t *args) {
|
||||
uint64_t wrapped_kek[2];
|
||||
uint8_t kek_source[0x10];
|
||||
|
|
Loading…
Reference in a new issue