📄 pgprandomx9_17.c
字号:
/*____________________________________________________________________________
Copyright (C) 2002 PGP Corporation
All rights reserved.
$Id: pgpRandomX9_17.c,v 1.14 2002/08/06 20:11:05 dallen Exp $
____________________________________________________________________________*/
/*
* pgpRandomX9_17.c -- a general RNG interface
*
* Written by: Colin Plumb and Derek Atkins <warlord@MIT.EDU>
*/
#include "pgpConfig.h"
#include "pgpMem.h"
#include "pgpContext.h"
#include "pgpCFBPriv.h"
#include "pgpSymmetricCipherPriv.h"
#include "pgpRandomPoolPriv.h"
#include "pgpRandomX9_17.h"
#include "pgpFIPSPriv.h"
#include "pgpKeyPriv.h"
/**************************** X9.17 RNG ********************************/
typedef struct X9_17Context
{
PGPCFBContext *cfb;
PGPRandomContext const *base_rc;
} X9_17Context;
static void
randomX9_17AddBytes(void *priv, PGPByte const *buf, unsigned len)
{
X9_17Context *ctx = (X9_17Context *)priv;
PGPCFBRandomWash(ctx->cfb, buf, len);
}
static void
randomX9_17GetBytesEntropy(
void *priv,
PGPByte *buf,
unsigned len,
unsigned bits)
{
X9_17Context *ctx = (X9_17Context *)priv;
PGPByte salt[ PGP_CFB_MAXBLOCKSIZE ];
PGPUInt32 blocksize;
PGPSize t;
blocksize = pgpCFBGetBlockSize( ctx->cfb );
/* Charge the entropy to the base RNG */
pgpRandomGetBytesEntropy(ctx->base_rc, buf, 0, bits);
(void)PGPCFBGetRandom(ctx->cfb, len, buf, &t);
len -= t;
buf += t;
while (len) {
pgpRandomGetBytesEntropy(ctx->base_rc, salt, blocksize, 0);
if( pgpFIPSModeEnabled() )
{
PGPByte inbuf[PGP_CFB_MAXBLOCKSIZE];
PGPByte outbuf[PGP_CFB_MAXBLOCKSIZE];
PGPUInt32 timeOfDay;
PGPSymmetricCipherContextRef sref;
PGPUInt32 index;
pgpClearMemory( inbuf, sizeof( inbuf ) );
pgpClearMemory( outbuf, sizeof( outbuf ) );
/* for FIPS, use a date/time approach to increase the entropy */
/* encrypt the date/time */
timeOfDay = PGPGetTime();
pgpCopyMemory( &timeOfDay, inbuf, sizeof( timeOfDay ) );
/* now encrypt the date/time information into outbuf */
PGPCFBGetSymmetricCipher( ctx->cfb, &sref );
PGPSymmetricCipherEncrypt( sref, inbuf, outbuf );
/* XOR outbuf into the salt */
for( index = 0; index < blocksize; index++ )
{
salt[index] ^= outbuf[index];
}
}
PGPCFBRandomCycle(ctx->cfb, salt);
(void)PGPCFBGetRandom(ctx->cfb, len, buf, &t);
len -= t;
buf += t;
}
}
static void
randomX9_17Stir(void *priv)
{
X9_17Context *ctx = (X9_17Context *)priv;
PGPCFBRandomWash(ctx->cfb, (PGPByte const *)"", 0);
pgpRandomStir(ctx->base_rc);
}
static void
randomX9_17Destroy (PGPRandomContext *rc)
{
X9_17Context * ctx = (X9_17Context *)rc->priv;
PGPCFBContextRef cfbp = ctx->cfb;
PGPContextRef cdkContext;
pgpAssertAddrValid( rc, PGPRandomContext );
cdkContext = rc->context;
pgpCFBWipe( cfbp );
PGPFreeCFBContext( cfbp );
pgpClearMemory( ctx, sizeof(*ctx) );
pgpContextMemFree( cdkContext, ctx );
pgpClearMemory( rc, sizeof(*rc) );
pgpContextMemFree( cdkContext, rc );
}
PGPRandomVTBL X9_17Desc = {
"X9.17-based cryptographic PRNG",
randomX9_17AddBytes, randomX9_17GetBytesEntropy,
randomX9_17Stir
};
PGPRandomContext *
pgpRandomCreateX9_17(
PGPContextRef context,
PGPCipherAlgorithm algorithm,
PGPRandomContext const * base)
{
PGPRandomContext * rc;
PGPCFBContextRef cfb;
X9_17Context * ctx;
PGPByte buf[32];
PGPMemoryMgrRef memoryMgr = PGPPeekContextMemoryMgr( context );
pgpAssert( context );
pgpAssert( base );
cfb = pgpCFBCreate( memoryMgr, pgpCipherGetVTBL( algorithm ) );
if (!cfb)
return 0;
rc = (PGPRandomContext *)pgpContextMemAlloc( context, sizeof(*rc),
kPGPMemoryMgrFlags_Clear );
if (!rc) {
PGPFreeCFBContext( cfb );
return 0;
}
ctx = (X9_17Context *)pgpContextMemAlloc( context, sizeof(*ctx),
kPGPMemoryMgrFlags_Clear );
if (!ctx) {
PGPFreeCFBContext( cfb );
pgpContextMemFree( context, rc );
return 0;
}
/* Get a reasonable initial state */
pgpRandomGetBytesEntropy( base, buf, sizeof(buf), 0 );
PGPCFBRandomWash( cfb, buf, sizeof(buf) );
pgpClearMemory( buf, sizeof(buf) );
rc->vtbl = &X9_17Desc;
rc->priv = ctx;
rc->context = context;
rc->destroy = randomX9_17Destroy;
ctx->cfb = cfb;
ctx->base_rc = base;
return rc;
}
/* The default: X9.17, with CAST5, based on the pgpRandomPool. */
static PGPRandomContext *
pgpRandomCreate_internal(PGPContextRef context)
{
PGPRandomContext * randomContext;
PGPCipherAlgorithm cipherAlg;
randomContext = pgpContextGetGlobalPoolRandomContext( context );
pgpAssert( randomContext );
if( pgpFIPSModeEnabled() )
{
cipherAlg = kPGPCipherAlgorithm_3DES;
}
else
{
cipherAlg = kPGPCipherAlgorithm_CAST5;
}
return pgpRandomCreateX9_17( context, cipherAlg, randomContext );
}
/******************** Front End RNG ********************************/
typedef struct rngFront {
PGPContextRef context;
PGPByte * allocBuf; /* Buffer holding reserved bits */
PGPSize allocBufSize; /* Size of allocBuf */
} rngFront;
/* This just passes requests through to the back end */
static void
randomFrontAddBytes(void *priv, PGPByte const *buf, unsigned len)
{
rngFront *rf = (rngFront *) priv;
(void)pgpRandomAddBytes_back( rf->context, buf, len );
}
static void
randomFrontGetBytesEntropy(
void *priv,
PGPByte *buf,
unsigned len,
unsigned bits)
{
PGPByte *randBuf;
PGPSize randBufLen;
rngFront *rf = (rngFront *) priv;
/* Satisfy request from local buffer if counting entropy */
if( rf->allocBufSize > 0 && bits > 0 )
{
PGPSize cnt = pgpMin( (bits+7)/8, len );
pgpAssert( (bits+7)/8 <= len ); // we handle even if this is false
cnt = pgpMin( cnt, rf->allocBufSize );
pgpCopyMemory( rf->allocBuf, buf, cnt );
len -= cnt;
if( bits > 8*cnt )
bits -= 8*cnt;
else
bits = 0;
buf += cnt;
if( rf->allocBufSize == cnt )
{
pgpClearMemory( rf->allocBuf, rf->allocBufSize );
pgpContextMemFree( rf->context, rf->allocBuf );
rf->allocBuf = NULL;
rf->allocBufSize = 0;
} else {
void *vbuf = rf->allocBuf;
PGPSize i;
/* Copy data back to beginning of buffer (overlapping copy) */
for( i=cnt; i<rf->allocBufSize; ++i )
rf->allocBuf[i-cnt] = rf->allocBuf[i];
pgpClearMemory( rf->allocBuf+rf->allocBufSize-cnt, cnt );
/* Shrink buffer to new size */
(void)pgpContextMemRealloc( rf->context, &vbuf,
rf->allocBufSize-cnt, 0 );
rf->allocBuf = vbuf;
rf->allocBufSize -= cnt;
}
if( len == 0 )
return;
}
(void)pgpRandomGetBytesEntropy_back( rf->context, len, bits,
&randBuf, &randBufLen );
pgpAssert( IsntNull( randBuf ) );
pgpCopyMemory( randBuf, buf, randBufLen );
pgpClearMemory( randBuf, randBufLen );
PGPFreeData( randBuf );
}
/* Reserve bytes so that we have at least minsize */
static PGPUInt32
randomFrontReserve( void *priv, PGPUInt32 minsize )
{
rngFront *rf = (rngFront *) priv;
PGPByte *randBuf;
PGPSize randBufLen;
PGPByte *fillptr;
PGPUInt32 avail;
PGPUInt32 request;
if( minsize <= rf->allocBufSize )
return rf->allocBufSize;
/* Calculate how much is being requested to add */
request = minsize - rf->allocBufSize;
avail = PGPGlobalRandomPoolGetEntropy();
request = pgpMin( request, avail );
(void)pgpRandomGetBytesEntropy_back( rf->context, request, request*8,
&randBuf, &randBufLen );
if( IsNull( randBuf ) )
return rf->allocBufSize;
if( IsntNull( rf->allocBuf ) )
{
void *vbuf = rf->allocBuf;
PGPError err = pgpContextMemRealloc( rf->context, &vbuf,
rf->allocBufSize+randBufLen, 0 );
if( IsPGPError( err ) )
{
PGPFreeData( randBuf );
return rf->allocBufSize;
}
rf->allocBuf = vbuf;
fillptr = rf->allocBuf + rf->allocBufSize;
} else {
rf->allocBuf = PGPNewSecureData( PGPPeekContextMemoryMgr(rf->context),
randBufLen, 0 );
if( IsNull( rf->allocBuf ) )
{
PGPFreeData( randBuf );
return 0;
}
fillptr = rf->allocBuf;
}
pgpCopyMemory( randBuf, fillptr, randBufLen );
pgpClearMemory( randBuf, randBufLen );
PGPFreeData( randBuf );
rf->allocBufSize += randBufLen;
return rf->allocBufSize;
}
static void
randomFrontStir(void *priv)
{
rngFront *rf = (rngFront *) priv;
(void)pgpRandomStir_back( rf->context );
}
static void
randomFrontDestroy (PGPRandomContext *rc)
{
PGPContextRef cdkContext;
rngFront * rf;
pgpAssertAddrValid( rc, PGPRandomContext );
cdkContext = rc->context;
rf = (rngFront *)rc->priv;
pgpClearMemory( rc, sizeof(*rc) );
pgpContextMemFree( cdkContext, rc );
if( IsntNull( rf->allocBuf ) )
{
pgpClearMemory( rf->allocBuf, rf->allocBufSize );
pgpContextMemFree( cdkContext, rf->allocBuf );
}
pgpClearMemory( rf, sizeof(*rf) );
pgpContextMemFree( cdkContext, rf );
}
PGPRandomVTBL frontDesc = {
"frontend to cryptographic PRNG",
randomFrontAddBytes, randomFrontGetBytesEntropy,
randomFrontStir
};
PGPRandomContext *
pgpRandomCreate_frontend(
PGPContextRef context )
{
PGPRandomContext * rc;
rngFront * rf;
pgpAssert( context );
rc = (PGPRandomContext *)pgpContextMemAlloc( context, sizeof(*rc),
kPGPMemoryMgrFlags_Clear );
if (!rc) {
return 0;
}
rf = (rngFront *)pgpContextMemAlloc( context, sizeof(*rf),
kPGPMemoryMgrFlags_Clear );
if (!rf) {
pgpContextMemFree( context, rc );
return 0;
}
rf->context = context;
rc->vtbl = &frontDesc;
rc->priv = (void *)rf;
rc->context = context;
rc->destroy = randomFrontDestroy;
return rc;
}
/********************** Pseudo-Random RNG *************************/
/*
* Pseudo-random number generator based on the dummy pool (which
* always produces zeros). This is useful for public data where we
* just want uniqueness but don't need cryptographic strength, and where
* we don't want to draw down the randomness in the true random pool.
* An example would be generating the public prime in DSA key.
* Use pgpRandomAddBytes to seed it.
*
* Note that you need to seed this with some really random data
* (using pgpRandomAddBytes) or it will generate the same data every time.
*
* DO NOT USE THIS unless you know what you're doing.
*/
PGPRandomContext *
pgpPseudoRandomCreate(PGPContextRef context)
{
PGPRandomContext * randomContext;
PGPCipherAlgorithm cipherAlg;
pgpAssert( context );
randomContext = pgpContextGetDummyRandomContext( context );
pgpAssert( randomContext );
if( pgpFIPSModeEnabled() )
{
cipherAlg = kPGPCipherAlgorithm_3DES;
}
else
{
cipherAlg = kPGPCipherAlgorithm_CAST5;
}
return pgpRandomCreateX9_17( context, cipherAlg, randomContext );
}
/******************************* Utilities ******************************/
PGPUInt32
pgpRandomReserveBytes( PGPRandomContext *rc, PGPUInt32 minsize )
{
/* If no front end, use entropy in pool as only we can access it */
if( rc->vtbl == &frontDesc )
return randomFrontReserve( rc->priv, minsize );
else
return PGPGlobalRandomPoolGetEntropy() / 8;
}
/*
* The simple get-byte function... entropy charged is the number of
* bits read out.
*/
void
pgpRandomGetBytes(
PGPRandomContext const *rc,
void * buf,
PGPSize len)
{
pgpRandomGetBytesEntropy(rc, (PGPByte *) buf, len, 8*len);
}
/*
* Generate a uniformly distributed random number from 0..range-1.
* range is limited to 65536.
*
* This is very careful to avoid all bias in its selection, without
* using too many input bytes. For a range of 256 or less, it uses
* one byte if possible, and an average of less then two, even for
* the worst-case range=129.
*/
PGPUInt32
pgpRandomRange(
PGPRandomContext const * rc,
PGPUInt32 range,
PGPUInt32 entropyBits)
{
PGPUInt32 d,
r;
PGPByte b[2];
/* Count entropy bits */
pgpRandomGetBytesEntropy(rc, b, 1, entropyBits);
if (range <= 1)
return 0;
if (range <= 256) {
d = 256/range;
do {
pgpRandomGetBytesEntropy(rc, b, 1, 0);
r = b[0]/d;
} while (r >= range);
} else {
d = (unsigned)(65536/range);
do {
pgpRandomGetBytesEntropy(rc, b, 2, 0);
r = ((unsigned)b[0] << 8 | b[1])/d;
} while (r >= range);
b[1] = 0;
}
b[0] = 0;
return r;
}
PGPRandomContext *
pgpRandomCreate(PGPContextRef context)
{
pgpAssert( context );
if( !pgpRPCEnabled() )
{
return pgpRandomCreate_internal( context );
}
/* Else create a randomcontext which relies on the back end */
return pgpRandomCreate_frontend( context );
}
/*__Editor_settings____
Local Variables:
tab-width: 4
End:
vi: ts=4 sw=4
vim: si
_____________________*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -