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