ランダムな文字列を生成する

X[n]の持つ重要な性質は、最下位ビットの周期が2、すなわち0と1が交互に現れ、 その隣のビットの周期が4、さらにその隣のビットは8、となり、 最上位ビットの周期は4294967296となることである。 これらのrand( )はxの最上位ビットを無視するので、全体の周期は2147483648になる。

つまりrand(3)を使う奴は素人*1ということであって、乱数の質にこだわるならもっと別の方法を使えということだ。そこで私はurandom(4)を使ってみた。勢いでやった。今は反省している。

ポイントは、urandom(4)から得られためちゃくちゃなcharの列(asciiテーブルのはしっこにあったりprintfするのがめんどい文字もごっちゃに入ってる)を、lettersという文字テーブルを使ってprintableな文字にマッピングしているところ。

//char table which you want to use in output string
char letters[] = "0123456789"
  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  "abcdefghijklmnopqrstuvwxyz"
  "_-";
char* rand_str( unsigned int w){
  int l = strlen(letters);
  int i;
  int src_fd = open( "/dev/urandom", O_RDONLY );
  if( src_fd < 0 )return "";
  //char buf[w+1];
  char * buf = (char*)malloc(sizeof(char)*(w+1) );
  int ret = read( src_fd, buf, w );
  if( ret < 0 ) perror( "SthWrong" );
  buf[w] = '\0';
  char * p;
  char tmp; 
  for(i=0,p=buf;i<w;++i,++p){   //p = buf; *p != '\0'; ++p ){
    /** caution!!!
     * when *p == -128, *p *= -1 does not changes the value of *p
     * because char type can't express +128.
     * -128 is 0x80, because 0xFF - 0x7F = 0x80.
     * 0x7F is 127, but 0x80 is -128. (0x81 is -127).
     **/
    *p %= l;
    if( *p < 0 )*p = - *p;
    assert( 0 <= *p && *p <= l );
    *p = letters[ *p ];
  }
  return buf;
}

/dev/random と /dev/urandom はどちらも安全であり, PGP の鍵や ssh のチャレンジ文字列の生成や, ランダムな数字を必要とする他のアプリケーションで利用できるはずです. これらを入力として数の初期シーケンスを与えても, 攻撃者が次の数を予測することは不可能なはずです. これらの入力から得た数字があらゆる意味において言葉通りランダムで あることを保証するため, 大変な努力が行われてきました.

*1:用途によります、勿論。私のように必要もないのに乱数の質に拘る馬鹿な輩もいいますし、[http://d.hatena.ne.jp/kuenishi/20080419/1208594465:title=APIを使えば十分なケース]もありますし、seedをtime(2)で与えてrand(3)で十分な用途がほとんどだろう