スポンサーリンク

このドキュメントの内容は、以下の通りです。

はじめに

乱数という言葉は聞いたことがあるでしょうか?乱数とは、ランダムな数ということです。このランダムな数は、いろいろなところで用いられています。

たとえば、ロールプレイングゲームの中で、敵が現れるかどうかは、乱数によって決めることができます。ロールプレイングゲームの中で、敵と戦うときに、攻撃が当たるかどうかも乱数で決めることができますね。キャラクタごとのいろんなパラメータを加味して、計算されるので、乱数だけで決まらないとは思いますが。

みなさんが利用しているインターネットでは、安全性を実現するために、暗号技術を利用しており、暗号技術では、乱数が利用されています。

いろいろなシーンで乱数は必要になります。ソフトウェアを開発するときに、乱数を必要とするケースもあるでしょう。

プログラミング言語ごとに、乱数を得るための関数・メソッド・モジュールなどが用意されています。ゲームならそこまで気にする必要はないかもしれませんが、もし暗号化のために乱数を使うのであれば、 暗号学的に安全な乱数を得る手段を使う必要があり、暗号や乱数の専門家でない限り、自身で実装すべきではないでしょう。

Unix環境における乱数の取得


Unix環境におけるC言語で、乱数を取得するには、rand()関数を利用すると簡単に乱数が得られますが、今回は、/dev/urandomから乱数を取得します。urandomは、 FreeBSD や Linux などの Unix に存在します。

/dev/urandomのほかに/dev/randomもあります。

/dev/random

エントロピーを集めて、プールしておき、乱数を生成します。
十分なデータがない場合、乱数が生成できません。 エントロピーのプールが空の場合は、ブロックされます。

/dev/urandom

エントロピーがより高くなるのを待つためのブロックが行われません。
十分なエントロピーがない場合は、ドライバで使っているアルゴリズムによって乱数が生成されますが、/dev/randomに比べ、脆弱になります

kern.random.sys.seeded


FreeBSDの場合、kern.random.sys.seededが0のときに、十分なエントロピーがないとブロックされます。1のときは、ノンブロッキングです。

urandom と random のどちらを利用すべきか?

Linux においては、 /dev/urandom よりも /dev/random のほうが安全だと言われています。

しかし、前述したとおり、 /dev/random は、ブロックするという特徴があることから、 /dev/urandom が好ましいケースもあります。

Linuxでの/dev/urandomと/dev/random


Linux の1つである Ubuntu の /dev/random と /dev/urandom を ls コマンドで確認しました。以下のように独立したものとなっています。

$ ls -l /dev/*random
crw-rw-rw- 1 root 1, 8  5月 31 12:10 /dev/random
crw-rw-rw- 1 root 1, 9  5月 31 12:10 /dev/urandom

FreeBSDでの/dev/urandomと/dev/random


FreeBSDでは、 /dev/urandom と /dev/random があります。
以下は、FreeBSD で ls コマンドを用いて確認をした結果です。

$ ls -ls /dev/*random
0 crw-rw-rw-  1 root  wheel  0x20  6月  2  2018 /dev/random
0 lrwxr-xr-x  1 root  wheel     6  6月 12  2018 /dev/urandom@ -> random

FreeBSD の /dev/urandom は、 /dev/random のシンボリックリンクになっています。

C言語のサンプルコード


#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>

#define DEV_RANDOM "/dev/urandom"
#define BUF 256

int
get_random (char * const buf, const int buflen, const int len)
{
	if (len > buflen)
	{
		warnx ("buffer size is small (%d / %d)", buflen, len);
		return (-1);
	}
	int fd = open(DEV_RANDOM, O_RDONLY);
	if (fd == -1)
	{
		warn ("can not open %s", DEV_RANDOM);
		return (-1);
	}
	int r = read (fd, buf, len);
	if (r < 0) {
		warn ("can not read");
		return (-1);
	}
	if (r != len)
	{
		warnx ("can not read(%d != %d)", r, len);
		return (-1);
	}

	(void) close (fd);
	return (0);
}

void
dump_hex(const char *p, const int len)
{
	int i = 0;
	for (i = 0; i < len; ++i)
	{
		unsigned char c = p[i];
		printf ("i=%d , %02x\n", i, c);
	}
	printf ("len = %d\n", len);
}

int
main (int argc, char *argv[])
{
	char	buf[BUF];
	int	buflen = sizeof(buf);

	int	len = 4;

	if (get_random(buf, buflen, len) == 0) {
		dump_hex(buf, len);
	}
	(void) printf ("\n");

	exit (EXIT_SUCCESS);
}

コンパイル


コンパイル方法は以下の通りです。
 cc urandom.c

実行例

% ./a.out
i=0 , a5
i=1 , 57
i=2 , ae
i=3 , 91
len = 4
% ./a.out

i=0 , be
i=1 , 48
i=2 , 01
i=3 , e7
len = 4


Linuxのシステムコール getrandom

Linux カーネル 3.17 で gerandom システムコールが追加されました。 getrandom は、 /dev/random か /dev/urandom からデータを読み出す機能を提供します。

getrandom の 第三引数のフラグで、ブロッキングモードや非ブロッキングモードが指定できます。

getrandom システムコールを利用するメリットは、open システムコールや close を呼ぶ必要もなくなるので、コードが短くなることだと思います。

#include <sys/random.h>
#include <stdio.h>
#include <stdlib.h>

#include <unistd.h> //write
int
main(int argc, char *argv[])
{
	ssize_t size;
	unsigned char buf[8];
	size_t buf_size = sizeof(buf);
	int flag = GRND_NONBLOCK;

	size = getrandom(buf, buf_size, flag);
	if(-1 == size) {
		perror ("getrandom");
		exit (1);
	}

	write(1, buf, size);
	return 0;
}

コンパイル方法は以下の通りです。
cc getrandom1.c

バッファの中は、そのまま printf してもわからないので、base64 してから表示します。乱数であるため、毎回実行結果は変わります。
$ ./a.out | base64
wEh6oQu18oI=

スポンサーリンク
スポンサーリンク
 
いつもシェア、ありがとうございます!


もっと情報を探しませんか?

関連記事

最近の記事

人気のページ

スポンサーリンク
 

過去ログ

2020 : 01 02 03 04 05 06 07 08 09 10 11 12
2019 : 01 02 03 04 05 06 07 08 09 10 11 12
2018 : 01 02 03 04 05 06 07 08 09 10 11 12
2017 : 01 02 03 04 05 06 07 08 09 10 11 12
2016 : 01 02 03 04 05 06 07 08 09 10 11 12
2015 : 01 02 03 04 05 06 07 08 09 10 11 12
2014 : 01 02 03 04 05 06 07 08 09 10 11 12
2013 : 01 02 03 04 05 06 07 08 09 10 11 12
2012 : 01 02 03 04 05 06 07 08 09 10 11 12
2011 : 01 02 03 04 05 06 07 08 09 10 11 12
2010 : 01 02 03 04 05 06 07 08 09 10 11 12
2009 : 01 02 03 04 05 06 07 08 09 10 11 12
2008 : 01 02 03 04 05 06 07 08 09 10 11 12
2007 : 01 02 03 04 05 06 07 08 09 10 11 12
2006 : 01 02 03 04 05 06 07 08 09 10 11 12
2005 : 01 02 03 04 05 06 07 08 09 10 11 12
2004 : 01 02 03 04 05 06 07 08 09 10 11 12
2003 : 01 02 03 04 05 06 07 08 09 10 11 12

サイト

Vim入門

C言語入門

C++入門

JavaScript/Node.js入門

Python入門

FreeBSD入門

Ubuntu入門

セキュリティ入門

パソコン自作入門

ブログ

トップ


プライバシーポリシー