Fort: Satunnaisuutta prosessoreiden rdrand ja rdseed toiminnoilla

Seuraavassa rutiinit rdrand ja rdseed toiminnolla haettavan satunnaisuuden käyttämiseen. Ensimmäiset rutiinit ovat molemmilla yhteisiä: rdrand:ia ja rdseed:iä ei ole otettu oletuksena käyttöön. Cpuid():llä haetaan prosessorin valmistajaa (vendor) ja tietoa siitä onko käytössä olevassa prosessorissa toteutettuna jompikumpi satunnaisbittigeneraattori. Myös is_cpu_vendor():ia käytetään prosessorin toimittajan nimeämiseen.

#define aFORT_USE_RDRAND 2
#define aFORT_USE_RDSEED 2

#if defined FORT_USE_RDRAND || \
    defined FORT_USE_RDSEED

// see: https://software.intel.com/content/www/us/en/develop/articles/intel-digital-random-number-generator-drng-software-implementation-guide.html

void _cpuid(unsigned int leaf, unsigned int subleaf,
            unsigned int *a, unsigned int *b, unsigned int *c, unsigned int *d)
{
  asm volatile("cpuid"
	       : "=a" (*a), "=b" (*b), "=c" (*c), "=d" (*d)
               : "a" (leaf), "c" (subleaf) );
}

int _is_cpu_vendor(unsigned char *cpuvendor)
{
  int ok=0;
  unsigned int a, b, c, d;

  _cpuid(0, 0, &a, &b, &c, &d);

  if(memcmp((char *)(&b), cpuvendor,4)==0 &&
     memcmp((char *)(&d), cpuvendor+4,4)==0 &&
     memcmp((char *)(&c), cpuvendor+8,4)==0)
    ok=1;

  return(ok);
}

#endif

Seuraavassa kuvassa rdrand-toiminnon käyttämiseen tarvittavat rutiinit: has_rdrand tarkistaa onko käytettävässä prosessorissa rdrand-toiminto. rdrand_long() palauttaa long pituisen satunnaisbittijonon. rdrand_bytes() käyttää edellisiä ja palauttaa varsinaisen bittijonon.

#ifdef FORT_USE_RDRAND

int _has_rdrand()
{
  int ok=0;
  unsigned int a, b, c, d;

  _cpuid(1, 0, &a, &b, &c, &d);
  if((c & 0x40000000) == 0x40000000) {
    ok=1;
  }

  return(ok);
}

int _rdrand_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdrand %0; setc %1"
               : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdrand_bytes(int buflen, unsigned char *buf)
{
  int n, ret = 0;
  unsigned long l;

  if(_is_cpu_vendor("GenuineIntel") && _has_rdrand()) {
    fprintf(stdout,"Intel rdrand");
    ret=1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdrand()) {
    fprintf(stdout,"AMD rdrand");
    ret=1;
  }

  if(ret) {
    while(buflen > 0) {
      if((ret = _rdrand_long(&l)) == 0) // 1 ok, 0 fail
        break;
      fprintf(stdout," %016lx",l);
      n = (buflen < sizeof(l) ? buflen : sizeof(l));
      memcpy(buf, (unsigned char *)&l, n);
      buf+=n;
      buflen-=n;
    }
  }
  return(ret);
}

#endif

Seuraavana vuorossa rdseed toiminnon käyttämiseen tarvittavat toiminnot: tässä on samaan tapaan rdrand:in kanssa kolme toimintoa. has_rdseed() tarkistaa onko käytetyssä prosessorissa ko toimintoa. rdseed_long() muodostaa yhden long:in pituisen satunnaisbittijonon. rdseed_bytes() muodostaa asiakkaan antamaan puskuriin halutun pituisen jonon satunnaisbittejä.

#ifdef FORT_USE_RDSEED

int _has_rdseed()
{
  int ok=0;
  unsigned int a, b, c, d;

  _cpuid(7, 0, &a, &b, &c, &d);
  if((b & 0x40000) == 0x40000) {
    ok=1;
  }

  return(ok);
}

int _rdseed_long(unsigned long *therand)
{
  unsigned char ret;

  asm volatile("rdseed %0; setc %1"
               : "=r" (*therand), "=qm" (ret) );

  return(int) ret;
}

int rdseed_bytes(int buflen, unsigned char *buf)
{
  int n, ret = 0;
  unsigned long l;

  if(_is_cpu_vendor("GenuineIntel") && _has_rdseed()) {
    fprintf(stdout,"Intel rdseed");
    ret=1;
  } else if(_is_cpu_vendor("AuthenticAMD") && _has_rdseed()) {
    fprintf(stdout,"AMD rdseed");
    ret=1;
  }

  if(ret) {
    while(buflen > 0) {
      if((ret = _rdseed_long(&l)) == 0) // 1 ok, 0 fail
        break;
      fprintf(stdout," %016lx",l);
      n = (buflen < sizeof(l) ? buflen : sizeof(l));
      memcpy(buf, (unsigned char *)&l, n);
      buf+=n;
      buflen-=n;
    }
  }
  return(ret);
}

#endif

Seuraavissa fort-init:in kappaleissa kutsutaan edellisiä rutiineja ja päivitetään fort:in avainnusta.

  unsigned char hash[HashLen];
  HashCtx hashctx;
  unsigned char temp[64];

#ifdef FORT_USE_RDRAND

  memset(temp,0,sizeof(temp));
  if(rdrand_bytes(sizeof(temp),temp)) {

    HashInit(&hashctx);
    HashUpdate(&hashctx, (unsigned char *) &cvar,
               sizeof(cvar));
    HashUpdate(&hashctx, temp, sizeof(temp));
    HashFinal(hash, &hashctx);

    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    fflush(stdout);
    fort_reseed(sizeof(hash), hash);
    inccvar();
  }

#endif

#ifdef FORT_USE_RDSEED

  memset(temp,0,sizeof(temp));
  if(rdseed_bytes(sizeof(temp),temp)) {

    HashInit(&hashctx);
    HashUpdate(&hashctx, (unsigned char *) &cvar,
               sizeof(cvar));
    HashUpdate(&hashctx, temp, sizeof(temp));
    HashFinal(hash, &hashctx);

    fprintf(stdout,", sha256: ");
    for(int c = 0;c < HashLen; c++) {
      fprintf(stdout,"%02x", hash[c]);
    }
    fprintf(stdout,"\n");
    fflush(stdout);
    fort_reseed(sizeof(hash), hash);
    inccvar();
  }

#endif