Back to index

lightning-sunbird  0.9+nobinonly
Classes | Defines | Typedefs | Functions | Variables
prng_fips1861.c File Reference
#include "prerr.h"
#include "secerr.h"
#include "prtypes.h"
#include "prinit.h"
#include "blapi.h"
#include "nssilock.h"
#include "secitem.h"
#include "sha_fast.h"
#include "sha256.h"
#include "secrng.h"
#include "secmpi.h"

Go to the source code of this file.

Classes

struct  RNGContextStr

Defines

#define MIN_SEED_COUNT   1024
#define FIPS_B   256
#define BSIZE   (FIPS_B / PR_BITS_PER_BYTE)
#define FIPS_G   160
#define GSIZE   (FIPS_G / PR_BITS_PER_BYTE)
#define ADD_B_BIT_PLUS_CARRY(dest, add1, add2, cy)
#define ADD_B_BIT_2(dest, add1, add2)   ADD_B_BIT_PLUS_CARRY(dest, add1, add2, 0)

Typedefs

typedef struct RNGContextStr

Functions

SECStatus FIPS186Change_ReduceModQForDSA (const unsigned char *w, const unsigned char *q, unsigned char *xj)
static void RNG_UpdateAndEnd_FIPS186_2 (SHA1Context *ctx, unsigned char *input, unsigned int inputLen, unsigned char *hashout, unsigned int *pDigestLen, unsigned int maxDigestLen)
static void freeRNGContext ()
SECStatus FIPS186Change_GenerateX (unsigned char *XKEY, const unsigned char *XSEEDj, unsigned char *x_j)
static SECStatus alg_fips186_2_cn_1 (RNGContext *rng, const unsigned char *XSEEDj)
static PRStatus rng_init (void)
SECStatus RNG_RNGInit (void)
static SECStatus prng_RandomUpdate (RNGContext *rng, const void *data, size_t bytes)
SECStatus RNG_RandomUpdate (const void *data, size_t bytes)
static SECStatus prng_GenerateGlobalRandomBytes (RNGContext *rng, void *dest, size_t len)
SECStatus RNG_GenerateGlobalRandomBytes (void *dest, size_t len)
void RNG_RNGShutdown (void)
SECStatus DSA_GenerateGlobalRandomBytes (void *dest, size_t len, const unsigned char *q)

Variables

static RNGContext * globalrng = NULL
static RNGContext theGlobalRng
static const PRCallOnceType pristineCallOnce
static PRCallOnceType coRNGInit

Class Documentation

struct RNGContextStr

Definition at line 165 of file prng_fips1861.c.

Class Members
PRUint8 avail
PRBool isValid
PZLock * lock
PRUint32 seedCount
PRUint8 Xj
PRUint8 XKEY

Define Documentation

#define ADD_B_BIT_2 (   dest,
  add1,
  add2 
)    ADD_B_BIT_PLUS_CARRY(dest, add1, add2, 0)

Definition at line 100 of file prng_fips1861.c.

#define ADD_B_BIT_PLUS_CARRY (   dest,
  add1,
  add2,
  cy 
)
Value:
carry = cy; \
    for (k=BSIZE-1; k>=0; --k) { \
       carry += add1[k] + add2[k]; \
       dest[k] = (PRUint8)carry; \
       carry >>= 8; \
    }

Definition at line 92 of file prng_fips1861.c.

Definition at line 78 of file prng_fips1861.c.

#define FIPS_B   256

Definition at line 77 of file prng_fips1861.c.

#define FIPS_G   160

Definition at line 84 of file prng_fips1861.c.

Definition at line 85 of file prng_fips1861.c.

#define MIN_SEED_COUNT   1024

Definition at line 65 of file prng_fips1861.c.


Typedef Documentation

typedef struct RNGContextStr

Definition at line 173 of file prng_fips1861.c.


Function Documentation

static SECStatus alg_fips186_2_cn_1 ( RNGContext *  rng,
const unsigned char *  XSEEDj 
) [static]

Definition at line 312 of file prng_fips1861.c.

{
    /* store a copy of the output to compare with the previous output */
    PRUint8 x_j[2*GSIZE];
    SECStatus rv;

    if (!rng->isValid) {
       /* RNG has alread entered an invalid state. */
       PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
       return SECFailure;
    }
    rv = FIPS186Change_GenerateX(rng->XKEY, XSEEDj, x_j);
    if (rv != SECSuccess) {
       goto done;
    }
    /*     [FIPS 140-2] verify output does not match previous output */
    if (memcmp(x_j, rng->Xj, 2*GSIZE) == 0) {
       /* failed FIPS 140-2 continuous RNG test.  RNG now invalid. */
       rng->isValid = PR_FALSE;
       PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
       rv = SECFailure;
       goto done;
    }
    /* Xj is the output */
    memcpy(rng->Xj, x_j, 2*GSIZE);
    /* Always have a full buffer after executing alg_cn_1() */
    rng->avail = 2*GSIZE;

done:
    /* housekeeping */
    memset(x_j, 0, 2*GSIZE);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

SECStatus DSA_GenerateGlobalRandomBytes ( void dest,
size_t  len,
const unsigned char *  q 
)

Definition at line 623 of file prng_fips1861.c.

{
    SECStatus rv;
    unsigned char w[2*GSIZE];

    PORT_Assert(q && len == DSA_SUBPRIME_LEN);
    if (len != DSA_SUBPRIME_LEN) {
       PORT_SetError(SEC_ERROR_OUTPUT_LEN);
       return SECFailure;
    }
    if (*q == 0) {
        ++q;
    }
    rv = prng_GenerateGlobalRandomBytes(globalrng, w, 2*GSIZE);
    if (rv != SECSuccess) {
       return rv;
    }
    FIPS186Change_ReduceModQForDSA(w, q, (unsigned char *)dest);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

SECStatus FIPS186Change_GenerateX ( unsigned char *  XKEY,
const unsigned char *  XSEEDj,
unsigned char *  x_j 
)

Definition at line 219 of file prng_fips1861.c.

{
    /* SHA1 context for G(t, XVAL) function */
    SHA1Context sha1cx;
    /* XKEY for iteration 1 */
    PRUint8 XKEY_1[BSIZE];
    const PRUint8 *XKEY_old;
    PRUint8 *XKEY_new;
    /* input to hash function */
    PRUint8 XVAL[BSIZE];
    /* used by ADD_B_BIT macros */
    int k, carry;
    /* store the output of G(t, XVAL) in the rightmost GSIZE bytes */
    PRUint8 w_i[BSIZE];
    int i;
    unsigned int len;
    SECStatus rv = SECSuccess;

#if GSIZE < BSIZE
    /* zero the leftmost bytes so we can pass it to ADD_B_BIT_PLUS_CARRY */
    memset(w_i, 0, BSIZE - GSIZE);
#endif
    /* 
     * <Step 2> Initialize t, taken care of in SHA-1 (same initial values)
     *
     * <Step 3.1> XSEEDj is optional user input
     */ 
    for (i = 0; i < 2; i++) {
       /* only update XKEY when both iterations have been completed */
       if (i == 0) {
           /* for iteration 0 */
           XKEY_old = XKEY;
           XKEY_new = XKEY_1;
       } else {
           /* for iteration 1 */
           XKEY_old = XKEY_1;
           XKEY_new = XKEY;
       }
       /* 
        * <Step 3.2a> XVAL = (XKEY + XSEEDj) mod 2^b
        *     :always reduced mod 2^b, since storing as b-bit value
        */
       if (XSEEDj) {
           /* XSEEDj > 0 */
           if (memcmp(XKEY_old, XSEEDj, BSIZE) == 0) {
              /* Should we add the error code SEC_ERROR_BAD_RNG_SEED? */
              PORT_SetError(SEC_ERROR_INVALID_ARGS);
              rv = SECFailure;
              goto done;
           }
           ADD_B_BIT_2(XVAL, XKEY_old, XSEEDj);
       } else {
           /* XSEEDj == 0 */
           memcpy(XVAL, XKEY_old, BSIZE);
       }
       /* 
        * <Step 3.2b> Wi = G(t, XVAL)
        *     :FIPS 186-2 specifies a different padding than the SHA1 180-1
        *     :specification, this function is implemented in
        *     :RNG_UpdateAndEnd_FIPS186_2 below.
        */ 
       SHA1_Begin(&sha1cx);
       RNG_UpdateAndEnd_FIPS186_2(&sha1cx, XVAL, BSIZE,
                               &w_i[BSIZE - GSIZE], &len, GSIZE);
       /* 
        * <Step 3.2c> XKEY = (1 + XKEY + Wi) mod 2^b
        *     :always reduced mod 2^b, since storing as 160-bit value 
        */
       ADD_B_BIT_PLUS_CARRY(XKEY_new, XKEY_old, w_i, 1);
       /*
        * <Step 3.3> Xj = (W0 || W1)
        */
       memcpy(&x_j[i*GSIZE], &w_i[BSIZE - GSIZE], GSIZE);
    }

done:
    /* housekeeping */
    memset(&w_i[BSIZE - GSIZE], 0, GSIZE);
    memset(XVAL, 0, BSIZE);
    memset(XKEY_1, 0, BSIZE);
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

SECStatus FIPS186Change_ReduceModQForDSA ( const unsigned char *  w,
const unsigned char *  q,
unsigned char *  xj 
)

Definition at line 113 of file prng_fips1861.c.

{
    mp_int W, Q, Xj;
    mp_err err;
    SECStatus rv = SECSuccess;

    /* Initialize MPI integers. */
    MP_DIGITS(&W) = 0;
    MP_DIGITS(&Q) = 0;
    MP_DIGITS(&Xj) = 0;
    CHECK_MPI_OK( mp_init(&W) );
    CHECK_MPI_OK( mp_init(&Q) );
    CHECK_MPI_OK( mp_init(&Xj) );
    /*
     * Convert input arguments into MPI integers.
     */
    CHECK_MPI_OK( mp_read_unsigned_octets(&W, w, 2*GSIZE) );
    CHECK_MPI_OK( mp_read_unsigned_octets(&Q, q, DSA_SUBPRIME_LEN) );
    /*
     * Algorithm 1 of FIPS 186-2 Change Notice 1, Step 3.3
     *
     * xj = (w0 || w1) mod q
     */
    CHECK_MPI_OK( mp_mod(&W, &Q, &Xj) );
    CHECK_MPI_OK( mp_to_fixlen_octets(&Xj, xj, DSA_SUBPRIME_LEN) );
cleanup:
    mp_clear(&W);
    mp_clear(&Q);
    mp_clear(&Xj);
    if (err) {
       MP_TO_SEC_ERROR(err);
       rv = SECFailure;
    }
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void freeRNGContext ( ) [static]

Definition at line 181 of file prng_fips1861.c.

{
    unsigned char inputhash[BSIZE];
    SECStatus rv;

    /* destroy context lock */
    PZ_DestroyLock(globalrng->lock);

    /* zero global RNG context except for XKEY to preserve entropy */
    rv = SHA256_HashBuf(inputhash, globalrng->XKEY, BSIZE);
    PORT_Assert(SECSuccess == rv);
    memset(globalrng, 0, sizeof(*globalrng));
    memcpy(globalrng->XKEY, inputhash, BSIZE);

    globalrng = NULL;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static SECStatus prng_GenerateGlobalRandomBytes ( RNGContext *  rng,
void dest,
size_t  len 
) [static]

Definition at line 489 of file prng_fips1861.c.

{
    PRUint8 num;
    SECStatus rv = SECSuccess;
    unsigned char *output = dest;
    /* check for a valid global RNG context */
    PORT_Assert(rng != NULL);
    if (rng == NULL) {
       PORT_SetError(SEC_ERROR_INVALID_ARGS);
       return SECFailure;
    }
    /* --- LOCKED --- */
    PZ_Lock(rng->lock);
    /* Check the amount of seed data in the generator.  If not enough,
     * don't produce any data.
     */
    if (rng->seedCount < MIN_SEED_COUNT) {
       PZ_Unlock(rng->lock);
       PORT_SetError(SEC_ERROR_NEED_RANDOM);
       return SECFailure;
    }
    /*
     * If there are enough bytes of random data, send back Xj, 
     * else call alg_cn_1() with 0's to generate more random data.
     */
    while (len > 0 && rv == SECSuccess) {
       if (rng->avail == 0) {
           /* All available bytes are used, so generate more. */
           rv = alg_fips186_2_cn_1(rng, NULL);
       }
       /* number of bytes to obtain on this iteration (max of 40) */
       num = PR_MIN(rng->avail, len);
       /*
        * if avail < 2*GSIZE, the first 2*GSIZE - avail bytes have
        * already been used.
        */
       if (num) {
           memcpy(output, rng->Xj + (2*GSIZE - rng->avail), num);
           rng->avail -= num;
           len -= num;
           output += num;
       }
    }
    PZ_Unlock(rng->lock);
    /* --- UNLOCKED --- */
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static SECStatus prng_RandomUpdate ( RNGContext *  rng,
const void data,
size_t  bytes 
) [static]

Definition at line 410 of file prng_fips1861.c.

{
    SECStatus rv = SECSuccess;
    /* check for a valid global RNG context */
    PORT_Assert(rng != NULL);
    if (rng == NULL) {
       PORT_SetError(SEC_ERROR_INVALID_ARGS);
       return SECFailure;
    }
    /* RNG_SystemInfoForRNG() sometimes does this, not really an error */
    if (bytes == 0)
       return SECSuccess;
    /* --- LOCKED --- */
    PZ_Lock(rng->lock);
    /*
     * Random information is initially supplied by a call to
     * RNG_SystemInfoForRNG().  That function collects entropy from
     * the system and calls RNG_RandomUpdate() to seed the generator.
     * Algorithm 1 of FIPS 186-2 Change Notice 1, step 1 specifies that
     * a secret value for the seed-key must be chosen before the
     * generator can begin.  The size of XKEY is b bits, so fill it
     * with the b-bit hash of the input to the first RNG_RandomUpdate()
     * call.
     */
    if (rng->seedCount == 0) {
       /* This is the first call to RandomUpdate().  Use a hash
        * of the input to set the seed-key, XKEY.
        *
        * <Step 1> copy hash of seed bytes into context's XKEY
        */
       SHA256_HashBuf(rng->XKEY, data, bytes);
       /* Now continue with algorithm. */
       rv = alg_fips186_2_cn_1(rng, NULL);
       /* As per FIPS 140-2 continuous RNG test requirement, the first
        * iteration of output is discarded.  So here there is really
        * no output available.  This forces another execution of alg_cn_1()
        * before any bytes can be extracted from the generator.
        */
       rng->avail = 0;
    } else if (bytes == BSIZE && memcmp(rng->XKEY, data, BSIZE) == 0) {
       /* Should we add the error code SEC_ERROR_BAD_RNG_SEED? */
       PORT_SetError(SEC_ERROR_INVALID_ARGS);
       rv = SECFailure;
    } else {
       /*
        * FIPS 186-2 does not specify how to reseed the RNG.  We retrofit
        * our RNG with a reseed function from NIST SP 800-90.
        *
        * Use a hash of the seed-key and the input to reseed the RNG.
        */
       SHA256Context ctx;
       SHA256_Begin(&ctx);
       SHA256_Update(&ctx, rng->XKEY, BSIZE);
       SHA256_Update(&ctx, data, bytes);
       SHA256_End(&ctx, rng->XKEY, NULL, BSIZE);
    }
    /* If got this far, have added bytes of seed data. */
    if (rv == SECSuccess)
       rng->seedCount += bytes;
    PZ_Unlock(rng->lock);
    /* --- UNLOCKED --- */
    return rv;
}

Here is the call graph for this function:

Here is the caller graph for this function:

SECStatus RNG_GenerateGlobalRandomBytes ( void dest,
size_t  len 
)

Definition at line 543 of file prng_fips1861.c.

Here is the call graph for this function:

Here is the caller graph for this function:

static PRStatus rng_init ( void  ) [static]

Definition at line 351 of file prng_fips1861.c.

{
    unsigned char bytes[SYSTEM_RNG_SEED_COUNT];
    unsigned int numBytes;
    if (globalrng == NULL) {
       /* create a new global RNG context */
       globalrng = &theGlobalRng;
        PORT_Assert(NULL == globalrng->lock);
       /* create a lock for it */
       globalrng->lock = PZ_NewLock(nssILockOther);
       if (globalrng->lock == NULL) {
           globalrng = NULL;
           PORT_SetError(PR_OUT_OF_MEMORY_ERROR);
           return PR_FAILURE;
       }
       /* the RNG is in a valid state */
       globalrng->isValid = PR_TRUE;
       /* Try to get some seed data for the RNG */
       numBytes = RNG_SystemRNG(bytes, sizeof bytes);
       PORT_Assert(numBytes == 0 || numBytes == sizeof bytes);
       if (numBytes != 0) {
           RNG_RandomUpdate(bytes, numBytes);
           memset(bytes, 0, numBytes);
       } else if (PORT_GetError() != PR_NOT_IMPLEMENTED_ERROR) {
           PZ_DestroyLock(globalrng->lock);
           globalrng->lock = NULL;
           globalrng->isValid = PR_FALSE;
           globalrng = NULL;
           return PR_FAILURE;
       }
       numBytes = RNG_GetNoise(bytes, sizeof bytes);
       RNG_RandomUpdate(bytes, numBytes);
    }
    return PR_SUCCESS;
}

Here is the call graph for this function:

Here is the caller graph for this function:

SECStatus RNG_RandomUpdate ( const void data,
size_t  bytes 
)

Definition at line 479 of file prng_fips1861.c.

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 397 of file prng_fips1861.c.

{
    /* Allow only one call to initialize the context */
    PR_CallOnce(&coRNGInit, rng_init);
    /* Make sure there is a context */
    return (globalrng != NULL) ? PR_SUCCESS : PR_FAILURE;
}

Here is the call graph for this function:

Here is the caller graph for this function:

Definition at line 549 of file prng_fips1861.c.

{
    /* check for a valid global RNG context */
    PORT_Assert(globalrng != NULL);
    if (globalrng == NULL) {
       /* Should set a "not initialized" error code. */
       PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
       return;
    }
    /* clear */
    freeRNGContext();
    /* reset the callonce struct to allow a new call to RNG_RNGInit() */
    coRNGInit = pristineCallOnce;
}

Here is the call graph for this function:

Here is the caller graph for this function:

static void RNG_UpdateAndEnd_FIPS186_2 ( SHA1Context *  ctx,
unsigned char *  input,
unsigned int  inputLen,
unsigned char *  hashout,
unsigned int pDigestLen,
unsigned int  maxDigestLen 
) [static]

Definition at line 574 of file prng_fips1861.c.

{
#if defined(SHA_NEED_TMP_VARIABLE)
    register PRUint32 tmp;
#endif
    static const unsigned char bulk_pad0[64] = { 0,0,0,0,0,0,0,0,0,0,
               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
               0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0  };

    PORT_Assert(maxDigestLen >= SHA1_LENGTH);
    PORT_Assert(inputLen <= SHA1_INPUT_LEN);

    /*
     *  Add the input
     */
    SHA1_Update(ctx, input, inputLen);
    /*
     *  Pad with zeroes
     *  This will fill the input block and cause the compression function
     *  to be called.
     */
    SHA1_Update(ctx, bulk_pad0, SHA1_INPUT_LEN - inputLen);

    /*
     *  Output hash
     */
    SHA_STORE_RESULT;
    *pDigestLen = SHA1_LENGTH;
}

Here is the call graph for this function:

Here is the caller graph for this function:


Variable Documentation

Definition at line 350 of file prng_fips1861.c.

RNGContext* globalrng = NULL [static]

Definition at line 174 of file prng_fips1861.c.

Definition at line 349 of file prng_fips1861.c.

RNGContext theGlobalRng [static]

Definition at line 175 of file prng_fips1861.c.