Add arc4random(3).

This commit is contained in:
Jonas 'Sortie' Termansen 2014-07-19 21:08:25 +02:00
parent e460be7a72
commit edb19f2394
5 changed files with 334 additions and 0 deletions

View File

@ -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 \

View File

@ -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

View 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;
}

View 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);
}

View 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;
}