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 katkokohdasssa 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 = 999999;
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==999999)
      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=999998;
}

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

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.

Ressu versio 1

Tässä oikeastaan ensimmäinen “virallinen” julkaisu Ressu-satunnaislukugenetaattorin versiosta 1.0.

Edit: muokattu julkaisun jälkeisten viikkojen aikana.

Satunnaislukugeneraattorihan koostuu kahdesta funktiosta, clockbyte(), joka palauttaa kellojonon seuraavan merkin, ja ressu_genbytes(), joka on varsinainen generaattori.

Ensimmäinen kierros genbytes() rutiinista vaihtaa jokaisen merkin bittien järjestystä ja summaa merkkiin seuraavan kellojonon merkin. Ajatuksena on että jokainen tuloksena tulevan puskurin bitti koskee kaikkia kellojonon merkin bittejä.

Toinen kierros sekoittaa tulokseksi saadun kellojonon kuin korttipakan käyden koko jonon läpi, ja vaihtaen kunkin merkin satunnaisen merkin kanssa. Satunnainen merkki valitaan kellojonon merkkien perusteella.

Näitä kahta kierrosta suoritetaan b kertaa, jossa b on kierrosten lukumäärä ja määritellään genbytes() kutsussa.

Eli siis kellon tarkin bitti koskettaa merkin tarkinta bittiä, sen jälkeen merkkiä siirretään satunnaisesti toiseen kohtaan puskurissa, tämän jälkeen toinen tarkin bitti kellojonosta koskettaa merkin seuraavaa tarkinta bittiä ja merkki siirretään taas. Eli merkin bitit tulevat täysin satunnaisista kellon kohdista ja kaikkien kellon bittien vaikutuskohdat leviävät tasaisesti puskurissa. Jos kierroksia on kahdeksan, tämä tehdään kahdeksan kertaa, ja puskurin kaikki bitit koskettavat kahdeksan kertaa satunnaista kellosignaalin tarkinta bittiä ja vaihtavat kosketusten välillä paikkojaan.

OWN_CLOCK #define muuttujalla voidaan määritellä oma clock rutiini. Sitä voisi teoriassa käyttää näppäimistölle tai valokuville.

Vielä kerran: ethän käytä tätä generaattoria ainoana generaattorina kriittisiin tarkoituksiin, vaan summaat aina useampia generaattoreita..

/*      
 * 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++) {
      f=(f+buffer[d])%size;
      e=buffer[d];
      buffer[d]=buffer[f];
      buffer[f]=e;
    }
  }
}

Clockbyte() palauttaa yksinkertaisesti alimmat bitit järjestelmän kellosta.

Ressu_genbytes() saa parametreinaan puskurin koon, osoitteen siihen ja kierrosten lukumäärän. Se generoi puskuriin satunnaisbittijonon. Rutiini sisältää kolme kierrosrakennetta, joista c luuppi laskee kierroksia, d laskee puskurin merkkejä (alusta loppuun), e:tä käytetään välimuuttujana, ja f laskee vaihdettavan merkin paikkaa. F:ään lisätään aina puskurin sisältö, eli näin myös kellojono, se vastaa siitä että kun yksikin bitti kellojonossa muuttuu, tulos muuttuu. Tästä tulee periaatteessa mahdollinen ongelma, eli jos kellojono on samanlainen, generaattori generoi samoja bittijonoja uudestaan. Näyttää kuitenkin siltä että kellojonossa on samanlaisia vain noin kilojen kokoisia jaksoja. Tämän perusteella voisi ehkä laskea puskurin pituutta. Jatkossa puskurin pituus on määritelty 65535 merkiksi.

Tässä vielä muutama apurutiini:

Ressu_genbyte() palauttaa yhden ressu generaattorin arpoman merkin. Ressu_genbyte_limit(int limit) palauttaa yhden haluttua rajaa pienemmän merkin (raja on parametrissa limit). ressu_genshort() palauttaa 16 bittisen intin ja ressu_genshort_limit(int limit) palauttaa rajaa pienemmän 16 bittisen luvun. ressu_clear() tyhjentää apurutiinitn ressu_bytes puskurin.

Jättämällä määrittelemättä #define muuttuja RESSU_CLEAR:in voimme estää puskurin tyhjentämisen uuden puskurin luonnin yhteydessä. Jos puskuria ei ole tyhjennetty, voimme alkaa riidellä siitä, kuinka paljon edellisistä biteistä voidaan arvata uuden puskurin bittejä. (todellisuudessahan yhteyttä edelliseen puskuriin ei ole, puolet biteistä vaihtuvat vähintään rounds kertaa).. Toisaalta jos puskuria ei tyhjennetä, satunnaisuus “kertyy” useampien kutsukertojen välillä.

B muuttuja seuraavan koodin alussa kertoo kuinka monta kierrosta generaattoria ajetaan. Se on nyt 45 ja olen ajatellut sen olevan riittävän suuri ettei ongelmia kriittistenkään satunnaisbittien generoinnissa tule. Samoin tuo puskurin pituus on arvaus, jolla ei pitäisi tulla ongelmia. Jos esimerkiksi haluat satunnaislukuja shakki peliin tai sudokujen laatimiseen voit ilman muuta valita pienemmät luvut. (Esimerkiksi kilon puskuri ja pari kierrosta riittävät hyvin perus pelin generaattoriin.) Olen itse tutkinut generaattoria ja tuohon tietoon voi tulla muutoksia. Toisaalta jos noudatat neuvoani ja et käytä tätä generaattoria pelkästään, ei tietenkään haittaa siirtyä pienempiin lukuihin.

#define RESSUBUFLEN 2048
unsigned char ressu_bytes[RESSUBUFLEN];

int ressu_byte=999999;
int ressu_b = 45;
#define aRESSU_CLEAR 2
unsigned char *file_b = "libressu1.b";

int ressu_genbyte()
{
  if(ressu_byte>=ressu_bytes) {
    if(ressu_buffer==NULL)
      ressu_buffersize(ressu_bytes);
#ifdef RESSU_CLEAR
    ressu_clear();
#endif
    ressu_genbytes(ressu_bytes, ressu_buffer,
        ressu_b);
    ressu_byte=0;
  }
  return(ressu_buffer[ressu_byte++]);
}

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

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

int ressu_genbyte_limit(int limit)
{
  int c;

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

int ressu_genshort()
{
  return(ressu_genbyte()+256*ressu_genbyte());
}

int ressu_genshort_limit(int limit)
{
  int c;

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

void ressu_clear()
{
  memset(ressu_bytes,0,sizeof(ressu_bytes));
  ressu_byte=999999;
}

Yksi ongelma on määritellä, miten pitkä kellojonoo tarvitaan, että siinä on riittävästi satunnaisbittejä puskuriin. Yksi tapa tarkastella sitä on päättää että yhdessä kellon vaihdoksessa on yksi bitti, eli jos yhden vaihdoksen pituus on 20 merkkiä ja tarvittavien satunnaismerkkien määrä olisi 2048, kaava olisi 2048*8*20. Tulokseksi saataisiin että b:n arvo olisi sama kuin kellojakson pituus eli 20.

Tässä ohjelma, joka hakee kellojonon pituuden ja palauttaa sen (lisäten 2).

int ressu_calculate_b()
{
  int c,d,ed,count,first,statmax,statlength,b,b2;
  unsigned char stats[512];

  while(clockbyte()!=0);
  while(clockbyte()==0);
  while(clockbyte()!=0);

  for(c=0;c<sizeof(stats);c++)
    stats[c]=0;
  first=1;
  for(c=0;c<65535;c++) {
    d=clockbyte();
    if(first==1) {
      ed=d;
      count=1;
      first=0;
    } else if(ed!=d) {
      ed=d;
      stats[count]++;
      count=1;
    } else {
      count++;
    }
  }

  for(c=0;c<512;c++)
    if(stats[c]>0)
      fprintf(stdout," %d:%2d",c,stats[c]);

  fprintf(stdout,"\n");

  statmax=0;
  statlength=-1;
  for(c=0;c<sizeof(stats);c++) {
    if(stats[c]>=statmax) {
      statmax=stats[c];
      statlength=c;
    }
  }
  fprintf(stdout,"most frequend clockchain size: %d",statlength);
  b2=int_read(file_b);
  fprintf(stdout,", b from file: %d",b2);
  if(b2>=statlength) {
    b=b2;
  } else {
    b=statlength;
    int_write(b,file_b);
    fprintf(stdout,", b written to file: %d",b);
  }
  fprintf(stdout,", returned b: %d\n",b+2);
  fflush(stdout);
  
  return(b+2);
}

int int_read(char *filename)
{
  int i;
  unsigned char buffer[10];
  FILE *fp1;

  i=0;
  
  if((fp1=fopen(filename,"r"))!=NULL) {
    fgets(buffer,sizeof(buffer),fp1);
    i=atoi(buffer);
    fclose(fp1);
  }
  return(i);
}

void int_write(int i, unsigned char *filename)
{
  FILE *fp1;

  if((fp1=fopen(filename,"w"))!=NULL) {
    fprintf(fp1,"%d\n",i);
    fclose(fp1);
  }
}

Jos haluat itse pongata pitkiä duplikaattimerkkijonoja voit aloittaa sen seuraavalla ohjelmalla:

#include <stdio.h>
#include <string.h>
#include <time.h>

#include "ressugen.c"

#define RESSUBYTES 16384*8
//#define RESSUBYTES 16384*48
unsigned char ressu_bytes[RESSUBYTES];

#define VERBOSE 2
#define aFAST 2

time_t now;
#define HTMLTIMEFORMAT "%a, %d %b %Y %H:%M:%S GMT"
unsigned char datestr[128];
char procname[] = "Ressutest v0.01";

void main(int argc,char *argv[])
{
  int c,d,e,limit,dup,first0;
  int low,high,middle;
  time_t now2,now3;

  
  now = time(NULL);
  strftime(datestr, sizeof(datestr),
       HTMLTIMEFORMAT, gmtime(&now));

  fprintf(stdout,"\n");
  fflush(stdout);
  
  fprintf(stdout,"%s\n%s\n\n",procname,datestr);
  fflush(stdout);

  for(;;) {
    for(c=0;c<sizeof(ressu_bytes);c++) {
      ressu_bytes[c]=clockbyte();
    }
    
    now2 = time(NULL);
    
    limit=sizeof(ressu_bytes);;
    low=1;
    high=10000;

    fprintf(stdout,"limit: %d",limit);
    fflush(stdout);

    while(low<=high) {
      middle=(low+high)/2;
#ifdef VERBOSE
      fprintf(stdout,"\nlimit: %d",limit);
      fprintf(stdout,", low:%d",low);
      fprintf(stdout,", high: %d",high);
      fprintf(stdout,", middle: %d",middle);
      fflush(stdout);
#endif
      dup=0;
      for(d=0; d<limit-middle; d++) {
    for(e=d+1; e<limit-middle; e++) {
      if(!memcmp(&ressu_bytes[d],
             &ressu_bytes[e],middle)) {
        dup++;
        e+=middle;
#ifdef FAST
        break;
#endif
      }
    }
#ifdef FAST
      if(dup)
    break;
#endif
      }
#ifdef VERBOSE
      fprintf(stdout,", dup: %d",dup);
      fflush(stdout);
#endif
      if(dup) {
    low=middle+1;
      } else {
    high=middle-1;
      }
    }
#ifdef VERBOSE
    fflush(stdout);
    fprintf(stdout,", middle: %d",middle-1);
#endif
    first0=middle-1;
    dup=0;
    for(d=0;d<limit-(middle-1);d++) {
      for(e=d+1;e<limit-(middle-1);e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle-1)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif      
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
#ifdef VERBOSE
    fprintf(stdout,", dup: %d",dup);  
    fflush(stdout);
#endif
#ifdef VERBOSE
    fprintf(stdout,", middle: %d",middle);
    fflush(stdout);
#endif
    dup=0;
    for(d=0;d<limit-middle;d++) {
      for(e=d+1;e<limit-middle;e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
#ifdef VERBOSE
    fprintf(stdout,", dup2: %d",dup);
    fflush(stdout);
#endif
#ifdef VERBOSE
    fprintf(stdout,", middle: %d",middle+1);
    fflush(stdout);
#endif
    dup=0;
    for(d=0;d<limit-(middle+1);d++) {
      for(e=d+1;e<limit-(middle+1);e++) {
    if(!memcmp(&ressu_bytes[d],
           &ressu_bytes[e],middle+1)) {
      dup++;
      e+=middle;
#ifdef FAST
      break;
#endif
    }
      }
#ifdef FAST
      if(dup)
    break;
#endif
    }
    if(dup!=0)
      first0++;
    now3 = time(NULL);
#ifdef VERBOSE
    fprintf(stdout,", dup3: %d",dup);
    fflush(stdout);
#endif
    fprintf(stdout,", first0: %d",first0);

    int time;

    time=now3-now2;

    fprintf(stdout,", took ");
    if((time/60)>0) {
      fprintf(stdout,"%d minutes, ",time/60);
      time%=60;
    }
    fprintf(stdout,"%d seconds.\n",time);
    fflush(stdout);
  }
}

Seuraavassa mallitulokset edellisestä ohjelmasta: ensimmäisessä listassa VERBOSE ei ole määritelty. Toisessa ja kolmannessa VERBOSE on määritelty. Toisessa listassa FAST on määritelty, kolmannessa FAST ei ole määritelty. Limit on puskurin koko. Low, high ja middle ovat binäärihaun muuttujat. Dup kertoo onko duplikaatteja. Dup on 0 tai 1, jos FAST on määritelty, muuten tuplamerkkijonojen lukumäärä. First0  on ensimmäinen pituus, jolla ei ole tuplamerkkijonoja.

limit: 131072, first0: 1818, took 17 minutes, 10 seconds.
limit: 131072, first0: 1618, took 13 minutes, 23 seconds.
limit: 131072, first0: 2790, took 15 minutes, 16 seconds.
limit: 131072, first0: 2616, took 11 minutes, 21 seconds.
limit: 131072, first0: 2598, took 16 minutes, 35 seconds.

limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 1
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 1
limit: 131072, low:1876, high: 2499, middle: 2187, dup: 0
limit: 131072, low:1876, high: 2186, middle: 2031, dup: 1
limit: 131072, low:2032, high: 2186, middle: 2109, dup: 1
limit: 131072, low:2110, high: 2186, middle: 2148, dup: 0
limit: 131072, low:2110, high: 2147, middle: 2128, dup: 1
limit: 131072, low:2129, high: 2147, middle: 2138, dup: 1
limit: 131072, low:2139, high: 2147, middle: 2143, dup: 1
limit: 131072, low:2144, high: 2147, middle: 2145, dup: 0
limit: 131072, low:2144, high: 2144, middle: 2144, dup: 1, middle: 2143, dup: 1, middle: 2144, dup2: 1, middle: 2145, dup3: 0, first0: 2145, took 13 minutes, 54 seconds.
limit: 131072
limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 1
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 0
limit: 131072, low:1251, high: 1874, middle: 1562, dup: 1
limit: 131072, low:1563, high: 1874, middle: 1718, dup: 0
limit: 131072, low:1563, high: 1717, middle: 1640, dup: 1
limit: 131072, low:1641, high: 1717, middle: 1679, dup: 0
limit: 131072, low:1641, high: 1678, middle: 1659, dup: 1
limit: 131072, low:1660, high: 1678, middle: 1669, dup: 0
limit: 131072, low:1660, high: 1668, middle: 1664, dup: 0
limit: 131072, low:1660, high: 1663, middle: 1661, dup: 1
limit: 131072, low:1662, high: 1663, middle: 1662, dup: 1
limit: 131072, low:1663, high: 1663, middle: 1663, dup: 1, middle: 1662, dup: 1, middle: 1663, dup2: 1, middle: 1664, dup3: 0, first0: 1664, took 16 minutes, 0 seconds.

limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 0
limit: 131072, low:1, high: 2499, middle: 1250, dup: 666
limit: 131072, low:1251, high: 2499, middle: 1875, dup: 0
limit: 131072, low:1251, high: 1874, middle: 1562, dup: 0
limit: 131072, low:1251, high: 1561, middle: 1406, dup: 252
limit: 131072, low:1407, high: 1561, middle: 1484, dup: 96
limit: 131072, low:1485, high: 1561, middle: 1523, dup: 34
limit: 131072, low:1524, high: 1561, middle: 1542, dup: 15
limit: 131072, low:1543, high: 1561, middle: 1552, dup: 5
limit: 131072, low:1553, high: 1561, middle: 1557, dup: 0
limit: 131072, low:1553, high: 1556, middle: 1554, dup: 3
limit: 131072, low:1555, high: 1556, middle: 1555, dup: 2
limit: 131072, low:1556, high: 1556, middle: 1556, dup: 1, middle: 1555, dup: 2, middle: 1556, dup2: 1, middle: 1557, dup3: 0, first0: 1557, took 25 minutes, 46 seconds.
limit: 131072
limit: 131072, low:1, high: 10000, middle: 5000, dup: 0
limit: 131072, low:1, high: 4999, middle: 2500, dup: 469
limit: 131072, low:2501, high: 4999, middle: 3750, dup: 0
limit: 131072, low:2501, high: 3749, middle: 3125, dup: 0
limit: 131072, low:2501, high: 3124, middle: 2812, dup: 102
limit: 131072, low:2813, high: 3124, middle: 2968, dup: 0
limit: 131072, low:2813, high: 2967, middle: 2890, dup: 24
limit: 131072, low:2891, high: 2967, middle: 2929, dup: 0
limit: 131072, low:2891, high: 2928, middle: 2909, dup: 5
limit: 131072, low:2910, high: 2928, middle: 2919, dup: 0
limit: 131072, low:2910, high: 2918, middle: 2914, dup: 0
limit: 131072, low:2910, high: 2913, middle: 2911, dup: 3
limit: 131072, low:2912, high: 2913, middle: 2912, dup: 2
limit: 131072, low:2913, high: 2913, middle: 2913, dup: 1, middle: 2912, dup: 2, middle: 2913, dup2: 1, middle: 2914, dup3: 0, first0: 2914, took 29 minutes, 26 seconds.

 

 

 

Salasanan salakirjoitus tertussa

Kaikki oikeudet tietenkin pidätetään. Viimeinen versio ohjelmasta löytyy seuraavasta linkistä: moijari.com:5002. Ressu satunnaislukuja löytyy osoitteesta: moijari.com:5001. Ikuinen kalenteri osoitteesta: moijari.com:5003.

Salasana on salakirjoitettuna muodossa:

sha256:ee84f5affcf47d:51567b8c4c20db2a00ea24eaae125a26f5c7c87fdbe5b90a02225fc97008b49a (oheinen salasana on muuten "SalaSana1234") ja vielä
unsigned char *peppper="l7IKrgcLMgsl_4Wv";

Alussa salatun salasanan tallennusformaatti, kaksoispisteen jälkeen alkaa salt:ti ja seuraavan kaksoispisteen jälkeen alkaa kryptattu salasana.

Tämä ensimmäinen rutiini salakirjoittaa password kentässä olevan salasanan ja muodostaa encpasswd kentän joka sisältää salasanan salakirjoitetussa muodossa.

int encryptpasswd(unsigned char encpasswd[ENCPASSWD_SIZE],unsigned char *password)
{
  int c,d;

  HashCtx ctx;

  unsigned char salt[7];
  unsigned char salthex[15];
  unsigned char twodigit[3];
  unsigned char temp[HashLen];
  unsigned char temphex[HashLen*2+1];

  memset(encpasswd,0,ENCPASSWD_SIZE);

  ressu_genbytes2(sizeof(salt),salt);

  ...

Ohjelman alussa varataan erilaisia ohjelman tarvitsemia muistialueita. Salt kentän tarkoituksena on erilaistaa sama salasana eri käyttäjillä ja vaikeuttaa kryptattujen salasanojen hakemiston muodostamista. Salt on salakirjoittamattomana salakirjoitetussa salasanassa , ja kopioidaan sellaisenaan salasanan tarkastukseen. Temp kenttää taas käytetään kryptatun salasanan tallettamiseen..

Seuraavat ohjelmarivit muuttavat salt:in heksamuotoon:

  ...
  for(c=0;c<sizeof(salt);c++) {
    sprintf(twodigit,"%02x",salt[c]);
    strcat(salthex,twodigit);
  }
  ...

Seuraava kappale tekee varsinaisen salakirjoituksen, Salakirjoitus tehdään sha256 tiivisterutiinilla. HASHROUNDS on tällä hetkellä sata tuhatta, ja siihen on valittu mahdollisimman suuri luku, joka silti pitää logon-vaiheen keston kohtuullisena. Pepper on aloitusarvo rutiinille.

  ...
  memset(temp,0,sizeof(temp));
  strcpy(temp,pepper);
  for(c=0;c<HASHROUNDS;c++) {
    HashInit(&ctx);
    HashUpdate(&ctx,temp,sizeof(temp));
    HashUpdate(&ctx,salt,sizeof(salt));
    HashUpdate(&ctx,password,strlen(password)+1);
    HashFinal(temp,&ctx);
  }
  ...

Seuraavassa edellisen koodikappaleen tulos temp muuttujassa laitetaan heksamuotoon:

  ...
  temphex[0]='\0';
  for(c=0;c<sizeof(temp);c++) {
    sprintf(twodigit,"%02x",temp[c]);
    strcat(temphex,twodigit);
  }
  ...

Kootaan salakirjoitettu salasana:

  ...
  strcpy(encpasswd,"sha256:");
  strcat(encpasswd,salthex);
  strcat(encpasswd,":");
  strcat(encpasswd,temphex);
  ...

Ja tyhjennetään vielä välikentät, jottei niitä vuoda ulos aliohjelmasta: ja funktion lopussa vielä return lause (0=ok).

  ...
  memset(salt,0,sizeof(salt));
  memset(salthex,0,sizeof(salthex));
  memset(temp,0,sizeof(temp));
  memset(temphex,0,sizeof(temphex));

  return(0);
}

Salasanan tarkistus funktio tekee oikeastaan samat asiat, hieman muuteltuna: koodikappaleen loppu tarkistaa salakirjoitetun salasanan tallennusformaatin ja  hakee salt:in heksamuodossa. Salt haetaan parametrina annetusta salakirjoitetusta salasanasta.

int checkpasswd(unsigned char encpasswd[ENCPASSWD_SIZE],unsigned char *password)
{
  int c,d,e,byte,ok;
  unsigned char *p,*s;
  HashCtx ctx;

  unsigned char salt[7];
  unsigned char salthex[15];
  unsigned char twodigit[3];
  unsigned char temp[HashLen];
  unsigned char temphex[HashLen*2+1];
  unsigned char encpasswd2[128];

  p=encpasswd;

  if(strncmp(p,"sha256:",7))
    return(1);

  p+=7;
  s=salthex;
  while(*p!=':') {
    *s++=*p++;
  }
  *s='\0';
  ...

Salasanan tarkistusrutiini hakee saltin satunnaisbittigeneraattorin sijasta annetusta salakirjoitetusta salasanasta.

Seuraava siirtää heksana olevan saltin binäärimuotoon:

  ...
  c=0;
  s=salthex;
  for(;;) {
    if((d=gethexdigit(*s++))==-1) break;
    if((e=gethexdigit(*s++))==-1) break;
    byte=d*16+e;
    salt[c++]=byte;
  }
  ...

Varsinainen salasanan kryptausosa on samanlainen kun edellisessä crypt rutiinissa:

  ...
  memset(temp,0,sizeof(temp));
  strcpy(temp,pepper);
  for(c=0;c<HASHROUNDS;c++) {
    HashInit(&ctx);
    HashUpdate(&ctx,temp,sizeof(temp));
    HashUpdate(&ctx,salt,sizeof(salt));
    HashUpdate(&ctx,password,strlen(password)+1);
    HashFinal(temp,&ctx);
  }
  ...

Muokanaan salakirjoitettu salasana temp kentästä temphex kenttään heksamuotoon:

  ...
  temphex[0]='\0';
  for(c=0;c<sizeof(temp);c++) {
    sprintf(twodigit,"%02x",temp[c]);
    strcat(temphex,twodigit);
  }
  ...

Muodostetaan varsinainen salasana salakirjoitetussa muodossa:

  ...
  strcpy(encpasswd2,"sha256:");
  strcat(encpasswd2,salthex);
  strcat(encpasswd2,":");
  strcat(encpasswd2,temphex);
  ...

Vertaillaan salakirjoitettuna annettua salasanaa ja selväkielisestä salasanasta muodostettua salakirjoitettua salasanaa keskenään, tyhjennellään kenttiä ja palautetaan totuusarvo:

  ...
  if(!strcmp(encpasswd,encpasswd2)) {
    fprintf(stdout,"\n*************ok");
    ok=0;
  } else {
    fprintf(stdout,"\n*************error");
    ok=1;
  }

  memset(encpasswd2,0,ENCPASSWD_SIZE);
  memset(salt,0,sizeof(salt));
  memset(salthex,0,sizeof(salthex));
  memset(temp,0,sizeof(temp));
  memset(temphex,0,sizeof(temphex));

  return(ok);
}

Tässä vielä gethexdigit:

int gethexdigit(char h)
{
  int byte;

  if(h>='0' && h<='9')
    byte=h-'0';
  else if(h>='a' && h<='f')
    byte=h-'a'+10;
  else if(h>='A' && h<='F')
    byte=h-'A'+10;
  else
    byte=-1;

  return(byte);
}

 

Kotisivu