FORT v0.4 uusi versio

Fortin edellisestä versiosta (http://moijari.com/?p=634) on kulunut jo jonkin verran aikaa ja uudessa versiossa on muutoksia, joten ajattelin postata uuden version. Oikeastaan varsinaisessa koodissa ei ole paljon muutoksia, ainoastaan nuo lohkon alun (FORT_INTERNAL_EVENTS_START) ja lopun (FORT_INTERNAL_EVENTS_END) makrot. Funktioiden esittelyt ovat muuttuneet siten, että tämän tiedoston (fort.c) sisäisiin apufunktioihin on lisätty static sana, joka estää funktioiden käytön fort.c tiedoston ulkopuolella. Lisäksi globaaleissa muuttujissa on static sana, jos ei ole toivottavaa että niitä käytetään fort.c:n ulkopuolelta. Fort_init:iin on lisätty mahdollisuus käyttää /dev/random ja /dev/urandom tiedostoja fort:in avainnukseen. Fort_reseed():iin on lisätty automaattinen tallennus 10 minuutin välein. Tallennus tapahtuu fort_reseed:issä, jos reseediä ei suoriteta tallennusta ei tapahdu. Tallennuksen yhteydessä tulostetaan tiedostoon fortpools.deb raporttia puskureiden sisältämistä merkkimääristä. Raportin määrä lyhennetään lukukelpoiseksi (4 merkkiä) rutiinilla readable_length_decimal(). Satunnaistapahtuman lisäykseen on lisätty 5 ja 6 moodit. Myös puskurien määrää voi kasvattaa 64:een (FORT_64_POOLS). Erotettu satunnaisgeneraattorin tilan tallennus ja puskurien kerätyt merkit raportti toisistaan. Näin niiden väliset ajat voidaan valita erikseen. Lisätty puskurien kerätyt merkit raporttiin binäärimoodi, jossa katkokohta on 1000 (decimal) sijasta 1024(binary). Moodi valitaan muuttujalla FORT_DUMP_POOLS_BIN. Binääritulosteesta on malli postin lopussa.

Jos haluat lisätietoa Fortunasta, tässä on wikipediasivu https://en.wikipedia.org/wiki/Fortuna_(PRNG).

Seuraavassa fort ohjelman käyttämät aliohjelmat, inccvar() ja clearcvar() 16 merkkisen laskurin käsittelyyn. Gettenths():llä fort_random_data() hakee sekunnin kymmenesosat, perustoiminnassa avainnus voidaan tehdä pienimmillään kymmenesosa sekunnin päästä edellisestä avainnuksesta. Getmicroseconds() taas on aikaan liittyvien satunnaistapahtumia tekevien rutiinien käytössä. Getseconds():illa haetaan sekunnit seuraavan automaattisen fort_save():n ajoitusta varten. Varsinainen ajoitettu save koodi on fort_reseed() funktiossa.

Huomaa, että näiden rutiinien määrittelyyn on lisätty static sana, joka tarkoittaa sitä että rutiinia voidaan kutsua vain tästä tiedostosta (fort.c). Samoin aiemmin globaaleihin muuttujiin, kuten seuraava cvar[] on lisätty static sana ja näin sen pitäisi olla käsiteltävissä vain tämän tiedoston rutiineilla.

Jos haluat käyttää forttia ilman ressua, voit lisätä pikku-a:n seuraavaan RESSU sanaan. Tarvitset kuitenkin SHA256:n (http://moijari.com/?p=752) tiivisteiden laskentaan. Jos poistat ressun lisäksi sisäiset tapahtumat, FORT_USE_RANDOM määritykset ja save():n luoman fort.rnd tiedoston, on main() rutiinin tulostama salasana-merkkijono aina sama.

#define RESSU 2
#define DEBUG 2
#define FORT_INTERNAL_EVENTS 2
static unsigned int fort_internal_events = 1;
static unsigned int fort_internal_event_mode = 1;
static unsigned int fort_reseed_always = 1;
static unsigned int fort_fill_pool_when_reseeding = 1;
#define FORT_USE_URANDOM 2
#define aFORT_USE_RANDOM 2

#define aFORT_64_POOLS 2

#ifdef FORT_64_POOLS
#define FORT_POOLS 64
typedef unsigned long FORT_COUNTER;
#else
#define FORT_POOLS 32
typedef unsigned int FORT_COUNTER;
#endif

static unsigned char cvar[16];

static void inccvar()
{
  int c = 0;

  /* 16 bytes, LSB first */
  while(++cvar[c] == 0 && c < sizeof(cvar)-1)
    c++;
}

static void clearcvar()
{
  int c;

  for(c=0; c < sizeof(cvar); c++)
    cvar[c] = 0;
}

static unsigned long gettenths()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((unsigned long)tv.tv_sec*10 +
      (int)tv.tv_usec/100000);
}

static unsigned long getmicroseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((unsigned long)tv.tv_sec*1000000 +
      tv.tv_usec);
}

static unsigned long getseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((unsigned long)tv.tv_sec);
}

Nämä ovat edellisestä postista (http://moijari.com/?p=937) tuttuja satunnaistapahtumien muodostusrutiineja, joita käytetään haluttujen lohkojen alussa ja lopussa. Huomaa taas static sanat pool ja pool2 muuttujissa, Static sanalla muuttujan arvo saadaan säilymään funktion kutsukertojen välillä.

//#define FORT_INTERNAL_EVENTS 2
//static unsigned int fort_internal_events = 1;
//static unsigned int fort_internal_event_mode = 2;

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_START(source) \
  unsigned long micros; \
  static int \
    pool=0, pool2=0; \
  if(fort_internal_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_internal_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_INTERNAL_EVENTS_START(source)
#endif

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_END(source) \
  if(fort_internal_events) \
    fort_add_random_event_timer_do(&pool2, \
      source, fort_internal_event_mode, \
      &micros);
#else
#define FORT_INTERNAL_EVENTS_END(source)
#endif

Seuraavassa satunnaistapahtumien luomiseen käytetyt kutsut: fort_add_random_event on päärutiini ja muut käyttävät sitä varsinaisen tapahtuman tekemiseen.

Luomisen yhteydessä tapahtuvaan seuraavan satunnaistapahtumapuskurin valintaan on kaksi uutta toimintoa. 1 on perusvaihtoehto, jossa puskurin numeroa kasvatetaan aina yhdellä. 2 on vaihtoehto, joka pyrkii täyttämään viimeksi käytetyt puskurit ensin. 3 ei tee puskurille mitään, kutsuja voi päättää seuraavan puskurin itse. 4 valitsee satunnaisen puskurin seuraavaksi puskuriksi. 5 toimii siten että jos käsitellään puskuria 0 täytetään puskuri minimiin, muuten vaihdetaan seuraavaan puskuriin. 6 täyttää kohdalle osuvan vajaan puskurin minimiin ja jatkaa sitten seuraavaan puskuriin. Näistä 5 ja 6 ovat uusia. Jos verrataan niitä kakkoseen ne täyttävät enemmän suuria puskureita,

Fort_add_random_event_timer_start() ja fort_add_random_event_timer_do() tekevät funktion tai lohkon suorittamisen kestosta ajan millisekunteina . Fort_add_random_event_time() tekee kellonajasta ajan millisekunteina. Molemmat lähettävät fort_add_random_event() rutiinilla satunnaistapahtuman puskureihin.

Fort_add_random_event_split() jakaa pidemmän tapahtuman useampaan puskuriin.

struct fort_pool {
  unsigned long length;
  HashCtx pool;
};
static struct fort_pool fort_pools[FORT_POOLS];

#ifdef DEBUG
static int event_id = 0;
static char fort_events_file[128] =
    "fortevents.deb";
#endif

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf)
{
  while(len>1 && buf[len-1]==0)
    len--;

  HashUpdate(&fort_pools[*pool].pool, buf, len);
  fort_pools[*pool].length+=len;

#ifdef DEBUG
  if(event_id < 65536) {
    FILE *fp1;
    if((fp1=fopen(fort_events_file, "a"))!=NULL) {
      fprintf(fp1,"id=%d", event_id);
      fprintf(fp1,", source=%02d", source);
      fprintf(fp1,", pool=%02d", *pool);
      fprintf(fp1,", mode=%d", mode);
      fprintf(fp1,", len=%d", len);
      fprintf(fp1,", data=");
      for(int c=0;c<len;c++)
        fprintf(fp1,"%02x", buf[c]);
      fprintf(fp1,"\n");
      fflush(fp1);
      fclose(fp1);
      event_id++;
    }
  }
#endif

  switch(mode) {

  case 1:
    (*pool) = ((*pool)+1) % FORT_POOLS;
    break;

  case 2:
    if((*pool) >= FORT_POOLS-1 ||
        fort_pools[*pool].length <
        fort_pools[*pool+1].length)
      (*pool) = 0;
    else
      (*pool) = ((*pool)+1) % FORT_POOLS;
    break;

  case 3:
    break; // caller decides next pool                                                                                                                                                                                                                          

  case 4:
#ifdef RESSU
    (*pool) = ressu_genbyte_limit(FORT_POOLS);
#else
    (*pool) = ((*pool)+1) % FORT_POOLS;
#endif
    break;

  case 5:
    if((*pool)==0 && fort_pools[*pool].length <
        FORT_MIN_POOL_SIZE)
      (*pool) = 0;
    else
      (*pool) = ((*pool)+1) % FORT_POOLS;
    break;

  case 6:
    if(fort_pools[*pool].length >=
        FORT_MIN_POOL_SIZE)
      (*pool) = ((*pool)+1) % FORT_POOLS;
    break;
  }
}

void fort_add_random_event_timer_start(unsigned long *micros)
{
  *micros = getmicroseconds();
}

void fort_add_random_event_timer_do(int *pool, int source, int mode, unsigned long *micros)
{
  unsigned char temp[2];
  unsigned long l;

  l = getmicroseconds()-*micros;
  temp[0] = l & 0xff;
  temp[1] = (l>>8) & 0xff;

  fort_add_random_event(pool, source, mode,
               sizeof(temp), temp);
}

void fort_add_random_event_time(int *pool, int source, int mode)
{
  unsigned char temp[2];
  unsigned long l;

  l = getmicroseconds();
  temp[0] = l & 0xff;
  temp[1] = (l>>8) & 0xff;

#ifdef RESSUEVENT
  ressu_genbuffer(sizeof(temp), temp);
#endif

  fort_add_random_event(pool, source, mode,
               sizeof(temp), temp);
}

void fort_add_random_event_split(int *pool, int source, int mode, int len, unsigned char *buf, int size)
{
  int n;

  while(len != 0) {
    n = (size<len)? size:len;
    fort_add_random_event(pool, source,
        mode, n, buf);
    buf += n;
    len -= n;
  }
}

Seuraavassa ensimmäiset satunnaisuutta keräävät tapahtumat. Näillä muodostetaan SHA256 tiiviste halutusta merkkijonosta. Init funktio täyttää vain SHA256 yhteysalueen state[] ja count kentät, ja sen tuottama kesto tapahtuma on yleensä nolla. Update funktio tallettaa lyhyet alle 64 merkkiset merkkijonot yhteysalueen buffer muuttujaan ja siinäkin usein kestotapahtuma on lähellä nollaa. Kun puskuri täyttyy, ajetaan transform ja se vie hiukan pidemmän ajan (katso http://moijari.com/?p=752). Final on samantapainen kun update, se ajaa lopuksi transformin ja siitä tulee pidempiä kestoja.

Suoritusaikatapahtumissa näissä kaikissa on satunnainen kellonaika (ainakin alimmat 8 bittiä), suoritusaikahan on mikrosekunteina.

static void hash_init(HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(10)

  HashInit(hash);

  FORT_INTERNAL_EVENTS_END(11)
}

static void hash_update(HashCtx *hash, unsigned char *data, int len)
{
  FORT_INTERNAL_EVENTS_START(12)

  HashUpdate(hash, data, len);

  FORT_INTERNAL_EVENTS_END(13)
}

static void hash_final(unsigned char digest[HashLen], HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(14)

  HashFinal(digest, hash);

  FORT_INTERNAL_EVENTS_END(15)
}

Seuraavana fort_pseudo_random_data() funktio ja sen apufunktio fort_rekey(). Fort_rekey() funktiolla palautetaan käyttäjän antamaan muistialueeseen (tässä buf) satunnaisbittisarja. Fort_pseudo_random_data() käyttää rekeytä satunnaisbittien tekemiseen ja fort_key avaimen päivittämiseen.

static unsigned char fort_key[HashLen];

void fort_rekey(unsigned char *buf)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(16)

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update(&hash, (unsigned char *)&cvar,
    sizeof(cvar));
  hash_final(buf, &hash);
  inccvar();

  // Forget hash
  memset(&hash, 0, sizeof(hash));

  FORT_INTERNAL_EVENTS_END(17)
}

#define FORT_PSEUDO_LIMIT 1048576

void fort_pseudo_random_data(int len, 
    unsigned char *buf)
{
  unsigned char tmp[HashLen];
  unsigned int n, blockbytes;

  FORT_INTERNAL_EVENTS_START(18)

  blockbytes = 0;

  while(len != 0) {
    FORT_INTERNAL_EVENTS_START(19)

    fort_rekey(tmp);
    n = (len<HashLen) ? len : HashLen;
    memcpy(buf, tmp, n);
    buf += n;
    len -= n;
    blockbytes += n;

    // rekey every 1048576 bytes if data left
    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {                                                                                                              
      fort_rekey(fort_key);
      blockbytes = 0;
    }

    FORT_INTERNAL_EVENTS_END(20)
  }

  fort_rekey(fort_key);

  // Forget last bytes
  memset(tmp, 0, sizeof(tmp));

  FORT_INTERNAL_EVENTS_END(21)
}

Fort_reseed laskee yhteistiivisteen edellisestä fort_key:stä, cvar laskurista ja käyttäjän antamasta merkkijonosta ja muodostaa niistä uuden fort_key avaimen. Pohjana on tässä edellinen fort_key avain, eli “uusi” satunnaisuus lisätään edelliseen.

Lisäksi fort_reseed tekee fort_save():n aina kymmenen minuutin välein (FORT_SECONDS_BETWEEN_SAVES). Save tehdään vasta kun ohjelma suorittaa seuraavan kerran fort_reseed():iä.

Reseed() kirjoittaa myös raportin puskurien sisältämästä merkkimäärästä tiedostoon fortpools.rnd. Myöhempänä on mallituloste.

Voit valita sekä talletuksien että raporttien välisen ajan sekunteina. Talletuksien väli valitaan FORT_SECONDS_BETWEEN_SAVES muuttujalla ja raporttien väli valitaan FORT_SECONDS_BETWEEN_POOLS_REPORTS:llä. Väli kerrotaan sekunteina. “Takuulla” toimivista vaihtoehdoista on lista koodin kommentissa. Voit toki valita ajat miten haluat, mutta nämä tasatunteihin tai vuorokausiin perustuvat ajat ovat helpompia ymmärtää.

Fort_random_data() tekee avainnuksen tarvittaessa uudestaan määritellen fort_key:n (kutsumalla fort_reseed():iä). Muodostettavaan avaimeen käytetään fort_pools[] puskureihin talletettujen satunnaismerkkien tiivisteitä. Mahdollisen avainnuksen jälkeen fort_random_data kutsuu fort_pseudo_random_data() rutiinia, joka palauttaa varsinaiset satunnaisbitit kutsujalle.

Fort_random_data() funktiossa suurimmat erot suoritusaika tapahtumissa johtuvat siitä että ressu_genbuffer() palauttaa suurimman osan tapahtumista puskuristaan ja lopuille täytetään puskuri uudestaan, joka vie aikaa.

// choose one of                                                                                                                                                                       
//     60 (1 minute)                                                                                                                                                                   
//     300 (5 minutes)                                                                                                                                                                 
//     600 (10 minutes)                                                                                                                                                                
//     900 (15 minutes)                                                                                                                                                                
//     1200 (20 minutes)                                                                                                                                                               
//     1800 (30 minutes)                                                                                                                                                               
//     3600 (hour)                                                                                                                                                                     
//     7200 (2 hours)                                                                                                                                                                  
//     10800 (3 hours)                                                                                                                                                                 
//     14400 (4 hours)                                                                                                                                                                 
//     21600 (6 hours)                                                                                                                                                                 
//     28800 (8 hours)                                                                                                                                                                 
//     43200 (12 hours                                                                                                                                                                 
//     86400 (24 hours)                                                                                                                                                                
// to get readable report..                                                                                                                                                            

#define FORT_SECONDS_BETWEEN_SAVES 600
static unsigned long fort_next_save = 0;

#ifdef DEBUG
#define FORT_SECONDS_BETWEEN_POOLS_REPORTS 3600
static unsigned long fort_next_pools_report = 0;
#define TIMEFORMAT "%Z%Y%m%d%H%M%S"
#endif

void dump_pools(char *header);

void fort_reseed(int len, unsigned char *buf)
{
  HashCtx hash;

  FORT_INTERNAL_EVENTS_START(22)

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update(&hash, (unsigned char *)&cvar,
      sizeof(cvar));
  hash_update(&hash, buf, len);
  hash_final(fort_key, &hash);
  inccvar();

  memset(&hash, 0, sizeof(hash));

  unsigned long seconds;
  seconds = getseconds();

  if(fort_next_save != 0 &&
      seconds >= fort_next_save) {

    unsigned long diff=
      timegm(localtime(&seconds))-seconds;

    fort_next_save = seconds -
      ((seconds+diff) %
      FORT_SECONDS_BETWEEN_SAVES) +
      FORT_SECONDS_BETWEEN_SAVES;

    fort_save();
  }

#ifdef DEBUG

  if(fort_next_pools_report != 0 &&
      seconds >= fort_next_pools_report) {

    unsigned long diff=
      timegm(localtime(&seconds))-seconds;

    char timebuf[128];
    strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
      localtime(&seconds));

    fort_next_pools_report = seconds -
      ((seconds+diff) %
      FORT_SECONDS_BETWEEN_POOLS_REPORTS) +
      FORT_SECONDS_BETWEEN_POOLS_REPORTS;

    dump_pools(timebuf);
  }
#endif

  FORT_INTERNAL_EVENTS_END(23)
}

//static unsigned int fort_reseed_always = 1;
//static unsigned int fort_fill_pool_when_reseeding = 1;
static FORT_COUNTER fort_reseed_count = 0;
static unsigned long fort_next_reseed = 0;
#define FORT_MIN_POOL_SIZE 64

void fort_random_data(int len, unsigned char *buf)
{
  int c;
  unsigned long tenths;
  HashCtx hash;
  unsigned char buffer[HashLen];

  FORT_INTERNAL_EVENTS_START(24)

  tenths=gettenths();

  if( (fort_reseed_always == 1) ||
      (fort_next_reseed == 0) ||
      (fort_next_reseed <= tenths &&
      fort_pools[0].length >=
      FORT_MIN_POOL_SIZE) ) {

    // next tenth of a second
    fort_next_reseed = tenths + 1;                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         
    fort_reseed_count++;

    hash_init(&hash);
    c=0;

    while(c < FORT_POOLS && (fort_reseed_count
        % (1<<c)) == 0) {

      FORT_INTERNAL_EVENTS_START(25)

      if(fort_fill_pool_when_reseeding==1 &&
          fort_pools[c].length <
          FORT_MIN_POOL_SIZE) {

        unsigned char temp[64];
        int len = FORT_MIN_POOL_SIZE -
            fort_pools[c].length;
        int n;

        while(len != 0) {

          FORT_INTERNAL_EVENTS_START(26)

          n = (len < sizeof(temp)) ? len :
              sizeof(temp);
          fort_pseudo_random_data(n, temp);
#ifdef RESSU
          ressu_genbuffer(n, temp);
#endif
          int pooltemp = c;
          fort_add_random_event(&pooltemp,
              27, fort_internal_event_mode,
              n, temp);
          len-=n;

          FORT_INTERNAL_EVENTS_END(28)
        }
        memset(temp, 0, sizeof(temp));
      }
      hash_final(buffer, &fort_pools[c].pool);
      hash_update(&hash, buffer, sizeof(buffer));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);
      // save earlier pool to new one
      hash_update(&fort_pools[c].pool,
          buffer, sizeof(buffer));                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
      c++;

      FORT_INTERNAL_EVENTS_END(29)
    }
    hash_update(&hash, (unsigned char *)
        &cvar, sizeof(cvar));
    hash_final(buffer, &hash);
    fort_reseed(sizeof(buffer), buffer);
    // Forget hash context
    memset(&hash, 0, sizeof(hash));
    // Forget reseed key
    memset(buffer, 0, sizeof(buffer));
    inccvar();
  }
  fort_pseudo_random_data(len, buf);

  FORT_INTERNAL_EVENTS_END(30)
}

Seuraavana fortin aliohjelmia: alun muuttujat muodostavat puskurin joka täytetään uudestaan sen tyhjentyessä. Fort_random_data_byte() täyttää puskurin uudestaan sen tyhjentyessä ja palauttaa seuraavan käyttämättömän merkin. Fort_clear():illa voidaan tyhjentää puskuri, jolloin seuraavalla esimerkiksi fort_random_data_byte():llä se täytetään uudestaan. Fort_clear():ia voi käyttää kriittisten satunnaislukujen haun alussa ja lopussa, jolloin bitit ovat muistissa “näkyvissä” vain minimiajan. Fort_random_data_byte_limit() palauttaa parametrinä annettua rajaa (limit) pienemmän satunnaisluvun. Fort_random_data_buffer() palauttaa puskurillisen satunnaisbittejä.

#define FORTCNT 128

static unsigned int fort_cnt = FORTCNT;
static unsigned char fort_bytes[FORTCNT];
static int fort_byte = 999999999;

int fort_random_data_byte()
{
  if(fort_byte >= fort_cnt) {
    fort_random_data(fort_cnt, fort_bytes);
    fort_byte = 0;
  }
  return(fort_bytes[fort_byte++]);
}

void fort_clear()
{
  memset(fort_bytes, 0, fort_cnt);
  fort_byte = 999999998;
}

int fort_random_data_byte_limit(int limit)
{
  int c;

  while((c = fort_random_data_byte())>=
      (256/limit)*limit);
  /* while((c = fort_random_data_byte())>
      (256/limit)*limit); little bug */
  return(c % limit);
}

void fort_random_data_buffer(int size,
    unsigned char *buffer)
{
  int c;

  for(c=0; c<size; c++)
    buffer[c] ^= fort_random_data_byte();
}

Fort_save() ja fort_restore() tallettavat ja lukevat takaisin fort generaattorin tilan. Fort_save tallettaa jonon fort_pseudo_random_data():lla luettuja merkkejä fort.rnd tiedostoon. Fort_restore() lukee edellisellä talletuksella kirjoitetut merkit ja tekee niillä fort_reseed():in. Jos fort_restore() ei onnistu avaamaan tiedostoa, se generoi merkit fort_reseed():ille käyttäen ressu_genbuffer():ia.

#define FORT_SAVE_SIZE 1024
char fort_random_file[128] = "fort.rnd";

void fort_save()
{
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  if((fp1 = fopen(fort_random_file, "w"))
      != NULL) {
    fort_pseudo_random_data(sizeof(buffer), buffer);
    fwrite(buffer, 1, sizeof(buffer), fp1);
    memset(buffer, 0, sizeof(buffer));
    fclose(fp1);
  }
}

void fort_restore()
{
  int d;
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  if((fp1 = fopen(fort_random_file, "rb"))
      != NULL) {
    d = fread(buffer, 1, sizeof(buffer), fp1);
    fclose(fp1);
  } else {
    fort_pseudo_random_data(sizeof(buffer), buffer);
#ifdef RESSU
    ressu_genbuffer(sizeof(buffer), buffer);
#endif
    d = sizeof(buffer);
  }
  fort_reseed(d, buffer);
  memset(buffer, 0, sizeof(buffer));

  fort_save();
}

Seuraavaa fort_init rutiinia kutsutaan ohjelman main rutiinin alussa ressu_init() rutiinin jälkeen. Fort_readfile_xor() on /dev/random:in ja /dev/urandom:in lukemista varten.

//#define FORT_USE_URANDOM 2
//#define aFORT_USE_RANDOM 2

#if defined FORT_USE_URANDOM || \
    defined FORT_USE_RANDOM

static void fort_readfile_xor(int len,
    unsigned char *buf,
    unsigned char *filename)
{
  int c, n;
  unsigned char temp[64];
  FILE *fp1;

  if((fp1 = fopen(filename, "rb"))
      != NULL) {
    while(len != 0) {
      n = (len < sizeof(temp)) ?
          len : sizeof(temp);
      fread(temp, 1, n, fp1);
      for(c = 0; c < n; c++)
        buf[c] ^= temp[c];
      len -= n;
      buf += n;
    }
    fclose(fp1);
  }
}

#endif

#define aFORT_DUMP_POOLS_BIN 2

#ifdef FORT_DUMP_POOLS_BIN
#define FORT_DUMP_POOLS_HIGH 1023
#define FORT_DUMP_POOLS_DIVIDER 1024
#define FORT_DUMP_POOLS_FIELD_WIDTH 6
#else
#define FORT_DUMP_POOLS_HIGH 999
#define FORT_DUMP_POOLS_DIVIDER 1000
#define FORT_DUMP_POOLS_FIELD_WIDTH 5
#endif

#define FORT_DUMP_POOLS_WIDTH 32

static void readable_length(char *buf,
    unsigned long length)
{
  int c, low;

  // B = byte                                                                                                                                                                                                                                                                                                     
  // K = kilo   10^3                                                                                                                                                                                                                                                                                              
  // M = mega   10^6                                                                                                                                                                                                                                                                                              
  // G = giga   10^9                                                                                                                                                                                                                                                                                              
  // T = tera   10^12                                                                                                                                                                                                                                                                                             
  // P = peta   10^15                                                                                                                                                                                                                                                                                             
  // E = exa    10^18                                                                                                                                                                                                                                                                                             
  // Z = zetta  10^21                                                                                                                                                                                                                                                                                             
  // Y = yotta  10^24                                                                                                                                                                                                                                                                                             
  char units[] = "BKMGTPEZY";

  strcpy(buf,"***");
  low=0;

  for(c=0; length>=low &&
      c<sizeof(units)-1; c++) {
    if(length>=low &&
        length<=FORT_DUMP_POOLS_HIGH) {
      if(units[c]!='B')
        sprintf(buf,"%ld%c", length, units[c]);
      else
        sprintf(buf,"%ld", length);
      break;
    }
    length/=FORT_DUMP_POOLS_DIVIDER;
    low=1;
  }
}

void dump_pools(char *header)
{
  FILE *fp1;
  unsigned char readable[10];

  if((fp1 = fopen("fortpools.deb", "a"))
      !=NULL) {
    fprintf(fp1,"%-25s", header);
    for(int c=0;c<FORT_POOLS;c++) {
      if(c>0 && c%FORT_DUMP_POOLS_WIDTH==0)
        fprintf(fp1,"\n%-25s","");
      readable_length(readable,
          fort_pools[c].length);
      fprintf(fp1,"%*s",
              FORT_DUMP_POOLS_FIELD_WIDTH,
              readable);
    }
    fprintf(fp1, "\n");
    fclose(fp1);
  }
}

void fort_init()
{
  int c, pooltemp;

  fprintf(stdout,"Fort v0.48");
  fprintf(stdout,", hash: %s",
      HashName);
  fprintf(stdout,", pools: %d*%ld", FORT_POOLS,
      sizeof(struct fort_pool));
  fprintf(stdout,", HashCtx: %ld",
      sizeof(HashCtx));
  fprintf(stdout,", hashsize: %d",
      HashLen);
  fprintf(stdout,"\n");

  unsigned int save_fort_internal_events;
  save_fort_internal_events=fort_internal_events;
  fort_internal_events=1;

  fort_next_save = 0;
  
  clearcvar();

#ifdef RESSU
  ressu_genbuffer(sizeof(fort_key), fort_key);
#endif

unsigned char temp[64];

#ifdef FORT_USE_URANDOM
  memset(temp, 0, sizeof(temp));
  fort_readfile_xor(sizeof(temp), temp,
      "/dev/urandom");
  fort_reseed(sizeof(temp), temp);
#endif

#ifdef FORT_USE_RANDOM
  memset(temp, 0, sizeof(temp));
  fort_readfile_xor(sizeof(temp), temp,
      "/dev/random");
  fort_reseed(sizeof(temp), temp);
#endif

  // Initialize buffers
  for(c=0; c<FORT_POOLS; c++) {
    fort_pools[c].length = 0;
    HashInit(&fort_pools[c].pool);
  }

  dump_pools("Initialize buffers");

#ifdef DEBUG
  FILE *fp1;
  // Empty events file
  if((fp1 = fopen(fort_events_file, "w"))!=NULL)
    fclose(fp1);
  event_id = 0;
#endif

  if((fp1 = fopen("fortpools.deb", "w"))!=NULL)
    fclose(fp1);

  fort_pseudo_random_data(sizeof(temp), temp);
#ifdef RESSU
  ressu_genbuffer(sizeof(temp), temp);
#endif
  pooltemp = 0;
  fort_add_random_event_split(&pooltemp,
      31, 2, sizeof(temp), temp, 1);

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events) {
    // Create some internal events
    for(c=0; c<64; c++)
      fort_random_data(sizeof(temp), temp);
  }
#endif
  dump_pools("Internal events");

  fort_reseed_count = 0;
  fort_next_reseed = 0;

  // Reseed fort_key with new events
  fort_random_data(sizeof(temp), temp);
  fort_reseed(sizeof(temp), temp);

  dump_pools("reseed");

  fort_restore();

  dump_pools("Restore");

  // Events to random pools
  fort_pseudo_random_data(sizeof(temp), temp);
#ifdef RESSU
  ressu_genbuffer(sizeof(temp), temp);
#endif
  pooltemp=0;
  fort_add_random_event_split(&pooltemp,
      32, 4, sizeof(temp), temp, 1);

  dump_pools("Events to random pools");

  // Fill first pool
  fort_pseudo_random_data(sizeof(temp), temp);
#ifdef RESSU
  ressu_genbuffer(sizeof(temp), temp);
#endif
  pooltemp=0;
  fort_add_random_event(&pooltemp,
      33, 1, sizeof(temp), temp);

  dump_pools("Fill 1st pool");

  // Forget temp
  memset(temp,0,sizeof(temp));

  fort_internal_events = save_fort_internal_events;

  fort_reseed_count = 0;
  fort_next_reseed = 0;

  fort_next_save = 1;
}

Tässä tuloste dump_pools():in raportista (fortpools.deb tiedostosta): Raportti on tulostettu 26.7 kello 20:30 (ja 20:40) runsaasti satunnaislukuja tekevällä ohjelmalla. Rivien alussa on aikavyöhyke, vuosi, kuukausi, päivä, tunnit, minuutit ja sekunnit. Loppurivi koostuu ohjelman puskurien kerättyjen merkkien määristä. Tässä puskureita on 64. Tällä raportilla on kaksi riviä, joiden väli on tuo talletusten väli 10 minuuttia. Yksiköt ovat tarkemmin funktiossa readable_length(). Tässä olevat yksiköt ovat kiloa(K), megaa(M) ja gigaa(G). Luvut ovat desimaaleja eli 1 kilo on 1000 merkkiä, mega on 1000 kiloa ja giga on 1000 megaa. Näin ne mahtuvat välilyönteineen viiteen merkkiin, raportti olisi leveämpi jos yksiköt olisivat binäärisiä eli 1024 merkkiä vastaa kiloa, 1024 kiloa vastaa megaa ja 1024 megaa vastaa gigaa. Oikeassa tulosteessa luku vie viisi merkkiä, tässä on näköjään 1 väli + numerot + yksikkö (=2-5 merkkiä), eli luvut eivät ole oikeissa sarakkeissa. Rivin alussa olevia puskureita käytetään useimmin ja rivin lopussa olevia käytetään harvemmin, tässä 2G puskureita ei ole vielä käytetty. Koska raporttirivi tulostetaan heti avainnuksen jälkeen voidaan arvailla että ensimmäisellä raporttirivillä on käytetty äskettäin puskuri 0 ja seuraavalla rivillä on käytetty puskurit 0, 1 ja 2. Noiden puskurien kerätty määrä on nollattu äskettäin..

EEST20200726203000 3 145 141 136 136 121 121 121 427 426 419 2K 2K 2K 2K 2K 55K 162K 162K 162K 1M 2M 2M 9M 23M 23M 23M 23M 23M 23M 902M 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G
EEST20200726204000 5 3 0 198 196 196 292 380 380 381 1K 2K 2K 2K 16K 43K 43K 43K 257K 686K 1M 1M 4M 11M 11M 39M 39M 39M 39M 39M 918M 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G 2G

Tässä vielä toinen versio pools raportista, jossa on käytetty binääri versiota tulosteesta (FORT_DUMP_POOLS_BIN): 1000 ja 1023:n väliin osuvat pituudet ovat sen verran harvinaisia ettei tähänkään raporttiin osunut kuin yksi sellainen tapaus, 1002K rivin alkupuolella.

EEST20200802123000 72 74 75 232 232 290 330 412 627 1K 1K 1K 1K 7K 7K 7K 59K 164K 164K 164K 1002K 2M 2M 9M 22M 48M 100M 100M 100M 100M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M 938M

Vielä pieni pääohjelma joka tekee fortilla salasanoiksi sopivaa merkkijonoa: ohjelmassa on malli fort_clear():in käytöstä, sekä satunnaistapahtumien luomisesta. Ensin kuitenkin uudet makrot satunnaistapahtumien lisäämiselle asiakasohjelmaan:

#define FORT_EVENTS 2
static unsigned int fort_events = 1;
static unsigned int fort_event_mode = 2;

#ifdef FORT_EVENTS
#define FORT_EVENTS_START(source) \
  unsigned long micros; \
  static int \
    pool=0, pool2=0; \
  if(fort_events) { \
    fort_add_random_event_time(&pool, \
    source, fort_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_EVENTS_START(source)
#endif

#ifdef FORT_EVENTS
#define FORT_EVENTS_END(source) \
  if(fort_events) \
    fort_add_random_event_timer_do(&pool2, \
	source, fort_event_mode, &micros);
#else
#define FORT_EVENTS_END(source)
#endif
int main(int argc, char *argv[])
{
#ifdef RESSU
  ressu_init();
#endif
  fort_init();

  dump_pools("To main");

  unsigned char chars[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
    "abcdefghijklmnopqrstuvwxyz" \
    "0123456789";

  fort_clear();
  for(int c=0; c<1024; c++) {
    FORT_EVENTS_START(100)
    
    if(c>0 && c%128 == 0)
      fprintf(stdout, "\n");
    int byte=fort_random_data_byte_limit(
        sizeof(chars)-1);
    fprintf(stdout,"%c", chars[byte]);

    FORT_EVENTS_END(101)
  }
  fort_clear();
  fprintf(stdout, "\n");
  fflush(stdout);

  dump_pools("End of main");

  fort_save();
}

FORT sisäisten satunnaistapahtumien lisääminen uudistunut

Tässä postissa esitelty tapa helpottaa fort koodin lukemista ja ohjelmasi omien tapahtumien lisäämistä. Tämä lyhentää koodia runsaasti ja helpottanee sen ymmärtämistä. Jos lisäät tapahtumia omaan ohjelmaasi, tee makroista ja internal_events -muuttujasta uudet versiot ilman internal sanaa. Ennen satunnaistapahtumat lisättiin seuraavalla lohkojen alkuun ja loppuun lisättävällä koodilla: Tässä lähetetään satunnaistapahtumia, joiden source on 10 ja 11.

void hash_init(HashCtx *hash)
{
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        10, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif
  HashInit(hash);
#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&init_pool,
        11, fort_event_mode, &micros);
#endif
}

Uudessa versiossa tehdään seuraavat uudet #define määritykset: FORT_INTERNAL_EVENTS_START on lohkon alkuun lisättävä makro ja FORT_INTERNAL_EVENTS_END on lohkon loppuun lisättävä makro. Nämä periaatteessa sisältävät nuo edelliset satunnaistapahtumien koodilohkot, mutta ne kirjoitetaan vain kerran makron määritykseen, ei enää uudestaan joka käyttökerralla. Nämä uudet makrot sisältävät myös pool ja pool2 määrittelyt, eli int-muuttujat joissa talletetaan seuraavan tapahtuman puskurinumero. Static tyyppisinä nämä tuntuvat toimivan niinkuin halutaan, eli että jokaisella lohkolla on omat muuttujansa, jopa sisäkkäisillä, ja että kenttien arvot säilyvät funktion ajokerrasta seuraavaan. Pool muuttujaa käytetään aikatapahtumiin ja pool2 muuttujaa käytetään kestotapahtumiin. Tosin ennen aikatapahtumille oli vain yksi pool-muuttuja, nyt niitä on oma jokaiselle FORT_INTERNAL_EVENTS lohkolle.

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_START(source) \
  unsigned long micros; \
  static int pool=0, pool2=0; \
  if(fort_internal_events) { \
    fort_add_random_event_time(&pool, source, \
        fort_event_mode); \
    fort_add_random_event_timer_start(&micros); \
  }
#else
#define FORT_INTERNAL_EVENTS_START(source)
#endif

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_END(source) \
  if(fort_internal_events) \
    fort_add_random_event_timer_do(&pool2, \
        source, fort_event_mode, &micros);
#else
#define FORT_INTERNAL_EVENTS_END(source)
#endif

Näiden makrojen avulla koodista tulee luettavampaa ja enää ei koodata erillisiä _pool-loppuisia muuttujia. Seuraavassa edellinen hash_init kirjoitettuna näillä uusilla makroilla: Sulkeissa kerrottu arvo on source kentän arvo. Huomaa että makrorivien lopussa ei ole puolipistettä (;).

void hash_init(HashCtx *hash)
{
  FORT_INTERNAL_EVENTS_START(10)

  HashInit(hash);

  FORT_INTERNAL_EVENTS_END(11)
}
Tässä vielä esimerkki pidemmästä rutiinista:
void fort_random_data(int len, unsigned char *buf)
{
  int c;
  unsigned long tenths;
  HashCtx hash;
  unsigned char buffer[HashLen];

  FORT_INTERNAL_EVENTS_START(22)

  tenths=gettenths();

  if( (fort_reseed_always==1) ||
      (fort_next_reseed==0) ||
      (fort_next_reseed<=tenths &&
       fort_pools[0].length >=
           FORT_MIN_POOL_SIZE) ) {

    // next tenth of a second
    fort_next_reseed = tenths + 1;                                                                                                                                                                                                                                                      
    fort_reseed_count++;

    hash_init(&hash);
    c=0;

    while(c < 32 &&
        (fort_reseed_count % (1<<c))==0) {

      FORT_INTERNAL_EVENTS_START(23)

#ifdef RESSU
      if(fort_fill_pool_when_reseeding==1 &&
         fort_pools[c].length <
              FORT_MIN_POOL_SIZE) {
        unsigned char tmp32[32];
        int len=FORT_MIN_POOL_SIZE -
            fort_pools[c].length;
        int n;

        while(len>0) {

          FORT_INTERNAL_EVENTS_START(24)

          n=(len<sizeof(tmp32)) ? len :
              sizeof(tmp32);
          ressu_genbuffer(n, tmp32);

          int pooltemp=c;
          fort_add_random_event(&pooltemp, 29,
              fort_event_mode, n, tmp32);
          len-=n;

          FORT_INTERNAL_EVENTS_END(25)
        } // while(len>0)
      } // if(fort_fill...
#endif
      hash_final(buffer, &fort_pools[c].pool);
      hash_update(&hash, buffer, sizeof(buffer));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);
      // save earlier pool to new one
      hash_update(&fort_pools[c].pool,
          buffer, sizeof(buffer));                                                                                                                                                                                                                   
      c++;

      FORT_INTERNAL_EVENTS_END(26)
    } // while(c < 32...
    hash_update(&hash, (unsigned char *)&cvar,
        sizeof(cvar));
    hash_final(buffer, &hash);
    fort_reseed(sizeof(buffer), buffer);
    // Forget hash context
    memset(&hash, 0, sizeof(hash));
    /* Forget reseed key */
    memset(buffer, 0, sizeof(buffer));
    inccvar();
  } // if( (fort_reseed...
  fort_pseudo_random_data(len, buf);

  FORT_INTERNAL_EVENTS_END(27)
}

Debuggaillaan ressun kelloketjuja

Tässä uusi debukkailuversio ressun kellon luvusta. Tämä lukee kelloa puskurillisen kerrallaan, tavallisen kellomerkki kerrallaan tavan sijasta. Jälkimmäinen tapa ei ole kovin mukava debukkailijan kannalta, koska kellojono tietysti etenee debukkaustulosteiden tulostamisen aikana ja kellojonot lyhenevät, joskus häviävät kokonaan. Kellomerkkejä voi olla vain yksi per kellon arvo.

Edit: Tehty korjauksia julkaisun jälkeisten viikkojen aikana. Lisäksi muotoilin ohjelman uudelleen siten että myös puuttuvat ketjut lisäävät tarvittaessa yhden bitin teoreettista satunnaisuutta. Muutokset ovat kappaleessa if(prevbyte != byte).

Tässä postissa on myös kellojonojen debukkailuun tarkoitettu versio ressu_genbytes() rutiinista. Se tulostaa teoreettisten satunnaisbittien laskentaan liittyviä tietoja.

Mutta ensin uusi versio kellon luennasta. Ohjelma lukee kellojonon merkit clock_bytes[] taulukkoon ja palauttaa merkkejä yhden kerrallaan. Uudessa luennassa yritetään uusi puskuri aloittaa edellisen puskurin viimeisellä arvolla (cb), jos tämä ei ole ensimmäinen puskuri (clock_byte!=99…). Clock_bytes[], clock_byte, clock_cnt ja niiden käyttö ovat tuttuja jo ressu_genbyte() rutiinista, toki ressu_ alkuisina kenttinä. Kun haluat lopettaa kellon debukkaamisen, lisää kommentti #define DEBUG_CLOCK lauseeseen tai lisää pikku-a sen DEBUG_CLOCK sanan ensimmäiseksi kirjaimeksi.

Kellojonot toki pitenevät tässä versiossa, koska ne talletetaan tässä nopeassa luupissa (for(int c=0…). Toisaalta luulisin että ilman debukkia kellojonojen pituuksissa on enemmän vaihtelua.

#define DEBUG_CLOCK 2

#ifdef DEBUG_CLOCK
#define CLOCKCNT 256

unsigned char clock_bytes[CLOCKCNT];
int clock_byte = 999999999;
int clock_cnt = CLOCKCNT;

unsigned char clockbyte() /* JariK 2020 */
{
  if(clock_byte >= clock_cnt) {
    struct timeval tv;
    if(clock_byte != 999999999) {
      unsigned char cb;
      gettimeofday(&tv, NULL);
      cb = tv.tv_usec & 0xff;
      while(cb != clock_bytes[clock_cnt-1]) {
        gettimeofday(&tv, NULL);
        cb = tv.tv_usec & 0xff;
      }
    }
    for(int c = 0; c < CLOCKCNT; c++) {
      gettimeofday(&tv, NULL);
      clock_bytes[c] = tv.tv_usec & 0xff;
    }
    clock_byte = 0;
  }
  return(clock_bytes[clock_byte++]);
}

#else //#ifdef DEBUG_CLOCK                                                                                                                                                                                  

unsigned char clockbyte() /* JariK 2013 */
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return(tv.tv_usec & 0xff);
}

#endif //#ifdef DEBUG_CLOCK

Sitten kellojonojen debukkailuun tarkoitettu versio ressu_genbytes() rutiinista: Raportin tulostaa kappale, joka alkaa tulostustiedoston avaamisella (fp1=fopen).

Toinen ero oikeaan versio 1.8 rutiiniin on tuo rndbits kentän kasvatus kahdella, eli yksi poikkeusjono on kaksi bittiä satunnaisuutta. Ajatus on että poikkeus olisi “ykkösbitti” ja sille voidaan laskea ylimääräinen “nollabitti” noista perusjonoista, joita yritetään olla laskematta. Tämä teoriassa lyhentää suoritusaikaa. Hmmmmm…

void ressu_genbytes(int size, unsigned char *buffer) /* JariK 2020 v1.8 */
{
  int c, d, e, f, g,
    byte, prevbyte,
    clockbytes = 0, rndbits = 0,
    chainbytes = 0, oldindex = 0, oldcount;
  unsigned char olddata[10];

  memset(olddata, 0, sizeof(olddata));
  prevbyte = clockbyte();
  f = 0;

  for(c = 0; c < 8 || c%8 != 0 || 
      clockbytes < 2000 ||
      rndbits < 8*size; c++) {

    for(d = 0; d < size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1);
      byte = clockbyte();
      buffer[d] = e^byte;

      if(prevbyte != byte) {
        oldcount = 0;
        for(g = 0; g < sizeof(olddata); g++) {
          if(olddata[g] == chainbytes)
            oldcount++;
        }

        int tbit, tbit2;
        tbit=0;
        tbit2=0;
        if(oldcount < 4) {
          tbit=1;
          rndbits+=2;                                                                                                                            
        } else if((prevbyte+1)%256 != byte) {
          tbit2=1;
          rndbits++;
        }

        FILE *fp1;
        if((fp1 = fopen("ressuchains.deb",
            "a")) != NULL) {
          fprintf(fp1,"id=%d", id);
          fprintf(fp1,", c=%3d", c);
          fprintf(fp1,", c%%8=%d", c%8);
          fprintf(fp1,", prevbyte=%3d",
              prevbyte);
          fprintf(fp1,", byte=%3d",
              byte);
          fprintf(fp1,", tbits=%4d",
              rndbits);
          fprintf(fp1,", length=%2d",
              chainbytes);
          fprintf(fp1,", tbit=%d", tbit);
          fprintf(fp1,", tbit2=%d", tbit2);
          fprintf(fp1,", oldcount=%d",
              oldcount);
          fprintf(fp1,", data=");
          for(g = 0; g < sizeof(olddata); g++)
            fprintf(fp1," %02d",
                olddata[g]);
          fprintf(fp1,"\n");
          fclose(fp1);
          id++;
        } // if((fp1

        olddata[oldindex] = chainbytes;
        oldindex = (oldindex + 1) %
            sizeof(olddata);

        clockbytes += chainbytes;
        chainbytes = 0;

        prevbyte = byte;
      } // if(prevbyte != byte)
      chainbytes++;
    } // for(d = 0;

    for(d = 0; d < size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    } // for(d = 0
  } // for(c = 0
}

Edellisen rutiinin tuloste: Tämä on ehkä vähän lukukelvoton tällä kapealla palstalla, mutta tässä näkyy kuitenkin tärkeimmät numerot, c:ssä on pääluupin (for(c=0) kierroksen numero, c%8 kertoo käsiteltävän bitin numeron välillä 0-7, byte kertoo kellomerkin arvon, tbits kertoo teoreettisten bittien lukumäärän, length taas tämän kellojonon pituuden, tbit kertoo onko tällä rivillä ollut teoreettinen satunnaisbitti vai ei, oldcount antaa tämän pituuden esiintymien lukumäärän. Data kentässä on kaikkien kymmenen viimeisen kellojonon pituudet.

id=568, c= 52, c%8=4, byte=127, tbits= 552, length=23, tbit=1, oldcount=1, data= 49 26 24 26 24 26 22 26 23 51
id=569, c= 53, c%8=5, byte=128, tbits= 552, length=26, tbit=0, oldcount=5, data= 49 26 24 26 24 26 22 26 23 26
id=570, c= 53, c%8=5, byte=129, tbits= 554, length=27, tbit=1, oldcount=1, data= 27 26 24 26 24 26 22 26 23 26
id=571, c= 53, c%8=5, byte=130, tbits= 556, length=25, tbit=1, oldcount=1, data= 27 25 24 26 24 26 22 26 23 26
id=572, c= 53, c%8=5, byte=131, tbits= 558, length=23, tbit=1, oldcount=2, data= 27 25 23 26 24 26 22 26 23 26
id=573, c= 53, c%8=5, byte=132, tbits= 560, length=27, tbit=1, oldcount=2, data= 27 25 23 27 24 26 22 26 23 26
id=574, c= 54, c%8=6, byte=133, tbits= 560, length=26, tbit=0, oldcount=4, data= 27 25 23 27 26 26 22 26 23 26
id=575, c= 54, c%8=6, byte=134, tbits= 562, length=25, tbit=1, oldcount=2, data= 27 25 23 27 26 25 22 26 23 26

Vielä yksi sivuhuomautus: tässä ovat esiintyneet kaikki byte arvot eli kellon arvot välillä 127-134, mikä lienee yleisin tapaus. Voisi kuvitella että on tilanteita jossa ohitetaan yksi kelloarvo kokonaan, esimerkiksi hidas kone tms. Se voisi olla teoreettisen satunnaisbitin paikka, ainakin siinä tapauksessa ettei bittejä muuten saada laskettua. Jos esimerkiksi kaikki kellojonot on yhden merkin pituisia, meillä on data taulukko täynnä ykkösiä (10 kpl), jälleen uutta ykköstä ei tietenkään lasketa. Jos tässä tilanteessa olisi kellon arvoilla välejä saataisiin silti kasvatettua satunnaisbittejä. Teoreettinen huomautus, toivottavasti.

Sitten pieni pääohjelma

void main(int argc,char *argv[])
{
  int c;
  unsigned char buffer[128];

  FILE *fp1;
  if((fp1 = fopen("ressuchains.deb",
      "w")) != NULL)
    fclose(fp1);

  for(c = 0; c < 20; c++)
    ressu_genbytes(sizeof(buffer), buffer);

  for(c = 0; c < sizeof(buffer); c++) {
    if(c > 0 && c%32 == 0)
      fprintf(stdout,"\n");
    fprintf(stdout," %02x", buffer[c]);
  }
  fprintf(stdout,"\n");
}

SHA256 tiivisteen laskentaohjelman vakiot

SHA256 laskentaohjelmaa kuvaavassa postissani mainitsin vakionumerot, joilla täytetään tiivisteen alustuksessa tiivisteen tila (state[]) ja transformissa käytettävä k256 taulu. Postissa sanoin, että ne tulevat alkulukujen neliöjuuren (state[]) ja kuutiojuuren (k256[]) desimaaleista. Tässä postissa käydään läpi ohjelma, joilla ne voidaan tarkistaa tai laskea. Postin mittaan käydään ohjelma ja sen tuloste läpi kappaleittain ja lopussa on ohjelma kokonaisuudessaan.

Ohjelman laskemiseen tarvitaan alkulukuja, joten ensimmäisissä kappaleissa tehdään lista niistä: ohjelmassa Eratostheneen seulan toteutus. Toteutuksessa haetaan alkuluvut 400 ensimmäisen luvun joukosta, tilataululle tarvitaan alkuluvut 2-19 ja k256[]-taululle alkuluvut 2-311. Alussa taulu alustetaan ykkösillä (for(c=0…) ja merkataan 0 ja 1 ei alkuluvuiksi. Sitten käydään taulu läpi taulun pituuden neliöjuureen asti (for(c=2…). Taulusta etsitään ykköset (alkulukuja) ja käydään läpi niiden kerrannaiset (for(d=c*2…) ja merkataan ne nolliksi, ne eivät kerrannaisina ole alkulukuja.

  int c, d, count, id;
  char primes[400];

  for(c=0; c<sizeof(primes); c++)
    primes[c] = 1;

  primes[0] = 0;
  primes[1] = 0;

  for(c=2; c<=sqrt(sizeof(primes)-1); c++) {
  // Little bug 30.6 JariK
  //for(c=2; c<=sqrt(sizeof(primes)); c++) {
    if(primes[c] == 1) {
      fprintf(stdout,"*%d", c);
      count = 0;
      for(d = c*2; d < sizeof(primes); d+=c) {
        primes[d] = 0;
        if(count++ < 20)
          fprintf(stdout," %d", d);
      }
      if(count > 20)
        fprintf(stdout,"...");
      fprintf(stdout,"\n");
    }
  }

Näiden ensimmäisten kappaleiden tuloste on seuraavanlainen: tähdellä merkitty on alkuluku, jota käsitellään ja samalla rivillä sen kanssa ovat kerrannaiset, jotka poistetaan listalta. Riville tulostetaan count muuttujan avulla vain 20 ensimmäistä kerrannaista. Ohjelman on käytävä läpi vain 19 ensimmäistä alkulukua, luku 20 ei ole alkuluku ja sen kerrannaiset menisivät taulun yli (20*20=400). Siis neliöjuuren jälkeen taulussa ei voi olla yhdistettyjä lukuja, joita ei ole vielä merkitty. Neliöjuurta pienempien tekijöiden luvut on jo merkitty, ja neliöjuurta suurempien tekijöiden luvut (joissa ei ole pientä tekijää) sijoittuvat taulun ulkopuolelle.

*2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42...
*3 6 9 12 15 18 21 24 27 30 33 36 39 42 45 48 51 54 57 60 63...
*5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100 105...
*7 14 21 28 35 42 49 56 63 70 77 84 91 98 105 112 119 126 133 140 147...
*11 22 33 44 55 66 77 88 99 110 121 132 143 154 165 176 187 198 209 220 231...
*13 26 39 52 65 78 91 104 117 130 143 156 169 182 195 208 221 234 247 260 273...
*17 34 51 68 85 102 119 136 153 170 187 204 221 238 255 272 289 306 323 340 357...
*19 38 57 76 95 114 133 152 171 190 209 228 247 266 285 304 323 342 361 380 399

Seuraavassa kappaleessa tulostetaan lista alkuluvuista 2-19 ja niiden perusteella lasketaan neliojuuri (sqrt()), neliöjuuren kokonaisosa (isqrt), näiden erotuksena neliöjuuren desimaalit (decimalpart), kerrotaan desimaalit kokonaisluvuksi ja sen perusteella tulostetaan neliöjuuren desimaalit desimaalina (decimal) ja heksana (hex). Näistä heksaluvuilla on täytetty state[] taulu.

  id=0;
  for(c=2; c<=19; c++) {
    if(primes[c] == 1) {
      int isqrt;
      fprintf(stdout,"id:%d", id);
      fprintf(stdout,", c:%2d", c);
      fprintf(stdout,", sqrt:%f", sqrt(c));
      isqrt = (int)sqrt(c);
      fprintf(stdout,", isqrt:%2d", isqrt);
      double decimalpart = sqrt(c)-isqrt;
      fprintf(stdout,", decimalpart:%f",
          decimalpart);
      fprintf(stdout,", decimalpart*16^8:%017f",
          decimalpart*pow(16,8));
      fprintf(stdout,", decimal:%010lu",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,", hex:%08lx",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,"\n");
      id++;
    }
  }

Edellinen kappale tulostaa seuraavan taulun: rivillä on alkuluku (c), sen neliöjuuri (sqrt), neliöjuuren kokonaisosa (isqrt), neliöjuuren desimaaliosa (decimalpart), edellinen kerrottuna kokonaisluvuksi, tulos desimaaleina ja heksana.

id:0, c: 2, sqrt:1.414214, isqrt: 1, decimalpart:0.414214, decimalpart*16^8:1779033703.952100, decimal:1779033703, hex:6a09e667
id:1, c: 3, sqrt:1.732051, isqrt: 1, decimalpart:0.732051, decimalpart*16^8:3144134277.518717, decimal:3144134277, hex:bb67ae85
id:2, c: 5, sqrt:2.236068, isqrt: 2, decimalpart:0.236068, decimalpart*16^8:1013904242.994461, decimal:1013904242, hex:3c6ef372
id:3, c: 7, sqrt:2.645751, isqrt: 2, decimalpart:0.645751, decimalpart*16^8:2773480762.371540, decimal:2773480762, hex:a54ff53a
id:4, c:11, sqrt:3.316625, isqrt: 3, decimalpart:0.316625, decimalpart*16^8:1359893119.679298, decimal:1359893119, hex:510e527f
id:5, c:13, sqrt:3.605551, isqrt: 3, decimalpart:0.605551, decimalpart*16^8:2600822924.168921, decimal:2600822924, hex:9b05688c
id:6, c:17, sqrt:4.123106, isqrt: 4, decimalpart:0.123106, decimalpart*16^8:0528734635.981472, decimal:0528734635, hex:1f83d9ab
id:7, c:19, sqrt:4.358899, isqrt: 4, decimalpart:0.358899, decimalpart*16^8:1541459225.076145, decimal:1541459225, hex:5be0cd19

Sitten alkuluvut 2-311 k256[] taulua varten: käsittely on oikeastaan kopio edellisestä paitsi että käytetään kuutiojuurta: lasketaan kuutiojuuri, sen kokonaisosa, niistä desimaaliosa, kerrotaan desimaaliosa kokonaisiksi, ja tulostetaan desimaaliosa desimaalina ja heksana.

  id=0;
  for(c=2; c<=311; c++) {
    if(primes[c] == 1) {
      int icbrt;
      fprintf(stdout,"id:%d", id);
      fprintf(stdout,", c:%3d", c);
      fprintf(stdout,", cbrt:%f", cbrt(c));
      icbrt = (int)cbrt((double)c);
      fprintf(stdout,", icbrt:%2d", icbrt);
      double decimalpart = cbrt(c)-icbrt;
      fprintf(stdout,", decimalpart:%f",
          decimalpart);
      fprintf(stdout,", decimalpart*16^8:%017f",
          decimalpart*pow(16,8));
      fprintf(stdout,", decimal:%010lu",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,", hex:%08lx",
          (long)(decimalpart*pow(16,8)));
      //if((long)(decimalpart*pow(16,8))
      //    == k256[id])
	//fprintf(stdout,"ok");
      fprintf(stdout,"\n");
      id++;
    }
  }

Edellisen tuloste:

id:0, c:  2, cbrt:1.259921, icbrt: 1, decimalpart:0.259921, decimalpart*16^8:1116352408.840466, decimal:1116352408, hex:428a2f98
id:1, c:  3, cbrt:1.442250, icbrt: 1, decimalpart:0.442250, decimalpart*16^8:1899447441.140371, decimal:1899447441, hex:71374491
id:2, c:  5, cbrt:1.709976, icbrt: 1, decimalpart:0.709976, decimalpart*16^8:3049323471.923053, decimal:3049323471, hex:b5c0fbcf
id:3, c:  7, cbrt:1.912931, icbrt: 1, decimalpart:0.912931, decimalpart*16^8:3921009573.506011, decimal:3921009573, hex:e9b5dba5
id:4, c: 11, cbrt:2.223980, icbrt: 2, decimalpart:0.223980, decimalpart*16^8:0961987163.950329, decimal:0961987163, hex:3956c25b
id:5, c: 13, cbrt:2.351335, icbrt: 2, decimalpart:0.351335, decimalpart*16^8:1508970993.711027, decimal:1508970993, hex:59f111f1
id:6, c: 17, cbrt:2.571282, icbrt: 2, decimalpart:0.571282, decimalpart*16^8:2453635748.683981, decimal:2453635748, hex:923f82a4
id:7, c: 19, cbrt:2.668402, icbrt: 2, decimalpart:0.668402, decimalpart*16^8:2870763221.853235, decimal:2870763221, hex:ab1c5ed5
id:8, c: 23, cbrt:2.843867, icbrt: 2, decimalpart:0.843867, decimalpart*16^8:3624381080.636766, decimal:3624381080, hex:d807aa98
id:9, c: 29, cbrt:3.072317, icbrt: 3, decimalpart:0.072317, decimalpart*16^8:0310598401.271248, decimal:0310598401, hex:12835b01
id:10, c: 31, cbrt:3.141381, icbrt: 3, decimalpart:0.141381, decimalpart*16^8:0607225278.308178, decimal:0607225278, hex:243185be
id:11, c: 37, cbrt:3.332222, icbrt: 3, decimalpart:0.332222, decimalpart*16^8:1426881987.835932, decimal:1426881987, hex:550c7dc3
id:12, c: 41, cbrt:3.448217, icbrt: 3, decimalpart:0.448217, decimalpart*16^8:1925078388.947197, decimal:1925078388, hex:72be5d74
id:13, c: 43, cbrt:3.503398, icbrt: 3, decimalpart:0.503398, decimalpart*16^8:2162078206.230814, decimal:2162078206, hex:80deb1fe
id:14, c: 47, cbrt:3.608826, icbrt: 3, decimalpart:0.608826, decimalpart*16^8:2614888103.147570, decimal:2614888103, hex:9bdc06a7
id:15, c: 53, cbrt:3.756286, icbrt: 3, decimalpart:0.756286, decimalpart*16^8:3248222580.810200, decimal:3248222580, hex:c19bf174
id:16, c: 59, cbrt:3.892996, icbrt: 3, decimalpart:0.892996, decimalpart*16^8:3835390401.620871, decimal:3835390401, hex:e49b69c1
id:17, c: 61, cbrt:3.936497, icbrt: 3, decimalpart:0.936497, decimalpart*16^8:4022224774.219959, decimal:4022224774, hex:efbe4786
id:18, c: 67, cbrt:4.061548, icbrt: 4, decimalpart:0.061548, decimalpart*16^8:0264347078.545116, decimal:0264347078, hex:0fc19dc6
id:19, c: 71, cbrt:4.140818, icbrt: 4, decimalpart:0.140818, decimalpart*16^8:0604807628.467480, decimal:0604807628, hex:240ca1cc
id:20, c: 73, cbrt:4.179339, icbrt: 4, decimalpart:0.179339, decimalpart*16^8:0770255983.348312, decimal:0770255983, hex:2de92c6f
id:21, c: 79, cbrt:4.290840, icbrt: 4, decimalpart:0.290840, decimalpart*16^8:1249150122.432236, decimal:1249150122, hex:4a7484aa
id:22, c: 83, cbrt:4.362071, icbrt: 4, decimalpart:0.362071, decimalpart*16^8:1555081692.739288, decimal:1555081692, hex:5cb0a9dc
id:23, c: 89, cbrt:4.464745, icbrt: 4, decimalpart:0.464745, decimalpart*16^8:1996064986.511982, decimal:1996064986, hex:76f988da
id:24, c: 97, cbrt:4.594701, icbrt: 4, decimalpart:0.594701, decimalpart*16^8:2554220882.931259, decimal:2554220882, hex:983e5152
id:25, c:101, cbrt:4.657010, icbrt: 4, decimalpart:0.657010, decimalpart*16^8:2821834349.178532, decimal:2821834349, hex:a831c66d
id:26, c:103, cbrt:4.687548, icbrt: 4, decimalpart:0.687548, decimalpart*16^8:2952996808.597584, decimal:2952996808, hex:b00327c8
id:27, c:107, cbrt:4.747459, icbrt: 4, decimalpart:0.747459, decimalpart*16^8:3210313671.745834, decimal:3210313671, hex:bf597fc7
id:28, c:109, cbrt:4.776856, icbrt: 4, decimalpart:0.776856, decimalpart*16^8:3336571891.240852, decimal:3336571891, hex:c6e00bf3
id:29, c:113, cbrt:4.834588, icbrt: 4, decimalpart:0.834588, decimalpart*16^8:3584528711.574383, decimal:3584528711, hex:d5a79147
id:30, c:127, cbrt:5.026526, icbrt: 5, decimalpart:0.026526, decimalpart*16^8:0113926993.875053, decimal:0113926993, hex:06ca6351
id:31, c:131, cbrt:5.078753, icbrt: 5, decimalpart:0.078753, decimalpart*16^8:0338241895.039284, decimal:0338241895, hex:14292967
id:32, c:137, cbrt:5.155137, icbrt: 5, decimalpart:0.155137, decimalpart*16^8:0666307205.276646, decimal:0666307205, hex:27b70a85
id:33, c:139, cbrt:5.180101, icbrt: 5, decimalpart:0.180101, decimalpart*16^8:0773529912.359966, decimal:0773529912, hex:2e1b2138
id:34, c:149, cbrt:5.301459, icbrt: 5, decimalpart:0.301459, decimalpart*16^8:1294757372.354557, decimal:1294757372, hex:4d2c6dfc
id:35, c:151, cbrt:5.325074, icbrt: 5, decimalpart:0.325074, decimalpart*16^8:1396182291.615566, decimal:1396182291, hex:53380d13
id:36, c:157, cbrt:5.394691, icbrt: 5, decimalpart:0.394691, decimalpart*16^8:1695183700.545647, decimal:1695183700, hex:650a7354
id:37, c:163, cbrt:5.462556, icbrt: 5, decimalpart:0.462556, decimalpart*16^8:1986661051.236202, decimal:1986661051, hex:766a0abb
id:38, c:167, cbrt:5.506878, icbrt: 5, decimalpart:0.506878, decimalpart*16^8:2177026350.280975, decimal:2177026350, hex:81c2c92e
id:39, c:173, cbrt:5.572055, icbrt: 5, decimalpart:0.572055, decimalpart*16^8:2456956037.080109, decimal:2456956037, hex:92722c85
id:40, c:179, cbrt:5.635741, icbrt: 5, decimalpart:0.635741, decimalpart*16^8:2730485921.300552, decimal:2730485921, hex:a2bfe8a1
id:41, c:181, cbrt:5.656653, icbrt: 5, decimalpart:0.656653, decimalpart*16^8:2820302411.735386, decimal:2820302411, hex:a81a664b
id:42, c:191, cbrt:5.758965, icbrt: 5, decimalpart:0.758965, decimalpart*16^8:3259730800.816296, decimal:3259730800, hex:c24b8b70
id:43, c:193, cbrt:5.778997, icbrt: 5, decimalpart:0.778997, decimalpart*16^8:3345764771.024734, decimal:3345764771, hex:c76c51a3
id:44, c:197, cbrt:5.818648, icbrt: 5, decimalpart:0.818648, decimalpart*16^8:3516065817.839592, decimal:3516065817, hex:d192e819
id:45, c:199, cbrt:5.838272, icbrt: 5, decimalpart:0.838272, decimalpart*16^8:3600352804.333588, decimal:3600352804, hex:d6990624
id:46, c:211, cbrt:5.953342, icbrt: 5, decimalpart:0.953342, decimalpart*16^8:4094571909.341568, decimal:4094571909, hex:f40e3585
id:47, c:223, cbrt:6.064127, icbrt: 6, decimalpart:0.064127, decimalpart*16^8:0275423344.198177, decimal:0275423344, hex:106aa070
id:48, c:227, cbrt:6.100170, icbrt: 6, decimalpart:0.100170, decimalpart*16^8:0430227734.721970, decimal:0430227734, hex:19a4c116
id:49, c:229, cbrt:6.118033, icbrt: 6, decimalpart:0.118033, decimalpart*16^8:0506948616.317406, decimal:0506948616, hex:1e376c08
id:50, c:233, cbrt:6.153449, icbrt: 6, decimalpart:0.153449, decimalpart*16^8:0659060556.873276, decimal:0659060556, hex:2748774c
id:51, c:239, cbrt:6.205822, icbrt: 6, decimalpart:0.205822, decimalpart*16^8:0883997877.881279, decimal:0883997877, hex:34b0bcb5
id:52, c:241, cbrt:6.223084, icbrt: 6, decimalpart:0.223084, decimalpart*16^8:0958139571.772606, decimal:0958139571, hex:391c0cb3
id:53, c:251, cbrt:6.307994, icbrt: 6, decimalpart:0.307994, decimalpart*16^8:1322822218.887722, decimal:1322822218, hex:4ed8aa4a
id:54, c:257, cbrt:6.357861, icbrt: 6, decimalpart:0.357861, decimalpart*16^8:1537002063.466370, decimal:1537002063, hex:5b9cca4f
id:55, c:263, cbrt:6.406959, icbrt: 6, decimalpart:0.406959, decimalpart*16^8:1747873779.838661, decimal:1747873779, hex:682e6ff3
id:56, c:269, cbrt:6.455315, icbrt: 6, decimalpart:0.455315, decimalpart*16^8:1955562222.366940, decimal:1955562222, hex:748f82ee
id:57, c:271, cbrt:6.471274, icbrt: 6, decimalpart:0.471274, decimalpart*16^8:2024104815.262074, decimal:2024104815, hex:78a5636f
id:58, c:277, cbrt:6.518684, icbrt: 6, decimalpart:0.518684, decimalpart*16^8:2227730452.632576, decimal:2227730452, hex:84c87814
id:59, c:281, cbrt:6.549912, icbrt: 6, decimalpart:0.549912, decimalpart*16^8:2361852424.103092, decimal:2361852424, hex:8cc70208
id:60, c:283, cbrt:6.565414, icbrt: 6, decimalpart:0.565414, decimalpart*16^8:2428436474.138229, decimal:2428436474, hex:90befffa
id:61, c:293, cbrt:6.641852, icbrt: 6, decimalpart:0.641852, decimalpart*16^8:2756734187.869183, decimal:2756734187, hex:a4506ceb
id:62, c:307, cbrt:6.745997, icbrt: 6, decimalpart:0.745997, decimalpart*16^8:3204031479.698341, decimal:3204031479, hex:bef9a3f7
id:63, c:311, cbrt:6.775169, icbrt: 6, decimalpart:0.775169, decimalpart*16^8:3329325298.888462, decimal:3329325298, hex:c67178f2

Tulosteessa on alkuluku (c), kuutiojuuri (cbrt), kuutiojuuren kokonaisosa (icbrt), desimaaliosa, desimaaliosa kerrottuna kokonaisosaksi, edellinen desimaalina ja heksana. Heksaarvoilla on täytetty taulu k256[].

Vielä ohjelma kokonaisuudessaan:

void main(int argc,char *argv[])
{
  int c, d, count, id;
  char primes[400];

  for(c=0; c<sizeof(primes); c++)
    primes[c] = 1;

  primes[0] = 0;
  primes[1] = 0;

  for(c=2; c<=sqrt(sizeof(primes)-1); c++) {
    if(primes[c] == 1) {
      fprintf(stdout,"*%d", c);
      count = 0;
      for(d=c*2; d<sizeof(primes); d+=c) {
        primes[d] = 0;
        if(count++ < 20)
          fprintf(stdout," %d", d);
      }
      if(count > 20)
        fprintf(stdout,"...");
      fprintf(stdout,"\n");
    }
  }

  fprintf(stdout,"\n");

  id = 0;
  for(c=2; c<=19; c++) {
    if(primes[c] == 1) {
      int isqrt;
      fprintf(stdout,"id:%d", id);
      fprintf(stdout,", c:%2d", c);
      fprintf(stdout,", sqrt:%f", sqrt(c));
      isqrt = (int)sqrt(c);
      fprintf(stdout,", isqrt:%2d", isqrt);
      double decimalpart = sqrt(c)-isqrt;
      fprintf(stdout,", decimalpart:%f",
          decimalpart);
      fprintf(stdout,", decimalpart*16^8:%017f",
          decimalpart*pow(16,8));
      fprintf(stdout,", decimal:%010lu",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,", hex:%08lx",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,"\n");
      id++;
    }
  }

  fprintf(stdout,"\n");

  id = 0;
  for(c=2; c<=311; c++) {
    if(primes[c] == 1) {
      int icbrt;
      fprintf(stdout,"id:%d", id);
      fprintf(stdout,", c:%3d", c);
      fprintf(stdout,", cbrt:%f", cbrt(c));
      icbrt = (int)cbrt((double)c);
      fprintf(stdout,", icbrt:%2d", icbrt);
      double decimalpart = cbrt(c)-icbrt;
      fprintf(stdout,", decimalpart:%f",
          decimalpart);
      fprintf(stdout,", decimalpart*16^8:%017f",
          decimalpart*pow(16,8));
      fprintf(stdout,", decimal:%010lu",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,", hex:%08lx",
          (long)(decimalpart*pow(16,8)));
      fprintf(stdout,"\n");
      id++;
    }
  }
}

RESSU 1.8

Ressun edellisessä versiossa(1.7) oli ongelma että myös perusriveistä laskettiin teoreettisia satunnaisbittejä. Tässä pyritään laskemaan mukaan ainoastaan poikkeukset säännöstä. Jos esimerkiksi perusketjut ovat kuuden merkin pituisia, tämän pitäisi laskea vain kuudesta poikkeavat ketjut, eli seiskat ja viitoset kuten edellisen postin esimerkissä. Tällä tavoin teoreettisten satunnaisbittien lukumäärä on lähempänä oikeata.

Tässä varsinainen uusi koodi kokonaisuudessaan, muutetut rivit kuvataan postin lopussa: Kuvaus koodista löytyy edellisestä postista http://moijari.com/?p=798.

void ressu_genbytes(int size, unsigned
    char *buffer) /* (c) JariK 2013-2020 v1.8 */
{
  int c, d, e, f, g,
    byte, prevbyte,
    clockbytes = 0, rndbits = 0,
    chainbytes = 0, oldindex = 0, oldcount;
  unsigned char olddata[10];

  memset(olddata, 0, sizeof(olddata));
  prevbyte=clockbyte();
  f=0;

  for(c=0; c<8 || c%8!=0 ||
      clockbytes<2000 ||
      rndbits < 8*size; c++) {
    for(d=0; d<size; d++) {
      e = buffer[d];
      e = ((e&0x80)>>7) | ((e&0x7f)<<1);
      byte = clockbyte();
      buffer[d] = e^byte;
      if(prevbyte != byte) {
        prevbyte = byte;
        olddata[oldindex] = chainbytes;
        oldindex = (oldindex + 1)
            % sizeof(olddata);
        oldcount = 0;
        for(g=0; g < sizeof(olddata); g++) {
          if(olddata[g] == chainbytes)
            oldcount++;
        }
        if(oldcount < 4)
          rndbits++;

        clockbytes += chainbytes;
        chainbytes = 0;
      } // if(prevbyte
      chainbytes++;
    } // for(d=0
    for(d=0; d<size; d++) {
      f = (f+buffer[d])%size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    } // for(d=0
  } // for(c=0
}

Lisätyt muuttujat: chainbytes sisältää tämän ketjun pituuden, oldindex kertoo, mihin “positioon” olddata taulussa seuraava ketjun pituus laitetaan, oldcount kenttään lasketaan tämänpituisten ketjujen lukumäärä ja olddata taulukkoon kirjoitetaan viimeisten kymmenen ketjun pituudet.

  int chainbytes = 0, oldindex = 0, oldcount;
  unsigned char olddata[10];

Tyhjennetään olddata taulukko:

  memset(olddata, 0, sizeof(olddata));

Olddata taulussa pidetään yllä kymmentä viimeistä ketjun pituutta, ja tässä uusi ketjun pituus lisätään tauluun: Oldindex kentän arvoa kasvatetaan aina uuden pituuden lisäyksen jälkeen yhdellä. Jos oldindex menee ympäri, aloitetaan taas nollasta.

  olddata[oldindex] = chainbytes;
  oldindex = (oldindex + 1) % sizeof(olddata);

Lasketaan kuinka monta edellisen pituista lohkoa taulussa on:

  oldcount = 0;
  for(g=0; g<sizeof(olddata); g++) {
    if(olddata[g] == chainbytes)
      oldcount++;
  }

Lisätään bitti satunnaisuuteen vain jos esiintymiä on vähemmän kuin 4:

  if(oldcount < 4)
    rndbits++;

Lisätään tämän ketjun pituus kokonaispituuteen ja nollataan tämän ketjun pituus:

  clockbytes += chainbytes;
  chainbytes = 0;

Lasketaan tämän ketjun pituutta.

  chainbytes++;

Apuohjelmat ja tarkemman kuvauksen saat tietysti edellisestä postista.

Uusi versio Ressu:sta (v 1.7)

Tässä vielä uusi versio ressu generaattorista: uudessa versiossa ei ole enää aiempien versioiden b-muuttujaa eikä siihen liittyvää laskentaa, eli ei tarvita erillistä pääohjelmaa. Edellisessä versiossa oli useampia tapoja laskea teoreettisia bittejä, niistä on vain yksi jäljellä.

Ressufunktio muodostuu pääluupista, jonka alla on kaksi pienempää luuppia. Pääluuppi (for(c=) sanelee että kaikki puskurin bitit pitää kiertää vähintään kerran (c<8), että generaattori ei lopeta ennenkuin kaikki merkin bitit on käsitelty (c%8!=0), eli generaattorin katkokohdassa on käsitelty kaikki puskurin merkkien kahdeksan bittiä. Kellosta tulevia merkkejä pitää käsitellä vähintään 2000 kappaletta (clockbytes<2000). Lisäksi varmistetaan että teoreettisia bittejä on vähintään yksi jokaiselle generoidulle bitille (rndbits < 8*size).

Pääluupin alla on kaksi aliluuppia, joista ensimmäinen järjestelee merkin bittejä siten että ylin bitti merkissä kierretään alimmaksi ja loput bitit siirretään yhden bitin verran ylemmäksi. Lisäksi bitteihin xor:ataan yksi merkki kellosarjaa. Vuorotellen kukin bitti siirtyy alimmaiseksi ja näin kahdeksan kierroksen aikana käydään kaikki bitit läpi. Ensimmäinen aliluuppi käsittelee kaikki puskurin merkit yksi merkki kerrallaan.

Lisäksi ensimmäinen aliluuppi laskee teoreettisia satunnaisuusbittejä. Teoreettisten satunnaisbittien määrää (rndbits) lisätään yhdellä aina kun kellosarjasta tulleen merkin arvo muuttuu. Seuraavassa bittisarjassa on yksi teoreettinen bitti numeroille 46, 47, 48, 49, 4a, 4b, 4c, 4d, 4e ja 4f, eli yhteensä 10 teoreettista bittiä.

4646464646464747474747474848484848484949494949494a4a4a4a4a4a4b4b4b4b4b4b4b4c4c4c4c4c4d4d4d4d4d4d4e4e4e4e4e4f4f4f4f4f4f

Satunnaisuus edellisessä kellojonossa tulee siitä että eri merkkien lukumäärä vaihtelee, seuraavassa kunkin merkin määrä kellojonossa: F muuttuja tekee sen, että jos yksikin f:ään lisättävä merkki muuttuu kaikki loppumerkit muuttuvat erilaisiksi. Voi muuten väittää että seuraavan taulukon kuutosriveissä ei ole kovin paljon satunnaista, että satunnaisuus tulee pääasiassa 7 ja 5 pituuksisista riveistä, eli poikkeuksista sääntöön.

464646464646    6
474747474747    6
484848484848    6
494949494949    6
4a4a4a4a4a4a    6
4b4b4b4b4b4b4b  7
4c4c4c4c4c      5
4d4d4d4d4d4d    6
4e4e4e4e4e      5
4f4f4f4f4f4f    6

Toinen aliluuppi käy myös kaikki merkit läpi ja vaihtaa jokaisen merkin (muuttuja d) satunnaisen puskurin merkin kanssa (muuttuja f).

Tässä varsinainen koodi pääfunktiolle.

void ressu_genbytes(int size,
        unsigned char *buffer) /* JariK 2020 v1.7 */
{
  int c, d, e, f,
    byte, prevbyte,
    clockbytes = 0, rndbits = 0;

  prevbyte = clockbyte();
  f = 0;

  for(c=0; c < 8 || c%8 != 0 ||
      clockbytes < 2000 ||
      rndbits < 8*size; c++) {
    for(d=0; d < size; d++) {
      e = buffer[d];
      e = ((e & 0x80) >> 7) |
          ((e & 0x7f) << 1);
      byte = clockbyte();
      buffer[d] = e ^ byte;
      if(prevbyte != byte) {
        prevbyte = byte;
        rndbits++;
      }
      clockbytes++;
    }
    for(d=0; d < size; d++) {
      f = (f + buffer[d]) % size;
      e = buffer[d];
      buffer[d] = buffer[f];
      buffer[f] = e;
    }
  }
}

Tässä vielä käytetty kellonluku:

unsigned char clockbyte() /* JariK 2013-2020 */
{
  struct timeval tv;

  gettimeofday(&tv,NULL);

  return(tv.tv_usec & 0xff);
}

Seuraavassa ohjelman apufunktiot.

#define RESSUCNT 128
#define RESSU_CLEAR 2

unsigned char ressu_bytes[RESSUCNT];
int ressu_byte = 999999999;
int ressu_cnt = RESSUCNT;

void ressu_init()
{
}

int ressu_genbyte()
{
  if(ressu_byte>=ressu_cnt) {
#ifdef RESSU_CLEAR
    memset(ressu_bytes,0,ressu_cnt);
#else
    if(ressu_byte == 999999999)
      memset(ressu_bytes, 0, ressu_cnt);
#endif
    ressu_genbytes(ressu_cnt,
        ressu_bytes);
    ressu_byte=0;
  }
  return(ressu_bytes[ressu_byte++]);
}

void ressu_clear()
{
    memset(ressu_bytes, 0, ressu_cnt);
    ressu_byte = 999999998;
}

int ressu_genbyte_limit(int limit)
{
  int c;

  while((c = ressu_genbyte())>
      (256 / limit) * limit);
  return(c % limit);
}

int ressu_genshort()
{
  return(ressu_genbyte()|
         ressu_genbyte() << 8);
}

int ressu_genshort_limit(int limit)
{
  int c;

  while((c = ressu_genshort())>
      (65536 / limit) * limit);
  return(c % limit);
}

int ressu_gen32bits()
{
  return(ressu_genbyte()|
         ressu_genbyte() << 8|
         ressu_genbyte() << 16|
         ressu_genbyte() << 24);
}

void ressu_genbuffer(int size, unsigned char *buffer)
{
  int c;

  for(c = 0; c < size; c++)
    buffer[c] ^= ressu_genbyte();
}

SHA256 tiivisteen laskentaohjelma

Tässä tiivisteohjelma edellista Fort satunnaisbittigeneraattoria varten.

Alun definet määrittelevät Fort-ohjelman käyttämille HashInit, HashUpdate ja HashFinal funktioille todelliset nimet. Lisäksi määritellään HashName, joka tulostetaan fort_init:in alussa ja HashLen joka sisältää tiivisteen pituuden.

Sitten typedef:it määrittelevät uudet tyypit IUBYTE, joka on 8 bittiä (1 merkkiä) pitkä., IUWORD, joka on 32 bittiä (4 merkkiä) pitkä ja IULONG, joka on 64 bittiä (8 merkkiä) pitkä.

Seuraava kappale kuvaa SHA256 yhteysalueen. Yhteysalueen jälkeen on ensimmäinen koodi init, joka alustaa SHA yhteysalueen. Count kentässä on tähänastisten merkkien määrä yhteysalueessa eli ohjelman suorituksen alussa nolla. State[] kenttien arvot ovat 8 ensimmäisen alkuluvun (2-19) neliöjuuren desimaaliosa 32 bittisenä lukuna.

#define HashName   "SHA256"
#define HashInit   SHA256Init
#define HashUpdate SHA256Update
#define HashFinal  SHA256Final
#define HashLen    32
#define HashCtx    SHA256_CONTEXT

typedef unsigned char IUBYTE;
typedef unsigned int IUWORD;
typedef unsigned long long IULONG;

typedef struct {
    IUWORD state[8];
    IULONG count;
    IUBYTE buffer[64];
} SHA256_CONTEXT;

void SHA256Init(SHA256_CONTEXT *sha256)
{
  sha256->state[0] = 0x6a09e667;
  sha256->state[1] = 0xbb67ae85;
  sha256->state[2] = 0x3c6ef372;
  sha256->state[3] = 0xa54ff53a;
  sha256->state[4] = 0x510e527f;
  sha256->state[5] = 0x9b05688c;
  sha256->state[6] = 0x1f83d9ab;
  sha256->state[7] = 0x5be0cd19;

  sha256->count = 0;
}

Seuraavana update funktio: Aliohjelma jakaa asiakkaan toimittaman datan 64 merkin pituisiin lohkoihin ja kutsuu transform rutiinia jokaisella lohkolla. Lohkossa voi olla ylijäämämerkkejä edellisestä update kutsusta. Samoin tämän kutsun jälkeen voi jäädä ylimääräisiä merkkejä. Ylimääräiset merkit talletetaan yhteysalueen buffer taulukkoon.

void SHA256Update(SHA256_CONTEXT *sha256,
    unsigned char *data, int len)
{
  int i,j;

  // overflow from previous update
  j = (int)sha256->count & 63;

  sha256->count+=len;

  // if full blocks available
  if((j+len)>63) {

    // last bytes of next block to buffer
    memcpy(&sha256->buffer[j],data,64-j); 

    // first full block from buffer
    SHA256Transform(sha256->state,
        sha256->buffer);

    // rest of the full blocks from data
    for (i = 64 - j ; i + 63 < len; i += 64) {
      SHA256Transform(sha256->state, &data[i]);
    }

    // rest of the bytes to beginning of buffer
    j = 0;
  } else
    // copy all data from beginning of data
    i = 0;

  // overflow from this update to buffer
  memcpy(&sha256->buffer[j],&data[i],len-i);
}

Transform funktion käyttämä taulukko: taulun arvot ovat 64 ensimmäisen alkuluvun (2-311) kuutiojuuren desimaaliosa 32 bittisenä lukuna.

IUWORD k256[64] = {
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
  0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
  0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
  0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
  0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
  0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
  0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
  0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
  0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
  0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
  0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
  0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
  0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
  0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
  0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
  0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
};

Transform:in käyttämät makrot

#define RR32(word,bits) ( ((word) >> (bits)) | \
        ((word) << (32 - (bits))) )
#define RS32(word,bits) ( ((word) >> (bits)) )

Transform funktio käsittelee yhden lohkon kutsujan toimittamaa tietoa ja päivittää tila kenttiä lohkon sisältämän tiedon perusteella.

Ensimmäinen “for(i=0” luuppi siirtää datalohkon (buffer) w taulukon 16 ensimmäiseen IUWORD:iin. Seuraava “for(i=16” luuppi muodostaa loput 64 IUWORD:stä yhdistelemällä aiempia rivejä.

Seuraavan kappale siirtää tiivistetoiminnon (hash) tilan a, b,c … h muuttujiin. Seuraava “for(i=64” luuppi tekee a-h, w[] ja k256 kentille laskentaa ja siirtelyä muodostaen uudet a-h kentät. Kun kaikki 64 kierrosta on tehty yhdistetään a-h kentät taas state[] kenttiin.

Sitten palataan laskemaan seuraavaa lohkoa.

void SHA256Transform(IUWORD state[8], IUBYTE buffer[64])
{
  int i;

  IUWORD w[64], s0;
  IUWORD a, b, c, d, e, f, g, h;
  IUWORD maj, t2, s1;
  IUWORD ch, t1;

  // fill first 16 w[] entries
  for(i=0;i<16;i++) {
    w[i] =
      (IUWORD)buffer[i*4+0] << 24 |
      (IUWORD)buffer[i*4+1] << 16 |
      (IUWORD)buffer[i*4+2] << 8  |
      (IUWORD)buffer[i*4+3];
  }

  // fill entries 16-63 of w[]
  for(i=16;i<64;i++) {
    s0 = (RR32(w[i-15],7)) ^ 
         (RR32(w[i-15],18)) ^
         (RS32(w[i-15],3));
    s1 = (RR32(w[i-2],17)) ^
         (RR32(w[i-2],19)) ^
         (RS32(w[i-2],10));
    w[i] = w[i-16] + s0 + w[i-7] + s1;
  }

  // copy state
  a = state[0];
  b = state[1];
  c = state[2];
  d = state[3];
  e = state[4];
  f = state[5];
  g = state[6];
  h = state[7];

  for(i=0;i<64;i++) {
    s0 = (RR32(a,2)) ^ (RR32(a,13)) ^
           (RR32(a,22));
    maj = (a & b) ^ (a & c) ^ (b & c);
    t2 = s0 + maj;
    s1 = (RR32(e,6)) ^ (RR32(e,11)) ^
           (RR32(e,25));
    ch = (e & f) ^ ((~ e) & g);
    t1 = h + s1 + ch + k256[i] + w[i];

    h = g;
    g = f;
    f = e;
    e = d + t1;
    d = c;
    c = b;
    b = a;
    a = t1 + t2;
  }

  // add compression result to state
  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;
  state[4] += e;
  state[5] += f;
  state[6] += g;
  state[7] += h;
}

Maj funktion testitaulu on seuraavankaltainen: a b ja c ovat parametrit ja m rivillä on maj:n arvo. Maj:ssa on bittiarvo, jota on eniten. Jos nollia on kolme tai kaksi, maj:n arvo on 0. Jos taas ykkösiä on kolme tai kaksi maj:n arvo on 1.

a01000011110001000011101001001111
b00000101110001110000111001100001
c11101111011010100011111101010111
M01000111110001100011111001000111

Ch funktio taas taas poimii bitin kentän e mukaan f:stä tai g:stä. Jos bitti on 0 poimitaan g:n bitti, jos taas 1 poimitaan f:n bitti.

e00001000111000011100101011111001
f00011000010001101000101000101000
g11011110100111111100110001011110
C11011110010111101000111000101110

Final funktio käsittelee viimeisen update kutsun ylijääneen osalohkon lisäten materiaalin kokonaismerkkien lukumäärän yhteysalueen count kentästä tiedon loppuun. LIsäksi funktio palauttaa SHA256 tiivisteen digest parametrissa.

void SHA256Final(unsigned char digest[32], SHA256_CONTEXT *sha256)
{
  int i,j;
  unsigned char filelenght[8];
  IULONG count;

  count=sha256->count << 3;
  SHA256Update(sha256,"\200",1);

  while((sha256->count & 63) != 56)
    SHA256Update(sha256,"\0",1);

  filelenght[0]=(unsigned char)
      (count >> 56)&0xff;
  filelenght[1]=(unsigned char)
      (count >> 48)&0xff;
  filelenght[2]=(unsigned char)
      (count >> 40)&0xff;
  filelenght[3]=(unsigned char)
      (count >> 32)&0xff;
  filelenght[4]=(unsigned char)
      (count >> 24)&0xff;
  filelenght[5]=(unsigned char)
      (count >> 16)&0xff;
  filelenght[6]=(unsigned char)
      (count >> 8 )&0xff;
  filelenght[7]=(unsigned char)
      (count&0xff);

  SHA256Update(sha256,filelenght,
      sizeof(filelenght));

  for(i=0,j=0;i<8;i++) {
    digest[j++]=(unsigned char)
        (sha256->state[i] >> 24)&0xff;
    digest[j++]=(unsigned char)
        (sha256->state[i] >> 16)&0xff;
    digest[j++]=(unsigned char)
        (sha256->state[i] >> 8)&0xff;
    digest[j++]=(unsigned char)
        (sha256->state[i] )&0xff;
  }
}

Pieni testipääohjelma:

int SHA256Test(char *string,int rounds,
      char *hashs) {
  int ok;
  char temp[3];
  unsigned char result[32];
  HashCtx hash;

  fprintf(stdout,"s=%s\n",string);
  fprintf(stdout,"h=%s\n",hashs);
  HashInit(&hash);
  for(int c=0;c<rounds;c++)
    HashUpdate(&hash,string,strlen(string));
  HashFinal(result,&hash);
  fprintf(stdout,"r=");
  ok=1;
  for(int c=0;c<sizeof(result);c++) {
    sprintf(temp,"%02x",result[c]);
    fprintf(stdout,"%s",temp);
    if(strncmp(temp,hashs+c*2,2))
      ok=0;
  }
  if(ok)
    fprintf(stdout,", ok");
  else
    fprintf(stdout,", error");
  fprintf(stdout,"\n");

  return(ok);
}

void main(int argc,char *argv[])
{
  int ok;

  fprintf(stdout,"sizeof(IUBYTE):%lu",
      sizeof(IUBYTE));
  fprintf(stdout,", sizeof(IUWORD):%lu",
      sizeof(IUWORD));
  fprintf(stdout,", sizeof(IULONG):%lu",
      sizeof(IULONG));
  fprintf(stdout,"\n");

SHA256Test("abc",1,"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
SHA256Test("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",1,"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
SHA256Test("a",1000000,"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0");
SHA256Test("The quick brown fox jumps over the lazy dog",1,"d7a8fbb307d7809469ca9abcb0082e4f8d5651e46d3cdb762d02d0bf37c9e592");
}

Fort, Fortunan kaltainen satunnaisbittigeneraattori

Fort:in päärakenne tallettaa satunnaisbittigeneraattorin lähteet (pool), joihin satunnaisuutta lisätään ja joiden pohjalta sitä generoidaan. Lähteitä on 32 kappaletta ja niiden muistirakenne sisältää satunnaismerkkien lukumäärän lähteessä ja hash toiminnon yhteysalueen (HashCtx).

struct fort_pool {
  int length;
  HashCtx pool;
};
struct fort_pool fort_pools[32];

Merkkien lukumäärää ensimmäisessä poolissa (0) käytetään sen selvittämiseksi onko lähteissä tarpeeksi satunnaisuutta seuraavaa avainnusta varten. Lähteen (tai puskurin) HashCtx yhteysalueeseen lisätään uusi ohjelmalle annettu satunnaisuus HashUpdate toiminnolla.

Satunnaisuus lisätään tapahtumilla, joita lisäävä pääfunktio on fort_add_random_event. Funktio lisää tapahtuman pool muuttujalla kerrottuun lähteeseen, satunnaisuuden ohjelmalähde kerrotaan source muuttujalla, seuraavan puskurin numeron laskentatapa kerrotaan muuttujalla mode ja satunnaismerkkien määrä merkkeinä on kentässä len ja varsinaiset satunnaismerkit ovat buf osoitteen osoittamassa puskurissa.

char fort_events_file[128] = "fortevents.deb";
int dump_events=0;

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf)
{
  while(len > 1 && buf[len-1] == 0)
    len--;
  
  HashUpdate(&fort_pools[*pool].pool, buf, len);
  fort_pools[*pool].length+=len;

  if(dump_events<4*1048576) {
    FILE *fp1;
    if((fp1=fopen(fort_events_file,"a"))!=NULL) {
      fprintf(fp1,"id=%d",dump_events);
      fprintf(fp1,", source=%d",source);
      fprintf(fp1,", pool=%d",*pool);
      fprintf(fp1,", mode=%d",mode);
      fprintf(fp1,", len=%d",len);
      fprintf(fp1,", data=");
      for(int c=0;c<len;c++)
        fprintf(fp1,"%02x",buf[c]);
      fprintf(fp1,"\n");
      fflush(fp1);
      fclose(fp1);
      dump_events++;
    }
  }

  switch(mode) {
  case 1:
    (*pool)=((*pool)+1)%32;
    break;
  case 2:
    if((*pool)>=31 || fort_pools[*pool].length <
      fort_pools[*pool+1].length)
      (*pool)=0;
    else
      (*pool)=((*pool)+1)%32;
    break;
  case 3:
    break; // caller decides next pool                                                                                    
  case 4:
#ifdef RESSU
    (*pool)=ressu_genbyte_limit(32);
#endif
    break;
  }
}

Funktiossa satunnaisuus lisätään satunnaislukulähteeseen HashUpdate funktiolla, lasketaan satunnaismerkkien lukumäärää, tehdään ensimmäisistä satunnaislukutapahtumista listaa ja mode-kentän mukaan lasketaan seuraavan käytettävän satunnaislukulähteen numero.

Ensimmäiset tapahtumat (4*1048576=4M tapahtumaa) raportoidaan fort_events_file kentän sisältämään tiedostoon.

Mode voi saada arvot yhdestä neljään:
1:s moodilla satunnaislukulähdettä kasvatetaan aina yhdellä. Kun päästään lähteeseen 32 lähteiden kierto aloitetaan alusta siirtymällä takaisin lähteeseen nolla
2:s moodilla täytetään numeroltaan pienimpiä satunnaislukulähteistä nopeasti, eli täytetään ne, jotka on viimeisessä uudelleen avainnuksessa käytetty
3:s moodi ei tee pool muuttujalle mitään, kutsujan odotetaan päättelevän seuraavan satunnaislukulähteen
4:s seuraava satunnaislukulähde on satunnainen välillä 0-31.

ressu_genbyte_limit() on aiemmassa postissa.

Fortevents.deb tiedostoon raportoidaan seuraavankaltaisia tietueita: tietueissa on tietueen järjestysnumero ja kenttien arvot add event kutsusta. Tiedostosta voi esimerkiksi grep:llä poimia tietyn lähteen rivejä ja katsoa niistä miten niiden data-arvot vaihtelevat (grep source=23 fortevents.deb), eli sisältävätkö rivit todella ja riittävästi satunnaisuutta.

id=0, source=100, pool=4, mode=3, len=2, data=b4f4
id=1, source=22, pool=4, mode=2, len=2, data=d6f4
id=2, source=10, pool=5, mode=2, len=2, data=e9f4
id=3, source=11, pool=30, mode=2, len=2, data=0000
id=4, source=23, pool=6, mode=2, len=2, data=0ff5
id=5, source=14, pool=7, mode=2, len=2, data=16f5
id=6, source=15, pool=6, mode=2, len=2, data=0200
id=7, source=12, pool=8, mode=2, len=2, data=26f5
id=8, source=13, pool=17, mode=2, len=2, data=0000
id=9, source=10, pool=9, mode=2, len=2, data=33f5
id=10, source=11, pool=31, mode=2, len=2, data=0000
id=11, source=12, pool=10, mode=2, len=2, data=41f5
id=12, source=13, pool=18, mode=2, len=2, data=0000
id=13, source=26, pool=0, mode=2, len=2, data=4a00
id=14, source=12, pool=11, mode=2, len=2, data=66f5
id=15, source=13, pool=19, mode=2, len=2, data=0000

Nämä rivit ovat sisäisistä lähteistä tulevia satunnaisbittejä, niissä source kentän arvo on välillä 10-27. Sisäisissä lähteissä on kahta eri mallia, ensimmäisessä data on suoritushetken kellonajan mikrosekuntien 16 alinta bittiä (fort_add_random_event_time) ja toisessa mallissa satunnaisuus tulee funktion suoritusajan eli keston alimmista 16 bitistä (fort_add_random_event_timer_star ja fort_add_random_event_timer_do).

Jos ohjelmasi lähettää riittävästi satunnaistapahtumia, voit halutessasi poistaa sisäiset lähteet fort:ista poistamalla #define FORT_INTERNAL_EVENTS määrityksen. Alkuperäisen Fortunan kuvauksessa ei ole sisäisiä lähteitä. Ilman ohjelmasi satunnaisuustapahtumia fort tarvitsee joko ressun tai sisäiset tapahtumat mieluummin molemmat, että sen tuottamat satunnaisbitit ovat luotettavia.

Satunnaistapahtumien lisäämiseen on myös seuraavat apufunktiot, joilla voidaan mitata esimerkiksi funktion suoritusaika mikrosekunteina, ja käyttää siitä alimmat 16 bittiä satunnaisuuslähteenä:

void fort_add_random_event_timer_start(unsigned long *micros)
{
  *micros=getmicroseconds();
}

void fort_add_random_event_timer_do(int *pool, int source, int mode, unsigned long *micros)
{
  unsigned char temp[2];
  unsigned long l;

  l=getmicroseconds()-*micros;
  temp[0] = l & 0xff;                                                                                                                           
  temp[1] = l>>8 & 0xff;                                                                                                                                 
                                                                                                                                                         
  fort_add_random_event(pool, source, mode,
               sizeof(temp), temp);                                                                                         
}

Tällä tehdään kutsumispisteen kellonajasta 16 bittinen satunnaislukutapahtuma.

void fort_add_random_event_time(int *pool, int source, int mode)
{
  unsigned char temp[2];
  unsigned long l;

  l=getmicroseconds();
  temp[0] = l & 0xff;
  temp[1] = l>>8 & 0xff;

#ifdef RESSUEVENT
  ressu_genbuffer(sizeof(temp),temp);
#endif
                                                                                                                                 
  fort_add_random_event(pool, source, mode,
               sizeof(temp), temp);
}

Tällä funktiolla jaetaan pidempi satunnaismerkkijono useampaan puskuriin:

void fort_add_random_event_split(int *pool,
  int source, int mode, int len,
  unsigned char *buf, int size)
{
  int n;

  while(len!=0) {
    n=(size<len)? size:len;
    fort_add_random_event(pool, source,
      mode, n, buf);
    buf+=n;
    len-=n;
  }
}

Edellisten apufunktio:

unsigned long getmicroseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((unsigned long)tv.tv_sec*1000000 +
          tv.tv_usec);
}

Seuraava funktio muodostaa uuden fort_key avaimen edellisestä fort_key avaimesta, juoksevasta 16 merkkisestä numerosarjasta cvar ja tallettaa sen parametrina annettuun puskuriin (buf). Lisäksi kasvatetaan cvar numerosarjaa (inccvar). Fort_rekey funktiota käytetään myös satunnaisbittien tekemiseen. Funktion alussa ja lopussa on satunnaistapahtumakutsut tapahtuman muodostukselle kellon ajasta (fort_add_random_event_time) ja funktion suorituksen kestosta (fort_add_random_event_timer_start ja fort_add_random_event_timer_do). time_pool ja rekey_pool muuttujissa talletetaan seuraavan satunnaisuuspuskurin numero kellonaikatapahtumille ja suoritusaikatapahtumille. Micros muuttujan avulla lasketaan funktion suorituksen kesto. Add random funktioiden toinen kenttä kertoo lähteen eli source kentän tapahtumatiedostossa. Tässä kellonaika tapahtuman source on 16 ja suoritusaika tapahtuman source on 17.

#define FORT_INTERNAL_EVENTS 1
int fort_internal_events=1;
unsigned int fort_event_mode = 2;
unsigned char fort_key[HashLen];
unsigned char cvar[16];
int time_pool=0;
int rekey_pool=0;

void fort_rekey(char *buf)
{
  HashCtx hash;
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
         16, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update(&hash, (unsigned char *)&cvar,
      sizeof(cvar));
  hash_final(buf, &hash);
  inccvar();

  /* Forget hash */
  memset(&hash, 0, sizeof(hash));

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&rekey_pool,
        17, fort_event_mode, &micros);
#endif
}

Edellisen apufunktiot:

void inccvar()
{
  int c=0;

  /* 16 bytes, LSB first */
  while(++cvar[c]==0 && c<sizeof(cvar)-1)
    c++;
}

void clearcvar()
{
  int c;

  for(c=0; c<sizeof(cvar); c++)
    cvar[c]=0;
}

cvar on 16 merkkiä pitkä muuttuja ja aina sitä käytettäessä siihen lisätään yksi.

Seuraavalla funktiolla muodostetaan kutsujan toimittamaan puskuriin buf len merkkiä pitkä satunnaisbittisarja. Funktion alussa ja lopussa ovat standardit satunnaistapahtumien muodostuskutsut aika ja kesto tapahtumille. Tämän funktion antamia satunnaisbittejä pääset katsomaan >grep source=18 fortevents.deb ja >grep source=19 fortevents.deb komennoilla.

#define FORT_PSEUDO_LIMIT 1048576
int pseudo_pool=0;

void fort_pseudo_random_data(int len, unsigned char *buf)
{
  unsigned char tmp[HashLen];
  unsigned int n,blockbytes;
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        18, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif

  blockbytes=0;

  while(len!=0) {
    fort_rekey(tmp);
    n=(len<HashLen) ? len : HashLen;
    memcpy(buf, tmp, n);
    buf+=n;
    len-=n;
    blockbytes+=n;
     // rekey every 1048576 bytes if data left
    if(blockbytes >= FORT_PSEUDO_LIMIT &&
        len > 0) {
      fort_rekey(fort_key);
      blockbytes=0;
    }
  }

  fort_rekey(fort_key);

  /* Forget last bytes */
  memset(tmp, 0, sizeof(tmp));

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&pseudo_pool,
        19, fort_event_mode, &micros);
#endif
}

Ohjelma muodostaa satunnaisbitit kutsumalla fort_rekey funktiota ja kopioimalla sen antamat bitit asiakkaan puskuriin ja toistamalla tätä kunnes kaikki tarvittavat bitit on luotu. Funktio tekee uuden fort_key:n aina 1048576 merkin jälkeen. Se tehdään myös samalla fort_rekey funktiolla. Suorituksen lopuksi uusitaan vielä fort_key samalla fort_rekey funktiolla.

Fort reseed funktiolla muodostetaan uusi fort_key avain asiakkaan len pituisesta buf bittijonosta. Avaimeen käytetään vanhaa fort_key:tä, cvaria ja asiakkaan bittijonoa. time_pool on määritelty aiemmin. Lisäksi kasvatetaan cvaria. Fort_reseedillä ei voi tässä saada aikaiseksi samoja satunnaisbittejä uudestaan, kuten alkuperäisessä fortuna dokumentaatiossa. Periaatteessa ominaisuuden voi toteuttaa poistamalla fort_key:n ja cvar:in HashUpdate:t koodista.

int reseed_pool=0;

void fort_reseed(int len, unsigned char *buf)
{
  HashCtx hash;
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        20, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif

  hash_init(&hash);
  hash_update(&hash, fort_key, sizeof(fort_key));
  hash_update(&hash, (unsigned char *)&cvar,
      sizeof(cvar));
  hash_update(&hash, buf, len);
  hash_final(fort_key, &hash);
  inccvar();

  memset(&hash, 0, sizeof(hash));
#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&reseed_pool,
        21, fort_event_mode, &micros);
#endif
}

Seuraavalla funktiolla muodostetaan satunnaisbittejä. Erona fort_pseudo_random_data funktioon on että tarvittaessa vaihdetaan fort_key avainta. Itseasiassa varsinaiset satunnaisbitit muodostetaan fort_key avaimen muodostuksen jälkeen aiemmalla fort_pseudo_random_data funktiolla.

Aliohjelman ensimmäisessä varsinaisessa iffissä päätellään tehdäänkö avaimen muodostus tällä kutsulla. Muodostus tehdään, jos halutaan tehdä avainnus jokaisella ajokerralla (fort_reseed_always==1), tai jos on ensimmäinen kutsukerta (fort_next_reseed==0) tai edellisestä avainnuksesta on kulunut sekunnin kymmennys (tenth) ja ensimmäinen puskuri on täynnä (64 merkkiä satunnaisuutta).

Jos avainnus tehdään, c muuttujaa käyttävässä luupissa käydään asiaankuuluvat puskurit läpi. Jos (fort_fill_pool_when_reseeding==1) lippu on päällä ja puskuri ei ole täynnä täytetään puskuri merkeillä ressu_genbuffer rutiinista. Kaikkien asiaan kuuluvien puskurien sisältö kerätään hash-nimiseen tilamuuttujaan (hash_update) ja nollataan käytettyjen puskurien pituus (length) muuttuja. Käytetyn puskurin hash tyhjennetään (hash_init) ja sen arvoksi asetetaan puskurin edellinen hash (hash_update). Kaikkien valittujen puskurien hash:illä (hash_final) päivitetään fort_reseed():llä fort_key:n uudeksi arvoksi.

Puskureita käytetään puskurista 0 lähtien siten että puskuria nolla käytetään jokaisessa avaimen muodostuksessa, puskuria yksi käytetään joka toisessa avaimen muodostuksessa, puskuria kaksi käytetään joka neljännessä avaimen muodostuksessa, puskuria kolme käytetään joka kahdeksanneksessa avaimen muodostuksessa: rivin ensimmäinen numero on fort_reseed_count ja sen jälkeen ovat puskurin numerot:

01 0
02 0 1
03 0
04 0 1 2
05 0
06 0 1
07 0
08 0 1 2 3
09 0
10 0 1
11 0
12 0 1 2
13 0
14 0 1
15 0
16 0 1 2 3 4

Eli puskuria 4 käytetään joka kuudennessatoista kierroksessa. Tässä vielä viimeiset kierrokset ennen kun laskuri menee ympäri:

4294967278 0 1
4294967279 0
4294967280 0 1 2 3 4
4294967281 0
4294967282 0 1
4294967283 0
4294967284 0 1 2
4294967285 0
4294967286 0 1
4294967287 0
4294967288 0 1 2 3
4294967289 0
4294967290 0 1
4294967291 0
4294967292 0 1 2
4294967293 0
4294967294 0 1
4294967295 0
00 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
20 21 22 23 24 25 26 27 28 29 30 31
01 0
02 0 1
03 0

Vielä lista kierroksista, joilla puskuria käytetään ensimmäisen kerran. Esimerkiksi puskuria 6 käytetään ensimmäisen kerran kierroksella 64.

02 0 1
04 0 1 2
08 0 1 2 3
16 0 1 2 3 4
32 0 1 2 3 4 5
64 0 1 2 3 4 5 6
128 0 1 2 3 4 5 6 7
256 0 1 2 3 4 5 6 7 8
512 0 1 2 3 4 5 6 7 8 9
1024 0 1 2 3 4 5 6 7 8 9 10
2048 0 1 2 3 4 5 6 7 8 9 10 11
4096 0 1 2 3 4 5 6 7 8 9 10 11 12
8192 0 1 2 3 4 5 6 7 8 9 10 11 12 13
16384 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
32768 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
65536 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
131072 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
262144 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
524288 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
1048576 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
2097152 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
4194304 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
8388608 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
16777216 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
33554432 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

Puskureita käytetään aina tietty määrä nollapuskurista ylöspäin, ja käytön jälkeen ne täyttyvät nopeimmin fort_add_random_event funktion modella 2.

Ja vielä varsinainen fort_random_data funktion koodi:

#define FORT_MIN_POOL_SIZE 64
unsigned int fort_reseed_always=1;
unsigned int fort_fill_pool_when_reseeding=1;
unsigned int fort_reseed_count=0;
unsigned long fort_next_reseed=0;

int random_pool=0;

void fort_random_data(int len, unsigned char *buf)
{
  int c;
  unsigned long tenths;
  HashCtx hash;
  unsigned char buffer[HashLen];
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        22, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif

  tenths=gettenths();

  if( (fort_reseed_always==1) ||
      (fort_next_reseed==0) ||
      (fort_next_reseed<=tenths &&
       fort_pools[0].length >=
           FORT_MIN_POOL_SIZE) ) {

    // next tenth of a second
    fort_next_reseed = tenths + 1;                                                                                                                                                                                                                                                                                                                                                                                                               
    fort_reseed_count++;
    hash_init(&hash);
    c=0;

    while(c < 32 &&
        (fort_reseed_count % (1<<c))==0) {
#ifdef FORT_INTERNAL_EVENTS
      unsigned long micros;
      if(fort_internal_events) {
        fort_add_random_event_time(&time_pool,
            23, fort_event_mode);
        fort_add_random_event_timer_start(
            &micros);
      } // if(fort_internal_events)
#endif
#ifdef RESSU
      if(fort_fill_pool_when_reseeding==1 &&
         fort_pools[c].length<
              FORT_MIN_POOL_SIZE) {
        unsigned char tmp32[32];
        int len=FORT_MIN_POOL_SIZE -
            fort_pools[c].length;
        int n;

        while(len>0) {
#ifdef FORT_INTERNAL_EVENTS
          unsigned long micros;
          if(fort_internal_events) {
            fort_add_random_event_time(
                &time_pool, 24,
                fort_event_mode);
            fort_add_random_event_timer_start(&micros);
          } // if(fort_internal_events)
#endif
          n=(len<sizeof(tmp32)) ?
              len : sizeof(tmp32);
          memset(tmp32, 0, sizeof(tmp32));
          ressu_genbuffer(n, tmp32);
          int pool2=c;
          fort_add_random_event(&pool2, 29,
              fort_event_mode, n, tmp32);
          len-=n;
#ifdef FORT_INTERNAL_EVENTS
          if(fort_internal_events)
            fort_add_random_event_timer_do(
                &random_pool, 25,
                fort_event_mode, &micros);
#endif
        } // while(len>0)
      } // if(fort_fill_pool_when_reseeding==1
#endif
      hash_final(buffer, &fort_pools[c].pool);
      hash_update(&hash, buffer, sizeof(buffer));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);
      // save earlier pool to new one
      hash_update(&fort_pools[c].pool,
          buffer, sizeof(buffer));                                                                                                                                                                                                                                                                                                                                                                            
      c++;
#ifdef FORT_INTERNAL_EVENTS
      if(fort_internal_events)
        fort_add_random_event_timer_do(
             &random_pool, 26,
             fort_event_mode, &micros);
#endif
    } // while(c < 32
    hash_update(&hash, (unsigned char *)&cvar,
        sizeof(cvar));
    hash_final(buffer, &hash);
    fort_reseed(sizeof(buffer), buffer);
    /* Forget hash context */
    memset(&hash, 0, sizeof(hash));
    /* Forget reseed key */
    memset(buffer, 0, sizeof(buffer));  
    inccvar();
  } // if( (fort_reseed_always==1
  fort_pseudo_random_data(len, buf);

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&random_pool,
        27, fort_event_mode,
        &micros);
#endif
}

Tässä lyhennetty versio, josta on poistettu FORT_INTERNAL_EVENTS ja RESSU rivit:

void fort_random_data_readable(int len, unsigned char *buf)
{
  int c;
  unsigned long tenths;
  HashCtx hash;
  unsigned char buffer[HashLen];

  tenths=gettenths();

  if( (fort_reseed_always==1) ||
      (fort_next_reseed==0) ||
      (fort_next_reseed<=tenths &&
       fort_pools[0].length >=
           FORT_MIN_POOL_SIZE) ) {

    // next tenth of a second
    fort_next_reseed = tenths + 1;                                                                                                                                                                                                                                                                                                                                                                                                               
    fort_reseed_count++;
    hash_init(&hash);
    c=0;

    while(c < 32 &&
        (fort_reseed_count % (1<<c))==0) {
      hash_final(buffer, &fort_pools[c].pool);
      hash_update(&hash, buffer, sizeof(buffer));
      fort_pools[c].length = 0;
      HashInit(&fort_pools[c].pool);
      // save earlier pool to new one
      hash_update(&fort_pools[c].pool,
          buffer, sizeof(buffer));                                                                                                                                                                                                                                                                                                                                                                            
      c++;
    } // while(c < 32
    hash_update(&hash, (unsigned char *)&cvar,
        sizeof(cvar));
    hash_final(buffer, &hash);
    fort_reseed(sizeof(buffer), buffer);
    /* Forget hash context */
    memset(&hash, 0, sizeof(hash));
    /* Forget reseed key */
    memset(buffer, 0, sizeof(buffer));  
    inccvar();
  } // if( (fort_reseed_always==1
  fort_pseudo_random_data(len, buf);
}

Edellisen aliohjelmat

unsigned long gettenths()
{
  struct timeval tv;
  gettimeofday(&tv, NULL);
  return((unsigned long)tv.tv_sec*10 +
      (int)tv.tv_usec/100000);
}

Fort_save toiminnolla voidaan tallettaa generaattorin “tila”, ja fort_read_file taas lukee generaattorin tilan.

char fort_random_file[128] = "fort.rnd";
#define FORT_SAVE_SIZE 1024

void fort_save()
{
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  fp1=fopen(fort_random_file,"w");

  if(fp1!=NULL) {
    fort_pseudo_random_data(sizeof(buffer),
        buffer);
    fwrite(buffer, 1, sizeof(buffer), fp1);
    fclose(fp1);
  }
  memset(buffer, 0, sizeof(buffer));
}

void fort_read_file()
{
  int d;
  FILE *fp1;
  unsigned char buffer[FORT_SAVE_SIZE];

  fp1=fopen(fort_random_file, "rb");

  if(fp1==NULL) {
    fort_pseudo_random_data(
        sizeof(buffer), buffer);
#ifdef RESSU
    ressu_genbuffer(sizeof(buffer), buffer);
#endif
    d=sizeof(buffer);
  } else {
    d=fread(buffer, 1, sizeof(buffer), fp1);
    fclose(fp1);
  }
  fort_reseed(d, buffer);

  fort_save();
  memset(buffer, 0, sizeof(buffer));
}

Fort_save generoi joukon satunnaisbittejä fort_pseudo_random_data funktiolla ja kirjoittaa ne tiedostoon. Fort_read_file taas lukee edellä generoidut satunnaisbitit ja määrittelee uuden fort_key avaimen fort_reseed() rutiinilla. Jos fort_read_file ei onnistu avaamaan tallennettujen satunnaismerkkien tiedostoa, se generoi avainnukseen käytetyt satunnaismerkit ressulla.

Käytetty ressugen aliohjelma:

void ressu_genbuffer(int size, unsigned char *buffer)
{
  int c;

  for(c=0;c<size;c++)
    buffer[c]^=ressu_genbyte();
}

Aliohjelmat yksittäisten satunnaisten merkkien ja puskurien luomiseen:

#define FORT_CLEAR

#define FORTCNT 128

unsigned int fort_cnt=FORTCNT;
unsigned char fort_bytes[FORTCNT];
int fort_byte = 999999999;

int fort_random_data_byte()
{
  if(fort_byte >= fort_cnt) {
#ifdef FORT_CLEAR
    memset(fort_bytes, 0, fort_cnt);
#else
    if(fort_byte == 999999999)
        memset(fort_bytes, 0, fort_cnt);
#endif
    fort_random_data(fort_cnt, fort_bytes);
    fort_byte=0;
  }
  return(fort_bytes[fort_byte++]);
}

int fort_random_data_byte_limit(int limit)
{
  int c;

  while((c=fort_random_data_byte())>
      (256/limit)*limit);
  return(c%limit);
}

void fort_random_data_buffer(int size, unsigned char *buffer)
{
  int c;

  for(c=0;c<size;c++)
    buffer[c]^=fort_random_data_byte();
}

Seuraavana funktio, jolla alustetaan generaattori käyttöä varten. Se ajetaan yleensä ressu_init:in jälkeen pääohjelmassa. Ohjelma tulostaa ensiksi rivin fort:in teknisistä tiedoista. Seuraavaksi tyhjennetään cvar ja alustetaan poolit (puskurit).

Huomaa että alustettaessa ei voida käyttää satunnaistapahtumia luovia versioita HashInit:istä, koska silloin puskuri voi vastaanottaa tapahtuman, vaikka sitä ei ole alustettu. Fort_random_data:ssa on sama ongelma kun vanha puskuri tyhjennetään ja alustetaan uudestaan.

Seuraavaksi generoidaan ressulla fort_key avain. Jos ressua ei ole, fort_key ei vielä tässä vaiheessa ole kunnossa. Jos sisäiset tapahtumat ovat käytössä, fort_key luodaan kunnolla myöhemmin tulevassa sisäisiä tapahtumia luovassa kappaleessa.

Fopen funktiolla tyhjennetään fortevents.deb tiedosto ja dump_events muuttujaan laitetaan ensimmäisen tapahtuman numero nolla.

Sitten tulee edellä mainittu sisäisten tapahtumien luontikappale. Tämän kappaleen suorituksen jälkeen puskureissa on hyvä määrä satunnaisuutta vaikka ressua ei olisikaan käytössä. Sisäisten tapahtumien luonnin jälkeen avainnetaan fort_key uudestaan.

Seuraava kappale tekee tapahtumia (events) satunnaisiin ressu:lla arvottuihin lähteisiin. (mode=4)

Lopuksi täytetään vielä 0 puskuria lisäämällä satunnaismerkkejä ja alustetaan fort_reseed_count ja fort_next_reseed nollaksi.

void fort_init()                                                                                                                                                                       
{
  fprintf(stdout,"Fort v0.4");
  fprintf(stdout,", hash: %s", HashName);
  fprintf(stdout,", pools: %d*%ld", 32,
      sizeof(HashCtx));
  fprintf(stdout,", hashsize: %d", HashLen);

  unsigned int save_fort_internal_events;
  save_fort_internal_events=fort_internal_events;
  fort_internal_events=1;

  clearcvar();
  
  // Initialize buffers
  for(int c=0;c<32;c++) {
    fort_pools[c].length = 0;
    HashInit(&fort_pools[c].pool);
  }

#ifdef RESSU
  ressu_genbuffer(sizeof(fort_key),fort_key);
#endif

  FILE *fp1;
  // Empty events file
  if((fp1=fopen(fort_events_file,"w"))!=NULL)
    fclose(fp1);
  dump_events=0;

  unsigned char temp[64];

  memset(temp, 0, sizeof(temp));

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events) {
    // Create some internal events
    for(int c=0;c<64;c++)
      fort_random_data(sizeof(temp), temp);
  }
#endif
  fort_reseed_count=0;
  fort_next_reseed=0;

  // Rekey fort_key with new events
  fort_random_data(sizeof(temp),temp);
#ifdef RESSU
  ressu_genbuffer(sizeof(temp),temp);
#endif
  fort_reseed(sizeof(temp),temp);

  fort_read_file();

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events) {
    // Events to random pools
    fort_pseudo_random_data(sizeof(temp), temp);
#ifdef RESSU
    ressu_genbuffer(sizeof(temp),temp);
#endif
    int pool2=0;
    fort_add_random_event_split(&pool2,
      30, 4, sizeof(temp), temp, 1);
  }
#endif

#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events) {
    // Fill first pool
    fort_pseudo_random_data(sizeof(temp), temp);
#ifdef RESSU
    ressu_genbuffer(sizeof(temp), temp);
#endif
    int pool2=0;
    fort_add_random_event(&pool2,
      28, 1, sizeof(temp), temp);
  }
#endif

  // Forget temp variable
  memset(temp, 0, sizeof(temp));

  fort_internal_events =
    save_fort_internal_events;

  fort_reseed_count=0;
  fort_next_reseed=0;
}

Tässä vielä apurutiineja, joiden avulla saadaan kerättyä satunnaisuutta hash:ien käsittelyä tekevistä init, update ja final rutiineista.

int time_pool=0;
int init_pool=0;

void hash_init(HashCtx *hash)
{
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        10, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif
  HashInit(hash);
#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&init_pool,
        11, fort_event_mode, &micros);
#endif
}

int update_pool=0;

void hash_update(HashCtx *hash, unsigned char *data, int len)
{
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool, 
        12, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif
  HashUpdate(hash, data, len);
#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&update_pool,
        13, fort_event_mode, &micros);
#endif
}

int final_pool=0;

void hash_final(unsigned char digest[HashLen], HashCtx *hash)
{
#ifdef FORT_INTERNAL_EVENTS
  unsigned long micros;
  if(fort_internal_events) {
    fort_add_random_event_time(&time_pool,
        14, fort_event_mode);
    fort_add_random_event_timer_start(&micros);
  }
#endif
  HashFinal(digest,hash);
#ifdef FORT_INTERNAL_EVENTS
  if(fort_internal_events)
    fort_add_random_event_timer_do(&final_pool,
        15, fort_event_mode, &micros);
#endif
}

Vielä pieni testipääohjelma:

int main(int argc,char *argv[])
{
  unsigned char buffer[32];

  ressu_init();
  fort_init();

  ressu_genbuffer(sizeof(buffer), buffer);
  fort_reseed(sizeof(buffer), buffer);

  fort_random_data(sizeof(buffer), buffer);

  for(int c=0;c<sizeof(buffer);c++)
    fprintf(stdout,"%02x",buffer[c]);
  fprintf(stdout,"\n");

  fort_save();

  return(0);
}

Kellosarjassa olevat satunnaiset bitit

Tässä uusimmassa versiossa yritetään laskea paremmin kellosarjasta tulevia satunnaisbittejä. Ohjelmassa lasketaan satunnaisbittejä neljällä eri tavalla. Ensimmäisessä tavassa (muuttuja b) lasketaan kellosarjojen pituuksia, eli sitä kuinka monta kertaa peräkkäin sama numero ilmenee. Tämä tapa on ollut mukana alusta asti. Toinen tapa on periaatteessa sama, aina kun kellosarjan numero vaihtuu, lisätään teoreettiseen satunnaisuuteen yksi bitti (muuttuja rndbits). Kolmannessa tavassa lisätään satunnaisuuteen yksi bitti aina kun kellosarjan pituus vaihtuu (muuttuja rndbits2). Neljännessä tavassa (rndbits3) tarkastellaan viittä erilaista edellistä kellosarjan pituutta, ja aina kun tulee edellisistä poikkeava kellosarjan pituus lisätään satunnaisuutta yhden bitin verran.

Kaikkien näiden tapojen pitää toteutua eli laskennallisia satunnaisbittejä jokaisella tavalla tulee olla enemmän kun haluttuja satunnaisbittejä puskurissa.

Clockbytes ehto varmistaa että kellomateriaalia käsitellään ainakin 2 kiloa.

Sen verran ovat palautetut satunnaisbitit parantuneet, että ensimmäisissä testeissä dieharder ei huomannut yhtään heikkoutta. Aiemmin niitä oli pari kolme.

void ressu_genbytes(int size, unsigned char *buffer, int b) /* (c) JariK 2013-2019 */
{
  int c, d, e, f, g,
    byte, prevbyte,
    chainbytes, prevchainbytes = -1,
    clockbytes = 0,
    rndbits = 0, rndbits2 = 0,
rndbits3 = 0; unsigned char oldchains[5] = { 0,0,0,0,0 }; prevbyte=clockbyte(); chainbytes=1; f=0; for(c=0; c < 8*b || clockbytes < 2000 || rndbits < 8*size || (rndbits2 > 0 && rndbits2 < 8*size ) || (rndbits3 > 0 && rndbits3 < 8*size); c++) { for(d=0; d<size; d++) { e = buffer[d]; e = ((e&0x80)>>7) | ((e&0x7f)<<1); byte = clockbyte(); buffer[d] = e^byte; if(prevbyte != byte) { prevbyte = byte; rndbits++; if(prevchainbytes != chainbytes) { prevchainbytes = chainbytes; rndbits2++; for(g = 0; g < sizeof(oldchains); g++) { if(oldchains[g] == chainbytes) break; } if(g == sizeof(oldchains)) { rndbits3++; g--; } while(g > 0) { oldchains[g] = oldchains[g-1]; g--; } oldchains[0] = chainbytes; } // if(prevchainbytes clockbytes+= chainbytes; chainbytes = 0; } // if(prevbyte chainbytes++; } // for(d for(d=0; d<size; d++) { f = (f+buffer[d])%size; e = buffer[d]; buffer[d] = buffer[f]; buffer[f] = e; } } // for(c }

Tässä vielä rutiini alkuperäisen b:n laskentaan.

int ressu_calculate_b()
{
  int c,d,e,total,count,bytes;

  total=0;
  count=0;
  d=clockbyte();
  for(c=0;c<4096;c++) {
    bytes=1;
    while((e=clockbyte())==d)
      bytes++;
    total+=bytes;
    count++;
    d=e;
  }

  return((int)((float)total/count));
}

Clockbyte löytyy aiemmasta postista.

Ressu kokeilu: satunnaisuuden leviämisen nopeuttaminen

Ressu kokeilu yrittää lisätä satunnaisuuden muodostamista siirtämällä kellojonon luvun jälkeen alimmasta eli “tarkimmasta” bitistä satunnaisuutta seuraavien merkkien ylempiin bitteihin. Se ei tietenkään pysty lisäämään satunnaisuutta kelloon mutta se nopeuttaa satunnaisuuden leviämistä kaikkiin puskurin bitteihin.

/*
 * Ressu−satunnaislukugeneraattori versio 1.0 (ressugen.c)
 *
 * (c)2013−2018 Jari Kuivaniemi, Kaikki oikeudet pidätetään!
 */
#include <stdio.h>
#include <sys/time.h>

unsigned char *ressu_name = "Ressu 1.0";

#ifndef OWN_CLOCK

unsigned char clockbyte() /* JariK 2013 */
{
  struct timeval tv;

  gettimeofday(&tv,NULL);

  return(tv.tv_usec & 0xff);
}

#endif

void ressu_genbytes(int size, unsigned char *buffer, int b) /* JariK 2013 */
{
  int c,d,e,f;

  f=0;
  for(c=0;c<8*b;c++) {
    for(d=0;d<size;d++) {
      e=buffer[d];
      e=((e&0x80)>>7) | ((e&0x7f)<<1);
      buffer[d]=e^clockbyte();
    }
    for(d=0;d<size;d++) {
      e=buffer[d]&1;
      buffer[(d+1)%size]^=(e<<1);
      buffer[(d+2)%size]^=(e<<2);
      buffer[(d+3)%size]^=(e<<3);
      buffer[(d+4)%size]^=(e<<4);
      buffer[(d+5)%size]^=(e<<5);
      buffer[(d+6)%size]^=(e<<6);
      buffer[(d+7)%size]^=(e<<7);
    }
    for(d=0;d<size;d++) {
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Uusi kappale koodissa on tuo toinen “for(d=0;”  -kappale. For d-toistorakenne käy koko puskurin läpi ja xor:aa sarjan alimman bitin seuraavan merkin toiseksi alimpaan merkkiin, kolmannen merkin kolmenneksi alimpaan bittiin, neljännen merkin neljänneksi alimpaan bittiin jne. Tätä toistetaan kunnes ensinnäkin tämän hetkisen sarjan kahdeksan merkkiä on käsitelty ja toisaalta kunnes kaikki puskurin  alimmat bitit on käsitelty.

Seuraa kierrosrakenne, joka vaihtaa korttipakkamaisesti kaikki puskurin merkit toiseen satunnaiseen merkkiin (f). Tämän jälkeen edelliset bitit eivät enää ole vierekkäin vaan ne siirtyvät satunnaisiin kohtiin puskuria.

Jos haluat katsella bittien vaihteluha, kopioi seuraava tulostusluuppi uuden kierrosrakenteen ensimmäiseksi ja viimeiseksi.

fprintf(stdout,"\n%2d %3d",c,d);
for(g=0;g<8;g++) {
  fprintf(stdout," %02x",buffer[d+g]);
}

JK: Toinen tapa nopeuttaa satunnaisuuden siirtymistä koko puskuriin on lisätä jokaiseen merkkiin satunnaisuutta seuraavista merkeistä: Ensimmäisessä luupissa sarjan ensimmäistä merkkiä muutetaan toisen, kolmannen ja neljännen merkkien perusteella. Tässä satunnaisuus saadaan kahden merkin and:in perusteella. Ensimmäinen pari on toinen ja kolmas  merkki, toisen parin muodostavat toinen ja neljäs merkki, kolmannen parin muodostavat kolmas ja neljäs merkki.  Parin puoliskot and:ataan keskenään ja näillä kolmella merkillä xor:ataan ensimmäistä merkkiä. Tämän jälkeen voidaan ajaa taas “korttipakan sekoitus”, jolloin bitit jotka vaikuttavat toisiinsa eivät ole enää vierekkäin. Toisaalta tässähän käydään koko puskuri läpi merkki kerrallaan, joten seuraavat merkit ilman korttipaikan sekoitustakin “häviävät”.

for(d=0;d<size;d++) { // see sha2
  buffer[d%size]^=
    ((buffer[(d+1)%size]&(buffer[(d+2)%size])) ^
     (buffer[(d+1)%size]&(buffer[(d+3)%size])) ^
     (buffer[(d+2)%size]&(buffer[(d+3)%size])) );
}

Toinen versio on samanlainen mutta tässä käytetään or:ia

for(d=0;d<size;d++) {
  buffer[d%size]^=
    ((buffer[(d+1)%size]|(buffer[(d+2)%size])) ^
     (buffer[(d+1)%size]|(buffer[(d+3)%size])) ^
     (buffer[(d+2)%size]|(buffer[(d+3)%size])) );
}

Kolmannessa käytetään satunnaisbittien hakemiseen ynnäystä ja satunnaisbittien yhteenlaskussa vieläkin xor:ia.

for(d=0;d<size;d++) {
  buffer[d%size]^=
    (unsigned char)(
    (buffer[(d+1)%size]+(buffer[(d+2)%size])) ^
      (buffer[(d+1)%size]+(buffer[(d+3)%size])) ^
    (buffer[(d+2)%size]+(buffer[(d+3)%size])) );
}

Olen näköjään juuttunut tähän ressuun, toivottavasti saan aikaiseksi jotain muuta sanottavaa tertusta seuraavaan postiin.

Kotisivu