openssl/crypto/rand/rand_vms.c
Dr. Matthias St. Pierre c16de9d832 Fix reseeding issues of the public RAND_DRBG
Reseeding is handled very differently by the classic RAND_METHOD API
and the new RAND_DRBG api. These differences led to some problems when
the new RAND_DRBG was made the default OpenSSL RNG. In particular,
RAND_add() did not work as expected anymore. These issues are discussed
on the thread '[openssl-dev] Plea for a new public OpenSSL RNG API'
and in Pull Request #4328. This commit fixes the mentioned issues,
introducing the following changes:

- Replace the fixed size RAND_BYTES_BUFFER by a new RAND_POOL API which
  facilitates collecting entropy by the get_entropy() callback.
- Don't use RAND_poll()/RAND_add() for collecting entropy from the
  get_entropy() callback anymore. Instead, replace RAND_poll() by
  RAND_POOL_acquire_entropy().
- Add a new function rand_drbg_restart() which tries to get the DRBG
  in an instantiated state by all means, regardless of the current
  state (uninstantiated, error, ...) the DRBG is in. If the caller
  provides entropy or additional input, it will be used for reseeding.
- Restore the original documented behaviour of RAND_add() and RAND_poll()
  (namely to reseed the DRBG immediately) by a new implementation based
  on rand_drbg_restart().
- Add automatic error recovery from temporary failures of the entropy
  source to RAND_DRBG_generate() using the rand_drbg_restart() function.

Reviewed-by: Paul Dale <paul.dale@oracle.com>
Reviewed-by: Kurt Roeckx <kurt@roeckx.be>
Reviewed-by: Rich Salz <rsalz@openssl.org>
Reviewed-by: Ben Kaduk <kaduk@mit.edu>
(Merged from https://github.com/openssl/openssl/pull/4328)
2017-10-18 08:39:20 -05:00

126 lines
3.6 KiB
C

/*
* Copyright 2001-2017 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include "e_os.h"
#if defined(OPENSSL_SYS_VMS)
# include <openssl/rand.h>
# include "rand_lcl.h"
# include <descrip.h>
# include <jpidef.h>
# include <ssdef.h>
# include <starlet.h>
# include <efndef>
# ifdef __DECC
# pragma message disable DOLLARID
# endif
# ifndef OPENSSL_RAND_SEED_OS
# error "Unsupported seeding method configured; must be os"
# endif
/*
* Use 32-bit pointers almost everywhere. Define the type to which to cast a
* pointer passed to an external function.
*/
# if __INITIAL_POINTER_SIZE == 64
# define PTR_T __void_ptr64
# pragma pointer_size save
# pragma pointer_size 32
# else
# define PTR_T void *
# endif
static struct items_data_st {
short length, code; /* length is number of bytes */
} items_data[] = {
{4, JPI$_BUFIO},
{4, JPI$_CPUTIM},
{4, JPI$_DIRIO},
{4, JPI$_IMAGECOUNT},
{8, JPI$_LAST_LOGIN_I},
{8, JPI$_LOGINTIM},
{4, JPI$_PAGEFLTS},
{4, JPI$_PID},
{4, JPI$_PPGCNT},
{4, JPI$_WSPEAK},
{4, JPI$_FINALEXC},
{0, 0}
};
size_t RAND_POOL_acquire_entropy(RAND_POOL *pool)
{
/* determine the number of items in the JPI array */
struct items_data_st item_entry;
int item_entry_count = OSSL_NELEM(items_data);
/* Create the JPI itemlist array to hold item_data content */
struct {
short length, code;
int *buffer;
int *retlen;
} item[item_entry_count], *pitem;
struct items_data_st *pitems_data;
int data_buffer[(item_entry_count * 2) + 4]; /* 8 bytes per entry max */
int iosb[2];
int sys_time[2];
int *ptr;
int i, j ;
int tmp_length = 0;
int total_length = 0;
/* Setup itemlist for GETJPI */
pitems_data = items_data;
for (pitem = item; pitems_data->length != 0; pitem++) {
pitem->length = pitems_data->length;
pitem->code = pitems_data->code;
pitem->buffer = &data_buffer[total_length];
pitem->retlen = 0;
/* total_length is in longwords */
total_length += pitems_data->length / 4;
pitems_data++;
}
pitem->length = pitem->code = 0;
/* Fill data_buffer with various info bits from this process */
if (sys$getjpiw(EFN$C_ENF, NULL, NULL, item, &iosb, 0, 0) != SS$_NORMAL)
return 0;
/* Now twist that data to seed the SSL random number init */
for (i = 0; i < total_length; i++) {
sys$gettim((struct _generic_64 *)&sys_time[0]);
srand(sys_time[0] * data_buffer[0] * data_buffer[1] + i);
if (i == (total_length - 1)) { /* for JPI$_FINALEXC */
ptr = &data_buffer[i];
for (j = 0; j < 4; j++) {
data_buffer[i + j] = ptr[j];
/* OK to use rand() just to scramble the seed */
data_buffer[i + j] ^= (sys_time[0] ^ rand());
tmp_length++;
}
} else {
/* OK to use rand() just to scramble the seed */
data_buffer[i] ^= (sys_time[0] ^ rand());
}
}
total_length += (tmp_length - 1);
/*
* Size of seed is total_length*4 bytes (64bytes). The original assumption
* was that it contains 4 bits of entropy per byte. This makes a total
* amount of total_length*16 bits (256bits).
*/
return RAND_POOL_add(pool,
(PTR_T)data_buffer, total_length * 4,
total_length * 16);
}
#endif