mirror of
https://gitlab.com/sortix/sortix.git
synced 2023-02-13 20:55:38 -05:00
Add arc4random(3).
This commit is contained in:
parent
e460be7a72
commit
edb19f2394
5 changed files with 334 additions and 0 deletions
|
@ -447,6 +447,9 @@ stdio/tmpnam.o \
|
|||
stdio/vfprintf.o \
|
||||
stdio/vprintf.o \
|
||||
stdio/vscanf.o \
|
||||
stdlib/arc4random_buf.o \
|
||||
stdlib/arc4random.o \
|
||||
stdlib/arc4random_uniform.o \
|
||||
stdlib/atexit.o \
|
||||
stdlib/canonicalize_file_name_at.o \
|
||||
stdlib/canonicalize_file_name.o \
|
||||
|
|
|
@ -29,6 +29,10 @@
|
|||
|
||||
#include <sys/__/types.h>
|
||||
|
||||
#if __USE_SORTIX
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
#include <sortix/wait.h>
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
@ -161,6 +165,13 @@ void srandom(unsigned);
|
|||
int unlockpt(int);
|
||||
#endif
|
||||
|
||||
/* Functions copied from elsewhere. */
|
||||
#if __USE_SORTIX
|
||||
uint32_t arc4random(void);
|
||||
void arc4random_buf(void*, size_t);
|
||||
uint32_t arc4random_uniform(uint32_t);
|
||||
#endif
|
||||
|
||||
__END_DECLS
|
||||
|
||||
#endif
|
||||
|
|
33
libc/stdlib/arc4random.cpp
Normal file
33
libc/stdlib/arc4random.cpp
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*******************************************************************************
|
||||
|
||||
Copyright(C) Jonas 'Sortie' Termansen 2014.
|
||||
|
||||
This file is part of the Sortix C Library.
|
||||
|
||||
The Sortix C Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
The Sortix C Library is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
||||
License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with the Sortix C Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
stdlib/arc4random.cpp
|
||||
Generate a cryptographically secure 32-bit value.
|
||||
|
||||
*******************************************************************************/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern "C" uint32_t arc4random(void)
|
||||
{
|
||||
uint32_t result;
|
||||
arc4random_buf(&result, sizeof(result));
|
||||
return result;
|
||||
}
|
232
libc/stdlib/arc4random_buf.cpp
Normal file
232
libc/stdlib/arc4random_buf.cpp
Normal file
|
@ -0,0 +1,232 @@
|
|||
/* $OpenBSD: arc4random.c,v 1.48 2014/07/19 00:08:41 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 1996, David Mazieres <dm@uun.org>
|
||||
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
|
||||
* Copyright (c) 2013, Markus Friedl <markus@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* $OpenBSD: chacha_private.h,v 1.2 2013/10/04 07:02:27 djm Exp $ */
|
||||
|
||||
/* Based on:
|
||||
* chacha-merged.c version 20080118
|
||||
* D. J. Bernstein
|
||||
* Public domain.
|
||||
*/
|
||||
|
||||
/* Adapted for Sortix libc by Jonas 'Sortie' Termansen in 2014. */
|
||||
|
||||
#include <assert.h>
|
||||
#include <endian.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct chacha
|
||||
{
|
||||
uint32_t input[16];
|
||||
};
|
||||
|
||||
static inline uint32_t chacha_read_little_uint32(const unsigned char* buf)
|
||||
{
|
||||
return (uint32_t) buf[0] << 0 |
|
||||
(uint32_t) buf[1] << 8 |
|
||||
(uint32_t) buf[2] << 16 |
|
||||
(uint32_t) buf[3] << 24;
|
||||
}
|
||||
|
||||
static void chacha_keysetup(struct chacha* ctx, const unsigned char* key)
|
||||
{
|
||||
const unsigned char* sigma = (const unsigned char*) "expand 32-byte k";
|
||||
|
||||
ctx->input[0] = chacha_read_little_uint32(sigma + 0 * sizeof(uint32_t));
|
||||
ctx->input[1] = chacha_read_little_uint32(sigma + 1 * sizeof(uint32_t));
|
||||
ctx->input[2] = chacha_read_little_uint32(sigma + 2 * sizeof(uint32_t));
|
||||
ctx->input[3] = chacha_read_little_uint32(sigma + 3 * sizeof(uint32_t));
|
||||
ctx->input[4] = chacha_read_little_uint32(key + 0 * sizeof(uint32_t));
|
||||
ctx->input[5] = chacha_read_little_uint32(key + 1 * sizeof(uint32_t));
|
||||
ctx->input[6] = chacha_read_little_uint32(key + 2 * sizeof(uint32_t));
|
||||
ctx->input[7] = chacha_read_little_uint32(key + 3 * sizeof(uint32_t));
|
||||
ctx->input[8] = chacha_read_little_uint32(key + 4 * sizeof(uint32_t));
|
||||
ctx->input[9] = chacha_read_little_uint32(key + 5 * sizeof(uint32_t));
|
||||
ctx->input[10] = chacha_read_little_uint32(key + 6 * sizeof(uint32_t));
|
||||
ctx->input[11] = chacha_read_little_uint32(key + 7 * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static void chacha_ivsetup(struct chacha* ctx, const unsigned char* iv)
|
||||
{
|
||||
ctx->input[12] = 0;
|
||||
ctx->input[13] = 0;
|
||||
ctx->input[14] = chacha_read_little_uint32(iv + 0 * sizeof(uint32_t));
|
||||
ctx->input[15] = chacha_read_little_uint32(iv + 1 * sizeof(uint32_t));
|
||||
}
|
||||
|
||||
static inline uint32_t chacha_rotate(uint32_t v, uint32_t n)
|
||||
{
|
||||
return (v << n) | (v >> (32 - n));
|
||||
}
|
||||
|
||||
static inline
|
||||
void chacha_quarter_round(uint32_t* a, uint32_t* b, uint32_t* c, uint32_t* d)
|
||||
{
|
||||
*a = *a + *b;
|
||||
*d = chacha_rotate(*d ^ *a, 16);
|
||||
*c = *c + *d;
|
||||
*b = chacha_rotate(*b ^ *c, 12);
|
||||
*a = *a + *b;
|
||||
*d = chacha_rotate(*d ^ *a, 8);
|
||||
*c = *c + *d;
|
||||
*b = chacha_rotate(*b ^ *c, 7);
|
||||
}
|
||||
|
||||
static
|
||||
void chacha_keystream(struct chacha* ctx,
|
||||
unsigned char* keystream,
|
||||
size_t size)
|
||||
{
|
||||
uint32_t work[16];
|
||||
|
||||
for ( size_t offset = 0; offset < size; )
|
||||
{
|
||||
size_t left = size - offset;
|
||||
|
||||
for ( size_t i = 0; i < 16; i++ )
|
||||
work[i] = ctx->input[i];
|
||||
|
||||
/* NOTE: This is 20 in the OpenBSD version, but 8 in Bernstein's. */
|
||||
/* TODO: Why decrement by 2 instead of 1? */
|
||||
for ( int i = 20; 0 < i; i -= 2 )
|
||||
{
|
||||
chacha_quarter_round(&work[0], &work[4], &work[8], &work[12]);
|
||||
chacha_quarter_round(&work[1], &work[5], &work[9], &work[13]);
|
||||
chacha_quarter_round(&work[2], &work[6], &work[10], &work[14]);
|
||||
chacha_quarter_round(&work[3], &work[7], &work[11], &work[15]);
|
||||
chacha_quarter_round(&work[0], &work[5], &work[10], &work[15]);
|
||||
chacha_quarter_round(&work[1], &work[6], &work[11], &work[12]);
|
||||
chacha_quarter_round(&work[2], &work[7], &work[8], &work[13]);
|
||||
chacha_quarter_round(&work[3], &work[4], &work[9], &work[14]);
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < 16; i++ )
|
||||
work[i] += ctx->input[i];
|
||||
|
||||
ctx->input[12] += 1;
|
||||
if ( ctx->input[12] == 0 )
|
||||
{
|
||||
ctx->input[13] += 1;
|
||||
/* Stopping at 2^70 bytes per nonce is user's responsibility. */
|
||||
}
|
||||
|
||||
for ( size_t i = 0; i < 16; i++ )
|
||||
work[i] = htole32(work[i]);
|
||||
|
||||
size_t amount = left < 64 ? left : 64;
|
||||
memcpy(keystream + offset, work, amount);
|
||||
offset += amount;
|
||||
}
|
||||
|
||||
explicit_bzero(work, sizeof(work));
|
||||
}
|
||||
|
||||
#define KEYSZ 32
|
||||
#define IVSZ 8
|
||||
|
||||
static pthread_mutex_t arc4random_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/* TODO: Use address space randomization of libc global variables such that
|
||||
these variables end up in unpredictable places. */
|
||||
|
||||
static bool rs_initialized = false;
|
||||
static pid_t rs_pid = 0;
|
||||
static size_t rs_have = 0;
|
||||
static size_t rs_count = 0;
|
||||
static struct chacha rs_chacha;
|
||||
static unsigned char rs_buf[16 * 64];
|
||||
|
||||
extern "C" void arc4random_buf(void* buffer_ptr, size_t size)
|
||||
{
|
||||
unsigned char* buffer = (unsigned char*) buffer_ptr;
|
||||
|
||||
pthread_mutex_lock(&arc4random_mutex);
|
||||
|
||||
/* TODO: Employ zero-memory-on-fork semantics instead. */
|
||||
/* pid_t are never reused on Sortix at the moment. */
|
||||
if ( getpid() != rs_pid )
|
||||
{
|
||||
rs_count = 0;
|
||||
rs_have = 0;
|
||||
memset(rs_buf, 0, sizeof(rs_buf));
|
||||
/* TODO: Should rs_chacha be zeroed as well? */
|
||||
rs_pid = getpid();
|
||||
}
|
||||
|
||||
while ( 0 < size )
|
||||
{
|
||||
size_t available = rs_have;
|
||||
if ( size < available )
|
||||
available = size;
|
||||
if ( rs_count < available )
|
||||
available = rs_count;
|
||||
|
||||
if ( 0 < available )
|
||||
{
|
||||
unsigned char* randomness = rs_buf + sizeof(rs_buf) - rs_have;
|
||||
memcpy(buffer, randomness, available);
|
||||
memset(randomness, 0, available);
|
||||
rs_count -= available;
|
||||
rs_have -= available;
|
||||
buffer += available;
|
||||
size -= available;
|
||||
}
|
||||
|
||||
if ( rs_count == 0 )
|
||||
{
|
||||
unsigned char entropy[KEYSZ + IVSZ];
|
||||
getentropy(entropy, sizeof(entropy));
|
||||
|
||||
if ( rs_initialized )
|
||||
{
|
||||
unsigned char old_entropy[sizeof(entropy)];
|
||||
chacha_keystream(&rs_chacha, old_entropy, sizeof(old_entropy));
|
||||
for ( size_t i = 0; i < sizeof(entropy); i++ )
|
||||
entropy[i] ^= old_entropy[i];
|
||||
explicit_bzero(old_entropy, sizeof(old_entropy));
|
||||
}
|
||||
|
||||
chacha_keysetup(&rs_chacha, entropy);
|
||||
chacha_ivsetup(&rs_chacha, entropy + KEYSZ);
|
||||
rs_initialized = true;
|
||||
|
||||
explicit_bzero(entropy, sizeof(entropy));
|
||||
|
||||
rs_have = 0;
|
||||
memset(rs_buf, 0, sizeof(rs_buf));
|
||||
|
||||
rs_count = 1600000;
|
||||
}
|
||||
|
||||
if ( rs_have == 0 )
|
||||
{
|
||||
chacha_keystream(&rs_chacha, rs_buf, sizeof(rs_buf));
|
||||
chacha_keysetup(&rs_chacha, rs_buf);
|
||||
chacha_ivsetup(&rs_chacha, rs_buf + KEYSZ);
|
||||
rs_have = sizeof(rs_buf) - KEYSZ - IVSZ;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&arc4random_mutex);
|
||||
}
|
55
libc/stdlib/arc4random_uniform.cpp
Normal file
55
libc/stdlib/arc4random_uniform.cpp
Normal file
|
@ -0,0 +1,55 @@
|
|||
/* $OpenBSD: arc4random_uniform.c,v 1.1 2014/07/12 13:24:54 deraadt Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008, Damien Miller <djm@openbsd.org>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Calculate a uniformly distributed random number less than upper_bound
|
||||
* avoiding "modulo bias".
|
||||
*
|
||||
* Uniformity is achieved by generating new random numbers until the one
|
||||
* returned is outside the range [0, 2**32 % upper_bound). This
|
||||
* guarantees the selected random number will be inside
|
||||
* [2**32 % upper_bound, 2**32) which maps back to [0, upper_bound)
|
||||
* after reduction modulo upper_bound.
|
||||
*/
|
||||
extern "C" uint32_t arc4random_uniform(uint32_t upper_bound)
|
||||
{
|
||||
uint32_t r, min;
|
||||
|
||||
if (upper_bound < 2)
|
||||
return 0;
|
||||
|
||||
/* 2**32 % x == (2**32 - x) % x */
|
||||
min = -upper_bound % upper_bound;
|
||||
|
||||
/*
|
||||
* This could theoretically loop forever but each retry has
|
||||
* p > 0.5 (worst case, usually far better) of selecting a
|
||||
* number inside the range we need, so it should rarely need
|
||||
* to re-roll.
|
||||
*/
|
||||
for (;;) {
|
||||
r = arc4random();
|
||||
if (r >= min)
|
||||
break;
|
||||
}
|
||||
|
||||
return r % upper_bound;
|
||||
}
|
Loading…
Add table
Reference in a new issue