RDTSC命令を用いた乱数seedの生成方法

| ← back |
| home |

別の擬似乱数の解説のページで、擬似乱数(xorshift)の初期値(seed)を別の周期の短い擬似乱数(M系列信号)を用いて生成するということをしています.

ではM系列信号のseedはどうやって生成するのだ?という疑問が出てくるのは当然だと思います. 普通は日付・時刻関数などで得た値を初期値として用いることが多いようです.
しかし擬似乱数の用途によっては、そのような初期値/seed生成手法は脆弱性の要因になり得ることを知っているので気持ちが悪い〜という方もいらっしゃるかもしれません.
そのような方のために、プロセッサのRDTSC(ReaD Time Stamp Counter)命令を用いた乱数モドキ、乱数seedの生成方法をご紹介します.

RDTSC命令を用いた実行クロック数計測と乱数seed生成の原理
printf系関数の処理の特徴
  1. (低速デバイスのI/O等を例外として)C言語のライブラリ関数の中でprintf系関数は群を抜いて処理に時間がかかるので、実行クロック数計測に適している. 遅いのでシステム全体の負荷変動の影響も大きく現れるはずである.
  2. printf系関数は引数の値に応じた一種のインタプリタの処理をおこなっているため、遅いだけでなく処理時間(処理クロック数)の揺らぎも大きく、その予測・再現が困難であると考えられる. さまざまなレベルでの最適化がかかりにく、最適化によって処理時間の揺らぎが低下しにくい.
乱数モドキから乱数seedへの変換
RDTSC命令を用いた乱数seedの生成プログラム
/* (c) 2022 cepstrum.co.jp */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define MATH_PI  3.14159265358979323846
#define MATH_E   2.7182818284590452354
#define CLK_TICK 16

//-----------------------------------------------------
// execute RDTSC instruction and return cycle counter value

inline unsigned long long rdtsc(void) {
  unsigned long long result;
  __asm__ volatile ("rdtsc" : "=A" (result));
  return result;
}

//-----------------------------------------------------
// physical(?) random number generator

uint32_t physical_rand32bit(void) {
  uint32_t           y;
  char               s1[80], s2[80];
  unsigned long long start_clk;
  unsigned long long stop_clk;
  unsigned           rnd_bit;
  unsigned           i;

  y=0;
  for (i=0; i<32; i=i+1) {
    start_clk=rdtsc();
    sprintf(s1, "%70.40lf", (double)y*MATH_PI);     // meaningless, waste time...
    sprintf(s2, "%lf", atof(s1)*MATH_E);            // meaningless, waste time...
    sprintf(s1, "%lf", atof(s2)/MATH_PI);           // meaningless, waste time...
    sprintf(s2, "%s", s1);                          // meaningless, waste time...
    stop_clk=rdtsc();
    rnd_bit=((stop_clk-start_clk)/CLK_TICK)%2;
    y=(y<<1)|rnd_bit;
    //printf("%u %u %u\n", (unsigned)(stop_clk-start_clk)/CLK_TICK, (unsigned)stop_clk, (unsigned)start_clk);
  }
  return y;
}

//-----------------------------------------------------
// convert unsigned 32bit value to 16bit seed for RNG
// unsigned 32bit value : 0-4294967295
// seed 16bit value     : 1-65535
// 4294967296=2^32
//      65535=2^16-1

uint16_t rand32bit2seed16bit(uint32_t x) {
  return ((double)x/(double)4294967296UL)*(double)65535.0+1;
}

//-----------------------------------------------------
// test physical_rand32bit() and rand32bit2seed16bit()

int main() {
  unsigned i;
  unsigned rand32bit, seed16bit;

  for (i=0; i<20; i=i+1) {
    rand32bit=physical_rand32bit();
    seed16bit=rand32bit2seed16bit(rand32bit);
    printf("%#010x %5u\n", rand32bit, seed16bit);
  }

  for (i=0; i<100; i=i+1) {
    rand32bit=physical_rand32bit();
    seed16bit=rand32bit2seed16bit(rand32bit);
    printf("%8u", seed16bit);
  }

}
| ← back | ↑top |

| home |