MD5メッセージ要約アルゴリズム(日本語訳)

これはMD5メッセージ要約アルゴリズム(The MD5 Message-Digest Algorithm, RFC 1321)を管理人が日本語訳したものです。 この文章の訳が正確であることは一切保証いたしません。 誤訳の指摘・よりよい訳の提案などがありましたら掲示板に書き込んでください。 ここの原文は"RFC 1321 (rfc1321) - The MD5 Message-Digest Algorithm"です。


ネットワーク作業部会
R.リベスト
解説の依頼:1321
MITコンピューター科学研究所
RSAデータセキュリティ社
1992年4月
MD5メッセージ要約アルゴリズム
この文章の位置づけ

この文章はインターネット社会のための情報を提供するものである。 これはインターネットにおける標準を指定するものではない。 この文章の配布に制限はない。

謝辞

我々はドン・コパースミス氏、バート・カリスキー氏、ラルフ・マークル氏、デビッド・チャウム氏、ノアム・ニサン氏の多大な助けとなる解説と提案に感謝したい。

目次
1. 実施概略
1
2. 専門用語と表記
2
3. MD5アルゴリズムの説明
3
4. 概説
6
5. MD4とMD5の差異
6
参照
7
付録A - リファレンス実装
7
安全性の考察
21
著者の住所
21
1. 実施概略

この文章はMD5メッセージ要約アルゴリズムの特徴を述べるものである。 このアルゴリズムは入力データとして任意の長さのメッセージを受け取り、出力データとして入力データの128ビットの「指紋」つまり「メッセージ要約」を生成する。 計算上、同じメッセージ要約を持つ2つのメッセージを生成する、もしくはあらかじめ指定された目標のメッセージ要約を持ついかなるメッセージを生成することも実行不可能であると推測されている。 MD5アルゴリズムはデジタル署名のアプリケーションに用いられる予定である。 その場合、大きいファイルはRSAのような公開鍵暗号方式のもとで秘密鍵により暗号化される前に安全な方法によって「圧縮」されなければならない。

MD5アルゴリズムは32ビットパソコン上で高速に動くように設計された。 加えてMD5アルゴリズムは大きな変換表を必要としない。 このアルゴリズムはかなり簡潔に組むことができる。

MD5アルゴリズムはMD4メッセージ要約アルゴリズム1,2]の拡張である。 MD5はMD4よりわずかに遅いがより「用心深く」設計されている。 MD5は、MD4がおそらく使用のため現在の評論家による批評により正しいとされるよりも速く採用されたと感じられたために設計された。 MD4は非常に速く動くように設計されたために暗号攻撃が成功する危険性にさらしてしまう限界の「縁」にある。 MD5はより大きな究極の安全の可能性のために速度を多少あきらめて少し後退した。 それは多方面の評論家の提案を組み入れ、付加的な最適化を含んでいる。 MD5アルゴリズムは批評および標準として採用されうるために公開の領域に位置している。

OSIに基づいたアプリケーション用のMD5オブジェクト識別子は

md5オブジェクト識別子 ::=
iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}

X.509のアルゴリズム識別子[3]においては、MD5用のパラメーターにはNULL型があるべきである。

2. 専門用語と表記

この文章において「ワード」とは32ビットの量、「バイト」は8ビットの量である。 ビットの連続は自然な方法でバイトの連続であると解釈できる。 その場合それぞれの連続した8ビットの組はそれぞれのバイトの高位(最上位)ビットが最初にきたバイトとして解釈できる。 同様にバイトの連続は32ビットワードの連続として解釈できる。 その場合それぞれの連続した4バイトの組は低位(最下位)のバイトが最初に与えられたワードとして解釈できる。

x_iは「xがiを従える」ことを表すことにする。 もし下付き文字が式であれば、x_{i+1}のようにそれを中括弧で囲む。 同様に^を上付き文字(べき乗)の代わりに使う。 よってx^iはxのi乗を表す。

「+」の符号はワードの加法(すなわち2^32を法とする加法)を表す。 X <<< sはXをsビット分循環的に左に移す(回転させる)ことによって得られる32ビット値を表すことにする。 not(X)はビットにおけるXの補数を表し、X v YはビットにおけるXとYの論理和を表すことにする。 X xor YはビットにおけるXとYの排他的論理和を表し、XYはビットにおけるXとYの論理積を表すことにする。

3. MD5アルゴリズムの説明

入力データとしてbビットのメッセージがあり、私たちはそのメッセージ要約を見つけたいと仮定することから始める。 ここでbは任意の負でない整数である。bは0であるかもしれない、bは8の倍数である必要はない、そして任意の長さであるかもしれない。 次のように書かれたメッセージのビットを想像する。

m_0 m_1 ... m_{b-1}

次の5つのステップを踏むことでそのメッセージのメッセージ要約の計算を行う。

3.1 ステップ1. 埋め合わせビットを付け加える

メッセージはその長さ(ビットで)が512を法として448と一致するように「埋め合わせ」られる(伸ばされる)。 つまりメッセージは512ビットの倍数の長さには64ビット欠ける状態になるように伸ばされる。 埋め合わせは常に、たとえメッセージの長さが既に512を法としたときに448になっていたとしても行われる。

埋め合わせは次のように行われる。 1つの「1」ビットがメッセージに付け加えられ、そして「0」ビットが埋め合わせられたメッセージのビットでの長さが512を法としたときに448になるように付け加えられる。 全部で少なくとも1ビット、多くて512ビットが付け加えられる。

3.2 ステップ2. 長さを付け加える

b(埋め合わせビットが加えられる前のメッセージの長さ)の64ビット表現を前のステップの結果に付け加える。 ありそうにないがbが2^64より大きい場合はbの低位の64ビットのみを使う。 (これらのビットは2つの32ビットワードを付け加えたものであり、前例に従い低位ワードが最初に付け加えられる。)

この時点で(ビットとbを付け加えた後の)結果のメッセージは正確な512ビットの倍数の長さである。 同様にこのメッセージは16の(32ビット)ワードの正確な倍数の長さである。 M[0 ... N-1]はその結果のメッセージのワードを表すことにする。その場合Nは16の倍数である。

3.3 ステップ3. MDバッファーを初期化する

4ワードのバッファーをメッセージ要約の計算に使う。 ここでA,B,C,Dはそれぞれ32ビットの記憶領域である。 これらの記憶領域は次の16進表記の値で初期化される。低位バイトが最初である):

ワードA: 01 23 45 67
ワードB: 89 ab cd ef
ワードC: fe dc ba 98
ワードD: 76 54 32 10
3.4 ステップ4. 16ワードブロックのメッセージを処理する

まずそれぞれが入力データとして3つの32ビットワードを受け取り出力データとして1つの32ビットワードを生成する4つの補助関数を定義する。

F(X,Y,Z) = XY v not(X) Z
G(X,Y,Z) = XZ v Y not(Z)
H(X,Y,Z) = X xor Y xor Z
I(X,Y,Z) = Y xor (X v not(Z))

それぞれのビットの位置でFは条件文の働きをする: もしXならY、そうでなければZ。 関数FはXYとnot(X)Zが決して同じビットの位置に1がないためvの代わりに+を使って定義することもできた。 ビットX,YそしてZのビットが独立しており偏りがないならば、F(X,Y,Z)のそれぞれのビットは独立しており偏りがないということは特筆するのに興味深いことである。

関数G,H,Iは、X,Y,Zの一致するビットが独立しており偏りがないならばG(X,Y,Z)、H(X,Y,Z)、I(X,Y,Z)のそれぞれのビットは独立しており偏りがないというような方法でX,Y,Zのビットから出力データを生成するのに「ビットにおいて並列」に働くという点で関数Fと類似している。 関数Hは入力データのビットにおける「排他的論理和」つまり「パリティ」関数であるということを特筆する。

このステップでは正弦関数から作られた64の要素がある表、T[1 ... 64]を使う。 T[i]は表の第i要素を表すことにする。それはabs(sin(i))の4294967296倍の値の整数部と等しい。その場合iは弧度単位である。 この表の要素は付録に与えられている。

次のようにしなさい。

/* それぞれの16ワードブロックを処理する */
For i = 0 to N/16-1 do

  /* iブロックをXにコピーする */
  For j = 0 to 15 do
    X[j] を M[i*16+j] に定める。
  end /* jのループの終了 */

  /* AをAAに、BをBBに、CをCCに、DをDDに保存する */
  AA = A
  BB = B

  CC = C
  DD = D

  /* ラウンド1 */
  /* [abcd k s i]は次の作業を表すことにする
       a = b + ((a + F(b,c,d) + X[k] + T[i]) <<< s) */
  /* 次の16の作業をしなさい */
  [ABCD  0  7  1]  [DABC  1 12  2]  [CDAB  2 17  3]  [BCDA  3 22  4]
  [ABCD  4  7  5]  [DABC  5 12  6]  [CDAB  6 17  7]  [BCDA  7 22  8]
  [ABCD  8  7  9]  [DABC  9 12 10]  [CDAB 10 17 11]  [BCDA 11 22 12]
  [ABCD 12  7 13]  [DABC 13 12 14]  [CDAB 14 17 15]  [BCDA 15 22 16]

  /* ラウンド2 */
  /* [abcd k s i]は次の作業を表すことにする
       a = b + ((a + G(b,c,d) + X[k] + T[i]) <<< s) */
  /* 次の16の作業をしなさい */
  [ABCD  1  5 17]  [DABC  6  9 18]  [CDAB 11 14 19]  [BCDA  0 20 20]
  [ABCD  5  5 21]  [DABC 10  9 22]  [CDAB 15 14 23]  [BCDA  4 20 24]
  [ABCD  9  5 25]  [DABC 14  9 26]  [CDAB  3 14 27]  [BCDA  8 20 28]
  [ABCD 13  5 29]  [DABC  2  9 30]  [CDAB  7 14 31]  [BCDA 12 20 32]

  /* ラウンド3 */
  /* [abcd k s i]は次の作業を表すことにする
       a = b + ((a + H(b,c,d) + X[k] + T[i]) <<< s) */
  /* 次の16の作業をしなさい */
  [ABCD  5  4 33]  [DABC  8 11 34]  [CDAB 11 16 35]  [BCDA 14 23 36]
  [ABCD  1  4 37]  [DABC  4 11 38]  [CDAB  7 16 39]  [BCDA 10 23 40]
  [ABCD 13  4 41]  [DABC  0 11 42]  [CDAB  3 16 43]  [BCDA  6 23 44]
  [ABCD  9  4 45]  [DABC 12 11 46]  [CDAB 15 16 47]  [BCDA  2 23 48]

  /* ラウンド4 */
  /* [abcd k s i]は次の作業を表すことにする
       a = b + ((a + I(b,c,d) + X[k] + T[i]) <<< s) */
  /* 次の16の作業をしなさい */
  [ABCD  0  6 49]  [DABC  7 10 50]  [CDAB 14 15 51]  [BCDA  5 21 52]
  [ABCD 12  6 53]  [DABC  3 10 54]  [CDAB 10 15 55]  [BCDA  1 21 56]
  [ABCD  8  6 57]  [DABC 15 10 58]  [CDAB  6 15 59]  [BCDA 13 21 60]
  [ABCD  4  6 61]  [DABC 11 10 62]  [CDAB  2 15 63]  [BCDA  9 21 64]

  /* さらに次の加算をしなさい (これは4つの記憶領域それぞれをこのブロックの始まる前の値で増やすものである) */
  A = A + AA
  B = B + BB
  C = C + CC
  D = D + DD

end /* iのループの終了 */
3.5 ステップ5. 出力する

出力データとして生成されるメッセージ要約はA,B,C,Dである。 つまりAの低位バイトで始まりDの高位バイトで終わる。

これでMD5の説明を終わる。 Cによるリファレンス実装は付録にある。

4. 概説

MD5メッセージ要約アルゴリズムは実装が簡単であり、そして「指紋」つまり任意の長さのメッセージのメッセージ要約を生成する。 同じメッセージ要約を持つ2つのメッセージを生成することの難しさは2^64の演算に似ており、与えられたメッセージ要約を持つメッセージを生成することの難しさは2^128の演算に似ていると推測されている。 MD5アルゴリズムは弱点について入念に吟味されてきた。 しかしながらこれは比較的新しいアルゴリズムであり、もちろんこの種のどんな新しい提案にもあることだが、さらなる安全性に関する分析は正当化される。

5. MD4とMD5の差異

次がMD4とMD5の差異である:

1. ラウンド4が追加された。
2. それぞれのステップに独自の付加的な定数がある。
3. ラウンド2の関数gがより対称的にならないようにするために(XY v XZ v YZ)から(XZ v Y not (Z))に変更された。
4. それぞれのステップで前のステップの結果を加えている。これはより速い「なだれ効果」を促進する。
5. ラウンド2とラウンド3で入力されたワードのデータを読み込む順番が、パターンが互いに類似しないように変更された。
6. より速い「なだれ効果」をもたらすようにそれぞれのラウンドで移動量が大体最適化された。 異なるラウンドでの移動は全く異なっている。
参照
[1] R.リベスト、「MD4メッセージ要約アルゴリズム」、REC 1320、MIT・RSAデータセキュリティ社、1992年4月。
[2] R.リベスト、「MD4メッセージ要約アルゴリズム」、A.J.メネジス・S.A.バンストーン編集、暗号学の進展 - クリプト '90会報、303-311ページ、スプリンガー・バーラグ、1991年。
[3] CCITT勧告X.509(1988)、「ディレクトリ - 認証の構成」
付録A - リファレンス実装

この付録にはRSAREFから取られた次のファイルが含まれている: プライバシーエンハンスドメール用暗号ツールキット:

global.h -- 全体のヘッダファイル
md5.h -- MD5用のヘッダファイル
md5c.c -- MD5用のソースコード

RSAREFについてさらに情報が欲しい場合は<rsaref@rsa.com>にメールを送ってください。

付録には次のファイルも含まれている:

mddriver.c -- MD2,MD4,MD5用テストドライバー

このドライバーはデフォルトではMD5用にコンパイルするが、Cコンパイラコマンドライン上でMDを2や4にすればMD2やMD4用にもコンパイルできる。

実装は移植性があり、さまざまなプラットフォーム上で動くべきである。 しかし特定のプラットフォーム上での実装を最適化するのは、実行は読者に任せるが難しいことではない。 例えば32ビットワードのうち最初のアドレスに格納されたバイトが最下位であり整列の制限がない「リトルエンディアン」のプラットフォーム上では、MD5Transform内でのDecodeの呼び出しは型変換に置き換えることができる。

A.1 global.h
/* GLOBAL.H - RSAREF 型と定数
 */

/* コンパイラーが関数の引数プロトタイプをサポートしている場合は、
   PROTOTYPESを1にするべきである。
次はもしPROTOTYPESが既にCコンパイラフラグで定義されていない場合に、
  初期値を0にするものである。
 */
#ifndef PROTOTYPES
#define PROTOTYPES 0
#endif

/* POINTERを汎用ポインタ型と定義する */
typedef unsigned char *POINTER;

/* UINT2を2バイトワードと定義する */
typedef unsigned short int UINT2;

/* UINT4を4バイトワードと定義する */
typedef unsigned long int UINT4;

/* PROTO_LISTはPROTOTYPESが上でどう定義されたかに依存して定義される。
PROTOTYPESを使う場合はPROTO_LISTはリストを返し、
 そうでない場合は空のリストを返す。
 */
#if PROTOTYPES
#define PROTO_LIST(list) list
#else
#define PROTO_LIST(list) ()
#endif
A.2 md5.h
/* MD5.H - MD5C.C用ヘッダファイル
 */

/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.

License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.

License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.

RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.

These notices must be retained in any copies of any part of this
documentation and/or software.

[訳者注:以下は上の文章を仮に訳したものであり正式な効力を持つのは上の英語
の文章です。]

このソフトまたは関数について述べるもしくは参考資料として付ける全ての資
料においてそれが「RSAデータセキュリティ社 MD5メッセージ要約アルゴリズ
ム」と明らかにされている場合に限り、このソフトを複製・使用するライセン
スを許諾する。

二次著作物について述べるもしくは参考資料として付ける全ての資料において
その著作物が「RSAデータセキュリティ社 MD5メッセージ要約アルゴリズムに
由来している」と明らかにされている場合に限り、二次著作物の作成・使用す
るライセンスを許諾する。

RSAデータセキュリティ社はこのソフトの商品性もしくはこのソフトの特定目
的への適合性に関するいかなる表明も行わない。これは明示・暗黙を問わずい
かなる保証もない「そのまま」の状態で配布される。

この文書および(もしくは)ソフトのいかなる部分の複製物にもこれらの注意書
きが書かれていなければならない。
 */

/* MD5コンテキスト */
typedef struct {
  UINT4 state[4];                                     /* 状態(ABCD) */
  UINT4 count[2];         /* 2^64を法としたビット数(下位ビットが先) */
  unsigned char buffer[64];                 /* 入力データのバッファ */
} MD5_CTX;

void MD5Init PROTO_LIST ((MD5_CTX *));
void MD5Update PROTO_LIST
  ((MD5_CTX *, unsigned char *, unsigned int));
void MD5Final PROTO_LIST ((unsigned char [16], MD5_CTX *));
A.3 md5c.c
/* MD5C.C - RSAデータセキュリティ社、MD5メッセージ要約アルゴリズム
 */

/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
rights reserved.

License to copy and use this software is granted provided that it
is identified as the "RSA Data Security, Inc. MD5 Message-Digest
Algorithm" in all material mentioning or referencing this software
or this function.

License is also granted to make and use derivative works provided
that such works are identified as "derived from the RSA Data
Security, Inc. MD5 Message-Digest Algorithm" in all material
mentioning or referencing the derived work.

RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.

These notices must be retained in any copies of any part of this
documentation and/or software.

[訳者注:以下は上の文章を仮に訳したものであり正式な効力を持つのは上の英語
の文章です。]

このソフトまたは関数について述べるもしくは参考資料として付ける全ての資
料においてそれが「RSAデータセキュリティ社 MD5メッセージ要約アルゴリズ
ム」と明らかにされている場合に限り、このソフトを複製・使用するライセン
スを許諾する。

二次著作物について述べるもしくは参考資料として付ける全ての資料において
その著作物が「RSAデータセキュリティ社 MD5メッセージ要約アルゴリズムに
由来している」と明らかにされている場合に限り、二次著作物の作成・使用す
るライセンスを許諾する。

RSAデータセキュリティ社はこのソフトの商品性もしくはこのソフトの特定目
的への適合性に関するいかなる表明も行わない。これは明示・暗黙を問わずい
かなる保証もない「そのまま」の状態で配布される。

この文書および(もしくは)ソフトのいかなる部分の複製物にもこれらの注意書
きが書かれていなければならない。
 */

#include "global.h"
#include "md5.h"

/* MD5Transformルーチン用定数
 */

#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

static void MD5Transform PROTO_LIST ((UINT4 [4], unsigned char [64]));
static void Encode PROTO_LIST
  ((unsigned char *, UINT4 *, unsigned int));
static void Decode PROTO_LIST
  ((UINT4 *, unsigned char *, unsigned int));
static void MD5_memcpy PROTO_LIST ((POINTER, POINTER, unsigned int));
static void MD5_memset PROTO_LIST ((POINTER, int, unsigned int));

static unsigned char PADDING[64] = {
  0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

/* F,G,H,IはMD5の基本関数である
 */
#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))

/* ROTATE_LEFTはxを左にnビット循環させる
 */
#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))

/* FF,GG,HH,IIはラウンド1,2,3,4用の変換関数である
循環は再計算を避けるため加算と分離されている
 */
#define FF(a, b, c, d, x, s, ac) { \
 (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \

 (a) += (b); \
  }
#define GG(a, b, c, d, x, s, ac) { \
 (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define HH(a, b, c, d, x, s, ac) { \
 (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }
#define II(a, b, c, d, x, s, ac) { \
 (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
 (a) = ROTATE_LEFT ((a), (s)); \
 (a) += (b); \
  }

/* MD5の初期化 新しいコンテキストを書き込みMD5の作業を始める
 */
void MD5Init (context)
MD5_CTX *context;                                   /* コンテキスト */
{
  context->count[0] = context->count[1] = 0;
  /* 異様な魅力を持つ初期定数を書き込む
*/
  context->state[0] = 0x67452301;
  context->state[1] = 0xefcdab89;
  context->state[2] = 0x98badcfe;
  context->state[3] = 0x10325476;
}

/* MD5ブロックは作業を最新のものにする 別のメッセージブロックを処理し
  コンテキストを最新のものにしながらMD5メッセージ要約アルゴリズムの作
  業を続ける
 */
void MD5Update (context, input, inputLen)
MD5_CTX *context;                                   /* コンテキスト */
unsigned char *input;                                 /* 入力データ */
unsigned int inputLen;                          /* 入力データの長さ */
{
  unsigned int i, index, partLen;

  /* 64を法としたバイト数を計算する */
  index = (unsigned int)((context->count[0] >> 3) & 0x3F);

  /* ビット数を最新のものにする */
  if ((context->count[0] += ((UINT4)inputLen << 3))

   < ((UINT4)inputLen << 3))
 context->count[1]++;
  context->count[1] += ((UINT4)inputLen >> 29);

  partLen = 64 - index;

  /* 可能な限り多くの回数、変換する
*/
  if (inputLen >= partLen) {
 MD5_memcpy
   ((POINTER)&context->buffer[index], (POINTER)input, partLen);
 MD5Transform (context->state, context->buffer);

 for (i = partLen; i + 63 < inputLen; i += 64)
   MD5Transform (context->state, &input[i]);

 index = 0;
  }
  else
 i = 0;

  /* 入力データを保ったバッファ */
  MD5_memcpy
 ((POINTER)&context->buffer[index], (POINTER)&input[i],
  inputLen-i);
}

/* MD5の仕上げ メッセージ要約を書きコンテキストをゼロにしてMD5メッセ
  ージ要約の作業を終える
 */
void MD5Final (digest, context)
unsigned char digest[16];                         /* メッセージ要約 */
MD5_CTX *context;                                   /* コンテキスト */
{
  unsigned char bits[8];
  unsigned int index, padLen;

  /* ビット数を保存する */
  Encode (bits, context->count, 8);

  /* 64を法として56になるように埋める
*/
  index = (unsigned int)((context->count[0] >> 3) & 0x3f);
  padLen = (index < 56) ? (56 - index) : (120 - index);
  MD5Update (context, PADDING, padLen);

  /* (埋め合わせ前の)長さを付け加える */
  MD5Update (context, bits, 8);

  /* 要約の状態を保存する */
  Encode (digest, context->state, 16);

  /* デリケートな情報のところをゼロにする
*/
  MD5_memset ((POINTER)context, 0, sizeof (*context));
}

/* MD5基本変換 ブロックに基づいてstateを変換する。
 */
static void MD5Transform (state, block)
UINT4 state[4];
unsigned char block[64];
{
  UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];

  Decode (x, block, 64);

  /* ラウンド1 */
  FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
  FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
  FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
  FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
  FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
  FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
  FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
  FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
  FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
  FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
  FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
  FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
  FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
  FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
  FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
  FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

 /*ラウンド2 */
  GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
  GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
  GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
  GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
  GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
  GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
  GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
  GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
  GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
  GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
  GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */

  GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
  GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
  GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
  GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
  GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

  /* ラウンド3 */
  HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
  HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
  HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
  HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
  HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
  HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
  HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
  HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
  HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
  HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
  HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
  HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
  HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
  HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
  HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
  HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */

  /* ラウンド4 */
  II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
  II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
  II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
  II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
  II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
  II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
  II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
  II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
  II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
  II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
  II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
  II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
  II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
  II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
  II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
  II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */

  state[0] += a;
  state[1] += b;
  state[2] += c;
  state[3] += d;

  /* デリケートな情報のところをゼロにする

*/
  MD5_memset ((POINTER)x, 0, sizeof (x));
}

/* 入力データ(UNIT4)を出力データ(unsigned char)にエンコードする
  長さは4の倍数であると見なす
 */
static void Encode (output, input, len)
unsigned char *output;
UINT4 *input;
unsigned int len;
{
  unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4) {
 output[j] = (unsigned char)(input[i] & 0xff);
 output[j+1] = (unsigned char)((input[i] >> 8) & 0xff);
 output[j+2] = (unsigned char)((input[i] >> 16) & 0xff);
 output[j+3] = (unsigned char)((input[i] >> 24) & 0xff);
  }
}

/* 入力データ(unsigned char)を出力データ(UNIT4)にデコードする
  長さは4の倍数であると見なす
 */
static void Decode (output, input, len)
UINT4 *output;
unsigned char *input;
unsigned int len;
{
  unsigned int i, j;

  for (i = 0, j = 0; j < len; i++, j += 4)
 output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
   (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
}

/* メモ: 可能ならば「for loop」を標準のmemcpyと置き換えなさい
 */

static void MD5_memcpy (output, input, len)
POINTER output;
POINTER input;
unsigned int len;
{
  unsigned int i;

  for (i = 0; i < len; i++)

 output[i] = input[i];
}

/* メモ: 可能ならば「for loop」を標準のmemsetと置き換えなさい
 */
static void MD5_memset (output, value, len)
POINTER output;
int value;
unsigned int len;
{
  unsigned int i;

  for (i = 0; i < len; i++)
 ((char *)output)[i] = (char)value;
}
A.4 mddriver.c
/* MDDRIVER.C - MD2,MD4,MD5用のテストドライバー
 */

/* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
rights reserved.

RSA Data Security, Inc. makes no representations concerning either
the merchantability of this software or the suitability of this
software for any particular purpose. It is provided "as is"
without express or implied warranty of any kind.

These notices must be retained in any copies of any part of this
documentation and/or software.

[訳者注:以下は上の文章を仮に訳したものであり正式な効力を持つのは上の英語
の文章です。]

RSAデータセキュリティ社はこのソフトの商品性もしくはこのソフトの特定目
的への適合性に関するいかなる表明も行わない。これは明示・暗黙を問わずい
かなる保証もない「そのまま」の状態で配布される。

この文書および(もしくは)ソフトのいかなる部分の複製物にもこれらの注意書
きが書かれていなければならない。
 */

/* Cコンパイラフラグによって定義されていない場合は下記によりMDの初期値
  はMD5にされる。
 */
#ifndef MD
#define MD MD5
#endif

#include <stdio.h>
#include <time.h>
#include <string.h>
#include "global.h"
#if MD == 2
#include "md2.h"
#endif
#if MD == 4

#include "md4.h"
#endif
#if MD == 5
#include "md5.h"
#endif

/* テストブロックの長さ、テストブロックの数
 */
#define TEST_BLOCK_LEN 1000
#define TEST_BLOCK_COUNT 1000

static void MDString PROTO_LIST ((char *));
static void MDTimeTrial PROTO_LIST ((void));
static void MDTestSuite PROTO_LIST ((void));
static void MDFile PROTO_LIST ((char *));
static void MDFilter PROTO_LIST ((void));
static void MDPrint PROTO_LIST ((unsigned char [16]));

#if MD == 2
#define MD_CTX MD2_CTX
#define MDInit MD2Init
#define MDUpdate MD2Update
#define MDFinal MD2Final
#endif
#if MD == 4
#define MD_CTX MD4_CTX
#define MDInit MD4Init
#define MDUpdate MD4Update
#define MDFinal MD4Final
#endif
#if MD == 5
#define MD_CTX MD5_CTX
#define MDInit MD5Init
#define MDUpdate MD5Update
#define MDFinal MD5Final
#endif

/* メインドライバー

Arguments (may be any combination):
  -sstring - digests string
  -t       - runs time trial
  -x       - runs test script
  filename - digests file
  (none)   - digests standard input
 */
int main (argc, argv)
int argc;

char *argv[];
{
  int i;

  if (argc > 1)
 for (i = 1; i < argc; i++)
   if (argv[i][0] == '-' && argv[i][1] == 's')
     MDString (argv[i] + 2);
   else if (strcmp (argv[i], "-t") == 0)
     MDTimeTrial ();
   else if (strcmp (argv[i], "-x") == 0)
     MDTestSuite ();
   else
     MDFile (argv[i]);
  else
 MDFilter ();

  return (0);
}

/* 文字列を要約し結果を出力する
 */
static void MDString (string)
char *string;
{
  MD_CTX context;
  unsigned char digest[16];
  unsigned int len = strlen (string);

  MDInit (&context);
  MDUpdate (&context, string, len);
  MDFinal (digest, &context);

  printf ("MD%d (\"%s\") = ", MD, string);
  MDPrint (digest);
  printf ("\n");
}

/* TEST_BLOCK_COUNTのTEST_BLOCK_LENバイトブロックを要約する時間を計る
 */
static void MDTimeTrial ()
{
  MD_CTX context;
  time_t endTime, startTime;
  unsigned char block[TEST_BLOCK_LEN], digest[16];
  unsigned int i;

  printf
 ("MD%d time trial. Digesting %d %d-byte blocks ...", MD,
  TEST_BLOCK_LEN, TEST_BLOCK_COUNT);

  /* Initialize block */
  for (i = 0; i < TEST_BLOCK_LEN; i++)
 block[i] = (unsigned char)(i & 0xff);

  /* Start timer */
  time (&startTime);

  /* Digest blocks */
  MDInit (&context);
  for (i = 0; i < TEST_BLOCK_COUNT; i++)
 MDUpdate (&context, block, TEST_BLOCK_LEN);
  MDFinal (digest, &context);

  /* Stop timer */
  time (&endTime);

  printf (" done\n");
  printf ("Digest = ");
  MDPrint (digest);
  printf ("\nTime = %ld seconds\n", (long)(endTime-startTime));
  printf
 ("Speed = %ld bytes/second\n",
  (long)TEST_BLOCK_LEN * (long)TEST_BLOCK_COUNT/(endTime-startTime));
}

/* 参考となる文字列の組を要約し結果を出力する
 */
static void MDTestSuite ()
{
  printf ("MD%d test suite:\n", MD);

  MDString ("");
  MDString ("a");
  MDString ("abc");
  MDString ("message digest");
  MDString ("abcdefghijklmnopqrstuvwxyz");
  MDString
 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
  MDString
 ("1234567890123456789012345678901234567890\
1234567890123456789012345678901234567890");
}

/* ファイルを要約し結果を出力する
 */
static void MDFile (filename)
char *filename;
{
  FILE *file;
  MD_CTX context;
  int len;
  unsigned char buffer[1024], digest[16];

  if ((file = fopen (filename, "rb")) == NULL)
 printf ("%s can't be opened\n", filename);

  else {
 MDInit (&context);
 while (len = fread (buffer, 1, 1024, file))
   MDUpdate (&context, buffer, len);
 MDFinal (digest, &context);

 fclose (file);

 printf ("MD%d (%s) = ", MD, filename);
 MDPrint (digest);
 printf ("\n");
  }
}

/* 標準入力を要約し結果を出力する
 */
static void MDFilter ()
{
  MD_CTX context;
  int len;
  unsigned char buffer[16], digest[16];

  MDInit (&context);
  while (len = fread (buffer, 1, 16, stdin))
 MDUpdate (&context, buffer, len);
  MDFinal (digest, &context);

  MDPrint (digest);
  printf ("\n");
}

/* 16進法でメッセージ要約を出力する
Prints a message digest in hexadecimal.
 */
static void MDPrint (digest)
unsigned char digest[16];
{

  unsigned int i;

  for (i = 0; i < 16; i++)
 printf ("%02x", digest[i]);
}
A.5 テストの組

MD5のテストの組(ドライバーオプション "-x")は下記のように出力されるはずである

MD5 test suite:
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f
MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a
安全性の考察

この文章で論議されたセキュリティーレベルは、MD5と公開鍵方式に基づく非常に安全性の高いハイブリッドデジタル署名計画の実行に十分であると考えられている。

著者の住所
ロナルド.L.リベスト(Ronald L. Rivest)
マサチューセッツ工科大学(Massachusetts Institute of Technology)
コンピューター科学研究室(Laboratory for Computer Science)
NE43-324
545 Technology Square
Cambridge, MA 02139-1986
電話: (617) 253-5880
Eメール: rivest@theory.lcs.mit.edu

ホーム > ぞーき・B・ばやし >