From ec4430d2e6ef589049c68dfcf78d19a6349f6aa1 Mon Sep 17 00:00:00 2001 From: plutoo Date: Thu, 18 Jan 2018 20:51:38 +0100 Subject: [PATCH] Introduce random --- nx/include/switch.h | 1 + nx/include/switch/kernel/random.h | 4 + nx/include/switch/result.h | 1 + nx/source/kernel/random.c | 159 ++++++++++++++++++++++++++++++ 4 files changed, 165 insertions(+) create mode 100644 nx/include/switch/kernel/random.h create mode 100644 nx/source/kernel/random.c diff --git a/nx/include/switch.h b/nx/include/switch.h index b0face39..cb00e0b2 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -19,6 +19,7 @@ extern "C" { #include #include #include +#include #include #include diff --git a/nx/include/switch/kernel/random.h b/nx/include/switch/kernel/random.h new file mode 100644 index 00000000..45abe1d6 --- /dev/null +++ b/nx/include/switch/kernel/random.h @@ -0,0 +1,4 @@ +// Copyright 2018 plutoo + +void randomGet(u8* buf, size_t len); +u64 randomGet64(u8* buf, size_t len); diff --git a/nx/include/switch/result.h b/nx/include/switch/result.h index a791ab1e..1c66669e 100644 --- a/nx/include/switch/result.h +++ b/nx/include/switch/result.h @@ -49,4 +49,5 @@ enum { LIBNX_INITFAIL_AM, LIBNX_INITFAIL_HID, LIBNX_INITFAIL_FS, + LIBNX_BADGETINFO_RNG, }; diff --git a/nx/source/kernel/random.c b/nx/source/kernel/random.c new file mode 100644 index 00000000..9390b12e --- /dev/null +++ b/nx/source/kernel/random.c @@ -0,0 +1,159 @@ +/* + chacha-ref.c version 20080118 + D. J. Bernstein + Public domain. + + Modified by plutoo. +*/ + +#include + +#define ROTL32(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +#define U32TO8_LITTLE(p, v) \ + (*(u32*)(p) = (v)) + +#define U8TO32_LITTLE(p) \ + (*(u32*)(p)) + +#define ROTATE(v,c) (ROTL32(v,c)) +#define XOR(v,w) ((v) ^ (w)) +#define PLUS(v,w) ((v) + (w)) +#define PLUSONE(v) (PLUS((v),1)) + +#define QUARTERROUND(a,b,c,d) \ + x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]),16); \ + x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]),12); \ + x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]), 8); \ + x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]), 7); + +typedef struct { + u32 input[16]; +} ChaCha; + +static void _Round(u8 output[64], const u32 input[16]) +{ + u32 x[16]; + int i; + + for (i = 0;i < 16;++i) + x[i] = input[i]; + + for (i = 8;i > 0;i -= 2) { + QUARTERROUND( 0, 4, 8,12); + QUARTERROUND( 1, 5, 9,13); + QUARTERROUND( 2, 6,10,14); + QUARTERROUND( 3, 7,11,15); + QUARTERROUND( 0, 5,10,15); + QUARTERROUND( 1, 6,11,12); + QUARTERROUND( 2, 7, 8,13); + QUARTERROUND( 3, 4, 9,14); + } + + for (i = 0;i < 16;++i) + x[i] = PLUS(x[i],input[i]); + + for (i = 0;i < 16;++i) + U32TO8_LITTLE(output + 4 * i,x[i]); +} + +static const char sigma[16] = "expand 32-byte k"; + +void chachaInit(ChaCha* x, const u8* key, const u8* iv) +{ + // Setup key. + x->input[0] = U8TO32_LITTLE(sigma + 0); + x->input[1] = U8TO32_LITTLE(sigma + 4); + x->input[2] = U8TO32_LITTLE(sigma + 8); + x->input[3] = U8TO32_LITTLE(sigma + 12); + x->input[4] = U8TO32_LITTLE(key + 0); + x->input[5] = U8TO32_LITTLE(key + 4); + x->input[6] = U8TO32_LITTLE(key + 8); + x->input[7] = U8TO32_LITTLE(key + 12); + x->input[8] = U8TO32_LITTLE(key + 16); + x->input[9] = U8TO32_LITTLE(key + 20); + x->input[10] = U8TO32_LITTLE(key + 24); + x->input[11] = U8TO32_LITTLE(key + 28); + + // Setup iv. + x->input[12] = 0; + x->input[13] = 0; + x->input[14] = U8TO32_LITTLE(iv + 0); + x->input[15] = U8TO32_LITTLE(iv + 4); +} + +void chachaEncrypt(ChaCha* x, const u8* m, u8* c, size_t bytes) +{ + u8 output[64]; + int i; + + while (bytes > 0) + { + _Round(output, x->input); + + x->input[12] = PLUSONE(x->input[12]); + + if (!x->input[12]) { + x->input[13] = PLUSONE(x->input[13]); + /* stopping at 2^70 bytes per nonce is user's responsibility */ + } + + if (bytes <= 64) { + for (i = 0;i < bytes;++i) + c[i] = m[i] ^ output[i]; + return; + } + + for (i = 0;i < 64;++i) + c[i] = m[i] ^ output[i]; + + bytes -= 64; + c += 64; + m += 64; + } +} + +static ChaCha g_chacha; +static bool g_randInit = false; +static Mutex g_randMutex; + +void _randomInit(void) +{ + // Has already initialized? + if (g_randInit) + return; + + u64 seed[4]; + + int i; + for (i=0; i<4; i++) + { + if (R_FAILED(svcGetInfo(&seed[i], 11, 0, i))) + fatalSimple(MAKERESULT(MODULE_LIBNX, LIBNX_BADGETINFO_RNG)); + } + + u8 iv[8]; + memset(iv, 0, sizeof iv); + + chachaInit(&g_chacha, (const u8*) seed, iv); + g_randInit = true; +} + +void randomGet(u8* buf, size_t len) +{ + mutexLock(&g_randMutex); + + _randomInit(); + + memset(buf, 0, len); + chachaEncrypt(&g_chacha, buf, buf, len); + + mutexUnlock(&g_randMutex); +} + +u64 randomGet64(u8* buf, size_t len) +{ + u64 tmp; + randomGet((u8*) &tmp, sizeof(tmp)); + return tmp; +}