Skip to content
Snippets Groups Projects
  • Marek Vavruša's avatar
    dbca2316
    contrib: created 'contrib' directory with ccan/isaac as csprng · dbca2316
    Marek Vavruša authored
    previously cryptolib random function was used to generate message id,
    this works well but it is slow especially when the entropy is low,
    replaced with cryptographically safe prng ISAAC
    
    the ccan directory is going to be used in the future, as it's include
    structure makes it easy to embed C snippets instead of reimplementing
    them
    dbca2316
    History
    contrib: created 'contrib' directory with ccan/isaac as csprng
    Marek Vavruša authored
    previously cryptolib random function was used to generate message id,
    this works well but it is slow especially when the entropy is low,
    replaced with cryptographically safe prng ISAAC
    
    the ccan directory is going to be used in the future, as it's include
    structure makes it easy to embed C snippets instead of reimplementing
    them
isaac.c 6.70 KiB
/*Written by Timothy B. Terriberry (tterribe@xiph.org) 1999-2009.
  CC0 (Public domain) - see LICENSE file for details
  Based on the public domain implementation by Robert J. Jenkins Jr.*/
#include <float.h>
#include <math.h>
#include <string.h>
#include <ccan/ilog/ilog.h>
#include "isaac.h"


#define ISAAC_MASK        (0xFFFFFFFFU)

/* Extract ISAAC_SZ_LOG bits (starting at bit 2). */
static inline uint32_t lower_bits(uint32_t x)
{
	return (x & ((ISAAC_SZ-1) << 2)) >> 2;
}

/* Extract next ISAAC_SZ_LOG bits (starting at bit ISAAC_SZ_LOG+2). */
static inline uint32_t upper_bits(uint32_t y)
{
	return (y >> (ISAAC_SZ_LOG+2)) & (ISAAC_SZ-1);
}

static void isaac_update(isaac_ctx *_ctx){
  uint32_t *m;
  uint32_t *r;
  uint32_t  a;
  uint32_t  b;
  uint32_t  x;
  uint32_t  y;
  int       i;
  m=_ctx->m;
  r=_ctx->r;
  a=_ctx->a;
  b=_ctx->b+(++_ctx->c);
  for(i=0;i<ISAAC_SZ/2;i++){
    x=m[i];
    a=(a^a<<13)+m[i+ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a>>6)+m[i+ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a<<2)+m[i+ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a>>16)+m[i+ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
  }
  for(i=ISAAC_SZ/2;i<ISAAC_SZ;i++){
    x=m[i];
    a=(a^a<<13)+m[i-ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a>>6)+m[i-ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a<<2)+m[i-ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
    x=m[++i];
    a=(a^a>>16)+m[i-ISAAC_SZ/2];
    m[i]=y=m[lower_bits(x)]+a+b;
    r[i]=b=m[upper_bits(y)]+x;
  }
  _ctx->b=b;
  _ctx->a=a;
  _ctx->n=ISAAC_SZ;
}

static void isaac_mix(uint32_t _x[8]){
  static const unsigned char SHIFT[8]={11,2,8,16,10,4,8,9};
  int i;
  for(i=0;i<8;i++){
    _x[i]^=_x[(i+1)&7]<<SHIFT[i];
    _x[(i+3)&7]+=_x[i];
    _x[(i+1)&7]+=_x[(i+2)&7];
    i++;
    _x[i]^=_x[(i+1)&7]>>SHIFT[i];
    _x[(i+3)&7]+=_x[i];
    _x[(i+1)&7]+=_x[(i+2)&7];
  }
}


void isaac_init(isaac_ctx *_ctx,const unsigned char *_seed,int _nseed){
  _ctx->a=_ctx->b=_ctx->c=0;
  memset(_ctx->r,0,sizeof(_ctx->r));
  isaac_reseed(_ctx,_seed,_nseed);
}

void isaac_reseed(isaac_ctx *_ctx,const unsigned char *_seed,int _nseed){
  uint32_t *m;
  uint32_t *r;
  uint32_t  x[8];
  int       i;
  int       j;
  m=_ctx->m;
  r=_ctx->r;
  if(_nseed>ISAAC_SEED_SZ_MAX)_nseed=ISAAC_SEED_SZ_MAX;
  for(i=0;i<_nseed>>2;i++){
    r[i]^=(uint32_t)_seed[i<<2|3]<<24|(uint32_t)_seed[i<<2|2]<<16|
     (uint32_t)_seed[i<<2|1]<<8|_seed[i<<2];
  }
  _nseed-=i<<2;
  if(_nseed>0){
    uint32_t ri;
    ri=_seed[i<<2];
    for(j=1;j<_nseed;j++)ri|=(uint32_t)_seed[i<<2|j]<<(j<<3);
    r[i++]^=ri;
  }
  x[0]=x[1]=x[2]=x[3]=x[4]=x[5]=x[6]=x[7]=0x9E3779B9U;
  for(i=0;i<4;i++)isaac_mix(x);
  for(i=0;i<ISAAC_SZ;i+=8){
    for(j=0;j<8;j++)x[j]+=r[i+j];
    isaac_mix(x);
    memcpy(m+i,x,sizeof(x));
  }
  for(i=0;i<ISAAC_SZ;i+=8){
    for(j=0;j<8;j++)x[j]+=m[i+j];
    isaac_mix(x);
    memcpy(m+i,x,sizeof(x));
  }
  isaac_update(_ctx);
}

uint32_t isaac_next_uint32(isaac_ctx *_ctx){
  if(!_ctx->n)isaac_update(_ctx);
  return _ctx->r[--_ctx->n];
}

uint32_t isaac_next_uint(isaac_ctx *_ctx,uint32_t _n){
  uint32_t r;
  uint32_t v;
  uint32_t d;
  do{
    r=isaac_next_uint32(_ctx);
    v=r%_n;
    d=r-v;
  }
  while(((d+_n-1)&ISAAC_MASK)<d);
  return v;
}

/*Returns a uniform random float.
  The expected value is within FLT_MIN (e.g., 1E-37) of 0.5.
  _bits: An initial set of random bits.
  _base: This should be -(the number of bits in _bits), up to -32.
  Return: A float uniformly distributed between 0 (inclusive) and 1
           (exclusive).
          The average value was measured over 2**32 samples to be
           0.50000037448772916.*/
static float isaac_float_bits(isaac_ctx *_ctx,uint32_t _bits,int _base){
  float ret;
  int   nbits_needed;
  while(!_bits){
    if(_base+FLT_MANT_DIG<FLT_MIN_EXP)return 0;
    _base-=32;
    _bits=isaac_next_uint32(_ctx);
  }
  /*Note: This could also be determined with frexp(), for a slightly more
     portable solution, but that takes twice as long, and one has to worry
     about rounding effects, which can over-estimate the exponent when given
     FLT_MANT_DIG+1 consecutive one bits.
    Even the fallback C implementation of ILOGNZ_32() yields an implementation
     25% faster than the frexp() method.*/
  nbits_needed=FLT_MANT_DIG-ilog32_nz(_bits);
#if FLT_MANT_DIG>32
  ret=ldexpf((float)_bits,_base);
# if FLT_MANT_DIG>65
  while(32-nbits_needed<0){
# else
  if(32-nbits_needed<0){
# endif
    _base-=32;
    nbits_needed-=32;
    ret+=ldexpf((float)isaac_next_uint32(_ctx),_base);
  }
  _bits=isaac_next_uint32(_ctx)>>32-nbits_needed;
  ret+=ldexpf((float)_bits,_base-nbits_needed);
#else
  if(nbits_needed>0){
    _bits=_bits<<nbits_needed|isaac_next_uint32(_ctx)>>(32-nbits_needed);
  }
# if FLT_MANT_DIG<32
  else _bits>>=-nbits_needed;
# endif
  ret=ldexpf((float)_bits,_base-nbits_needed);
#endif
  return ret;
}

float isaac_next_float(isaac_ctx *_ctx){
  return isaac_float_bits(_ctx,0,0);
}

float isaac_next_signed_float(isaac_ctx *_ctx){
  uint32_t bits;
  bits=isaac_next_uint32(_ctx);
  return (1|-((int)bits&1))*isaac_float_bits(_ctx,bits>>1,-31);
}

/*Returns a uniform random double.
  _bits: An initial set of random bits.
  _base: This should be -(the number of bits in _bits), up to -32.
  Return: A double uniformly distributed between 0 (inclusive) and 1
           (exclusive).
          The average value was measured over 2**32 samples to be
           0.500006289408060911*/
static double isaac_double_bits(isaac_ctx *_ctx,uint32_t _bits,int _base){
  double ret;
  int    nbits_needed;
  while(!_bits){
    if(_base+DBL_MANT_DIG<DBL_MIN_EXP)return 0;
    _base-=32;
    _bits=isaac_next_uint32(_ctx);
  }
  nbits_needed=DBL_MANT_DIG-ilog32_nz(_bits);
#if DBL_MANT_DIG>32
  ret=ldexp((double)_bits,_base);
# if DBL_MANT_DIG>65
  while(32-nbits_needed<0){
# else
  if(32-nbits_needed<0){
# endif
    _base-=32;
    nbits_needed-=32;
    ret+=ldexp((double)isaac_next_uint32(_ctx),_base);
  }
  _bits=isaac_next_uint32(_ctx)>>(32-nbits_needed);
  ret+=ldexp((double)_bits,_base-nbits_needed);
#else
  if(nbits_needed>0){
    _bits=_bits<<nbits_needed|isaac_next_uint32(_ctx)>>32-nbits_needed;
  }
# if DBL_MANT_DIG<32
  else _bits>>=-nbits_needed;
# endif
  ret=ldexp((double)_bits,exp-DBL_MANT_DIG);
#endif
  return ret;
}

double isaac_next_double(isaac_ctx *_ctx){
  return isaac_double_bits(_ctx,0,0);
}

double isaac_next_signed_double(isaac_ctx *_ctx){
  uint32_t bits;
  bits=isaac_next_uint32(_ctx);
  return (1|-((int)bits&1))*isaac_double_bits(_ctx,bits>>1,-31);
}