NetBSD/sys/crypto/nist_ctr_drbg/nist_ctr_drbg.c
tls 3afd44cf08 First step of random number subsystem rework described in
<20111022023242.BA26F14A158@mail.netbsd.org>.  This change includes
the following:

	An initial cleanup and minor reorganization of the entropy pool
	code in sys/dev/rnd.c and sys/dev/rndpool.c.  Several bugs are
	fixed.  Some effort is made to accumulate entropy more quickly at
	boot time.

	A generic interface, "rndsink", is added, for stream generators to
	request that they be re-keyed with good quality entropy from the pool
	as soon as it is available.

	The arc4random()/arc4randbytes() implementation in libkern is
	adjusted to use the rndsink interface for rekeying, which helps
	address the problem of low-quality keys at boot time.

	An implementation of the FIPS 140-2 statistical tests for random
	number generator quality is provided (libkern/rngtest.c).  This
	is based on Greg Rose's implementation from Qualcomm.

	A new random stream generator, nist_ctr_drbg, is provided.  It is
	based on an implementation of the NIST SP800-90 CTR_DRBG by
	Henric Jungheim.  This generator users AES in a modified counter
	mode to generate a backtracking-resistant random stream.

	An abstraction layer, "cprng", is provided for in-kernel consumers
	of randomness.  The arc4random/arc4randbytes API is deprecated for
	in-kernel use.  It is replaced by "cprng_strong".  The current
	cprng_fast implementation wraps the existing arc4random
	implementation.  The current cprng_strong implementation wraps the
	new CTR_DRBG implementation.  Both interfaces are rekeyed from
	the entropy pool automatically at intervals justifiable from best
	current cryptographic practice.

	In some quick tests, cprng_fast() is about the same speed as
	the old arc4randbytes(), and cprng_strong() is about 20% faster
	than rnd_extract_data().  Performance is expected to improve.

	The AES code in src/crypto/rijndael is no longer an optional
	kernel component, as it is required by cprng_strong, which is
	not an optional kernel component.

	The entropy pool output is subjected to the rngtest tests at
	startup time; if it fails, the system will reboot.  There is
	approximately a 3/10000 chance of a false positive from these
	tests.  Entropy pool _input_ from hardware random numbers is
	subjected to the rngtest tests at attach time, as well as the
	FIPS continuous-output test, to detect bad or stuck hardware
	RNGs; if any are detected, they are detached, but the system
	continues to run.

	A problem with rndctl(8) is fixed -- datastructures with
	pointers in arrays are no longer passed to userspace (this
	was not a security problem, but rather a major issue for
	compat32).  A new kernel will require a new rndctl.

	The sysctl kern.arandom() and kern.urandom() nodes are hooked
	up to the new generators, but the /dev/*random pseudodevices
	are not, yet.

	Manual pages for the new kernel interfaces are forthcoming.
2011-11-19 22:51:18 +00:00

665 lines
17 KiB
C

/* $NetBSD: nist_ctr_drbg.c,v 1.1 2011/11/19 22:51:22 tls Exp $ */
/*-
* Copyright (c) 2011 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Thor Lancelot Simon.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Copyright (c) 2007 Henric Jungheim <software@henric.info>
*
* 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.
*/
/*
* NIST SP 800-90 CTR_DRBG (Random Number Generator)
*/
#include <sys/types.h>
#include <sys/systm.h>
#include <crypto/nist_ctr_drbg/nist_ctr_drbg.h>
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: nist_ctr_drbg.c,v 1.1 2011/11/19 22:51:22 tls Exp $");
/*
* NIST SP 800-90 March 2007
* 10.4.2 Derivation Function Using a Block Cipher Algorithm
* Global Constants
*/
static NIST_Key nist_cipher_df_ctx;
static unsigned char nist_cipher_df_encrypted_iv[NIST_BLOCK_SEEDLEN / NIST_BLOCK_OUTLEN][NIST_BLOCK_OUTLEN_BYTES];
/*
* NIST SP 800-90 March 2007
* 10.2.1.3.2 The Process Steps for Instantiation When a Derivation
* Function is Used
* Global Constants
*/
static NIST_Key nist_cipher_zero_ctx;
/*
* NIST SP 800-90 March 2007
* 10.2.1.5.2 The Process Steps for Generating Pseudorandom Bits When a
* Derivation Function is Used for the DRBG Implementation
* Global Constants
*/
static const unsigned int
nist_ctr_drgb_generate_null_input[NIST_BLOCK_SEEDLEN_INTS] = { 0 };
/*
* Utility
*/
/*
* nist_increment_block
* Increment the output block as a big-endian number.
*/
static inline void
nist_increment_block(unsigned long *V)
{
int i;
unsigned long x;
for (i = NIST_BLOCK_OUTLEN_LONGS - 1; i >= 0; --i) {
x = NIST_NTOHL(V[i]) + 1;
V[i] = NIST_HTONL(x);
if (x) /* There was only a carry if we are zero */
return;
}
}
/*
* NIST SP 800-90 March 2007
* 10.4.3 BCC Function
*/
static void
nist_ctr_drbg_bcc_update(const NIST_Key *ctx, const unsigned int *data,
int n, unsigned int *chaining_value)
{
int i, j;
unsigned int input_block[NIST_BLOCK_OUTLEN_INTS];
/* [4] for i = 1 to n */
for (i = 0; i < n; ++i) {
/* [4.1] input_block = chaining_value XOR block_i */
for (j = 0; j < NIST_BLOCK_OUTLEN_INTS; ++j)
input_block[j] = chaining_value[j] ^ *data++;
/* [4.2] chaining_value = Block_Encrypt(Key, input_block) */
Block_Encrypt(ctx, &input_block[0], &chaining_value[0]);
}
/* [5] output_block = chaining_value */
/* chaining_value already is output_block, so no copy is required */
}
static void
nist_ctr_drbg_bcc(NIST_Key *ctx, const unsigned int *data,
int n, unsigned int *output_block)
{
unsigned int *chaining_value = output_block;
/* [1] chaining_value = 0^outlen */
memset(&chaining_value[0], 0, NIST_BLOCK_OUTLEN_BYTES);
nist_ctr_drbg_bcc_update(ctx, data, n, output_block);
}
/*
* NIST SP 800-90 March 2007
* 10.4.2 Derivation Function Using a Block Cipher Algorithm
*/
typedef struct {
int index;
unsigned char S[NIST_BLOCK_OUTLEN_BYTES];
} NIST_CTR_DRBG_DF_BCC_CTX;
static inline int
check_int_alignment(const void *p)
{
intptr_t ip = (const char *)p - (const char *)0;
if (ip & (sizeof(int) - 1))
return 0;
return 1;
}
static void
nist_ctr_drbg_df_bcc_init(NIST_CTR_DRBG_DF_BCC_CTX *ctx, int L, int N)
{
unsigned int *S = (unsigned int *)ctx->S;
/* [4] S = L || N || input_string || 0x80 */
S[0] = NIST_HTONL(L);
S[1] = NIST_HTONL(N);
ctx->index = 2 * sizeof(S[0]);
}
static void
nist_ctr_drbg_df_bcc_update(NIST_CTR_DRBG_DF_BCC_CTX *ctx,
const char *input_string,
int input_string_length, unsigned int *temp)
{
int i, len;
int index = ctx->index;
unsigned char *S = ctx->S;
if (index) {
KASSERT(index < NIST_BLOCK_OUTLEN_BYTES);
len = NIST_BLOCK_OUTLEN_BYTES - index;
if (input_string_length < len)
len = input_string_length;
memcpy(&S[index], input_string, len);
index += len;
input_string += len;
input_string_length -= len;
if (index < NIST_BLOCK_OUTLEN_BYTES) {
ctx->index = index;
return;
}
/* We have a full block in S, so let's process it */
/* [9.2] BCC */
nist_ctr_drbg_bcc_update(&nist_cipher_df_ctx,
(unsigned int *)&S[0], 1, temp);
index = 0;
}
/* ctx->S is empty, so let's handle as many input blocks as we can */
len = input_string_length / NIST_BLOCK_OUTLEN_BYTES;
if (len > 0) {
if (check_int_alignment(input_string)) {
/* [9.2] BCC */
nist_ctr_drbg_bcc_update(&nist_cipher_df_ctx,
(const unsigned int *)
input_string, len, temp);
input_string += len * NIST_BLOCK_OUTLEN_BYTES;
input_string_length -= len * NIST_BLOCK_OUTLEN_BYTES;
} else {
for (i = 0; i < len; ++i) {
memcpy(&S[0], input_string,
NIST_BLOCK_OUTLEN_BYTES);
/* [9.2] BCC */
nist_ctr_drbg_bcc_update(&nist_cipher_df_ctx,
(unsigned int *)
&S[0], 1, temp);
input_string += NIST_BLOCK_OUTLEN_BYTES;
input_string_length -= NIST_BLOCK_OUTLEN_BYTES;
}
}
}
KASSERT(input_string_length < NIST_BLOCK_OUTLEN_BYTES);
if (input_string_length) {
memcpy(&S[0], input_string, input_string_length);
index = input_string_length;
}
ctx->index = index;
}
static void
nist_ctr_drbg_df_bcc_final(NIST_CTR_DRBG_DF_BCC_CTX *ctx, unsigned int *temp)
{
int index;
unsigned char* S = ctx->S;
static const char endmark[] = { 0x80 };
nist_ctr_drbg_df_bcc_update(ctx, endmark, sizeof(endmark), temp);
index = ctx->index;
if (index) {
memset(&S[index], 0, NIST_BLOCK_OUTLEN_BYTES - index);
/* [9.2] BCC */
nist_ctr_drbg_bcc_update(&nist_cipher_df_ctx,
(unsigned int *)&S[0], 1, temp);
}
}
static int
nist_ctr_drbg_block_cipher_df(const char *input_string[], unsigned int L[],
int input_string_count,
unsigned char *output_string, unsigned int N)
{
int j, k, blocks, sum_L;
unsigned int *temp;
unsigned int *X;
NIST_Key ctx;
NIST_CTR_DRBG_DF_BCC_CTX df_bcc_ctx;
unsigned int buffer[NIST_BLOCK_SEEDLEN_INTS];
/*
* NIST SP 800-90 March 2007 10.4.2 states that 512 bits is
* the maximum length for the approved block cipher algorithms.
*/
unsigned int output_buffer[512 / 8 / sizeof(unsigned int)];
if (N > sizeof(output_buffer) || N < 1)
return 0;
sum_L = 0;
for (j = 0; j < input_string_count; ++j)
sum_L += L[j];
/* [6] temp = Null string */
temp = buffer;
/* [9] while len(temp) < keylen + outlen, do */
for (j = 0; j < NIST_BLOCK_SEEDLEN / NIST_BLOCK_OUTLEN; ++j) {
/* [9.2] temp = temp || BCC(K, (IV || S)) */
/* Since we have precomputed BCC(K, IV), we start with that... */
memcpy(&temp[0], &nist_cipher_df_encrypted_iv[j][0],
NIST_BLOCK_OUTLEN_BYTES);
nist_ctr_drbg_df_bcc_init(&df_bcc_ctx, sum_L, N);
/* Compute the rest of BCC(K, (IV || S)) */
for (k = 0; k < input_string_count; ++k)
nist_ctr_drbg_df_bcc_update(&df_bcc_ctx,
input_string[k],
L[k], temp);
nist_ctr_drbg_df_bcc_final(&df_bcc_ctx, temp);
temp += NIST_BLOCK_OUTLEN_INTS;
}
nist_zeroize(&df_bcc_ctx, sizeof(df_bcc_ctx));
/* [6] temp = Null string */
temp = buffer;
/* [10] K = Leftmost keylen bits of temp */
Block_Schedule_Encryption(&ctx, &temp[0]);
/* [11] X = next outlen bits of temp */
X = &temp[NIST_BLOCK_KEYLEN_INTS];
/* [12] temp = Null string */
temp = output_buffer;
/* [13] While len(temp) < number_of_bits_to_return, do */
blocks = (int)(N / NIST_BLOCK_OUTLEN_BYTES);
if (N & (NIST_BLOCK_OUTLEN_BYTES - 1))
++blocks;
for (j = 0; j < blocks; ++j) {
/* [13.1] X = Block_Encrypt(K, X) */
Block_Encrypt(&ctx, X, temp);
X = temp;
temp += NIST_BLOCK_OUTLEN_INTS;
}
/* [14] requested_bits = Leftmost number_of_bits_to_return of temp */
memcpy(output_string, output_buffer, N);
nist_zeroize(&ctx, sizeof(ctx));
return 0;
}
static int
nist_ctr_drbg_block_cipher_df_initialize(void)
{
int i, err;
unsigned char K[NIST_BLOCK_KEYLEN_BYTES];
unsigned int IV[NIST_BLOCK_OUTLEN_INTS];
/* [8] K = Leftmost keylen bits of 0x00010203 ... 1D1E1F */
for (i = 0; i < sizeof(K); ++i)
K[i] = (unsigned char)i;
err = Block_Schedule_Encryption(&nist_cipher_df_ctx, K);
if (err)
return err;
/*
* Precompute the partial BCC result from encrypting the IVs:
* nist_cipher_df_encrypted_iv[i] = BCC(K, IV(i))
*/
/* [7] i = 0 */
/* [9.1] IV = i || 0^(outlen - len(i)) */
memset(&IV[0], 0, sizeof(IV));
/* [9.3] i = i + 1 */
for (i = 0; i < NIST_BLOCK_SEEDLEN / NIST_BLOCK_OUTLEN; ++i) {
/* [9.1] IV = i || 0^(outlen - len(i)) */
IV[0] = NIST_HTONL(i);
/*
* [9.2] temp = temp || BCC(K, (IV || S))
* (the IV part, at least)
*/
nist_ctr_drbg_bcc(&nist_cipher_df_ctx, &IV[0], 1,
(unsigned int *)
&nist_cipher_df_encrypted_iv[i][0]);
}
return 0;
}
/*
* NIST SP 800-90 March 2007
* 10.2.1.2 The Update Function
*/
static void
nist_ctr_drbg_update(NIST_CTR_DRBG *drbg, const unsigned int *provided_data)
{
int i;
unsigned int temp[NIST_BLOCK_SEEDLEN_INTS];
unsigned int* output_block;
/* 2. while (len(temp) < seedlen) do */
for (output_block = temp;
output_block < &temp[NIST_BLOCK_SEEDLEN_INTS];
output_block += NIST_BLOCK_OUTLEN_INTS) {
/* 2.1 V = (V + 1) mod 2^outlen */
nist_increment_block((unsigned long *)&drbg->V[0]);
/* 2.2 output_block = Block_Encrypt(K, V) */
Block_Encrypt(&drbg->ctx, drbg->V, output_block);
}
/* 3 temp is already of size seedlen (NIST_BLOCK_SEEDLEN_INTS) */
/* 4 (part 1) temp = temp XOR provided_data */
for (i = 0; i < NIST_BLOCK_KEYLEN_INTS; ++i)
temp[i] ^= *provided_data++;
/* 5 Key = leftmost keylen bits of temp */
Block_Schedule_Encryption(&drbg->ctx, &temp[0]);
/* 4 (part 2) combined with 6 V = rightmost outlen bits of temp */
for (i = 0; i < NIST_BLOCK_OUTLEN_INTS; ++i)
drbg->V[i] =
temp[NIST_BLOCK_KEYLEN_INTS + i] ^ *provided_data++;
}
/*
* NIST SP 800-90 March 2007
* 10.2.1.3.2 The Process Steps for Instantiation When a Derivation
* Function is Used
*/
int
nist_ctr_drbg_instantiate(NIST_CTR_DRBG* drbg,
const void *entropy_input, int entropy_input_length,
const void *nonce, int nonce_length,
const void *personalization_string, int personalization_string_length)
{
int err, count;
unsigned int seed_material[NIST_BLOCK_SEEDLEN_INTS];
unsigned int length[3];
const char *input_string[3];
/* [1] seed_material = entropy_input ||
* nonce || personalization_string
*/
input_string[0] = entropy_input;
length[0] = entropy_input_length;
input_string[1] = nonce;
length[1] = nonce_length;
count = 2;
if (personalization_string) {
input_string[count] = personalization_string;
length[count] = personalization_string_length;
++count;
}
/* [2] seed_material = Block_Cipher_df(seed_material, seedlen) */
err = nist_ctr_drbg_block_cipher_df(input_string, length, count,
(unsigned char *)seed_material,
sizeof(seed_material));
if (err)
return err;
/* [3] Key = 0^keylen */
memcpy(&drbg->ctx, &nist_cipher_zero_ctx, sizeof(drbg->ctx));
/* [4] V = 0^outlen */
memset(&drbg->V, 0, sizeof(drbg->V));
/* [5] (Key, V) = Update(seed_material, Key, V) */
nist_ctr_drbg_update(drbg, seed_material);
/* [6] reseed_counter = 1 */
drbg->reseed_counter = 1;
return 0;
}
static int
nist_ctr_drbg_instantiate_initialize(void)
{
int err;
unsigned char K[NIST_BLOCK_KEYLEN_BYTES];
memset(&K[0], 0, sizeof(K));
err = Block_Schedule_Encryption(&nist_cipher_zero_ctx, &K[0]);
return err;
}
/*
* NIST SP 800-90 March 2007
* 10.2.1.4.2 The Process Steps for Reseeding When a Derivation
* Function is Used
*/
int
nist_ctr_drbg_reseed(NIST_CTR_DRBG *drbg,
const void *entropy_input, int entropy_input_length,
const void *additional_input,
int additional_input_length)
{
int err, count;
const char *input_string[2];
unsigned int length[2];
unsigned int seed_material[NIST_BLOCK_SEEDLEN_INTS];
/* [1] seed_material = entropy_input || additional_input */
input_string[0] = entropy_input;
length[0] = entropy_input_length;
count = 1;
if (additional_input) {
input_string[count] = additional_input;
length[count] = additional_input_length;
++count;
}
/* [2] seed_material = Block_Cipher_df(seed_material, seedlen) */
err = nist_ctr_drbg_block_cipher_df(input_string, length, count,
(unsigned char *)seed_material,
sizeof(seed_material));
if (err)
return err;
/* [3] (Key, V) = Update(seed_material, Key, V) */
nist_ctr_drbg_update(drbg, seed_material);
/* [4] reseed_counter = 1 */
drbg->reseed_counter = 1;
return 0;
}
/*
* NIST SP 800-90 March 2007
* 10.2.1.5.2 The Process Steps for Generating Pseudorandom Bits When a
* Derivation Function is Used for the DRBG Implementation
*/
static void
nist_ctr_drbg_generate_block(NIST_CTR_DRBG *drbg, unsigned int *output_block)
{
/* [4.1] V = (V + 1) mod 2^outlen */
nist_increment_block((unsigned long *)&drbg->V[0]);
/* [4.2] output_block = Block_Encrypt(Key, V) */
Block_Encrypt(&drbg->ctx, &drbg->V[0], output_block);
}
int
nist_ctr_drbg_generate(NIST_CTR_DRBG * drbg,
void *output_string, int output_string_length,
const void *additional_input,
int additional_input_length)
{
int i, len, err;
int blocks = output_string_length / NIST_BLOCK_OUTLEN_BYTES;
unsigned char* p;
unsigned int* temp;
const char *input_string[1];
unsigned int length[1];
unsigned int buffer[NIST_BLOCK_OUTLEN_BYTES];
unsigned int additional_input_buffer[NIST_BLOCK_SEEDLEN_INTS];
int ret = 0;
if (output_string_length < 1)
return 1;
/* [1] If reseed_counter > reseed_interval ... */
if (drbg->reseed_counter >= NIST_CTR_DRBG_RESEED_INTERVAL) {
ret = 1;
goto out;
}
/* [2] If (addional_input != Null), then */
if (additional_input) {
input_string[0] = additional_input;
length[0] = additional_input_length;
/*
* [2.1] additional_input =
* Block_Cipher_df(additional_input, seedlen)
*/
err = nist_ctr_drbg_block_cipher_df(input_string, length, 1,
(unsigned char *)additional_input_buffer,
sizeof(additional_input_buffer));
if (err) {
ret = err;
goto out;
}
/* [2.2] (Key, V) = Update(additional_input, Key, V) */
nist_ctr_drbg_update(drbg, additional_input_buffer);
}
if (blocks && check_int_alignment(output_string)) {
/* [3] temp = Null */
temp = (unsigned int *)output_string;
for (i = 0; i < blocks; ++i) {
nist_ctr_drbg_generate_block(drbg, temp);
temp += NIST_BLOCK_OUTLEN_INTS;
output_string_length -= NIST_BLOCK_OUTLEN_BYTES;
}
output_string = (unsigned char *)temp;
}
/* [3] temp = Null */
temp = buffer;
len = NIST_BLOCK_OUTLEN_BYTES;
/* [4] While (len(temp) < requested_number_of_bits) do: */
p = output_string;
while (output_string_length > 0) {
nist_ctr_drbg_generate_block(drbg, temp);
if (output_string_length < NIST_BLOCK_OUTLEN_BYTES)
len = output_string_length;
memcpy(p, temp, len);
p += len;
output_string_length -= len;
}
/* [6] (Key, V) = Update(additional_input, Key, V) */
nist_ctr_drbg_update(drbg, additional_input ?
&additional_input_buffer[0] :
&nist_ctr_drgb_generate_null_input[0]);
/* [7] reseed_counter = reseed_counter + 1 */
++drbg->reseed_counter;
out:
return ret;
}
int
nist_ctr_initialize(void)
{
int err;
err = nist_ctr_drbg_instantiate_initialize();
if (err)
return err;
err = nist_ctr_drbg_block_cipher_df_initialize();
if (err)
return err;
return 0;
}
int
nist_ctr_drbg_destroy(NIST_CTR_DRBG* drbg)
{
nist_zeroize(drbg, sizeof(*drbg));
drbg->reseed_counter = ~0U;
return 1;
}