次の例で資格を説明します。
/* * 資格は 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; /* クライアントの新しいニックネーム */};
この暗号化手法では、2 つの定数 PROOT と HEXMODULUSを使用します。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 ビット鍵が生成されます。