diff options
Diffstat (limited to 'fips-1.0/rand/fips_rand.c')
-rw-r--r-- | fips-1.0/rand/fips_rand.c | 475 |
1 files changed, 241 insertions, 234 deletions
diff --git a/fips-1.0/rand/fips_rand.c b/fips-1.0/rand/fips_rand.c index 7df2dc804e..7e7a036231 100644 --- a/fips-1.0/rand/fips_rand.c +++ b/fips-1.0/rand/fips_rand.c @@ -1,5 +1,5 @@ /* ==================================================================== - * Copyright (c) 2003 The OpenSSL Project. All rights reserved. + * Copyright (c) 2007 The OpenSSL Project. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -48,7 +48,7 @@ */ /* - * This is a FIPS approved PRNG, ANSI X9.31 A.2.4. + * This is a FIPS approved AES PRNG based on ANSI X9.31 A.2.4. */ #include "e_os.h" @@ -60,8 +60,8 @@ #define _XOPEN_SOURCE_EXTENDED 1 #endif -#include <openssl/des.h> #include <openssl/rand.h> +#include <openssl/aes.h> #include <openssl/err.h> #include <openssl/fips_rand.h> #ifndef OPENSSL_SYS_WIN32 @@ -79,281 +79,288 @@ void *OPENSSL_stderr(void); -#ifdef OPENSSL_FIPS +#define AES_BLOCK_LENGTH 16 -#define SEED_SIZE 8 -static unsigned char seed[SEED_SIZE]; -static FIPS_RAND_SIZE_T n_seed; -static FIPS_RAND_SIZE_T o_seed; -static DES_cblock key1; -static DES_cblock key2; -static DES_key_schedule ks1,ks2; -static int key_set; -static int key_init; -static int test_mode; -static unsigned char test_faketime[8]; +/* AES FIPS PRNG implementation */ -#ifndef GETPID_IS_MEANINGLESS -static int seed_pid; -static int key_pid; -#endif - -static void fips_rand_cleanup(void); -static void fips_rand_add(const void *buf, FIPS_RAND_SIZE_T num, double add_entropy); -static int fips_rand_bytes(unsigned char *buf, FIPS_RAND_SIZE_T num); -static int fips_rand_status(void); - -static const RAND_METHOD rand_fips_meth= - { - FIPS_rand_seed, - fips_rand_bytes, - fips_rand_cleanup, - fips_rand_add, - fips_rand_bytes, - fips_rand_status - }; +typedef struct + { + int seeded; + int keyed; + int test_mode; + int second; + int error; + unsigned long counter; + AES_KEY ks; + int vpos; + unsigned char V[AES_BLOCK_LENGTH]; + unsigned char DT[AES_BLOCK_LENGTH]; + unsigned char last[AES_BLOCK_LENGTH]; + } FIPS_PRNG_CTX; + +static FIPS_PRNG_CTX sctx; + +void fips_rand_prng_reset(FIPS_PRNG_CTX *ctx) + { + ctx->seeded = 0; + ctx->keyed = 0; + ctx->test_mode = 0; + ctx->counter = 0; + ctx->second = 0; + ctx->error = 0; + ctx->vpos = 0; + OPENSSL_cleanse(ctx->V, AES_BLOCK_LENGTH); + OPENSSL_cleanse(&ctx->ks, sizeof(AES_KEY)); + } + -static int second; +static int fips_set_prng_key(FIPS_PRNG_CTX *ctx, + const unsigned char *key, FIPS_RAND_SIZE_T keylen) + { + if (keylen != 16 && keylen != 24 && keylen != 32) + { + /* error: invalid key size */ + return 0; + } + AES_set_encrypt_key(key, keylen << 3, &ctx->ks); + ctx->keyed = 1; + ctx->seeded = 0; + ctx->second = 0; + return 1; + } -const RAND_METHOD *FIPS_rand_method(void) -{ - return &rand_fips_meth; -} +static int fips_set_prng_seed(FIPS_PRNG_CTX *ctx, + const unsigned char *seed, FIPS_RAND_SIZE_T seedlen) + { + int i; + if (!ctx->keyed) + return 0; + /* In test mode seed is just supplied data */ + if (ctx->test_mode) + { + if (seedlen != AES_BLOCK_LENGTH) + return 0; + memcpy(ctx->V, seed, AES_BLOCK_LENGTH); + ctx->seeded = 1; + return 1; + } + /* Outside test mode XOR supplied data with existing seed */ + for (i = 0; i < seedlen; i++) + { + ctx->V[ctx->vpos++] ^= seed[i]; + if (ctx->vpos == AES_BLOCK_LENGTH) + { + ctx->vpos = 0; + ctx->seeded = 1; + } + } + return 1; + } -void FIPS_set_prng_key(const unsigned char k1[8],const unsigned char k2[8]) - { - memcpy(&key1,k1,sizeof key1); - memcpy(&key2,k2,sizeof key2); - key_set=1; -#ifndef GETPID_IS_MEANINGLESS - key_pid=getpid(); -#endif - second=0; - } +int fips_set_test_mode(FIPS_PRNG_CTX *ctx) + { + if (ctx->keyed) + { + RANDerr(RAND_F_FIPS_SET_TEST_MODE,RAND_R_PRNG_KEYED); + return 0; + } + ctx->test_mode = 1; + return 1; + } -void FIPS_test_mode(int test,const unsigned char faketime[8]) - { - test_mode=test; - if(!test_mode) - return; - memcpy(test_faketime,faketime,sizeof test_faketime); - } +int FIPS_rand_test_mode(void) + { + return fips_set_test_mode(&sctx); + } -/* NB: this returns true if _partially_ seeded */ -int FIPS_rand_seeded() - { return key_set || n_seed; } +int FIPS_rand_set_dt(unsigned char *dt) + { + if (!sctx.test_mode) + { + RANDerr(RAND_F_FIPS_SET_DT,RAND_R_NOT_IN_TEST_MODE); + return 0; + } + memcpy(sctx.DT, dt, AES_BLOCK_LENGTH); + return 1; + } -static void fips_gettime(unsigned char buf[8]) +static void fips_get_dt(FIPS_PRNG_CTX *ctx) { #ifdef OPENSSL_SYS_WIN32 - FILETIME ft; + FILETIME ft; #else - struct timeval tv; + struct timeval tv; #endif + unsigned char *buf = ctx->DT; + + unsigned long pid; - if(test_mode) - { - /* fprintf(OPENSSL_stderr(),"WARNING!!! PRNG IN TEST MODE!!!\n"); */ - memcpy(buf,test_faketime,sizeof test_faketime); - return; - } #ifdef OPENSSL_SYS_WIN32 - GetSystemTimeAsFileTime(&ft); - buf[0] = (unsigned char) (ft.dwHighDateTime & 0xff); - buf[1] = (unsigned char) ((ft.dwHighDateTime >> 8) & 0xff); - buf[2] = (unsigned char) ((ft.dwHighDateTime >> 16) & 0xff); - buf[3] = (unsigned char) ((ft.dwHighDateTime >> 24) & 0xff); - buf[4] = (unsigned char) (ft.dwLowDateTime & 0xff); - buf[5] = (unsigned char) ((ft.dwLowDateTime >> 8) & 0xff); - buf[6] = (unsigned char) ((ft.dwLowDateTime >> 16) & 0xff); - buf[7] = (unsigned char) ((ft.dwLowDateTime >> 24) & 0xff); + GetSystemTimeAsFileTime(&ft); + buf[0] = (unsigned char) (ft.dwHighDateTime & 0xff); + buf[1] = (unsigned char) ((ft.dwHighDateTime >> 8) & 0xff); + buf[2] = (unsigned char) ((ft.dwHighDateTime >> 16) & 0xff); + buf[3] = (unsigned char) ((ft.dwHighDateTime >> 24) & 0xff); + buf[4] = (unsigned char) (ft.dwLowDateTime & 0xff); + buf[5] = (unsigned char) ((ft.dwLowDateTime >> 8) & 0xff); + buf[6] = (unsigned char) ((ft.dwLowDateTime >> 16) & 0xff); + buf[7] = (unsigned char) ((ft.dwLowDateTime >> 24) & 0xff); #else - gettimeofday(&tv,NULL); - buf[0] = (unsigned char) (tv.tv_sec & 0xff); - buf[1] = (unsigned char) ((tv.tv_sec >> 8) & 0xff); - buf[2] = (unsigned char) ((tv.tv_sec >> 16) & 0xff); - buf[3] = (unsigned char) ((tv.tv_sec >> 24) & 0xff); - buf[4] = (unsigned char) (tv.tv_usec & 0xff); - buf[5] = (unsigned char) ((tv.tv_usec >> 8) & 0xff); - buf[6] = (unsigned char) ((tv.tv_usec >> 16) & 0xff); - buf[7] = (unsigned char) ((tv.tv_usec >> 24) & 0xff); + gettimeofday(&tv,NULL); + buf[0] = (unsigned char) (tv.tv_sec & 0xff); + buf[1] = (unsigned char) ((tv.tv_sec >> 8) & 0xff); + buf[2] = (unsigned char) ((tv.tv_sec >> 16) & 0xff); + buf[3] = (unsigned char) ((tv.tv_sec >> 24) & 0xff); + buf[4] = (unsigned char) (tv.tv_usec & 0xff); + buf[5] = (unsigned char) ((tv.tv_usec >> 8) & 0xff); + buf[6] = (unsigned char) ((tv.tv_usec >> 16) & 0xff); + buf[7] = (unsigned char) ((tv.tv_usec >> 24) & 0xff); #endif + buf[8] = (unsigned char) (ctx->counter & 0xff); + buf[9] = (unsigned char) ((ctx->counter >> 8) & 0xff); + buf[10] = (unsigned char) ((ctx->counter >> 16) & 0xff); + buf[11] = (unsigned char) ((ctx->counter >> 24) & 0xff); + + pid=(unsigned long)getpid(); -#if 0 /* This eminently sensible strategy is not acceptable to NIST. Sigh. */ #ifndef GETPID_IS_MEANINGLESS - /* we mix in the PID to ensure that after a fork the children don't give - * the same results as each other - */ - pid=getpid(); - /* make sure we shift the pid to the MSB */ - if((pid&0xffff0000) == 0) - pid<<=16; - *(long *)&buf[0]^=pid; + buf[12] = (unsigned char) (pid & 0xff); + buf[13] = (unsigned char) ((pid >> 8) & 0xff); + buf[14] = (unsigned char) ((pid >> 16) & 0xff); + buf[15] = (unsigned char) ((pid >> 24) & 0xff); #endif -#endif - } - -static void fips_rand_encrypt(unsigned char *out,const unsigned char *in) - { - DES_ecb2_encrypt(in,out,&ks1,&ks2,1); - } - -static void fips_rand_cleanup(void) - { - OPENSSL_cleanse(seed,sizeof seed); - n_seed=0; - o_seed=0; - key_init=0; } -void FIPS_rand_seed(const void *buf_, FIPS_RAND_SIZE_T num) - { - const char *buf=buf_; - FIPS_RAND_SIZE_T n; +static int fips_rand(FIPS_PRNG_CTX *ctx, + unsigned char *out, FIPS_RAND_SIZE_T outlen) + { + unsigned char R[AES_BLOCK_LENGTH], I[AES_BLOCK_LENGTH]; + unsigned char tmp[AES_BLOCK_LENGTH]; + int i; + if (ctx->error) + { + RANDerr(RAND_F_FIPS_RAND,RAND_R_PRNG_ERROR); + return 0; + } + if (!ctx->keyed) + { + RANDerr(RAND_F_FIPS_RAND,RAND_R_NO_KEY_SET); + return 0; + } + if (!ctx->seeded) + { + RANDerr(RAND_F_FIPS_RAND,RAND_R_PRNG_NOT_SEEDED); + return 0; + } + for (;;) + { + if (!ctx->test_mode) + fips_get_dt(ctx); + AES_encrypt(ctx->DT, I, &ctx->ks); + for (i = 0; i < AES_BLOCK_LENGTH; i++) + tmp[i] = I[i] ^ ctx->V[i]; + AES_encrypt(tmp, R, &ctx->ks); + for (i = 0; i < AES_BLOCK_LENGTH; i++) + tmp[i] = R[i] ^ I[i]; + AES_encrypt(tmp, ctx->V, &ctx->ks); + if (ctx->second) + { + if (!memcmp(R, ctx->last, AES_BLOCK_LENGTH)) + { + RANDerr(RAND_F_FIPS_RAND,RAND_R_PRNG_STUCK); + ctx->error = 1; + return 0; + } + } + memcpy(ctx->last, R, AES_BLOCK_LENGTH); + if (!ctx->second) + { + ctx->second = 1; + if (!ctx->test_mode) + continue; + } + + if (outlen <= AES_BLOCK_LENGTH) + { + memcpy(out, R, outlen); + break; + } + + memcpy(out, R, AES_BLOCK_LENGTH); + out += AES_BLOCK_LENGTH; + outlen -= AES_BLOCK_LENGTH; + } + return 1; + } - /* If the key hasn't been set, we can't seed! */ - if(!key_set) - return; - CRYPTO_w_lock(CRYPTO_LOCK_RAND); - if(!key_init) +int FIPS_rand_set_key(const unsigned char *key, FIPS_RAND_SIZE_T keylen) { - key_init=1; - DES_set_key(&key1,&ks1); - DES_set_key(&key2,&ks2); + int ret; + CRYPTO_w_lock(CRYPTO_LOCK_RAND); + ret = fips_set_prng_key(&sctx, key, keylen); + CRYPTO_w_unlock(CRYPTO_LOCK_RAND); + return ret; } - /* - * This algorithm only uses 64 bits of seed, so ensure that we use - * the most recent 64 bits. - */ - for(n=0 ; n < num ; ) +int FIPS_rand_seed(const void *seed, FIPS_RAND_SIZE_T seedlen) { - FIPS_RAND_SIZE_T t=num-n; - - if(o_seed+t > sizeof seed) - t=sizeof seed-o_seed; - memcpy(seed+o_seed,buf+n,t); - n+=t; - o_seed+=t; - if(o_seed == sizeof seed) - o_seed=0; - if(n_seed < sizeof seed) - n_seed+=t; + int ret; + CRYPTO_w_lock(CRYPTO_LOCK_RAND); + ret = fips_set_prng_seed(&sctx, seed, seedlen); + CRYPTO_w_unlock(CRYPTO_LOCK_RAND); + return ret; } -#ifndef GETPID_IS_MEANINGLESS - seed_pid=getpid(); -#endif - - CRYPTO_w_unlock(CRYPTO_LOCK_RAND); - } - -static void fips_rand_add(const void *buf, FIPS_RAND_SIZE_T num, double add_entropy) - { - FIPS_rand_seed(buf,num); - } - -static int fips_rand_bytes(unsigned char *buf,FIPS_RAND_SIZE_T num) - { - FIPS_RAND_SIZE_T n; - unsigned char timeseed[8]; - unsigned char intermediate[SEED_SIZE]; - unsigned char output[SEED_SIZE]; - static unsigned char previous[SEED_SIZE]; -#ifndef GETPID_IS_MEANINGLESS - int pid; -#endif - if(n_seed < sizeof seed) +int FIPS_rand_bytes(unsigned char *out, FIPS_RAND_SIZE_T count) { - RANDerr(RAND_F_FIPS_RAND_BYTES,RAND_R_PRNG_NOT_SEEDED); - return 0; + int ret; + CRYPTO_w_lock(CRYPTO_LOCK_RAND); + ret = fips_rand(&sctx, out, count); + CRYPTO_w_unlock(CRYPTO_LOCK_RAND); + return ret; } -#ifdef FIPS_RAND_MAX_SIZE_T - if (num > FIPS_RAND_MAX_SIZE_T) +int FIPS_rand_status(void) { -#ifdef RAND_R_PRNG_ASKING_FOR_TOO_MUCH - RANDerr(RAND_F_FIPS_RAND_BYTES,RAND_R_PRNG_ASKING_FOR_TOO_MUCH); - return 0; -#else - return -1; /* signal "not supported" condition */ -#endif + int ret; + CRYPTO_r_lock(CRYPTO_LOCK_RAND); + ret = sctx.seeded; + CRYPTO_r_unlock(CRYPTO_LOCK_RAND); + return ret; } -#endif -#ifndef GETPID_IS_MEANINGLESS - pid=getpid(); - if(pid != seed_pid) +void FIPS_rand_reset(void) { - RANDerr(RAND_F_FIPS_RAND_BYTES,RAND_R_PRNG_NOT_RESEEDED); - return 0; + CRYPTO_w_lock(CRYPTO_LOCK_RAND); + fips_rand_prng_reset(&sctx); + CRYPTO_w_unlock(CRYPTO_LOCK_RAND); } - if(pid != key_pid) + +static void fips_do_rand_seed(const void *seed, FIPS_RAND_SIZE_T seedlen) { - RANDerr(RAND_F_FIPS_RAND_BYTES,RAND_R_PRNG_NOT_REKEYED); - return 0; + FIPS_rand_seed(seed, seedlen); } -#endif - - CRYPTO_w_lock(CRYPTO_LOCK_RAND); - for(n=0 ; n < num ; ) +static void fips_do_rand_add(const void *seed, FIPS_RAND_SIZE_T seedlen, + double add_entropy) { - unsigned char t[SEED_SIZE]; - FIPS_RAND_SIZE_T l; - - /* ANS X9.31 A.2.4: I = ede*K(DT) - timeseed == DT - intermediate == I - */ - fips_gettime(timeseed); - fips_rand_encrypt(intermediate,timeseed); - - /* ANS X9.31 A.2.4: R = ede*K(I^V) - intermediate == I - seed == V - output == R - */ - for(l=0 ; l < sizeof t ; ++l) - t[l]=intermediate[l]^seed[l]; - fips_rand_encrypt(output,t); - - /* ANS X9.31 A.2.4: V = ede*K(R^I) - output == R - intermediate == I - seed == V - */ - for(l=0 ; l < sizeof t ; ++l) - t[l]=output[l]^intermediate[l]; - fips_rand_encrypt(seed,t); - - if(second && !memcmp(output,previous,sizeof previous)) - { - RANDerr(RAND_F_FIPS_RAND_BYTES,RAND_R_PRNG_STUCK); - CRYPTO_w_unlock(CRYPTO_LOCK_RAND); - return 0; - } - memcpy(previous,output,sizeof previous); - second=1; - - /* Successive values of R may be concatenated to produce a - pseudo random number of the desired length */ - l=SEED_SIZE < num-n ? SEED_SIZE : num-n; - memcpy(buf+n,output,l); - n+=l; + FIPS_rand_seed(seed, seedlen); } - CRYPTO_w_unlock(CRYPTO_LOCK_RAND); - - return 1; - } - -static int fips_rand_status(void) +static const RAND_METHOD rand_fips_meth= { - return n_seed == sizeof seed; - } + fips_do_rand_seed, + FIPS_rand_bytes, + FIPS_rand_reset, + fips_do_rand_add, + FIPS_rand_bytes, + FIPS_rand_status + }; -#endif /* OPENSSL_FIPS */ +const RAND_METHOD *FIPS_rand_method(void) +{ + return &rand_fips_meth; +} |