ONC+ 開発ガイド

DES 認証プロトコル (XDR 言語で記述)


例 B-2 AUTH_DES 認証プロトコル

/*
 * 認証証明には 2 種類あります。1 つは完全なネットワーク名を使用する方法で、
 
 * もう 1 つはサーバーがクライアントに割り当てたニックネーム (符号なし整数) を
 * 使用する方法です。クライアントからサーバーへの初めてのトランザクションでは
 * 完全なネットワーク名を使用しなければなりません。それに対して、サーバーは
 * クライアントにニックネームを返します。クライアントはそれ以降、サーバーへの
 * トランザクションでニックネームを使用できます。必ずニックネームを
 * 使用しなければならないわけではありませんが、パフォーマンスから考えても、
 * ニックネームを使用する方がよいでしょう。
 */
enum authdes_namekind {
 	ADN_FULLNAME = 0,
 	ADN_NICKNAME = 1
 };
 
/*
 * 暗号化した DES データのための 64 ビットブロック
 */
 typedef opaque des_block[8];
 
/*
 * ユーザーのネットワーク名の最大長
 */
const MAXNETNAMELEN = 255;
 
/*
 * クライアントのネットワーク名、暗号化された会話鍵、
 * ウィンドウの含まれたフルネーム。
 * ウィンドウは認証証明の有効な時間を示します。
 * ベリファイアに示されたタイムスタンプにウィンドウを加えた時刻が
 * 過ぎている場合は、サーバーは要求を無効として許可しません。要求を
 * 再送信しないために、最初のトランザクション以外は、
 * タイムスタンプが以前の値より大きくないと、サーバーはそれを受け入れません。
 * 最初のトランザクションの場合は、サーバーはウィンドウベリファイアが
 * ウィンドウより 1 小さい値であることを確認します。
 */
struct authdes_fullname {
 	string name<MAXNETNAMELEN>; /* クライアント名 */
 	des_block key;              /* PK で暗号化された会話鍵 */
 	unsigned int window;        /* 暗号化されたウィンドウ */
};                             /* 注: PK は公開鍵の略 */
 
/*
 * 認証証明はフルネームかニックネーム
*/
unionauthdes_credswitch(authdes_namekindadc_namekind){
 	case ADN_FULLNAME:
 		authdes_fullname adc_fullname;
 	case ADN_NICKNAME:
 		unsigned int adc_nickname;
};
 
/*
 * タイムスタンプには、1970 年 1 月 1 日の午前 0 時からの秒数を符号化
 */
struct timestamp {
 	unsigned int seconds;      /* 秒数 */
 	unsigned int useconds;     /* マイクロ秒 */
};
 
/*
 * ベリファイア: クライアント側
 */
struct authdes_verf_clnt {
 	timestamp adv_timestamp;   /* 暗号化されたタイムスタンプ */
 	unsigned int adv_winverf;  /* 暗号化されたウィンドウベリファイア */
};
 
/*
 * ベリファイア: サーバー側
 * サーバーは、クライアントから渡されたタイムスタンプから 1 秒少ない値を 
 * 暗号化して返します。また、クライアントが以降のトランザクションで使用できる 
 * ニックネームを暗号化せずに渡します。
 */
struct authdes_verf_svr {
 	timestamp adv_timeverf;    /* 暗号化されたベリファイア */
 	unsigned int adv_nickname; /* クライアントの新しいニックネーム */
};
 

Diffie-Hellman の暗号化手法

この暗号化手法では、2 つの定数 PROOTHEXMODULUSを使用します。DES 認証プロトコルでは、この 2 つの定数として次の値を使用します。

const PROOT = 3;
const HEXMODULUS =	/* 16 進 */
 "d4a0ba0250b6fd2ec626e7efd637df76c716e22d0944b88b";

この暗号化手法は次の例で説明するとわかりやすいでしょう。ここに A と B という 2 人の人が互いに暗号化したメッセージを送信するとします。Aと B はそれぞれランダムに秘密鍵を生成し、この鍵は誰にも教えません。秘密鍵をそれぞれ SK(A) と SK(B) とします。また、2 人は公開ディレクトリにそれぞれ公開鍵を示します。公開鍵は次のように計算されます。

PK(A) = (PROOT ** SK(A)) mod HEXMODULUS
PK(B) = (PROOT ** SK(B)) mod HEXMODULUS

** という記号はべき乗を表します。

ここで A と B は、互いに秘密鍵を知らせ合うことなく 2 人の間の共通鍵 CK(A,B) を求めることができます。

A は次のように計算します。

CK(A, B) = (PK(B) ** SK(A)) mod HEXMODULUS

B は次のように計算します。

CK(A, B) = (PK(A) ** SK(B)) mod HEXMODULUS

上の 2 つの式から次の等式が得られます。

(PK(B)**SK(A)) mod HEXMODULUS = (PK(A)**SK(B)) mod HEXMODULUS

ここで、mod HEXMODULUS という部分を両辺から取り除いてモジュロ計算を省略し、プロセスを簡単にします。

PK(B) ** SK(A) = PK(A) ** SK(B)

次に、PK(B) を先に B が計算した値で置き換えます。PK(A) も同様に置き換えます。

((PROOT ** SK(B)) ** SK(A) = (PROOT ** SK(A)) ** SK(B)

この式は次のように書き換えられます。

PROOT ** (SK(A) * SK(B)) = PROOT ** (SK(A) * SK(B))

共通鍵 CK(A,B) は、プロトコルで使用されるタイムスタンプの暗号化には使用しません。共通鍵は会話鍵の暗号化にだけ使用し、タイムスタンプの暗号化には会話鍵を使用します。これは、共通鍵を使用する回数をできるだけ少なくして、共通鍵が破られないようにするためです。会話時間は比較的短いため、会話鍵の方が破られる心配がずっと少ないからです。

会話鍵は、56 ビットの DES 鍵を使用して暗号化します。共通鍵は 192 ビットなので、共通鍵から次のようにして 56 ビットを選択し、ビット数を減らします。共通鍵から中央の 8 バイトを選択し、各バイトの下位ビットにパリティを加えます。こうして、8 ビットのパリティの付いた 56 ビット鍵が生成されます。