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 = 999999;

int fort_random_data_byte()
{
  if(fort_byte >= fort_cnt) {
#ifdef FORT_CLEAR
    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);
}

Leave a Reply