<!--
    The FreeBSD Documentation Project
    The FreeBSD Japanese Documentation Project

    $FreeBSD$
    Original revision: 1.11
-->
<!DOCTYPE article PUBLIC "-//FreeBSD//DTD DocBook V4.1-Based Extension//EN" [
<!ENTITY % man PUBLIC "-//FreeBSD//ENTITIES DocBook Manual Page Entities//EN">
%man;
]>

<article>
  <articleinfo>
    <title>FreeBSD の IPsec 機能を独立検証するには</title>

    <author>
      <firstname>David</firstname>
      <surname>Honig</surname>

      <affiliation>
        <address><email>honig@sprynet.com</email></address>
      </affiliation>
    </author>

    <pubdate>1999 年 5 月 3 日</pubdate>

    <abstract>
      <para>IPsec をインストールした時、
        それがきちんと動作しているかどうか調べるにはどうしたら良いでしょう?
        ここでは、IPsec の動作を検証する実験的な方法を紹介します。</para>
    </abstract>
  </articleinfo>

  <sect1>
    <title>問題</title>

    <para>まず、<link linkend="ipsec-install"><emphasis>IPsec</emphasis>
        がインストールされていること</link>を前提に話を進めます。
      IPsec が<link linkend="caveat">きちんと動作している</link>かどうか知るにはどうしたら良いでしょう?
      もちろん設定が間違っていればネットワーク接続が行なえないでしょうし、
      接続できたということは設定が合っているからだ、という認識は間違っていません。
      接続状態は &man.netstat.1; コマンドで確かめることができます。
      しかし、それを独立して検証することは可能なのでしょうか?</para>
  </sect1>

  <sect1>
    <title>解決方法</title>

    <para>最初に、暗号に使われている情報理論について考えます。</para>

    <orderedlist>
      <listitem>
	<para>暗号化されたデータは、一様に分布している。つまり、
          各情報源シンボルは最大のエントロピーを持っている。</para>
      </listitem>

      <listitem>
	<para>通常、未処理のデータや圧縮されていないデータは冗長である。
          つまり、各情報源シンボルのエントロピーは最大ではない。</para>
      </listitem>
    </orderedlist>

    <para>ネットワークインターフェイスを入出力するデータのエントロピーを測定できると仮定すると、
      「暗号化されていないデータ」と「暗号化されたデータ」の両者に、
      違いを見ることができるはずです。
      このことは、パケットのルーティングが行なわれる場合の一番外側の IP ヘッダなど、
      データの一部が <quote>暗号化モード</quote> で暗号化されなかったとしても成立します。</para>

    <sect2 id="MUST">
      <title>MUST</title>

      <para>Ueli Maurer 氏の <quote>Universal Statistical Test for Random
	Bit Generators</quote>
        (<ulink url="http://www.geocities.com/SiliconValley/Code/4704/universal.pdf"><acronym>MUST</acronym></ulink>)
        は、サンプルデータのエントロピーを高速に測定します。
        これには圧縮と良く似たアルゴリズムが使われています。
        <link linkend="code">文末に示すのは</link>、
	一つのファイル中で連続するデータ (最大 0.25 メガバイト)
        を測定するコードです。</para>
    </sect2>

    <sect2 id="tcpdump">
      <title>Tcpdump</title>

      <para>さて次に、上記に加えてネットワーク上の生データを捕捉するための手段も必要になります。
      それを実現するプログラムに、&man.tcpdump.1; と呼ばれるものがあります。
      ただし、tcpdump を使うには、
      <link linkend="kernel">カーネルコンフィグレーションファイル</link>において
      <emphasis>Berkeley Packet Filter</emphasis>
      インターフェイスが有効化されていなければなりません。</para>

      <para>次のコマンド:</para>

      <screen><userinput><command>tcpdump</command> -c 4000 -s 10000 -w <replaceable>dumpfile.bin</replaceable></userinput></screen>

      <para>は、4000 個の生パケットを捕捉し、<replaceable>dumpfile.bin</replaceable> に記録します。
      この例のでは 10,000 バイト以下のパケットのみ記録されます。</para>
    </sect2>
  </sect1>

  <sect1>
    <title>実験</title>

    <para>では、実験してみましょう。</para>

    <procedure>
      <step>
	<para>IPsec ホストと IPsec
      を使っていないホストの両方にネットワーク接続してください。</para>
      </step>

      <step>
	<para>そして <link linkend="tcpdump">パケットの捕捉
	  </link>を開始します。</para>
      </step>

      <step>
	<para>次に、<quote>IPsec を使っている</quote> 接続で &man.yes.1; という unix コマンドを実行します。
      これは、<literal>y</literal> という文字の連続データを出力するものです。
      しばらくしたらコマンドを停止させ、IPsec
      を使っていない接続に対して同じコマンドを実行します。
      こちらも、しばらくしたらコマンドを停止させてください。</para>
      </step>

      <step>
	<para>ここで、<link linkend="code">MUST</link>
	を捕捉したパケットに実行すると、次のような出力が得られるはずです。
      この中で重要なのは、期待値 (7.18) に対して、
      IPsec を使った接続が 93% (6.7)、
      <quote>通常の</quote>接続が 29% (2.1)
      という結果になっていることです。</para>

    <screen>&prompt.user; <userinput>tcpdump -c 4000 -s 10000 -w <replaceable>ipsecdemo.bin</replaceable></userinput>
&prompt.user; <userinput>uliscan <replaceable>ipsecdemo.bin</replaceable></userinput>

Uliscan 21 Dec 98
L=8 256 258560
Measuring file ipsecdemo.bin
Init done
Expected value for L=8 is 7.1836656
6.9396 --------------------------------------------------------
6.6177 -----------------------------------------------------
6.4100 ---------------------------------------------------
2.1101 -----------------
2.0838 -----------------
2.0983 -----------------</screen>
      </step>
    </procedure>
  </sect1>

    <sect1 id="caveat">
      <title>注意</title>

    <para>この実験は暗号化の理論が示すとおり、IPsec
      を使った通信では<emphasis>確かに</emphasis>ペイロード中のデータに含まれるシンボルの生起確率が<emphasis>一様に</emphasis>分布する、
      ということを示しています。
      しかし、ここで示した実験ではシステム上の欠陥 (あるのかどうか知りませんが)
      を検出することは<emphasis>できません</emphasis>。
      ここで言う「欠陥」とは、たとえば暗号鍵生成や交換の不備や、
      データや暗号鍵が他人に見られていないかどうかといった問題、
      あるいはアルゴリズムの強度はどうか、
      カーネルのバージョンは合っているかといったことです。
      これらはソースを調べれば確かめることができます。</para>
  </sect1>

  <sect1 id="IPsec">
    <title>IPsec の定義</title>

    <para>インターネットプロトコル セキュリティ拡張
      (Internet Protocol security extensions) は
      IP v4 と IP v6 に適用され、IP v6 への実装は必須となっています。
      このプロトコルは IP (ホスト間) レベルで暗号化と認証を実現するためのものです。
      たとえば SSL は一つのアプリケーションソケット、<application>SSH</application>  はログイン、
      <application>PGP</application> は特定のファイルやメッセージのみに対してそれぞれ安全性を提供しますが、
      IPsec は 2 ホスト間のすべての通信を暗号化します。</para>
  </sect1>

  <sect1 id="ipsec-install">
    <title>IPsec のインストール</title>

    <para>FreeBSD の最近のバージョンでは
      IPsec のサポートが基本のソースコードに含まれています。
      それ故、あなたはおそらく
      <option>IPSEC</option> オプションをカーネルコンフィグファイルに追加し、
      カーネルを再構築/再インストールして &man.setkey.8; コマンドで
      IPsec 接続を設定すればよいはずです。</para>

    <para>FreeBSD で IPsec を実行する包括的なガイドは
      <ulink url="../../books/handbook/ipsec.html">FreeBSD
        ハンドブック</ulink>で提供されています。</para>
  </sect1>

  <sect1 id="kernel">
    <title>src/sys/i386/conf/KERNELNAME</title>

    <para>ネットワークデータを &man.tcpdump.1;
      で補足するためにはカーネルコンフィグファイルには以下の行が必要です。
      追加後 &man.config.8; を実行しカーネルの再構築/再インストールを
      行なってください。</para>

    <programlisting>device	bpf</programlisting>
  </sect1>

  <sect1 id="code">
    <title>Maurer's Universal Statistical Test (ブロックサイズ = 8 ビット)</title>
    
    <para>同一のコードを
      <ulink url="http://www.geocities.com/SiliconValley/Code/4704/uliscanc.txt">
        このリンク</ulink>から入手することができます。</para>

<programlisting>/*
  ULISCAN.c   ---blocksize of 8

  1 Oct 98
  1 Dec 98
  21 Dec 98       uliscan.c derived from ueli8.c

  This version has // comments removed for Sun cc

  This implements Ueli M Maurer's "Universal Statistical Test for Random
  Bit Generators" using L=8

  Accepts a filename on the command line; writes its results, with other
  info, to stdout.

  Handles input file exhaustion gracefully.

  Ref: J. Cryptology v 5 no 2, 1992 pp 89-105
  also on the web somewhere, which is where I found it.

  -David Honig
  honig@sprynet.com

  Usage:
  ULISCAN filename
  outputs to stdout
*/

#define L 8
#define V (1&lt;&lt;L)
#define Q (10*V)
#define K (100   *Q)
#define MAXSAMP (Q + K)

#include &lt;stdio.h&gt;
#include &lt;math.h&gt;

int main(argc, argv)
int argc;
char **argv;
{
  FILE *fptr;
  int i,j;
  int b, c;
  int table[V];
  double sum = 0.0;
  int iproduct = 1;
  int run;

  extern double   log(/* double x */);

  printf("Uliscan 21 Dec 98 \nL=%d %d %d \n", L, V, MAXSAMP);

  if (argc &lt; 2) {
    printf("Usage: Uliscan filename\n");
    exit(-1);
  } else {
    printf("Measuring file %s\n", argv[1]);
  }

  fptr = fopen(argv[1],"rb");

  if (fptr == NULL) {
    printf("Can't find %s\n", argv[1]);
    exit(-1);
  }

  for (i = 0; i &lt; V; i++) {
    table[i] = 0;
  }

  for (i = 0; i &lt; Q; i++) {
    b = fgetc(fptr);
    table[b] = i;
  }

  printf("Init done\n");

  printf("Expected value for L=8 is 7.1836656\n");

  run = 1;

  while (run) {
    sum = 0.0;
    iproduct = 1;

    if (run)
      for (i = Q; run && i &lt; Q + K; i++) {
        j = i;
        b = fgetc(fptr);

        if (b &lt; 0)
          run = 0;

        if (run) {
          if (table[b] &gt; j)
            j += K;

          sum += log((double)(j-table[b]));

          table[b] = i;
        }
      }

    if (!run)
      printf("Premature end of file; read %d blocks.\n", i - Q);

    sum = (sum/((double)(i - Q))) /  log(2.0);
    printf("%4.4f ", sum);

    for (i = 0; i &lt; (int)(sum*8.0 + 0.50); i++)
      printf("-");

    printf("\n");

    /* refill initial table */
    if (0) {
      for (i = 0; i &lt; Q; i++) {
        b = fgetc(fptr);
        if (b &lt; 0) {
          run = 0;
        } else {
          table[b] = i;
        }
      }
    }
  }
}</programlisting>
  </sect1>
</article>