メイン | 文字列の操作を行うjavaScript Class »

JavaScriptによるMD5の実装

実力試しにJavaScriptでMD5の実装をしてみた。今後、色々なハッシュを組み込む事を考え、MessageDigestクラスのクラスメソッドとしている。
/*
================================================================================
    Name        :   MessageDigest
    In          :   [none]      
    Out         :   [none]      
    Note        :   メッセージダイジェスト
--------------------------------------------------------------------------------
    Version     :   Ver1.0.0    |   2006/11/18  |   新規作成
--------------------------------------------------------------------------------
    License     :   New BSD license
    URL         :   www.kanasansoft.com
================================================================================
*/

/*--------------------------------------------------------------------------------
    コンストラクタ
--------------------------------------------------------------------------------*/
function MessageDigest(){}

/*--------------------------------------------------------------------------------
    定数
--------------------------------------------------------------------------------*/
MessageDigest.BY_STRING     =   "string";
MessageDigest.BY_ARRAY      =   "array";

///*--------------------------------------------------------------------------------
//  バイト配列に変換する(内部関数)
//--------------------------------------------------------------------------------*/
//MessageDigest._changeParameterToByteArray
//= function(param){
//
//  var rtn;
//
//  switch(param.constructor){
//      case Number:
//          rtn =   MessageDigest._changeNumberToByteArray(param);
//          break;
//      case String:
//          rtn =   MessageDigest._changeStringToByteArray(param);
//          break;
//      default:
//          rtn =   null;
//          break;
//  }
//
//  return rtn;
//
//}

/*--------------------------------------------------------------------------------
    数値をバイト配列に変換する(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest._changeNumberToByteArray
=   function(num){

    //参照渡し破棄
    var numbuf          =   Number(num);

    var ary             =   [];
    //下位桁より配列化
    while(numbuf!=0){
        ary             .   push(numbuf & 0xFF);
        numbuf          =   numbuf >>> 8;
    }
    //バイトを反転
    ary                 .   reverse();

    return ary;

}

/*--------------------------------------------------------------------------------
    文字を文字コード配列に変換する(内部関数)
    (2バイト以上の場合は1バイト毎)
--------------------------------------------------------------------------------*/
MessageDigest._changeStringToByteArray
=   function(str){

    var bytearray           =   [];
    var strlen              =   str.length;

    //上位ワードより配列化
    for(var i=0;i<strlen;i++){
        bytearray           =   bytearray.concat(
                                    MessageDigest._changeNumberToByteArray(
                                        str.charCodeAt(i)
                                    )
                                );
    }

    return bytearray;

}

/*--------------------------------------------------------------------------------
    ビットを回転する(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest._rotateBit
=   function(bit,max,num){
    var mask    =   Math.pow(2,max)-1;
    var buf     =   bit & mask;
    if(false){
    }else if(num>0){
        return (( (buf <<  ( num)) | (buf >>> (max-num)) ) & mask);
    }else if(num<0){
        return (( (buf >>> (-num)) | (buf <<  (max+num)) ) & mask);
    }else if(num==0){
        return buf;
    }
}

/*--------------------------------------------------------------------------------
    MD5本体
--------------------------------------------------------------------------------*/
MessageDigest.MD5
=   function(param,by){

    //MessageDigest参照オブジェクトの作成
    var MD          =   MessageDigest;

    //バイト配列変換
    var charArray   =   MD._changeStringToByteArray(param);

    //バイト配列長と拡張バイト長の取得
    var charLen     =   charArray.length;
    var paddingLen  =   512-((charLen*8+64)%512);

    //拡張バイト補填
    charArray       =   charArray.concat(
                                [   0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ,
                                    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 ].slice(0,paddingLen/8)
                        );

    //バイト長補填
    var lenArray    =   MD._changeNumberToByteArray(charLen*8);
    lenArray        .   reverse();
    lenArray        =   lenArray.concat([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]).slice(0,8);
    charArray       =   charArray.concat(lenArray);

    //レジスタ(32ビット)の初期化
    var wordA       =   0x67452301;
    var wordB       =   0xefcdab89;
    var wordC       =   0x98badcfe;
    var wordD       =   0x10325476;

    //[T]テーブルの作成
    var table       =   MD.MD5._makeTable();

    //バイト配列を64バイト単位で処理
    var charLen64   =   charArray.length;
    for(var i=0;i<charLen64;i+=64){

        //64バイトのデータを4バイト単位で格納
        //(4バイトは下位より格納)
        var numX    =   [];
        for(var j=0;j<16;j++){
            numX[j] =   0;
            for(var k=0;k<4;k++){
                numX[j] +=  charArray[i+j*4+k]*Math.pow(2,k*8);
            }
        }

        //データ退避
        var wordAA      =   wordA;
        var wordBB      =   wordB;
        var wordCC      =   wordC;
        var wordDD      =   wordD;

        //ハッシュ生成(Round 1)
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[0] , 7,table[ 1]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[1] ,12,table[ 2]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[2] ,17,table[ 3]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[3] ,22,table[ 4]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[4] , 7,table[ 5]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[5] ,12,table[ 6]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[6] ,17,table[ 7]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[7] ,22,table[ 8]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[8] , 7,table[ 9]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[9] ,12,table[10]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[10],17,table[11]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[11],22,table[12]);
        wordA   =   MD.MD5._rogicRound1(wordA,wordB,wordC,wordD,numX[12], 7,table[13]);
        wordD   =   MD.MD5._rogicRound1(wordD,wordA,wordB,wordC,numX[13],12,table[14]);
        wordC   =   MD.MD5._rogicRound1(wordC,wordD,wordA,wordB,numX[14],17,table[15]);
        wordB   =   MD.MD5._rogicRound1(wordB,wordC,wordD,wordA,numX[15],22,table[16]);

        //ハッシュ生成(Round 2)
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[1] , 5,table[17]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[6] , 9,table[18]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[11],14,table[19]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[0] ,20,table[20]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[5] , 5,table[21]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[10], 9,table[22]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[15],14,table[23]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[4] ,20,table[24]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[9] , 5,table[25]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[14], 9,table[26]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[3] ,14,table[27]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[8] ,20,table[28]);
        wordA   =   MD.MD5._rogicRound2(wordA,wordB,wordC,wordD,numX[13], 5,table[29]);
        wordD   =   MD.MD5._rogicRound2(wordD,wordA,wordB,wordC,numX[2] , 9,table[30]);
        wordC   =   MD.MD5._rogicRound2(wordC,wordD,wordA,wordB,numX[7] ,14,table[31]);
        wordB   =   MD.MD5._rogicRound2(wordB,wordC,wordD,wordA,numX[12],20,table[32]);

        //ハッシュ生成(Round 3)
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[5] , 4,table[33]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[8] ,11,table[34]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[11],16,table[35]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[14],23,table[36]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[1] , 4,table[37]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[4] ,11,table[38]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[7] ,16,table[39]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[10],23,table[40]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[13], 4,table[41]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[0] ,11,table[42]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[3] ,16,table[43]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[6] ,23,table[44]);
        wordA   =   MD.MD5._rogicRound3(wordA,wordB,wordC,wordD,numX[9] , 4,table[45]);
        wordD   =   MD.MD5._rogicRound3(wordD,wordA,wordB,wordC,numX[12],11,table[46]);
        wordC   =   MD.MD5._rogicRound3(wordC,wordD,wordA,wordB,numX[15],16,table[47]);
        wordB   =   MD.MD5._rogicRound3(wordB,wordC,wordD,wordA,numX[2] ,23,table[48]);

        //ハッシュ生成(Round 4)
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[0] , 6,table[49]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[7] ,10,table[50]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[14],15,table[51]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[5] ,21,table[52]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[12], 6,table[53]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[3] ,10,table[54]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[10],15,table[55]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[1] ,21,table[56]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[8] , 6,table[57]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[15],10,table[58]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[6] ,15,table[59]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[13],21,table[60]);
        wordA   =   MD.MD5._rogicRound4(wordA,wordB,wordC,wordD,numX[4] , 6,table[61]);
        wordD   =   MD.MD5._rogicRound4(wordD,wordA,wordB,wordC,numX[11],10,table[62]);
        wordC   =   MD.MD5._rogicRound4(wordC,wordD,wordA,wordB,numX[2] ,15,table[63]);
        wordB   =   MD.MD5._rogicRound4(wordB,wordC,wordD,wordA,numX[9] ,21,table[64]);

        //退避していたデータを加算
        wordA   +=  wordAA;
        wordB   +=  wordBB;
        wordC   +=  wordCC;
        wordD   +=  wordDD;

        //32ビットへの丸め込み(レジスタのサイズ)
        wordA   =   wordA & 0xffffffff;
        wordB   =   wordB & 0xffffffff;
        wordC   =   wordC & 0xffffffff;
        wordD   =   wordD & 0xffffffff;

    }

    //各々を下位バイト順にした後(反転した後)、順に連結
    var rtnArray    =   [];
    var rtnA    =   MD._changeNumberToByteArray(wordA);
    var rtnB    =   MD._changeNumberToByteArray(wordB);
    var rtnC    =   MD._changeNumberToByteArray(wordC);
    var rtnD    =   MD._changeNumberToByteArray(wordD);
    rtnA.reverse();
    rtnB.reverse();
    rtnC.reverse();
    rtnD.reverse();
    rtnArray        =   rtnArray.concat(rtnA);
    rtnArray        =   rtnArray.concat(rtnB);
    rtnArray        =   rtnArray.concat(rtnC);
    rtnArray        =   rtnArray.concat(rtnD);

    //取得指定方法毎に戻り値処理
    switch(by){
        case MD.BY_ARRAY:
            return rtnArray;
            break;
        case MD.BY_STRING:
        default:
            var rtnArrayLen =   rtnArray.length;
            var rtnString   =   "";
            for(var i=0;i<rtnArrayLen;i++){
                rtnString   +=  ("00"+rtnArray[i].toString(16)).slice(-2);
            }
            return rtnString;
            break;
    }

}

/*--------------------------------------------------------------------------------
    補助関数(内側)(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._rogicF       =   function(x,y,z){return ( ( x) & ( y) ) | ( (~x) & ( z) ) };
MessageDigest.MD5._rogicG       =   function(x,y,z){return ( ( x) & ( z) ) | ( ( y) & (~z) ) };
MessageDigest.MD5._rogicH       =   function(x,y,z){return ( x) ^   ( y) ^ ( z)   };
MessageDigest.MD5._rogicI       =   function(x,y,z){return ( y) ^ ( ( x) | (~z) ) };

/*--------------------------------------------------------------------------------
    補助関数(外側)(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._rogicRound1  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicF(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound2  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicG(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound3  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicH(b,c,d)+K+I),32,s)};
MessageDigest.MD5._rogicRound4  =   function(a,b,c,d,K,s,I){return b+MessageDigest._rotateBit((a+MessageDigest.MD5._rogicI(b,c,d)+K+I),32,s)};

/*--------------------------------------------------------------------------------
    テーブル作成(内部関数)
--------------------------------------------------------------------------------*/
MessageDigest.MD5._makeTable
=   function(){
    var table                   =   [];
    for(var i=0;i<=64;i++){
        table[i]                =   Math.floor(0xffffffff*Math.abs(Math.sin(i)));
    }
    return table;
}

トラックバック

このエントリーのトラックバックURL:
http://www.kanasansoft.com/cgi/mt/mt-tb.cgi/1

コメントを投稿

(いままで、ここでコメントしたことがないときは、コメントを表示する前にこのブログのオーナーの承認が必要になることがあります。承認されるまではコメントは表示されません。そのときはしばらく待ってください。)

Google