FORT v0.4 uusi versio

Fortin edellisestä versiosta (https://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.

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

Edit: tehty korjauksia ja muutoksia julkaisun jälkeisten viikkojen aikana:

Poistettu fort_add_random_event_time():sta toistuva toinen merkki. Näin tapahtuma tehdään vain alimmista kahdeksasta bitistä, joissa on eniten satunnaisuutta ja seuraavat kahdeksan bittiä, joissa satunnaisuutta ei ole ohitetaan. Toinen merkki lähetetään puskuriin vain jos se poikkeaa edellisestä toisesta merkistä. Poistosta etuna saadaan se että puskurien kerättyjen merkkien määrä kuvaa tarkemmin todella satunnaisia merkkejä, ja tällä lisätään satunnaisuutta satunnaistapahtumien datan pituuteen.

Lisätty fort_reseed() koodiin aikavyöhykkeen vaihto. Jos ajon aikana aikavyöhyke vaihtuu, suoritetaan save ja report koodit. Näin saadaan raporttien välit laskettua oikein heti uuden aikavyöhykkeen alusta. Ennen vaihtoa uuden aikavyöhykkeen ensimmäinen katko tuli edellisen aikavyöhykkeen mukaan, jolloin katko ei ollut selkeä. Nyt raportissa näkyy aikavyöhykkeen vaihto rivi vaihdon kellonajalla ja heti seuraava katko on laskettu oikein huomioiden uusi aikavyöhyke.

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 tehdä esittelytiedoston fort:in toiminnoista tässä mahdollinen fort.h tiedosto pääfunktioista.

typedef unsigned long long IUTIME;

void fort_add_random_event(int *pool, int source, int mode, int len, unsigned char *buf);
void fort_add_random_event_timer_start(IUTIME *micros);
void fort_add_random_event_timer_do(int *pool, int source, int mode, IUTIME *micros);
void fort_add_random_event_time(int *pool, int source, int mode);
void fort_add_random_event_split(int *pool, int source, int mode, int len, unsigned char *buf, int size);
void fort_rekey(unsigned char *buf);
void fort_pseudo_random_data(int len, unsigned char *buf);
void fort_reseed(int len, unsigned char *buf);
void fort_random_data(int len, unsigned char *buf);
int fort_random_data_byte();
void fort_clear();
int fort_random_data_byte_limit(int limit);
void fort_random_data_buffer(int size, unsigned char *buffer);
void fort_save();
void fort_restore();
void dump_pools(char *header);

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 (https://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 IUTIME gettenths()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

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

static IUTIME getmicroseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

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

static IUTIME getseconds()
{
  struct timeval tv;

  gettimeofday(&tv, NULL);

  return((IUTIME)tv.tv_sec);
}

Nämä ovat edellisestä postista (https://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ä.

#ifdef FORT_INTERNAL_EVENTS
#define FORT_INTERNAL_EVENTS_START(source) \
  IUTIME 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];

#define FORT_MIN_POOL_SIZE 64

#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(IUTIME *micros)
{
  *micros = getmicroseconds();
}

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

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

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

void fort_add_random_event_time(int *pool, int source, int mode)
{
  int len;
  unsigned char temp[2];
  IUTIME t;
  static int prev_second_byte = -1;

  t = getmicroseconds();
  temp[0] = t & 0xff;
  temp[1] = (t>>8) & 0xff;
  len=2;

  if(prev_second_byte == temp[1]) {
    len--;
  } else {
    prev_second_byte = temp[1];
  }

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

  fort_add_random_event(pool, source, mode,
	       len, 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 https://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 IUTIME fort_next_save = 0;

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

void dump_pools(char *header);

static char current_timezone[10];

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));

  if(fort_next_save != 0 ||
     fort_next_pools_report != 0) {
    IUTIME seconds;
    seconds = getseconds();

    char timezone[10];
    strftime(timezone, sizeof(timezone), "%Z",
             localtime((time_t *)&seconds) );
    if(strcmp(current_timezone,timezone)) {
      fort_next_save=1;
      fort_next_pools_report=1;
      strcpy(current_timezone,timezone);
    }

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

      IUTIME diff=
        timegm(localtime((time_t *)&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) {

      IUTIME diff=
        timegm(localtime((time_t *)&seconds))-
          seconds;

      char timebuf[128];
      strftime(timebuf, sizeof(timebuf), TIMEFORMAT,
               localtime((time_t *)&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 FORT_COUNTER fort_reseed_count = 0;
static IUTIME fort_next_reseed = 0;

void fort_random_data(int len, unsigned char *buf)
{
  int c;
  IUTIME 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.

#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   2^10
  // M = mega   10^6   2^20
  // G = giga   10^9   2^30
  // T = tera   10^12  2^40
  // P = peta   10^15  2^50
  // E = exa    10^18  2^60
  // Z = zetta  10^21  2^70
  // Y = yotta  10^24  2^80
  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.491");
  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();

  FILE *fp1;

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

  dump_pools("Emptying pools report");

#ifdef RESSU
  ressu_genbuffer(sizeof(fort_key), fort_key);
  dump_pools("Generate fort key w ressu");
#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
  // Empty events file                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               
  if((fp1 = fopen(fort_events_file, "w"))!=NULL)
    fclose(fp1);
  event_id = 0;

  dump_pools("Emptying events");

#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);

  dump_pools("Randomness from urandom");
#endif

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

  dump_pools("Randomness from random");
#endif

  for(c=0; c<FORT_POOLS; c++) {
    unsigned char buffer[HashLen];
    fort_pseudo_random_data(sizeof(buffer), buffer);
#ifdef RESSU
    ressu_genbuffer(sizeof(buffer), buffer);
#endif
    hash_update(&fort_pools[c].pool,
      buffer, sizeof(buffer));
  }
  dump_pools("Initialize buffers 2");

  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);

  dump_pools("Fill first buffers");

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

  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;
  fort_next_pools_report = 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 tässä 10 minuuttia eli 600 sekuntia (FORT_SECONDS_BETWEEN_POOLS_REPORTS). Yksiköt ovat tarkemmin funktiossa readable_length(). Kerättyjen määrien yksiköt 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 = 6;

#ifdef FORT_EVENTS
#define FORT_EVENTS_START(source) \
  IUTIME 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();
}