diff --git a/libc-top-half/sources/arc4random.c b/libc-top-half/sources/arc4random.c index 9898206bb..b86ec03aa 100644 --- a/libc-top-half/sources/arc4random.c +++ b/libc-top-half/sources/arc4random.c @@ -4,130 +4,20 @@ #include #include -#define RNG_RESERVE_LEN 512 - -#define CHACHA20_KEYBYTES 32 -#define CHACHA20_BLOCKBYTES 64 - -#define ROTL32(x, b) (uint32_t)(((x) << (b)) | ((x) >> (32 - (b)))) - -#define CHACHA20_QUARTERROUND(A, B, C, D) \ - A += B; \ - D = ROTL32(D ^ A, 16); \ - C += D; \ - B = ROTL32(B ^ C, 12); \ - A += B; \ - D = ROTL32(D ^ A, 8); \ - C += D; \ - B = ROTL32(B ^ C, 7) - -static void CHACHA20_ROUNDS(uint32_t st[16]) -{ - int i; - - for (i = 0; i < 20; i += 2) { - CHACHA20_QUARTERROUND(st[0], st[4], st[8], st[12]); - CHACHA20_QUARTERROUND(st[1], st[5], st[9], st[13]); - CHACHA20_QUARTERROUND(st[2], st[6], st[10], st[14]); - CHACHA20_QUARTERROUND(st[3], st[7], st[11], st[15]); - CHACHA20_QUARTERROUND(st[0], st[5], st[10], st[15]); - CHACHA20_QUARTERROUND(st[1], st[6], st[11], st[12]); - CHACHA20_QUARTERROUND(st[2], st[7], st[8], st[13]); - CHACHA20_QUARTERROUND(st[3], st[4], st[9], st[14]); - } -} - -static void chacha20_update(uint8_t out[CHACHA20_BLOCKBYTES], uint32_t st[16]) -{ - uint32_t ks[16]; - int i; - - memcpy(ks, st, 4 * 16); - CHACHA20_ROUNDS(st); - for (i = 0; i < 16; i++) { - ks[i] += st[i]; - } - memcpy(out, ks, CHACHA20_BLOCKBYTES); - st[12]++; -} - -static void chacha20_init(uint32_t st[16], const uint8_t key[CHACHA20_KEYBYTES]) -{ - static const uint32_t constants[4] = { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 }; - memcpy(&st[0], constants, 4 * 4); - memcpy(&st[4], key, CHACHA20_KEYBYTES); - memset(&st[12], 0, 4 * 4); -} - -static int chacha20_rng(uint8_t* out, size_t len, uint8_t key[CHACHA20_KEYBYTES]) -{ - uint32_t st[16]; - size_t off; - - chacha20_init(st, key); - chacha20_update(&out[0], st); - memcpy(key, out, CHACHA20_KEYBYTES); - off = 0; - while (len >= CHACHA20_BLOCKBYTES) { - chacha20_update(&out[off], st); - len -= CHACHA20_BLOCKBYTES; - off += CHACHA20_BLOCKBYTES; - } - if (len > 0) { - uint8_t tmp[CHACHA20_BLOCKBYTES]; - chacha20_update(tmp, st); - memcpy(&out[off], tmp, len); - } - return 0; -} - -struct rng_state { - int initialized; - size_t off; - uint8_t key[CHACHA20_KEYBYTES]; - uint8_t reserve[RNG_RESERVE_LEN]; -}; - void arc4random_buf(void* buffer, size_t len) { - static _Thread_local struct rng_state rng_state; - - unsigned char* buffer_ = (unsigned char*)buffer; - size_t off; - size_t remaining; - size_t partial; - - if (!rng_state.initialized) { - if (getentropy(rng_state.key, sizeof rng_state.key) != 0) { - assert(0); - } - rng_state.off = RNG_RESERVE_LEN; - rng_state.initialized = 1; - } - off = 0; - remaining = len; - while (remaining > 0) { - if (rng_state.off == RNG_RESERVE_LEN) { - while (remaining >= RNG_RESERVE_LEN) { - chacha20_rng(&buffer_[off], RNG_RESERVE_LEN, rng_state.key); - off += RNG_RESERVE_LEN; - remaining -= RNG_RESERVE_LEN; - } - if (remaining == 0) { - break; - } - chacha20_rng(&rng_state.reserve[0], RNG_RESERVE_LEN, rng_state.key); - rng_state.off = 0; - } - partial = RNG_RESERVE_LEN - rng_state.off; - if (remaining < partial) { - partial = remaining; - } - memcpy(&buffer_[off], &rng_state.reserve[rng_state.off], partial); - memset(&rng_state.reserve[rng_state.off], 0, partial); - rng_state.off += partial; - remaining -= partial; - off += partial; + // Always call `__wasi_random_get` rather than doing a guest PRNG, because + // Wasm engines may snapshot or even clone Wasm state, which we don't have + // any visibility into. + // + // We therefore effectively expect that `__wasi_random_get` is "fast", + // presumably using a PRNG-style implementation rather than a slower + // raw-entropy-style implementation. + int r = __wasi_random_get(buffer, len); + + // `__wasi_random_get` should always succeed. + if (r != __WASI_ERRNO_SUCCESS) { + __builtin_trap(); } }