#include "userdefines.h"
#include "assertverify.h"
#include "dev/random/YarrowCoreLib/include/yarrowUtils.h"
#if defined(macintosh) || defined(__APPLE__)
#include "macOnly.h"
#endif
#include "smf.h"
#include "sha1mod.h"
#include "entropysources.h"
#include "comp.h"
#include "dev/random/YarrowCoreLib/include/yarrow.h"
#include "prng.h"
#include "prngpriv.h"
#define _MAX(a,b) (((a)>(b))?(a):(b))
#define _MIN(a,b) (((a)<(b))?(a):(b))
#if defined(macintosh) || defined(__APPLE__)
#define MUTEX_ENABLE 0
#include <string.h>
#if TARGET_API_MAC_OSX
#include <sys/time.h>
#elif TARGET_API_MAC_CARBON
#include <Timer.h>
#include <Math64.h>
#elif KERNEL_BUILD
#include <sys/time.h>
#else
#error Unknown TARGET_API
#endif
#else
#define MUTEX_ENABLE 1
#endif
#if MUTEX_ENABLE
static HANDLE Statmutex = NULL;
static DWORD mutexCreatorId = 0;
#endif
#pragma mark -
#pragma mark * * * Static Utility functions * * *
static void
prng_do_SHA1(GEN_CTX *ctx)
{
SHA1_CTX sha;
SHA1Init(&sha);
SHA1Update(&sha,ctx->IV,20);
SHA1Update(&sha,ctx->out,20);
SHA1Final(ctx->out,&sha);
ctx->index = 0;
}
static void
prng_make_new_state(GEN_CTX *ctx,BYTE *newState)
{
SHA1_CTX sha;
memcpy(ctx->IV,newState,20);
SHA1Init(&sha);
SHA1Update(&sha,ctx->IV,20);
SHA1Final(ctx->out,&sha);
ctx->numout = 0;
ctx->index = 0;
}
#if SLOW_POLL_ENABLE
#define SPLEN 65536
static void
prng_slow_init(PRNG *p)
{
SHA1_CTX* ctx = NULL;
MMPTR mmctx = MM_NULL;
BYTE* bigbuf = NULL;
MMPTR mmbigbuf = MM_NULL;
BYTE* buf = NULL;
MMPTR mmbuf = MM_NULL;
DWORD polllength;
mmbigbuf = mmMalloc(SPLEN);
if(mmbigbuf == MM_NULL) {goto cleanup_slow_init;}
bigbuf = (BYTE*)mmGetPtr(mmbigbuf);
mmbuf = mmMalloc(20);
if(mmbuf == MM_NULL) {goto cleanup_slow_init;}
buf = (BYTE*)mmGetPtr(mmbuf);
mmctx = mmMalloc(sizeof(SHA1_CTX));
if(mmctx == MM_NULL) {goto cleanup_slow_init;}
ctx = (SHA1_CTX*)mmGetPtr(mmctx);
SHA1Init(&p->pool);
polllength = prng_slow_poll(bigbuf,SPLEN);
SHA1Init(ctx);
SHA1Update(ctx,bigbuf,polllength);
SHA1Final(buf,ctx);
prng_make_new_state(&p->outstate, buf);
cleanup_slow_init:
mmFree(mmctx);
mmFree(mmbigbuf);
mmFree(mmbuf);
return;
}
#endif
static void
bubbleSort(UINT *data,UINT len)
{
UINT i,last,newlast,temp;
last = len-1;
while(last!=-1)
{
newlast = -1;
for(i=0;i<last;i++)
{
if(data[i+1] > data[i])
{
newlast = i;
temp = data[i];
data[i] = data[i+1];
data[i+1] = temp;
}
}
last = newlast;
}
}
#pragma mark -
#pragma mark * * * Public functions * * *
prng_error_status
prngInitialize(PrngRef *prng)
{
UINT i;
comp_error_status resp;
prng_error_status retval = PRNG_ERR_LOW_MEMORY;
MMPTR mmp;
PRNG *p;
mmInit();
#if MUTEX_ENABLE
if(mutexCreatorId!=0) {return PRNG_ERR_REINIT;}
Statmutex = CreateMutex(NULL,TRUE,NULL);
if(Statmutex == NULL) {mutexCreatorId = 0; return PRNG_ERR_MUTEX;}
DuplicateHandle(GetCurrentProcess(),Statmutex,GetCurrentProcess(),&mutex,SYNCHRONIZE,FALSE,0);
mutexCreatorId = GetCurrentProcessId();
#endif
mmp = mmMalloc(sizeof(PRNG));
if(mmp==MM_NULL)
{
goto cleanup_init;
}
else
{
p = (PRNG*)mmGetPtr(mmp);
memset(p, 0, sizeof(PRNG));
}
for(i=0;i<TOTAL_SOURCES;i++)
{
p->poolSize[i] = 0;
p->poolEstBits[i] = 0;
}
#ifdef WIN_NT
prng_set_NT_security();
#endif
SHA1Init(&p->pool);
#if SLOW_POLL_ENABLE
prng_slow_init(p);
#else
prng_do_SHA1(&p->outstate);
prng_make_new_state(&p->outstate, p->outstate.out);
#endif
for(i=0;i<COMP_SOURCES;i++)
{
resp = comp_init((p->comp_state)+i);
if(resp!=COMP_SUCCESS) {retval = PRNG_ERR_COMPRESSION; goto cleanup_init;}
}
p->ready = PRNG_READY;
*prng = (PrngRef)p;
return PRNG_SUCCESS;
cleanup_init:
mmFree(mmp);
mmp = MM_NULL;
#if MUTEX_ENABLE
CloseHandle(Statmutex);
Statmutex = NULL;
mutexCreatorId = 0;
#endif
return retval;
}
prng_error_status
prngOutput(PRNG *p, BYTE *outbuf,UINT outbuflen)
{
UINT i;
GEN_CTX *ctx = &p->outstate;
CHECKSTATE(p);
GENCHECK(p);
PCHECK(outbuf);
chASSERT(BACKTRACKLIMIT > 0);
for(i=0;i<outbuflen;i++,ctx->index++,ctx->numout++)
{
if(ctx->numout > BACKTRACKLIMIT)
{
prng_do_SHA1(ctx);
prng_make_new_state(ctx, ctx->out);
}
if(ctx->index>=20)
{
prng_do_SHA1(ctx);
}
outbuf[i] = (ctx->out)[ctx->index];
}
return PRNG_SUCCESS;
}
prng_error_status
prngForceReseed(PRNG *p, LONGLONG ticks)
{
int i;
#ifdef WIN_NT
FILETIME a,b,c,usertime;
#endif
BYTE buf[64];
BYTE dig[20];
#if defined(macintosh) || defined(__APPLE__)
#if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
struct timeval tv;
int64_t endTime, curTime;
#else
UnsignedWide uwide;
LONGLONG start;
LONGLONG now;
#endif
#endif
CHECKSTATE(p);
POOLCHECK(p);
ZCHECK(ticks);
#if defined(macintosh) || defined(__APPLE__)
#if (defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD))
#ifdef KERNEL_BUILD
microuptime (&tv);
#else
gettimeofday(&tv, NULL);
#endif
endTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec + ticks;
#else
Microseconds(&uwide);
start = UnsignedWideToUInt64(uwide);
#endif
#endif
do
{
prngOutput(p, buf,64);
SHA1Update(&p->pool,buf,64);
prngOutput(p, buf,64);
SHA1Update(&p->pool,buf,64);
prngOutput(p, buf,64);
SHA1Update(&p->pool,buf,64);
prngOutput(p, buf,64);
SHA1Update(&p->pool,buf,64);
prngOutput(p, buf,64);
SHA1Update(&p->pool,buf,64);
#if defined(macintosh) || defined(__APPLE__)
#if defined(TARGET_API_MAC_OSX) || defined(KERNEL_BUILD)
#ifdef TARGET_API_MAC_OSX
gettimeofday(&tv, NULL);
#else
microuptime (&tv);
curTime = (int64_t)tv.tv_sec*1000000LL + (int64_t)tv.tv_usec;
#endif
} while(curTime < endTime);
#else
Microseconds(&uwide);
now = UnsignedWideToUInt64(uwide);
} while ( (now-start) < ticks) ;
#endif
#else
} while ( (now-start) < ticks) ;
#endif
SHA1Final(dig,&p->pool);
SHA1Update(&p->pool,dig,20);
SHA1Final(dig,&p->pool);
SHA1Init(&p->pool);
prng_make_new_state(&p->outstate,dig);
for(i=0;i<TOTAL_SOURCES;i++)
{
p->poolSize[i] = 0;
p->poolEstBits[i] = 0;
}
trashMemory(dig,20*sizeof(char));
trashMemory(buf,64*sizeof(char));
return PRNG_SUCCESS;
}
prng_error_status
prngProcessSeedBuffer(PRNG *p, BYTE *buf,LONGLONG ticks)
{
CHECKSTATE(p);
GENCHECK(p);
PCHECK(buf);
SHA1Update(&p->pool,buf,20);
prng_do_SHA1(&p->outstate);
SHA1Update(&p->pool,p->outstate.out,20);
prngForceReseed(p, ticks);
return prngOutput(p, buf,20);
}
prng_error_status
prngStretch(BYTE *inbuf,UINT inbuflen,BYTE *outbuf,UINT outbuflen) {
long int left,prev;
SHA1_CTX ctx;
BYTE dig[20];
PCHECK(inbuf);
PCHECK(outbuf);
if(inbuflen >= outbuflen)
{
memcpy(outbuf,inbuf,outbuflen);
return PRNG_SUCCESS;
}
else
{
SHA1Init(&ctx);
SHA1Update(&ctx,inbuf,inbuflen);
SHA1Final(dig,&ctx);
for(prev=0,left=outbuflen;left>0;prev+=20,left-=20)
{
SHA1Update(&ctx,dig,20);
SHA1Final(dig,&ctx);
memcpy(outbuf+prev,dig,(left>20)?20:left);
}
trashMemory(dig,20*sizeof(BYTE));
return PRNG_SUCCESS;
}
return PRNG_ERR_PROGRAM_FLOW;
}
prng_error_status
prngInput(PRNG *p, BYTE *inbuf,UINT inbuflen,UINT poolnum,UINT estbits)
{
#ifndef YARROW_KERNEL
comp_error_status resp;
#endif
CHECKSTATE(p);
POOLCHECK(p);
PCHECK(inbuf);
if(poolnum >= TOTAL_SOURCES) {return PRNG_ERR_OUT_OF_BOUNDS;}
SHA1Update(&p->pool,inbuf,inbuflen);
#ifndef YARROW_KERNEL
p->poolSize[poolnum] += inbuflen;
p->poolEstBits[poolnum] += estbits;
if(poolnum<COMP_SOURCES)
{
resp = comp_add_data((p->comp_state)+poolnum,inbuf,inbuflen);
if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
}
#endif
return PRNG_SUCCESS;
}
prng_error_status
prngAllowReseed(PRNG *p, LONGLONG ticks)
{
UINT temp[TOTAL_SOURCES];
UINT i,sum;
#ifndef KERNEL_BUILD
float ratio;
#endif
comp_error_status resp;
CHECKSTATE(p);
for(i=0;i<ENTROPY_SOURCES;i++)
{
#ifndef KERNEL_BUILD // floating point in a kernel is BAD!
resp = comp_get_ratio((p->comp_state)+i,&ratio);
if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
temp[i] = (int)(ratio*p->poolSize[i]*4);
#else
temp[i] = p->poolSize[i] * 4;
#endif
}
for(i=ENTROPY_SOURCES;i<COMP_SOURCES;i++)
{
#ifndef KERNEL_BUILD
resp = comp_get_ratio((p->comp_state)+i,&ratio);
if(resp!=COMP_SUCCESS) {return PRNG_ERR_COMPRESSION;}
temp[i] = _MIN((int)(ratio*p->poolSize[i]*4),(int)p->poolEstBits[i]);
#else
temp[i] = _MIN (p->poolSize[i] * 4, p->poolEstBits[i]);
#endif
}
for(i=COMP_SOURCES;i<TOTAL_SOURCES;i++) {temp[i] = p->poolEstBits[i];}
if(K > 0) {
bubbleSort(temp,TOTAL_SOURCES);
}
for(i=K,sum=0;i<TOTAL_SOURCES;sum+=temp[i++]);
if(sum>THRESHOLD)
return prngForceReseed(p, ticks);
else
return PRNG_ERR_NOT_ENOUGH_ENTROPY;
return PRNG_ERR_PROGRAM_FLOW;
}
#if SLOW_POLL_ENABLE
static prng_error_status
prngSlowPoll(PRNG *p, UINT pollsize)
{
BYTE *buf;
DWORD len;
prng_error_status retval;
CHECKSTATE(p);
buf = (BYTE*)malloc(pollsize);
if(buf==NULL) {return PRNG_ERR_LOW_MEMORY;}
len = prng_slow_poll(buf,pollsize);
retval = prngInput(p, buf,len,SLOWPOLLSOURCE, len * 8);
trashMemory(buf,pollsize);
free(buf);
return retval;
}
#endif
prng_error_status
prngDestroy(PRNG *p)
{
UINT i;
#if MUTEX_ENABLE
if(GetCurrentProcessId()!=mutexCreatorId) {return PRNG_ERR_WRONG_CALLER;}
#endif
if(p==NULL) {return PRNG_SUCCESS;}
p->ready = PRNG_NOT_READY;
for(i=0;i<COMP_SOURCES;i++)
{
comp_end((p->comp_state)+i);
}
#if MUTEX_ENABLE
CloseHandle(Statmutex);
Statmutex = NULL;
mutexCreatorId = 0;
#endif
return PRNG_SUCCESS;
}