﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-bilicon-文章分类-网上择文</title><link>http://www.cppblog.com/bilicon/category/4783.html</link><description>继续学习中</description><language>zh-cn</language><lastBuildDate>Wed, 02 Jul 2008 20:12:20 GMT</lastBuildDate><pubDate>Wed, 02 Jul 2008 20:12:20 GMT</pubDate><ttl>60</ttl><item><title>Rijndael加密算法的介绍 </title><link>http://www.cppblog.com/bilicon/articles/29454.html</link><dc:creator>bilicon</dc:creator><author>bilicon</author><pubDate>Mon, 06 Aug 2007 14:45:00 GMT</pubDate><guid>http://www.cppblog.com/bilicon/articles/29454.html</guid><wfw:comment>http://www.cppblog.com/bilicon/comments/29454.html</wfw:comment><comments>http://www.cppblog.com/bilicon/articles/29454.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/bilicon/comments/commentRss/29454.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/bilicon/services/trackbacks/29454.html</trackback:ping><description><![CDATA[<span style="FONT-SIZE: 8pt">&nbsp;作者:<span>林祝兴 叶义雄 </span><span lang=EN-US><span>杨国鸿</span></span> </span>
<p><span><span style="FONT-SIZE: 10pt"></p>
<p><span>本文针对</span><span lang=EN-US>Rijndael</span><span>加密算法的数学理论背景，算法的架构，回合的转换，金钥的产生，以及各种攻击破密法等等，做了一些简单的介绍。</span></p>
<p><span>一、简介</span></p>
<p><span>在</span><span lang=EN-US>AES ( Advanced Encryption Standard ) </span><span>的选拔中，从最初的十五个算法，到十个、五个，逐步筛选出适合用来作为下一代加密算法的标准。</span><span lang=EN-US>Rijndael</span><span>在经过了一番时日的考验之后，也一直名列前矛。直至<st1:chsdate w:st="on" IsROCDate="False" IsLunarDate="False" Day="2" Month="10" Year="2005">十月二日</st1:chsdate>，</span><span lang=EN-US>Rijndael</span><span>才脱颖而出，这篇文章便是针对</span><span lang=EN-US>Rijndael</span><span>作简要的介绍。</span></p>
<p><span lang=EN-US>Rijndael</span><span>是一个反复运算的加密算法，它允许可变动的数据区块及金钥的长度。数据区块与金钥长度的变动是各自独立的。</span></p>
<p><span>在</span><span lang=EN-US>Rijndael</span><span>算法中定义了几个名词，分述如下：</span></p>
<p><span lang=EN-US>State</span><span>：在运算过程中所产生的中间值，是一个</span><span lang=EN-US>4</span><span>&#215;</span><span lang=EN-US>Nb</span><span>的矩阵，</span><span lang=EN-US>Nb</span><span>可由数据长度除以</span><span lang=EN-US>32</span><span>位求得，也就是把数据分割成</span><span lang=EN-US>Nb</span><span>个区块。</span></p>
<p><span lang=EN-US>Cipher Key</span><span>：用来做加密运算的金钥，形式是一个</span><span lang=EN-US>4</span><span>&#215;</span><span lang=EN-US>Nk</span><span>的矩阵，</span><span lang=EN-US>Nk</span><span>可由金钥长度除以</span><span lang=EN-US>32</span><span>位求得，也就是把金钥分割成</span><span lang=EN-US>Nk</span><span>个</span><span lang=EN-US>32</span><span>位的子金钥。</span></p>
<p><span>在</span><span lang=EN-US>Rijndael</span><span>算法中，运算的回合数</span><span lang=EN-US>(Nr)</span><span>是由</span><span lang=EN-US>Nb</span><span>及</span><span lang=EN-US>Nk</span><span>所决定的，回合数的变动定义如下表。</span></p>
<p><span lang=EN-US>&nbsp;</span></p>
<div align=center>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nr</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nb=4</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nb=6</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nb=8</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nk=4</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>10</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>12</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>14</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nk=6</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>12</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>12</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>14</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>Nk=8</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>14</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>14</span></p>
            </td>
            <td vAlign=top width=67>
            <p align=center><span lang=EN-US>14</span></p>
            </td>
        </tr>
    </tbody>
</table>
</div>
<p><strong><span>二、</span></strong><strong><span lang=EN-US>Rijndael</span></strong><strong><span>的数学背景</span></strong><strong></strong></p>
<p><span>在</span><span lang=EN-US>Rijndael</span><span>中使用了许多字节层级的运算，而这些运算是以</span><span lang=EN-US>GF(2<sup>8</sup>)</span><span>为基础架构。也有一些采用了</span><span lang=EN-US>4-byte</span><span>的字组运算。在这部分，我们将介绍这些基本的数学原理。</span></p>
<p><strong><span lang=EN-US><span>(1)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span lang=EN-US>GF(2<sup>8</sup>)</span></strong><strong><span>的定义</span></strong><strong></strong></p>
<p><span>假设一个字节</span><span lang=EN-US>b</span><span>由</span><span lang=EN-US>b<sub>7</sub>b<sub>6</sub>b<sub>5</sub>b<sub>4</sub>b<sub>3</sub>b<sub>2</sub>b<sub>1</sub>b<sub>0</sub></span><span>组成，我们可以把这些</span><span lang=EN-US>b<sub>i</sub></span><span>想象成一个</span><span lang=EN-US>7</span><span>次多项式的系数，而这些系数不是</span><span lang=EN-US>0</span><span>就是</span><span lang=EN-US>1</span><span>：</span></p>
<p><span lang=EN-US>b<sub>7</sub> x<sup>7</sup>+ b<sub>6</sub> x<sup>6</sup>+ b<sub>5</sub> x<sup>5</sup>+ b<sub>4</sub> x<sup>4</sup>+ b<sub>3</sub> x<sup>3</sup>+ b<sub>2</sub> x<sup>2</sup>+ b<sub>1</sub> x + b<sub>0</sub></span><span>，</span></p>
<p><span>例如，</span><span lang=EN-US>(57)<sub>16</sub></span><span>的二进制表示法为</span><span lang=EN-US>(0101,0111)<sub>2</sub></span><span>表示成多项式，则为：</span></p>
<p><span lang=EN-US>x<sup>6</sup>+ x<sup>4</sup>+ x<sup>2</sup>+ x + 1 .</span></p>
<p><strong><span lang=EN-US><span>(2)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>加法</span></strong><strong></strong></p>
<p><span>两个多项式的加法，则是定义为相同指数项的系数和再模余</span><span lang=EN-US>2</span><span>，简单的说就是作</span><span lang=EN-US>EXOR</span><span>运算</span><span lang=EN-US>(i.e., 1+1=0)</span><span>。例如：</span></p>
<p><span lang=EN-US>(57)<sub>16</sub>+(83)<sub>16</sub>=(01010111)<sub>2</sub>+(10000011)<sub>2 </sub>= (11010100)<sub>2 </sub>= (D4)<sub>16</sub></span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>或是</span><span lang=EN-US>(x<sup>6</sup>+x<sup>4</sup>+x<sup>2</sup>+x+1) + (x<sup>7</sup>+x+1) = x<sup>7</sup>+x<sup>6</sup>+x<sup>4</sup>+x<sup>2</sup></span></p>
<p><strong><span lang=EN-US><span>(3)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>乘法</span></strong><strong></strong></p>
<p><span>在乘法里面，多项式相乘之后的结果很容易造成溢位的问题，解决溢位的方式是把相乘的结果，再模余一个不可分解的多项式</span><span lang=EN-US>m(x)</span><span>。在</span><span lang=EN-US>Rijndael</span><span>中，定义一个这样子的多项式为</span></p>
<p><span lang=EN-US>m(x)=x<sup>8</sup>+x<sup>4</sup>+x<sup>3</sup>+x+1</span><span>或是</span><span lang=EN-US>(11B)<sub>16</sub></span></p>
<p><span>例如：</span></p>
<p><span lang=EN-US>(57)<sub>16</sub></span><span>‧</span><span lang=EN-US>(83)<sub>16</sub> = </span><span lang=EN-US>( x<sup>6</sup>+ x<sup>4</sup>+ x<sup>2</sup>+ x + 1)</span><span>‧</span><span> </span><span lang=EN-US>( x<sup>7</sup>+ x + 1) = x<sup>13</sup>+ x<sup>11</sup>+ x<sup>9</sup>+ x<sup>8</sup>+ x<sup>7</sup>+x<sup>7</sup>+ x<sup>5</sup>+ x<sup>3</sup>+ x<sup>2</sup>+x+x<sup>6</sup>+ x<sup>4</sup>+ x<sup>2</sup>+ x + 1</span></p>
<p><span lang=EN-US>= (x<sup>13</sup>+ x<sup>11</sup>+ x<sup>9</sup>+ x<sup>8</sup>+ x<sup>6</sup>+ x<sup>5</sup>+ x<sup>4</sup>+ x<sup>3</sup>+ 1+x<sup>13</sup>+ x<sup>11</sup>+ x<sup>9</sup>+ x<sup>8</sup>+ x<sup>6</sup>+ x<sup>5</sup>+ x<sup>4</sup>+ x<sup>3</sup>+ 1) modulo (x<sup>8</sup>+ x<sup>4</sup>+ x<sup>3</sup>+ x + 1)</span></p>
<p><span lang=EN-US>= x<sup>7</sup>+ x<sup>6</sup>+ 1=(C1)<sub>16</sub></span></p>
<p><strong><span lang=EN-US><span>(4)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>乘以</span></strong><strong><span lang=EN-US>x</span></strong><strong></strong></p>
<p><span>若把</span><span lang=EN-US>b(x)</span><span>乘上</span><span lang=EN-US>x</span><span>，得到</span><span lang=EN-US>b<sub>7</sub> x<sup>8</sup>+ b<sub>6</sub> x<sup>7</sup>+ b<sub>5</sub> x<sup>6</sup>+ b<sub>4</sub> x<sup>5</sup>+ b<sub>3</sub> x<sup>4</sup>+ b<sub>2</sub> x<sup>3</sup>+ b<sub>1</sub> x<sup>2</sup> + b<sub>0</sub>x</span><span>。若</span><span lang=EN-US>b<sub>7</sub>=0</span><span>，不会发生溢位问题，答案即是正确的；若</span><span lang=EN-US>b<sub>7</sub>=1</span><span>，发生溢位问题，必须减去</span><span lang=EN-US>m(x)</span><span>。我们可以把这种运算表示为</span><span lang=EN-US>xtime(x)</span><span>，其运算方式为</span><span lang=EN-US>left shift(</span><span>若溢位则和</span><span lang=EN-US>(1B)<sub>16</sub></span><span>做</span><span lang=EN-US>EXOR</span><span>运算</span><span lang=EN-US>)</span><span>，例如：</span><span lang=EN-US>&#8216;57&#8217; &#183; &#8216;13&#8217; = &#8216;FE&#8217;</span></p>
<p><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="&#8217;">02&#8217;</st1:chmetcnv> = xtime(57) = &#8216;AE&#8217;</span></p>
<p><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="4" UnitName="&#8217;">04&#8217;</st1:chmetcnv> = xtime(AE) = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="47" UnitName="&#8217;">47&#8217;</st1:chmetcnv></span></p>
<p><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="8" UnitName="&#8217;">08&#8217;</st1:chmetcnv> = xtime(47) = &#8216;8E&#8217;</span></p>
<p><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="10" UnitName="&#8217;">10&#8217;</st1:chmetcnv> = xtime(8E) = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="&#8217;">07&#8217;</st1:chmetcnv></span></p>
<p><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="13" UnitName="&#8217;">13&#8217;</st1:chmetcnv> = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> &#183; (&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv></span><span>&#8853;</span><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="&#8217;">02&#8217;</st1:chmetcnv></span><span>&#8853;</span><span lang=EN-US>&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="10" UnitName="&#8217;">10&#8217;</st1:chmetcnv>) = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="57" UnitName="&#8217;">57&#8217;</st1:chmetcnv> </span><span>&#8853;</span><span lang=EN-US> &#8216;AE&#8217; </span><span>&#8853;</span><span lang=EN-US> &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="7" UnitName="&#8217;">07&#8217;</st1:chmetcnv> = &#8216;FE&#8217;</span></p>
<p><span lang=EN-US>&nbsp;</span></p>
<p><strong><span>三、</span></strong><strong><span lang=EN-US>Rijndael</span></strong><strong><span>的加密架构</span></strong><strong></strong></p>
<p><span lang=EN-US>Rijndael</span><span>加密算法是由一个</span><span lang=EN-US>initial Round Key addition</span><span>，</span><span lang=EN-US>Nr-1</span><span>个回合运算，及一个</span><span lang=EN-US>final round</span><span>所组成。加密过程以</span><span lang=EN-US>C</span><span>语言伪码叙述如下：</span></p>
<p><span lang=EN-US>Rijndael(State, CipherKey)</span><span lang=EN-US> </span></p>
<p><span lang=EN-US>//state</span><span>表示输入的数据明文，</span></p>
<p><span lang=EN-US>//CipherKey</span><span>表示使用的加密金钥，</span></p>
<p><span lang=EN-US>//ExpandedKey</span><span>表示每个</span><span lang=EN-US>Round</span><span>使用的子金钥。</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>KeyExpansion(CipherKey, ExpandedKey);</span></p>
<p><span lang=EN-US>AddRoundKey(State, ExpandedKey);</span></p>
<p><span lang=EN-US>For ( i=1; i&lt;Nr; i++)</span></p>
<p><span lang=EN-US>Round(State, ExpandedKey+Nb</span><span>&#215;</span><span lang=EN-US>i);</span></p>
<p><span lang=EN-US>FinalRound(State, ExpandedKey+Nb</span><span>&#215;</span><span lang=EN-US>Nr);</span></p>
<p><span lang=EN-US>}</span></p>
<p><span>上述算法中的</span><span lang=EN-US>Key Expansion</span><span>，可以先行计算出来，所以加密过程可以简化为：</span></p>
<p><span lang=EN-US>Rijndael(State,ExpandedKey)</span></p>
<p><span lang=EN-US>//State</span><span>表示输入的数据明文，</span></p>
<p><span lang=EN-US>//ExpandedKey</span><span>表示每个</span><span lang=EN-US>Round</span><span>使用的子金钥。</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>AddRoundKey(State,ExpandedKey);</span></p>
<p><span lang=EN-US>For( i=1 ; i&lt;Nr ; i++ )</span><span lang=EN-US> </span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>Round(State,ExpandedKey + Nb</span><span>&#215;</span><span lang=EN-US>i) ;</span></p>
<p><span lang=EN-US>}</span></p>
<p><span lang=EN-US>FinalRound (State, ExpandedKey + Nb</span><span>&#215;</span><span lang=EN-US>Nr);</span></p>
<p><span lang=EN-US>}</span></p>
<p><span>各个子运算介绍如下。</span></p>
<p><strong><span>回合转换</span></strong><strong><span lang=EN-US>(Round transformation)</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>回合转换包含四个不同的工作，其算法如下：</span></p>
<p><span lang=EN-US>Round(State,RoundKey)</span></p>
<p><span lang=EN-US>//State</span><span>表示输入的数据明文，</span></p>
<p><span lang=EN-US>//RoundKey</span><span>表示每个</span><span lang=EN-US>Round</span><span>使用的子金钥。</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>ByteSub(State);</span></p>
<p><span lang=EN-US>ShiftRow(State);</span></p>
<p><span lang=EN-US>MixColumn(State);</span></p>
<p><span lang=EN-US>AddRoundKey(State,RoundKey);</span></p>
<p><span lang=EN-US>}</span></p>
<p><span lang=EN-US>&nbsp;</span></p>
<p><span lang=EN-US>&nbsp;</span></p>
<p><span>算法中的终止回合</span><span lang=EN-US>(Final round)</span><span>包含下列工作项目：</span></p>
<p><span lang=EN-US>FinalRound(State,RoundKey)</span></p>
<p><span lang=EN-US>//State</span><span>表示输入的数据明文，</span></p>
<p><span lang=EN-US>//RoundKey</span><span>表示每个</span><span lang=EN-US>Round</span><span>使用的子金钥。</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>ByteSub(State) ;</span></p>
<p><span lang=EN-US>ShiftRow(State) ;</span></p>
<p><span lang=EN-US>AddRoundKey(State,RoundKey);</span></p>
<p><span lang=EN-US>}</span></p>
<p><span>以下针对每个回合转换的运算过程，作一个深入的介绍，可以更清楚算法的过程。</span></p>
<p><strong><span lang=EN-US><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>字节取代转换</span></strong><strong><span lang=EN-US>(ByteSub transformation)</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>字节转换是一个以字节为单位的非线性取代运算，取代表</span><span lang=EN-US>(S-Box)</span><span>是经过两个运算过程而建立，并且是可逆的。</span></p>
<p><span>首先找出每个字节在</span><span lang=EN-US>GF(2<sup>8</sup>)</span><span>中的乘法反元素；</span></p>
<p><span>接着经过一个仿射</span><span lang=EN-US>(Affine)</span><span>转换运算，定义如下：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span>字节取代</span><span lang=EN-US>(ByteSub)</span><span>运算对</span><span lang=EN-US>State</span><span>的影响，如下图所示：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span>字节取代</span><span lang=EN-US>(ByteSub)</span><span>转换的反运算：</span></p>
<p><span>计算仿射对应之后的相反运算可得到</span><span lang=EN-US>S<sup>-1</sup>-Box</span><span>，以此</span><span lang=EN-US>S<sup>-1</sup>-Box</span><span>做字节取代</span><span lang=EN-US>(ByteSub)</span><span>即可。</span></p>
<p><strong><span lang=EN-US><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>移列转换</span></strong><strong><span lang=EN-US>( ShiftRow transformation )</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>在这个转换中，</span><span lang=EN-US>State</span><span>的每一列以不同的偏移量做环状位移，第</span><span lang=EN-US>0</span><span>列不动，第一列位移</span><span lang=EN-US>C1</span><span>个字节，第二列位移</span><span lang=EN-US>C2</span><span>个字节，第三列位移</span><span lang=EN-US>C3</span><span>个字节。位移的偏移量</span><span lang=EN-US>C1,C2,C3</span><span>跟区块的数目</span><span lang=EN-US>(Nb)</span><span>有关，定义如下表：</span></p>
<p>
<table cellSpacing=0 cellPadding=0 border=1>
    <tbody>
        <tr>
            <td vAlign=top width=59>
            <p align=center><strong><span lang=EN-US>Nb</span></strong><strong></strong></p>
            </td>
            <td vAlign=top width=58>
            <h1 align=center><span lang=EN-US>C1</span></h1>
            </td>
            <td vAlign=top width=58>
            <h1 align=center><span lang=EN-US>C2</span></h1>
            </td>
            <td vAlign=top width=58>
            <h1 align=center><span lang=EN-US>C3</span></h1>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=59>
            <p align=center><span lang=EN-US>4</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>1</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>2</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>3</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=59>
            <p align=center><span lang=EN-US>6</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>1</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>2</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>3</span></p>
            </td>
        </tr>
        <tr>
            <td vAlign=top width=59>
            <p align=center><span lang=EN-US>8</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>1</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>3</span></p>
            </td>
            <td vAlign=top width=58>
            <p align=center><span lang=EN-US>4</span></p>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p><span>移列转换</span><span lang=EN-US>(ShiftRow)</span><span>运算对于</span><span lang=EN-US>State</span><span>的影响，图示如下：</span><span lang=EN-US> </span></p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span>移列转换</span><span lang=EN-US>(ShiftRow)</span><span>的反运算：</span></p>
<p><span>对第二第三及第四列做</span><span lang=EN-US>Nb-C1,Nb-C2,Nb-C3</span><span>个字节的环状位移即可。</span></p>
<p><strong><span lang=EN-US><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>混行转换</span></strong><strong><span lang=EN-US>(MixColumn transformation)</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>在这个转换中，把</span><span lang=EN-US>State</span><span>当作一个存在</span><span lang=EN-US>GF(2<sup>8</sup>)</span><span>中的多项式。并且对一个固定的多项式</span><span lang=EN-US>c(x)</span><span>作乘法，如果发生溢位，则再模余</span><span lang=EN-US>x<sup>4</sup>+1</span><span>。表示如下：</span></p>
<p><span lang=EN-US>c(x) = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="3" UnitName="&#8217;">03&#8217;</st1:chmetcnv> x<sup>3</sup> + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv> x<sup>2</sup> + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv> x + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="&#8217;">02&#8217;</st1:chmetcnv> .</span></p>
<p><span lang=EN-US>c(x)</span><span>与</span><span lang=EN-US>x<sup>4</sup>+1</span><span>互质，令</span><span lang=EN-US>b(x) = c(x) </span><span lang=EN-US><span>&#196;</span><span lang=EN-US> a(x)</span><span>，以矩阵乘法表示如下：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span lang=EN-US>State</span><span>经过混行</span><span lang=EN-US>(MixColumn)</span><span>运算之后的变化如下：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span>混行</span><span lang=EN-US>(MixColumn)</span><span>转换的反运算，则是乘上一个特殊的多项式</span><span lang=EN-US>d(x)</span><span>，</span></p>
<p><span lang=EN-US>(&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="3" UnitName="&#8217;">03&#8217;</st1:chmetcnv>x<sup>3</sup> + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv>x<sup>2</sup> + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv>x + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="2" UnitName="&#8217;">02&#8217;</st1:chmetcnv> ) </span><span lang=EN-US><span>&#196;</span><span lang=EN-US> d(x) = &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv>,</span></p>
<p><span lang=EN-US>d(x) = &#8216;0B&#8217;x<sup>3</sup> + &#8216;0D&#8217;x<sup>2</sup> + &#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="9" UnitName="&#8217;">09&#8217;</st1:chmetcnv>x + &#8216;0E&#8217; .</span></p>
<p><strong><span lang=EN-US><span>4.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span lang=EN-US>The Round Key Addition</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>这个运算主要是把每一个回合金钥</span><span lang=EN-US>(Round Key)</span><span>透过简单的</span><span lang=EN-US>bitwise EXOR</span><span>加入到每一个</span><span lang=EN-US>State</span><span>中，以图示如下：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><strong><span>四、金钥的排程</span></strong><strong><span lang=EN-US>(Key Schedule)</span></strong><strong></strong></p>
<p><span>回合金钥</span><span lang=EN-US>(Round Key)</span><span>是从加密金钥</span><span lang=EN-US>(Cipher Key)</span><span>经过运算产生出来的。金钥排程</span><span lang=EN-US>(Key Schedule)</span><span>是由金钥扩充</span><span lang=EN-US>(Key Expansion)</span><span>及回合金钥的选择</span><span lang=EN-US>(Round Key Selection)</span><span>组成的，基本的理论如下：</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>所有回合金钥的总位数是把区块长度</span><span lang=EN-US>(block length)</span><span>乘上回合数加</span><span lang=EN-US>1</span><span>，</span><span lang=EN-US>(</span><span>有</span><span lang=EN-US>Nr-1</span><span>个回合，加上一个终止回合</span><span lang=EN-US>(final round))</span><span>，例如，</span><span lang=EN-US>128</span><span>个位的区块长度经过</span><span lang=EN-US>10</span><span>个回合运算，所需要用到的所有回合金钥的总位数为</span><span lang=EN-US>1408</span><span>个位。</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>加密金钥</span><span lang=EN-US>(Cipher Key)</span><span>必须扩充为扩充金钥</span><span lang=EN-US>(Expanded Key)</span><span>。</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>回合金钥是从扩充金钥中选出来的，选择的方式如下：</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>第一个回合金钥由前</span><span lang=EN-US>Nb</span><span>个字组组成，第二个回合金钥由接下来的</span><span lang=EN-US>Nb</span><span>个字组组成，余此类推。</span></p>
<p><strong><span lang=EN-US><span>(1)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>金钥的扩充</span></strong><strong><span lang=EN-US>( Key Expansion )</span></strong><strong><span>：</span></strong><strong></strong></p>
<p><span>扩充后的金钥是一个</span><span lang=EN-US>4-byte</span><span>的线性数组，表示为</span><span lang=EN-US>W[Nb</span><span>&#215;</span><span lang=EN-US>(Nr+1)]</span><span>。前</span><span lang=EN-US>Nk</span><span>个字组包含了加密金钥</span><span lang=EN-US>(Cipher Key)</span><span>。</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>金钥扩充函式和</span><span lang=EN-US>Nk</span><span>是息息相关的，分为两种情况运作，一是当</span><span lang=EN-US>Nk</span><span>小于或等于</span><span lang=EN-US>6</span><span>，另外则是当</span><span lang=EN-US>Nk</span><span>大于</span><span lang=EN-US>6</span><span>，以伪码叙述如下：</span></p>
<p><span>当</span><span lang=EN-US>Nk</span><span>≦</span><span lang=EN-US>6</span><span>时，</span></p>
<p><span lang=EN-US>KeyExpansion(byte Key[4</span><span>&#215;</span><span lang=EN-US>Nk] word W[Nb</span><span>&#215;</span><span lang=EN-US>(Nr+1)])</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>for(i = 0; i &lt; Nk; i++)</span></p>
<p><span lang=EN-US>W[i] = (Key[4</span><span>&#215;</span><span lang=EN-US>i], Key[4</span><span>&#215;</span><span lang=EN-US>i+1], Key[4</span><span>&#215;</span><span lang=EN-US>i+2], Key[4</span><span>&#215;</span><span lang=EN-US>i</span><span lang=EN-US>+3] );</span></p>
<p><span lang=EN-US>for(i = Nk; i &lt; Nb</span><span>&#215;</span><span lang=EN-US>(Nr + 1); i++)</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>temp = W[i - 1];</span></p>
<p><span lang=EN-US>if (i % Nk == 0)</span></p>
<p><span lang=EN-US>temp = SubByte(RotByte(temp)) ^ Rcon[i / Nk];</span></p>
<p><span lang=EN-US>W[i] = W[i - Nk] ^ temp;</span></p>
<p><span lang=EN-US>}</span></p>
<p><span lang=EN-US>}</span></p>
<p><span>在上面的子程序中，</span><span lang=EN-US>SubByte(W)</span><span>传回一个</span><span lang=EN-US>4-byte</span><span>的字组，这些字组是输入的字组经过</span><span lang=EN-US>S-box</span><span>的转换所产生的相对字组。</span><span lang=EN-US>RotByte(W)</span><span>则是传回经过旋转的字组。</span></p>
<p><span>当</span><span lang=EN-US>Nk</span><span>＞</span><span lang=EN-US>6</span><span>时，</span></p>
<p><span lang=EN-US>KeyExpansion(byte Key[4</span><span>&#215;</span><span lang=EN-US>Nk] word W[Nb</span><span>&#215;</span><span lang=EN-US>(Nr+1)])</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>for(i = 0; i &lt; Nk; i++)</span></p>
<p><span lang=EN-US>W[i] = <span>&nbsp;</span>(key[4</span><span>&#215;</span><span lang=EN-US>i],key[4</span><span>&#215;</span><span lang=EN-US>i+1], key[4</span><span>&#215;</span><span lang=EN-US>i+2], key[4</span><span>&#215;</span><span lang=EN-US>i+3] );</span></p>
<p><span lang=EN-US>for(i = Nk; i &lt; Nb</span><span>&#215;</span><span lang=EN-US>(Nr + 1); i++)</span></p>
<p><span lang=EN-US>{</span></p>
<p><span lang=EN-US>temp = W[i - 1];</span></p>
<p><span lang=EN-US>if (i % Nk == 0)</span></p>
<p><span lang=EN-US>temp = SubByte(RotByte(temp)) ^ Rcon[i / Nk];</span></p>
<p><span lang=EN-US>else if (i % Nk == 4)</span></p>
<p><span lang=EN-US>temp = SubByte(temp);</span></p>
<p><span lang=EN-US>W[i] = W[i - Nk] ^ temp;</span></p>
<p><span lang=EN-US>}</span></p>
<p><span lang=EN-US>}</span></p>
<p><span>以上两种情况的相异处在于当</span><span lang=EN-US>Nk</span><span>≦</span><span lang=EN-US>6</span><span>时，</span><span lang=EN-US>(i-4)</span><span>是</span><span lang=EN-US>Nk</span><span>的倍数时，对于</span><span lang=EN-US>W[i-1]</span><span>先执行</span><span lang=EN-US>SubByte</span><span>，再执行</span><span lang=EN-US>EXOR</span><span>。</span></p>
<p><span>上述回合常数定义如下：</span></p>
<p><span lang=EN-US>Rcon[i] = (RC[i],&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="&#8217;">00&#8217;</st1:chmetcnv>,&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="&#8217;">00&#8217;</st1:chmetcnv>,&#8216;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="0" UnitName="&#8217;">00&#8217;</st1:chmetcnv>)</span><span>，其中</span><span lang=EN-US>RC[0]=&#8217;<st1:chmetcnv w:st="on" TCSC="0" NumberType="1" Negative="False" HasSpace="False" SourceValue="1" UnitName="&#8217;">01&#8217;</st1:chmetcnv></span><span>，</span><span lang=EN-US>RC[i]=xtime(Rcon[i-1])</span><span>。</span><strong></strong></p>
<p><strong><span lang=EN-US><span>(2)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span>选择回合金钥</span></strong><strong><span lang=EN-US>(Round Key Selection)</span></strong><strong></strong></p>
<p><span>第</span><span lang=EN-US>i</span><span>个回合金钥是指在存在回合金钥缓冲区的字组</span><span lang=EN-US>W[Nb*i]</span><span>到</span><span lang=EN-US>W[Nb*(i+1)]</span><span>，图示如下：</span></p>
<p>&#160;</p>
<p><span lang=EN-US>(</span><span>本图摘录自参考文献</span><span lang=EN-US>[1])</span></p>
<p><span>五、安全性分析</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>我们针对以下已知的攻击法对</span><span lang=EN-US>Rijndael</span><span>的安全性分析作一简要叙述，包括差分攻击法</span><span lang=EN-US>(Differential Cryptanalysis)</span><span>，线性攻击法</span><span lang=EN-US>(Linear Cryptanalysis)</span><span>，平方攻击法</span><span lang=EN-US>(The Square Attack)</span><span>，内插攻击法</span><span lang=EN-US>(Interpolation attacks)</span><span>等攻击方式。</span></p>
<p><span lang=EN-US><span>(1)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>差分攻击法</span><span lang=EN-US>( Differential Cryptanalysis )</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>此攻击法是一种</span><span lang=EN-US>Chosen-plaintext attack</span><span>，利用大量已知的明文</span><span lang=EN-US>/</span><span>密文对之间的差异，据以推测出金钥的位值。在大部分的回合运算中</span><span lang=EN-US>(</span><span>回合数超过</span><span lang=EN-US>3)</span><span>，若存在超过</span><span lang=EN-US>2<sup>1-n</sup>(n</span><span>指的是区块长度</span><span lang=EN-US>)</span><span>比例的可预测性的差异，这个攻击法就可以推测出金钥的位值。在</span><span lang=EN-US>Rijndael</span><span>中，已经证明在经过</span><span lang=EN-US>Rijndael</span><span>四个回合的运算后，存在不超过</span><span lang=EN-US>2<sup>-150</sup></span><span>比例的可预测性差异，在八个回合运算中不超过</span><span lang=EN-US>2<sup>-300</sup></span><span>。详细证明过程，请参照参考文献。</span></p>
<p><span lang=EN-US><span>(2)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>线性攻击法</span><span lang=EN-US>( Linear Cryptanalysis )</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这是一种</span><span lang=EN-US>Known-plaintext</span><span>攻击法，利用大量搜集到的明文</span><span lang=EN-US>/</span><span>密文对的相关性，对加密法进行攻击。明文</span><span lang=EN-US>/</span><span>密文对的相关性由线性轨迹</span><span lang=EN-US>(Linear trails)</span><span>所组成，由于线性轨迹的相关系数与</span><span lang=EN-US>Round keys</span><span>的值有密切关系，透过相关系数的正负号，线性攻击法就可以找出金钥值。要对抗这种攻击法，有一个必要条件就是使这种相关系数大于</span><span lang=EN-US>2<sup>n/2</sup></span><span>的线性轨迹不存在。在</span><span lang=EN-US>Rijndael</span><span>中，已经证明出当执行四个回合时，不存在相关系数大于</span><span lang=EN-US>2<sup>-75</sup></span><span>的线性轨迹；在执行八个回合时，其相关系数大于</span><span lang=EN-US>2<sup>-150</sup></span><span>的相关系数亦不存在。详细证明过程请参照参考文献。</span></p>
<p><span lang=EN-US><span>(3)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>平方攻击法</span><span lang=EN-US>( The Square attack )</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>这种攻击法是一种</span><span lang=EN-US>chosen- plaintext attack</span><span>，而且和字节取代</span><span lang=EN-US>(ByteSub)</span><span>，混行</span><span lang=EN-US>(MixColumn)</span><span>时的多项式乘法，金钥的排程</span><span lang=EN-US>(Key Schedule)</span><span>等运算无关。当</span><span lang=EN-US>Rijndael</span><span>执行</span><span lang=EN-US>6</span><span>个回合以上时，此种方式比完全的金钥搜寻</span><span lang=EN-US>(exhaustive key search)</span><span>来的更有效率。关于此种攻击方式的详尽描述及</span><span lang=EN-US>Rijndael</span><span>如何延伸此种攻击方式，请参照参考文献。</span></p>
<p><span lang=EN-US><span>(4)<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>内插攻击法</span><span lang=EN-US>( Interpolation attacks )</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>在这种攻击法中，攻击者利用加密的输入及输出配对，建立一些多项式。如果加密的组件有一个简洁的代数展开式，并且和管理的复杂度结合在一起时，这种攻击法便是可行的。基本的攻击方式是如果攻击者建立的代数展开式的阶度</span><span lang=EN-US>(degree)</span><span>很小，只需要一些加密法的输入及输出配对就可以得到代数展开式的各项系数。然而，在</span><span lang=EN-US>GF(2<sup>8</sup>)</span><span>中的取代矩阵</span><span lang=EN-US>(S-box)</span><span>，它的展开式为：</span><span lang=EN-US>63+8fx<sup>127</sup>+b5x<sup>191</sup>+01x<sup>223</sup>+f4x<sup>239</sup>+25x<sup>247</sup>+f9x<sup>251</sup>+09x<sup>253</sup>+05x<sup>254</sup></span><span>。其余介绍，请参照参考文献。</span></p>
<p><span lang=EN-US>(5)</span><span>、弱金钥</span><span lang=EN-US>(Weak keys)</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>关于弱金钥的发生，基本上是因为加密法的非线性运算与实际金钥值有密切关系。而这种问题不存在于</span><span lang=EN-US>Rijndael</span><span>之中，因为在</span><span lang=EN-US>Rijndael</span><span>中，金钥是以</span><span lang=EN-US>EXOR</span><span>运算，而所有的非线性运算都定义在取代矩阵</span><span lang=EN-US>(S-box)</span><span>中。在</span><span lang=EN-US>Rijndael</span><span>中，对金钥的选择，是没有限制的。</span></p>
<p><span>六、结论：</span></p>
<p><span lang=EN-US><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span>以上对</span><span lang=EN-US>Rijndael</span><span>作一简要介绍之后，我们以</span><span lang=EN-US>Rijndael</span><span>的优点与限制作为我们的结论。</span></p>
<p><span lang=EN-US>(1)</span><span>、</span><span lang=EN-US>Rijndael</span><span>有以下优点</span><span lang=EN-US>—</span></p>
<p><span>以实作观点而言</span></p>
<p><span lang=EN-US><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang=EN-US>Rijndael</span><span>可以实作在</span><span lang=EN-US>Pentium ( Pro ) </span><span>等计算机上，并已相当快的速度处理运算；而在表格大小与效率之间是可以做取舍的。</span></p>
<p><span lang=EN-US><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span lang=EN-US>Rijndael</span><span>可以实作在智能卡</span><span lang=EN-US>(Smart Card)</span><span>上，使用少量的</span><span lang=EN-US>RAM</span><span>，少量的程序代码；在</span><span lang=EN-US>ROM</span><span>与效率之间也是可以做取舍的。</span></p>
<p><span lang=EN-US><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>在设计上，回合的转换是可平行处理的。</span></p>
<p><span lang=EN-US><span>4.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>加密法不采用算术运算，不会因为不同处理器架构而有所偏差。</span></p>
<p><span>设计简单化：</span></p>
<p><span lang=EN-US><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>设计上不引用其它加密组件，如</span><span lang=EN-US>S-box</span><span>。</span></p>
<p><span lang=EN-US><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>安全度不建立在一些分析不够明确的算术运算之上。</span></p>
<p><span lang=EN-US><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>加密法紧凑，不易藏入暗门等程序代码。</span></p>
<p><span>除此之外，</span><span lang=EN-US>Rijndael</span><span>更允许可变动的区块长度及金钥长度，其长度可由</span><span lang=EN-US>128</span><span>位到</span><span lang=EN-US>256</span><span>位之间；所以回合数也是可变动的。</span></p>
<p><span lang=EN-US>(2)Rijndael</span><span>的限制：</span></p>
<p><span>在解密过程中有以下限制</span></p>
<p><span lang=EN-US><span>1.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>实作在智慧卡时，解密不如加密来的有效率，解密需要更多的程序代码及</span><span lang=EN-US>cycles</span><span>，但是跟其它算法比起来，仍然是快速的。</span></p>
<p><span lang=EN-US><span>2.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>以软件而言，加密和解密使用不同的程序和表格。</span></p>
<p><span lang=EN-US><span>3.<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span><span>以硬件而言，解密只能重用部分加密的电路。</span></span></p>
<p></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></p>
<img src ="http://www.cppblog.com/bilicon/aggbug/29454.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/bilicon/" target="_blank">bilicon</a> 2007-08-06 22:45 <a href="http://www.cppblog.com/bilicon/articles/29454.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>MD5~RFC 1321</title><link>http://www.cppblog.com/bilicon/articles/29263.html</link><dc:creator>bilicon</dc:creator><author>bilicon</author><pubDate>Fri, 03 Aug 2007 04:35:00 GMT</pubDate><guid>http://www.cppblog.com/bilicon/articles/29263.html</guid><wfw:comment>http://www.cppblog.com/bilicon/comments/29263.html</wfw:comment><comments>http://www.cppblog.com/bilicon/articles/29263.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/bilicon/comments/commentRss/29263.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/bilicon/services/trackbacks/29263.html</trackback:ping><description><![CDATA[<pre>
Network Working Group                                          R. Rivest
Request for Comments: 1321           MIT Laboratory for Computer Science
and RSA Data Security, Inc.
April 1992
The MD5 Message-Digest Algorithm
Status of this Memo
This memo provides information for the Internet community.  It does
not specify an Internet standard.  Distribution of this memo is
unlimited.
Acknowlegements
We would like to thank Don Coppersmith, Burt Kaliski, Ralph Merkle,
David Chaum, and Noam Nisan for numerous helpful comments and
suggestions.
Table of Contents
1. Executive Summary                                                1
2. Terminology and Notation                                         2
3. MD5 Algorithm Description                                        3
4. Summary                                                          6
5. Differences Between MD4 and MD5                                  6
References                                                          7
APPENDIX A - Reference Implementation                               7
Security Considerations                                            21
Author's Address                                                   21
1. Executive Summary
This document describes the MD5 message-digest algorithm. The
algorithm takes as input a message of arbitrary length and produces
as output a 128-bit "fingerprint" or "message digest" of the input.
It is conjectured that it is computationally infeasible to produce
two messages having the same message digest, or to produce any
message having a given prespecified target message digest. The MD5
algorithm is intended for digital signature applications, where a
large file must be "compressed" in a secure manner before being
encrypted with a private (secret) key under a public-key cryptosystem
such as RSA.
Rivest                                                          [Page 1]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
The MD5 algorithm is designed to be quite fast on 32-bit machines. In
addition, the MD5 algorithm does not require any large substitution
tables; the algorithm can be coded quite compactly.
The MD5 algorithm is an extension of the MD4 message-digest algorithm
1,2]. MD5 is slightly slower than MD4, but is more "conservative" in
design. MD5 was designed because it was felt that MD4 was perhaps
being adopted for use more quickly than justified by the existing
critical review; because MD4 was designed to be exceptionally fast,
it is "at the edge" in terms of risking successful cryptanalytic
attack. MD5 backs off a bit, giving up a little in speed for a much
greater likelihood of ultimate security. It incorporates some
suggestions made by various reviewers, and contains additional
optimizations. The MD5 algorithm is being placed in the public domain
for review and possible adoption as a standard.
For OSI-based applications, MD5's object identifier is
md5 OBJECT IDENTIFIER ::=
iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
In the X.509 type AlgorithmIdentifier [3], the parameters for MD5
should have type NULL.
2. Terminology and Notation
In this document a "word" is a 32-bit quantity and a "byte" is an
eight-bit quantity. A sequence of bits can be interpreted in a
natural manner as a sequence of bytes, where each consecutive group
of eight bits is interpreted as a byte with the high-order (most
significant) bit of each byte listed first. Similarly, a sequence of
bytes can be interpreted as a sequence of 32-bit words, where each
consecutive group of four bytes is interpreted as a word with the
low-order (least significant) byte given first.
Let x_i denote "x sub i". If the subscript is an expression, we
surround it in braces, as in x_{i+1}. Similarly, we use ^ for
superscripts (exponentiation), so that x^i denotes x to the i-th
power.
Let the symbol "+" denote addition of words (i.e., modulo-2^32
addition). Let X &lt;&lt;&lt; s denote the 32-bit value obtained by circularly
shifting (rotating) X left by s bit positions. Let not(X) denote the
bit-wise complement of X, and let X v Y denote the bit-wise OR of X
and Y. Let X xor Y denote the bit-wise XOR of X and Y, and let XY
denote the bit-wise AND of X and Y.
Rivest                                                          [Page 2]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
3. MD5 Algorithm Description
We begin by supposing that we have a b-bit message as input, and that
we wish to find its message digest. Here b is an arbitrary
nonnegative integer; b may be zero, it need not be a multiple of
eight, and it may be arbitrarily large. We imagine the bits of the
message written down as follows:
m_0 m_1 ... m_{b-1}
The following five steps are performed to compute the message digest
of the message.
3.1 Step 1. Append Padding Bits
The message is "padded" (extended) so that its length (in bits) is
congruent to 448, modulo 512. That is, the message is extended so
that it is just 64 bits shy of being a multiple of 512 bits long.
Padding is always performed, even if the length of the message is
already congruent to 448, modulo 512.
Padding is performed as follows: a single "1" bit is appended to the
message, and then "0" bits are appended so that the length in bits of
the padded message becomes congruent to 448, modulo 512. In all, at
least one bit and at most 512 bits are appended.
3.2 Step 2. Append Length
A 64-bit representation of b (the length of the message before the
padding bits were added) is appended to the result of the previous
step. In the unlikely event that b is greater than 2^64, then only
the low-order 64 bits of b are used. (These bits are appended as two
32-bit words and appended low-order word first in accordance with the
previous conventions.)
At this point the resulting message (after padding with bits and with
b) has a length that is an exact multiple of 512 bits. Equivalently,
this message has a length that is an exact multiple of 16 (32-bit)
words. Let M[0 ... N-1] denote the words of the resulting message,
where N is a multiple of 16.
3.3 Step 3. Initialize MD Buffer
A four-word buffer (A,B,C,D) is used to compute the message digest.
Here each of A, B, C, D is a 32-bit register. These registers are
initialized to the following values in hexadecimal, low-order bytes
first):
Rivest                                                          [Page 3]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
word A: 01 23 45 67
word B: 89 ab cd ef
word C: fe dc ba 98
word D: 76 54 32 10
3.4 Step 4. Process Message in 16-Word Blocks
We first define four auxiliary functions that each take as input
three 32-bit words and produce as output one 32-bit word.
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))
In each bit position F acts as a conditional: if X then Y else Z.
The function F could have been defined using + instead of v since XY
and not(X)Z will never have 1's in the same bit position.) It is
interesting to note that if the bits of X, Y, and Z are independent
and unbiased, the each bit of F(X,Y,Z) will be independent and
unbiased.
The functions G, H, and I are similar to the function F, in that they
act in "bitwise parallel" to produce their output from the bits of X,
Y, and Z, in such a manner that if the corresponding bits of X, Y,
and Z are independent and unbiased, then each bit of G(X,Y,Z),
H(X,Y,Z), and I(X,Y,Z) will be independent and unbiased. Note that
the function H is the bit-wise "xor" or "parity" function of its
inputs.
This step uses a 64-element table T[1 ... 64] constructed from the
sine function. Let T[i] denote the i-th element of the table, which
is equal to the integer part of 4294967296 times abs(sin(i)), where i
is in radians. The elements of the table are given in the appendix.
Do the following:
/* Process each 16-word block. */
For i = 0 to N/16-1 do
/* Copy block i into X. */
For j = 0 to 15 do
Set X[j] to M[i*16+j].
end /* of loop on j */
/* Save A as AA, B as BB, C as CC, and D as DD. */
AA = A
BB = B
Rivest                                                          [Page 4]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
CC = C
DD = D
/* Round 1. */
/* Let [abcd k s i] denote the operation
a = b + ((a + F(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */
/* Do the following 16 operations. */
[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]
/* Round 2. */
/* Let [abcd k s i] denote the operation
a = b + ((a + G(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */
/* Do the following 16 operations. */
[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]
/* Round 3. */
/* Let [abcd k s t] denote the operation
a = b + ((a + H(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */
/* Do the following 16 operations. */
[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]
/* Round 4. */
/* Let [abcd k s t] denote the operation
a = b + ((a + I(b,c,d) + X[k] + T[i]) &lt;&lt;&lt; s). */
/* Do the following 16 operations. */
[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]
/* Then perform the following additions. (That is increment each
of the four registers by the value it had before this block
was started.) */
A = A + AA
B = B + BB
C = C + CC
D = D + DD
end /* of loop on i */
Rivest                                                          [Page 5]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
3.5 Step 5. Output
The message digest produced as output is A, B, C, D. That is, we
begin with the low-order byte of A, and end with the high-order byte
of D.
This completes the description of MD5. A reference implementation in
C is given in the appendix.
4. Summary
The MD5 message-digest algorithm is simple to implement, and provides
a "fingerprint" or message digest of a message of arbitrary length.
It is conjectured that the difficulty of coming up with two messages
having the same message digest is on the order of 2^64 operations,
and that the difficulty of coming up with any message having a given
message digest is on the order of 2^128 operations. The MD5 algorithm
has been carefully scrutinized for weaknesses. It is, however, a
relatively new algorithm and further security analysis is of course
justified, as is the case with any new proposal of this sort.
5. Differences Between MD4 and MD5
The following are the differences between MD4 and MD5:
1.   A fourth round has been added.
2.   Each step now has a unique additive constant.
3.   The function g in round 2 was changed from (XY v XZ v YZ) to
(XZ v Y not(Z)) to make g less symmetric.
4.   Each step now adds in the result of the previous step.  This
promotes a faster "avalanche effect".
5.   The order in which input words are accessed in rounds 2 and
3 is changed, to make these patterns less like each other.
6.   The shift amounts in each round have been approximately
optimized, to yield a faster "avalanche effect." The shifts in
different rounds are distinct.
Rivest                                                          [Page 6]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
References
[1] Rivest, R., "The MD4 Message Digest Algorithm", RFC 1320, MIT and
RSA Data Security, Inc., April 1992.
[2] Rivest, R., "The MD4 message digest algorithm", in A.J.  Menezes
and S.A. Vanstone, editors, Advances in Cryptology - CRYPTO '90
Proceedings, pages 303-311, Springer-Verlag, 1991.
[3] CCITT Recommendation X.509 (1988), "The Directory -
Authentication Framework."
APPENDIX A - Reference Implementation
This appendix contains the following files taken from RSAREF: A
Cryptographic Toolkit for Privacy-Enhanced Mail:
global.h -- global header file
md5.h -- header file for MD5
md5c.c -- source code for MD5
For more information on RSAREF, send email to &lt;rsaref@rsa.com&gt;.
The appendix also includes the following file:
mddriver.c -- test driver for MD2, MD4 and MD5
The driver compiles for MD5 by default but can compile for MD2 or MD4
if the symbol MD is defined on the C compiler command line as 2 or 4.
The implementation is portable and should work on many different
plaforms. However, it is not difficult to optimize the implementation
on particular platforms, an exercise left to the reader. For example,
on "little-endian" platforms where the lowest-addressed byte in a 32-
bit word is the least significant and there are no alignment
restrictions, the call to Decode in MD5Transform can be replaced with
a typecast.
A.1 global.h
/* GLOBAL.H - RSAREF types and constants
*/
/* PROTOTYPES should be set to one if and only if the compiler supports
function argument prototyping.
The following makes PROTOTYPES default to 0 if it has not already
Rivest                                                          [Page 7]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
been defined with C compiler flags.
*/
#ifndef PROTOTYPES
#define PROTOTYPES 0
#endif
/* POINTER defines a generic pointer type */
typedef unsigned char *POINTER;
/* UINT2 defines a two byte word */
typedef unsigned short int UINT2;
/* UINT4 defines a four byte word */
typedef unsigned long int UINT4;
/* PROTO_LIST is defined depending on how PROTOTYPES is defined above.
If using PROTOTYPES, then PROTO_LIST returns the list, otherwise it
returns an empty list.
*/
#if PROTOTYPES
#define PROTO_LIST(list) list
#else
#define PROTO_LIST(list) ()
#endif
A.2 md5.h
/* MD5.H - header file for 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.
Rivest                                                          [Page 8]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
These notices must be retained in any copies of any part of this
documentation and/or software.
*/
/* MD5 context. */
typedef struct {
UINT4 state[4];                                   /* state (ABCD) */
UINT4 count[2];        /* number of bits, modulo 2^64 (lsb first) */
unsigned char buffer[64];                         /* input buffer */
} 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 Data Security, Inc., MD5 message-digest algorithm
*/
/* 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.
*/
#include "global.h"
#include "md5.h"
/* Constants for MD5Transform routine.
*/
Rivest                                                          [Page 9]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
#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 and I are basic MD5 functions.
*/
#define F(x, y, z) (((x) &amp; (y)) | ((~x) &amp; (z)))
#define G(x, y, z) (((x) &amp; (z)) | ((y) &amp; (~z)))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) ((y) ^ ((x) | (~z)))
/* ROTATE_LEFT rotates x left n bits.
*/
#define ROTATE_LEFT(x, n) (((x) &lt;&lt; (n)) | ((x) &gt;&gt; (32-(n))))
/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
Rotation is separate from addition to prevent recomputation.
*/
#define FF(a, b, c, d, x, s, ac) { \
(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
(a) = ROTATE_LEFT ((a), (s)); \
Rivest                                                         [Page 10]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
(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 initialization. Begins an MD5 operation, writing a new context.
*/
void MD5Init (context)
MD5_CTX *context;                                        /* context */
{
context-&gt;count[0] = context-&gt;count[1] = 0;
/* Load magic initialization constants.
*/
context-&gt;state[0] = 0x67452301;
context-&gt;state[1] = 0xefcdab89;
context-&gt;state[2] = 0x98badcfe;
context-&gt;state[3] = 0x10325476;
}
/* MD5 block update operation. Continues an MD5 message-digest
operation, processing another message block, and updating the
context.
*/
void MD5Update (context, input, inputLen)
MD5_CTX *context;                                        /* context */
unsigned char *input;                                /* input block */
unsigned int inputLen;                     /* length of input block */
{
unsigned int i, index, partLen;
/* Compute number of bytes mod 64 */
index = (unsigned int)((context-&gt;count[0] &gt;&gt; 3) &amp; 0x3F);
/* Update number of bits */
if ((context-&gt;count[0] += ((UINT4)inputLen &lt;&lt; 3))
Rivest                                                         [Page 11]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
&lt; ((UINT4)inputLen &lt;&lt; 3))
context-&gt;count[1]++;
context-&gt;count[1] += ((UINT4)inputLen &gt;&gt; 29);
partLen = 64 - index;
/* Transform as many times as possible.
*/
if (inputLen &gt;= partLen) {
MD5_memcpy
((POINTER)&amp;context-&gt;buffer[index], (POINTER)input, partLen);
MD5Transform (context-&gt;state, context-&gt;buffer);
for (i = partLen; i + 63 &lt; inputLen; i += 64)
MD5Transform (context-&gt;state, &amp;input[i]);
index = 0;
}
else
i = 0;
/* Buffer remaining input */
MD5_memcpy
((POINTER)&amp;context-&gt;buffer[index], (POINTER)&amp;input[i],
inputLen-i);
}
/* MD5 finalization. Ends an MD5 message-digest operation, writing the
the message digest and zeroizing the context.
*/
void MD5Final (digest, context)
unsigned char digest[16];                         /* message digest */
MD5_CTX *context;                                       /* context */
{
unsigned char bits[8];
unsigned int index, padLen;
/* Save number of bits */
Encode (bits, context-&gt;count, 8);
/* Pad out to 56 mod 64.
*/
index = (unsigned int)((context-&gt;count[0] &gt;&gt; 3) &amp; 0x3f);
padLen = (index &lt; 56) ? (56 - index) : (120 - index);
MD5Update (context, PADDING, padLen);
/* Append length (before padding) */
MD5Update (context, bits, 8);
Rivest                                                         [Page 12]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
/* Store state in digest */
Encode (digest, context-&gt;state, 16);
/* Zeroize sensitive information.
*/
MD5_memset ((POINTER)context, 0, sizeof (*context));
}
/* MD5 basic transformation. Transforms state based on block.
*/
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);
/* Round 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 */
/* Round 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 */
Rivest                                                         [Page 13]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
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 */
/* Round 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 */
/* Round 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;
/* Zeroize sensitive information.
Rivest                                                         [Page 14]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
*/
MD5_memset ((POINTER)x, 0, sizeof (x));
}
/* Encodes input (UINT4) into output (unsigned char). Assumes len is
a multiple of 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 &lt; len; i++, j += 4) {
output[j] = (unsigned char)(input[i] &amp; 0xff);
output[j+1] = (unsigned char)((input[i] &gt;&gt; 8) &amp; 0xff);
output[j+2] = (unsigned char)((input[i] &gt;&gt; 16) &amp; 0xff);
output[j+3] = (unsigned char)((input[i] &gt;&gt; 24) &amp; 0xff);
}
}
/* Decodes input (unsigned char) into output (UINT4). Assumes len is
a multiple of 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 &lt; len; i++, j += 4)
output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) &lt;&lt; 8) |
(((UINT4)input[j+2]) &lt;&lt; 16) | (((UINT4)input[j+3]) &lt;&lt; 24);
}
/* Note: Replace "for loop" with standard memcpy if possible.
*/
static void MD5_memcpy (output, input, len)
POINTER output;
POINTER input;
unsigned int len;
{
unsigned int i;
for (i = 0; i &lt; len; i++)
Rivest                                                         [Page 15]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
output[i] = input[i];
}
/* Note: Replace "for loop" with standard memset if possible.
*/
static void MD5_memset (output, value, len)
POINTER output;
int value;
unsigned int len;
{
unsigned int i;
for (i = 0; i &lt; len; i++)
((char *)output)[i] = (char)value;
}
A.4 mddriver.c
/* MDDRIVER.C - test driver for MD2, MD4 and 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.
*/
/* The following makes MD default to MD5 if it has not already been
defined with C compiler flags.
*/
#ifndef MD
#define MD MD5
#endif
#include &lt;stdio.h&gt;
#include &lt;time.h&gt;
#include &lt;string.h&gt;
#include "global.h"
#if MD == 2
#include "md2.h"
#endif
#if MD == 4
Rivest                                                         [Page 16]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
#include "md4.h"
#endif
#if MD == 5
#include "md5.h"
#endif
/* Length of test block, number of test blocks.
*/
#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
/* Main driver.
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;
Rivest                                                         [Page 17]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
char *argv[];
{
int i;
if (argc &gt; 1)
for (i = 1; i &lt; argc; i++)
if (argv[i][0] == '-' &amp;&amp; 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);
}
/* Digests a string and prints the result.
*/
static void MDString (string)
char *string;
{
MD_CTX context;
unsigned char digest[16];
unsigned int len = strlen (string);
MDInit (&amp;context);
MDUpdate (&amp;context, string, len);
MDFinal (digest, &amp;context);
printf ("MD%d (\"%s\") = ", MD, string);
MDPrint (digest);
printf ("\n");
}
/* Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte
blocks.
*/
static void MDTimeTrial ()
{
MD_CTX context;
time_t endTime, startTime;
unsigned char block[TEST_BLOCK_LEN], digest[16];
unsigned int i;
Rivest                                                         [Page 18]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
printf
("MD%d time trial. Digesting %d %d-byte blocks ...", MD,
TEST_BLOCK_LEN, TEST_BLOCK_COUNT);
/* Initialize block */
for (i = 0; i &lt; TEST_BLOCK_LEN; i++)
block[i] = (unsigned char)(i &amp; 0xff);
/* Start timer */
time (&amp;startTime);
/* Digest blocks */
MDInit (&amp;context);
for (i = 0; i &lt; TEST_BLOCK_COUNT; i++)
MDUpdate (&amp;context, block, TEST_BLOCK_LEN);
MDFinal (digest, &amp;context);
/* Stop timer */
time (&amp;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));
}
/* Digests a reference suite of strings and prints the results.
*/
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");
}
/* Digests a file and prints the result.
Rivest                                                         [Page 19]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
*/
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 (&amp;context);
while (len = fread (buffer, 1, 1024, file))
MDUpdate (&amp;context, buffer, len);
MDFinal (digest, &amp;context);
fclose (file);
printf ("MD%d (%s) = ", MD, filename);
MDPrint (digest);
printf ("\n");
}
}
/* Digests the standard input and prints the result.
*/
static void MDFilter ()
{
MD_CTX context;
int len;
unsigned char buffer[16], digest[16];
MDInit (&amp;context);
while (len = fread (buffer, 1, 16, stdin))
MDUpdate (&amp;context, buffer, len);
MDFinal (digest, &amp;context);
MDPrint (digest);
printf ("\n");
}
/* Prints a message digest in hexadecimal.
*/
static void MDPrint (digest)
unsigned char digest[16];
{
Rivest                                                         [Page 20]
RFC 1321              MD5 Message-Digest Algorithm            April 1992
unsigned int i;
for (i = 0; i &lt; 16; i++)
printf ("%02x", digest[i]);
}
A.5 Test suite
The MD5 test suite (driver option "-x") should print the following
results:
MD5 test suite:
MD5 ("") = d41d8cd98f00b204e9800998ecf8427e
MD5 ("a") = 0cc175b9c0f1b6a831c399e269772661
MD5 ("abc") = 900150983cd24fb0d6963f7d28e17f72
MD5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0
MD5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b
MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") =
d174ab98d277d9f5a5611c2c9f419d9f
MD5 ("123456789012345678901234567890123456789012345678901234567890123456
78901234567890") = 57edf4a22be3c955ac49da2e2107b67a
Security Considerations
The level of security discussed in this memo is considered to be
sufficient for implementing very high security hybrid digital-
signature schemes based on MD5 and a public-key cryptosystem.
Author's Address
Ronald L. Rivest
Massachusetts Institute of Technology
Laboratory for Computer Science
NE43-324
545 Technology Square
Cambridge, MA  02139-1986
Phone: (617) 253-5880
EMail: rivest@theory.lcs.mit.edu
Rivest                                                         [Page 21]
</pre>
<img src ="http://www.cppblog.com/bilicon/aggbug/29263.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/bilicon/" target="_blank">bilicon</a> 2007-08-03 12:35 <a href="http://www.cppblog.com/bilicon/articles/29263.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[zt]MD5算法原理 </title><link>http://www.cppblog.com/bilicon/articles/29262.html</link><dc:creator>bilicon</dc:creator><author>bilicon</author><pubDate>Fri, 03 Aug 2007 04:34:00 GMT</pubDate><guid>http://www.cppblog.com/bilicon/articles/29262.html</guid><wfw:comment>http://www.cppblog.com/bilicon/comments/29262.html</wfw:comment><comments>http://www.cppblog.com/bilicon/articles/29262.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/bilicon/comments/commentRss/29262.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/bilicon/services/trackbacks/29262.html</trackback:ping><description><![CDATA[<strong>综述</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5的全称是message-digest algorithm 5（信息-摘要算法），在90年代初由mit laboratory for computer science和rsa data security inc的ronald l. rivest开发出来，经md2、md3和md4发展而来。它的作用是让大容量信息在用数字签名软件签署私人密匙前被"压缩"成一种保密的格式（就是把一个任意长度的字节串变换成一定长的大整数）。不管是md2、md4还是md5，它们都需要获得一个随机长度的信息并产生一个128位的信息摘要。虽然这些算法的结构或多或少有些相似，但md2的设计与md4和md5完全不同，那是因为md2是为8位机器做过设计优化的，而md4和md5却是面向32位的电脑。这三个算法的描述和c语言源代码在internet rfcs 1321中有详细的描述（http://www.ietf.org/rfc/rfc1321.txt），这是一份最权威的文档，由ronald l. rivest在1992年8月向ieft提交。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;rivest在1989年开发出md2算法。在这个算法中，首先对信息进行数据补位，使信息的字节长度是16的倍数。然后，以一个16位的检验和追加到信息末尾。并且根据这个新产生的信息计算出散列值。后来，rogier和chauvaud发现如果忽略了检验和将产生md2冲突。md2算法的加密后结果是唯一的--既没有重复。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;为了加强算法的安全性，rivest在1990年又开发出md4算法。md4算法同样需要填补信息以确保信息的字节长度加上448后能被512整除（信息字节长度mod 512 = 448）。然后，一个以64位二进制表示的信息的最初长度被添加进来。信息被处理成512位damg?rd/merkle迭代结构的区块，而且每个区块要通过三个不同步骤的处理。den boer和bosselaers以及其他人很快的发现了攻击md4版本中第一步和第三步的漏洞。dobbertin向大家演示了如何利用一部普通的个人电脑在几分钟内找到md4完整版本中的冲突（这个冲突实际上是一种漏洞，它将导致对不同的内容进行加密却可能得到相同的加密后结果）。毫无疑问，md4就此被淘汰掉了。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;尽管md4算法在安全上有个这么大的漏洞，但它对在其后才被开发出来的好几种信息安全加密算法的出现却有着不可忽视的引导作用。除了md5以外，其中比较有名的还有sha-1、ripe-md以及haval等。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;一年以后，即1991年，rivest开发出技术上更为趋近成熟的md5算法。它在md4的基础上增加了"安全-带子"（safety-belts）的概念。虽然md5比md4稍微慢一些，但却更为安全。这个算法很明显的由四个和md4设计有少许不同的步骤组成。在md5算法中，信息-摘要的大小和填充的必要条件与md4完全相同。den boer和bosselaers曾发现md5算法中的假冲突（pseudo-collisions），但除此之外就没有其他被发现的加密后结果了。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;van oorschot和wiener曾经考虑过一个在散列中暴力搜寻冲突的函数（brute-force hash function），而且他们猜测一个被设计专门用来搜索md5冲突的机器（这台机器在1994年的制造成本大约是一百万美元）可以平均每24天就找到一个冲突。但单从1991年到2001年这10年间，竟没有出现替代md5算法的md6或被叫做其他什么名字的新算法这一点，我们就可以看出这个瑕疵并没有太多的影响md5的安全性。上面所有这些都不足以成为md5的在实际应用中的问题。并且，由于md5算法的使用不需要支付任何版权费用的，所以在一般的情况下（非绝密应用领域。但即便是应用在绝密领域内，md5也不失为一种非常优秀的中间技术），md5怎么都应该算得上是非常安全的了。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>算法的应用</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5的典型应用是对一段信息（message）产生信息摘要（message-digest），以防止被篡改。比如，在unix下有很多软件在下载的时候都有一个文件名相同，文件扩展名为.md5的文件，在这个文件中通常只有一行文本，大致结构如： <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5 (tanajiya.tar.gz) = 0ca175b9c0f726a831d895e269332461 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;这就是tanajiya.tar.gz文件的数字签名。md5将整个文件当作一个大文本信息，通过其不可逆的字符串变换算法，产生了这个唯一的md5信息摘要。如果在以后传播这个文件的过程中，无论文件的内容发生了任何形式的改变（包括人为修改或者下载过程中线路不稳定引起的传输错误等），只要你对这个文件重新计算md5时就会发现信息摘要不相同，由此可以确定你得到的只是一个不正确的文件。如果再有一个第三方的认证机构，用md5还可以防止文件作者的"抵赖"，这就是所谓的数字签名应用。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5还广泛用于加密和解密技术上。比如在unix系统中用户的密码就是以md5（或其它类似的算法）经加密后存储在文件系统中。当用户登录的时候，系统把用户输入的密码计算成md5值，然后再去和保存在文件系统中的md5值进行比较，进而确定输入的密码是否正确。通过这样的步骤，系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。这不但可以避免用户的密码被具有系统管理员权限的用户知道，而且还在一定程度上增加了密码被破解的难度。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;正是因为这个原因，现在被黑客使用最多的一种破译密码的方法就是一种被称为"跑字典"的方法。有两种方法得到字典，一种是日常搜集的用做密码的字符串表，另一种是用排列组合方法生成的，先用md5程序计算出这些字典项的md5值，然后再用目标的md5值在这个字典中检索。我们假设密码的最大长度为8位字节（8 bytes），同时密码只能是字母和数字，共26+26+10=62个字符，排列组合出的字典的项数则是p(62,1)+p(62,2)&#8230;.+p(62,8)，那也已经是一个很天文的数字了，存储这个字典就需要tb级的磁盘阵列，而且这种方法还有一个前提，就是能获得目标账户的密码md5值的情况下才可以。这种加密技术被广泛的应用于unix系统中，这也是为什么unix系统比一般操作系统更为坚固一个重要原因。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>算法描述</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;对md5算法简要的叙述可以为：md5以512位分组来处理输入的信息，且每一分组又被划分为16个32位子分组，经过了一系列的处理后，算法的输出由四个32位分组组成，将这四个32位分组级联后将生成一个128位散列值。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;在md5算法中，首先需要对信息进行填充，使其字节长度对512求余的结果等于448。因此，信息的字节长度（bits length）将被扩展至n*512+448，即n*64+56个字节（bytes），n为一个正整数。填充的方法如下，在信息的后面填充一个1和无数个0，直到满足上面的条件时才停止用0对信息的填充。然后，在在这个结果后面附加一个以64位二进制表示的填充前信息长度。经过这两步的处理，现在的信息字节长度=n*512+448+64=(n+1)*512，即长度恰好是512的整数倍。这样做的原因是为满足后面处理中对信息长度的要求。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5中有四个32位被称作链接变量（chaining variable）的整数参数，他们分别为：a=0x01234567，b=0x89abcdef，c=0xfedcba98，d=0x76543210。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;当设置好这四个链接变量后，就开始进入算法的四轮循环运算。循环的次数是信息中512位信息分组的数目。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;将上面四个链接变量复制到另外四个变量中：a到a，b到b，c到c，d到d。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;主循环有四轮（md4只有三轮），每轮循环都很相似。第一轮进行16次操作。每次操作对a、b、c和d中的其中三个作一次非线性函数运算，然后将所得结果加上第四个变量，文本的一个子分组和一个常数。再将所得结果向右环移一个不定的数，并加上a、b、c或d中之一。最后用该结果取代a、b、c或d中之一。 <br>&nbsp;&nbsp;&nbsp;&nbsp;以一下是每次操作中用到的四个非线性函数（每轮一个）。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;f(x,y,z) =(x&amp;y)|((~x)&amp;z) <br>&nbsp;&nbsp;&nbsp;&nbsp;g(x,y,z) =(x&amp;z)|(y&amp;(~z)) <br>&nbsp;&nbsp;&nbsp;&nbsp;h(x,y,z) =x^y^z <br>&nbsp;&nbsp;&nbsp;&nbsp;i(x,y,z)=y^(x|(~z)) <br>&nbsp;&nbsp;&nbsp;&nbsp;（&amp;是与，|是或，~是非，^是异或） <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;这四个函数的说明：如果x、y和z的对应位是独立和均匀的，那么结果的每一位也应是独立和均匀的。 <br>&nbsp;&nbsp;&nbsp;&nbsp;f是一个逐位运算的函数。即，如果x，那么y，否则z。函数h是逐位奇偶操作符。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;假设mj表示消息的第j个子分组（从0到15），&lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(a,b,c,d,mj,s,ti)表示a=b+((a+(f(b,c,d)+mj+ti)&lt;&lt; gg(a,b,c,d,mj,s,ti)表示a=b+((a+(g(b,c,d)+mj+ti)&lt;&lt; hh(a,b,c,d,mj,s,ti)表示a=b+((a+(h(b,c,d)+mj+ti)&lt;&lt; ii(a,b,c,d,mj,s,ti)表示a=b+((a+(i(b,c,d)+mj+ti)&lt;&lt; <br>&nbsp;&nbsp;&nbsp;&nbsp;这四轮（64步）是： <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;第一轮 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ff(a,b,c,d,m0,7,0xd76aa478) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(d,a,b,c,m1,12,0xe8c7b756) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(c,d,a,b,m2,17,0x242070db) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(b,c,d,a,m3,22,0xc1bdceee) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(a,b,c,d,m4,7,0xf57c0faf) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(d,a,b,c,m5,12,0x4787c62a) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(c,d,a,b,m6,17,0xa8304613) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(b,c,d,a,m7,22,0xfd469501) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(a,b,c,d,m8,7,0x698098d8) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(d,a,b,c,m9,12,0x8b44f7af) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(c,d,a,b,m10,17,0xffff5bb1) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(b,c,d,a,m11,22,0x895cd7be) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(a,b,c,d,m12,7,0x6b901122) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(d,a,b,c,m13,12,0xfd987193) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(c,d,a,b,m14,17,0xa679438e) <br>&nbsp;&nbsp;&nbsp;&nbsp;ff(b,c,d,a,m15,22,0x49b40821) <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;第二轮 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;gg(a,b,c,d,m1,5,0xf61e2562) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(d,a,b,c,m6,9,0xc040b340) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(c,d,a,b,m11,14,0x265e5a51) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(b,c,d,a,m0,20,0xe9b6c7aa) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(a,b,c,d,m5,5,0xd62f105d) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(d,a,b,c,m10,9,0x02441453) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(c,d,a,b,m15,14,0xd8a1e681) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(b,c,d,a,m4,20,0xe7d3fbc8) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(a,b,c,d,m9,5,0x21e1cde6) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(d,a,b,c,m14,9,0xc33707d6) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(c,d,a,b,m3,14,0xf4d50d87) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(b,c,d,a,m8,20,0x455a14ed) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(a,b,c,d,m13,5,0xa9e3e905) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(d,a,b,c,m2,9,0xfcefa3f8) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(c,d,a,b,m7,14,0x676f02d9) <br>&nbsp;&nbsp;&nbsp;&nbsp;gg(b,c,d,a,m12,20,0x8d2a4c8a) <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;第三轮 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;hh(a,b,c,d,m5,4,0xfffa3942) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(d,a,b,c,m8,11,0x8771f681) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(c,d,a,b,m11,16,0x6d9d6122) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(b,c,d,a,m14,23,0xfde5380c) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(a,b,c,d,m1,4,0xa4beea44) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(d,a,b,c,m4,11,0x4bdecfa9) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(c,d,a,b,m7,16,0xf6bb4b60) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(b,c,d,a,m10,23,0xbebfbc70) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(a,b,c,d,m13,4,0x289b7ec6) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(d,a,b,c,m0,11,0xeaa127fa) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(c,d,a,b,m3,16,0xd4ef3085) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(b,c,d,a,m6,23,0x04881d05) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(a,b,c,d,m9,4,0xd9d4d039) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(d,a,b,c,m12,11,0xe6db99e5) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(c,d,a,b,m15,16,0x1fa27cf8) <br>&nbsp;&nbsp;&nbsp;&nbsp;hh(b,c,d,a,m2,23,0xc4ac5665) <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;第四轮 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;ii(a,b,c,d,m0,6,0xf4292244) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(d,a,b,c,m7,10,0x432aff97) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(c,d,a,b,m14,15,0xab9423a7) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(b,c,d,a,m5,21,0xfc93a039) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(a,b,c,d,m12,6,0x655b59c3) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(d,a,b,c,m3,10,0x8f0ccc92) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(c,d,a,b,m10,15,0xffeff47d) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(b,c,d,a,m1,21,0x85845dd1) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(a,b,c,d,m8,6,0x6fa87e4f) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(d,a,b,c,m15,10,0xfe2ce6e0) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(c,d,a,b,m6,15,0xa3014314) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(b,c,d,a,m13,21,0x4e0811a1) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(a,b,c,d,m4,6,0xf7537e82) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(d,a,b,c,m11,10,0xbd3af235) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(c,d,a,b,m2,15,0x2ad7d2bb) <br>&nbsp;&nbsp;&nbsp;&nbsp;ii(b,c,d,a,m9,21,0xeb86d391) <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;常数ti可以如下选择： <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;在第i步中，ti是4294967296*abs(sin(i))的整数部分，i的单位是弧度。(4294967296等于2的32次方) <br>&nbsp;&nbsp;&nbsp;&nbsp;所有这些完成之后，将a、b、c、d分别加上a、b、c、d。然后用下一分组数据继续运行算法，最后的输出是a、b、c和d的级联。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;当你按照我上面所说的方法实现md5算法以后，你可以用以下几个信息对你做出来的程序作一个简单的测试，看看程序有没有错误。 <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("") = d41d8cd98f00b204e9800998ecf8427e <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("a") = 0cc175b9c0f1b6a831c399e269772661 <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("abc") = 900150983cd24fb0d6963f7d28e17f72 <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("message digest") = f96b697d7cb7938d525a2f31aaf161d0 <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789") = <br>&nbsp;&nbsp;&nbsp;&nbsp;d174ab98d277d9f5a5611c2c9f419d9f <br>&nbsp;&nbsp;&nbsp;&nbsp;md5 ("123456789012345678901234567890123456789012345678901234567890123456789 <br>&nbsp;&nbsp;&nbsp;&nbsp;01234567890") = 57edf4a22be3c955ac49da2e2107b67a <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;如果你用上面的信息分别对你做的md5算法实例做测试，最后得出的结论和标准答案完全一样，那我就要在这里象你道一声祝贺了。要知道，我的程序在第一次编译成功的时候是没有得出和上面相同的结果的。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br><strong>md5的安全性</strong> <br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;md5相对md4所作的改进：&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;1. 增加了第四轮；&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;2. 每一步均有唯一的加法常数；&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;3. 为减弱第二轮中函数g的对称性从(x&amp;y)|(x&amp;z)|(y&amp;z)变为(x&amp;z)|(y&amp;(~z))；&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;4. 第一步加上了上一步的结果，这将引起更快的雪崩效应；&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;5. 改变了第二轮和第三轮中访问消息子分组的次序，使其更不相似；&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;6. 近似优化了每一轮中的循环左移位移量以实现更快的雪崩效应。各轮的位移量互不相同。&nbsp;<br>
<img src ="http://www.cppblog.com/bilicon/aggbug/29262.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/bilicon/" target="_blank">bilicon</a> 2007-08-03 12:34 <a href="http://www.cppblog.com/bilicon/articles/29262.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]使用CPU时间戳进行高精度计时</title><link>http://www.cppblog.com/bilicon/articles/28807.html</link><dc:creator>bilicon</dc:creator><author>bilicon</author><pubDate>Thu, 26 Jul 2007 02:57:00 GMT</pubDate><guid>http://www.cppblog.com/bilicon/articles/28807.html</guid><wfw:comment>http://www.cppblog.com/bilicon/comments/28807.html</wfw:comment><comments>http://www.cppblog.com/bilicon/articles/28807.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/bilicon/comments/commentRss/28807.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/bilicon/services/trackbacks/28807.html</trackback:ping><description><![CDATA[<div class=cnt>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">对关注性能的程序开发人员而言，一个好的计时部件既是益友，也是良师。计时器既可以作为程序组件帮助程序员精确的控制程序进程，又是一件有力的调试武器，在有经验的程序员手里可以尽快的确定程序的性能瓶颈，或者对不同的算法作出有说服力的性能比较。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br></span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">平台下，常用的计时器有两种，一种是</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">timeGetTime</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">多媒体计时器，它可以提供毫秒级的计时。但这个精度对很多应用场合而言还是太粗糙了。另一种是</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformanceCount</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">计数器，随系统的不同可以提供微秒级的计数。对于实时图形处理、多媒体数据流处理、或者实时系统构造的程序员，善用</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformanceCount/QueryPerformanceFrequency</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">是一项基本功。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br></span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">本文要介绍的，是另一种直接利用</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Pentium CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">内部时间戳进行计时的高精度计时手段。以下讨论主要得益于《</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">图形编程》一书，第</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">15</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">页－</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">17</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">页，有兴趣的读者可以直接参考该书。关于</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令的详细讨论，可以参考</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Intel</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">产品手册。本文仅仅作抛砖之用。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Intel Pentium</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">以上级别的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">中，有一个称为</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">&#8220;</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">时间戳（</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Time Stamp</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">）</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">&#8221;</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的部件，它以</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">64</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">位无符号整型数的格式，记录了自</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">上电以来所经过的时钟周期数。由于目前的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">主频都非常高，因此这个部件可以达到纳秒级的计时精度。这个精确性是上述两种方法所无法比拟的。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br></span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Pentium</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">以上的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">中，提供了一条机器指令</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">（</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Read Time Stamp Counter</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">）来读取这个时间戳的数字，并将其保存在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">EDX:EAX</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">寄存器对中。由于</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">EDX:EAX</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">寄存器对恰好是</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Win32</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">平台下</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">C++</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">语言保存函数返回值的寄存器，所以我们可以把这条指令看成是一个普通的函数调用。像这样：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">inline unsigned __int64 GetCycleCount()<br>{<br>__asm RDTSC<br>} </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">但是不行，因为</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">不被</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">C++</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的内嵌汇编器直接支持，所以我们要用</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">_emit</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">伪指令直接嵌入该指令的机器码形式</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">0X0F</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">、</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">0X31</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">，如下：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">inline unsigned __int64 GetCycleCount()<br>{<br>__asm _emit 0x0F<br>__asm _emit 0x31<br>} </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">以后在需要计数器的场合，可以像使用普通的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Win32 API</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">一样，调用两次</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">GetCycleCount</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数，比较两个返回值的差，像这样：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">unsigned long t;<br>t = (unsigned long)GetCycleCount();<br>//Do Something time-intensive ...<br>t -= (unsigned long)GetCycleCount(); </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">《</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">图形编程》第</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">15</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">页编写了一个类，把这个计数器封装起来。有兴趣的读者可以去参考那个类的代码。作者为了更精确的定时，做了一点小小的改进，把执行</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令的时间，通过连续两次调用</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">GetCycleCount</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数计算出来并保存了起来，以后每次计时结束后，都从实际得到的计数中减掉这一小段时间，以得到更准确的计时数字。但我个人觉得这一点点改进意义不大。在我的机器上实测，这条指令大概花掉了几十到</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">100</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">多个周期，在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Celeron 800MHz</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的机器上，这不过是十分之一微秒的时间。对大多数应用来说，这点时间完全可以忽略不计；而对那些确实要精确到纳秒数量级的应用来说，这个补偿也过于粗糙了。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">这个方法的优点是：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>1.</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">高精度。可以直接达到纳秒级的计时精度（在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">1GHz</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">上每个时钟周期就是一纳秒），这是其他计时方法所难以企及的。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>2.</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">成本低。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">timeGetTime </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数需要链接多媒体库</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">winmm.lib</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">，</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformance* </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数根据</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">MSDN</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的说明，需要硬件的支持（虽然我还没有见过不支持的机器）和</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">KERNEL</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">库的支持，所以二者都只能在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">平台下使用（关于</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">DOS</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">平台下的高精度计时问题，可以参考《图形程序开发人员指南》，里面有关于控制定时器</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">8253</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的详细说明）。但</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令是一条</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令，凡是</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">i386</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">平台下</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Pentium</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">以上的机器均支持，甚至没有平台的限制（我相信</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">i386</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">版本</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">UNIX</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">和</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Linux</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">下这个方法同样适用，但没有条件试验），而且函数调用的开销是最小的。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>3.</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">具有和</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">主频直接对应的速率关系。一个计数相当于</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">1/(CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">主频</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Hz</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">数</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">)</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">秒，这样只要知道了</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的主频，可以直接计算出时间。这和</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformanceCount</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">不同，后者需要通过</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformanceFrequency</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">获取当前计数器每秒的计数次数才能换算成时间。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">这个方法的缺点是：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>1.</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">现有的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">C/C++</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">编译器多数不直接支持使用</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令，需要用直接嵌入机器码的方式编程，比较麻烦。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>2.</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">数据抖动比较厉害。其实对任何计量手段而言，精度和稳定性永远是一对矛盾。如果用低精度的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">timeGetTime</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">来计时，基本上每次计时的结果都是相同的；而</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令每次结果都不一样，经常有几百甚至上千的差距。这是这种方法高精度本身固有的矛盾。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">关于这个方法计时的最大长度，我们可以简单的用下列公式计算：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">自</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">上电以来的秒数</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> = RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">读出的周期数</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> / CPU</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">主频速率（</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Hz</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">）</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">64</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">位无符号整数所能表达的最大数字是</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">1.8&#215;10^19</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">，在我的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Celeron 800</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">上可以计时大约</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">700</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">年（书中说可以在</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">200MHz</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Pentium</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">上计时</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">117</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">年，这个数字不知道是怎么得出来的，与我的计算有出入）。无论如何，我们大可不必关心溢出的问题。</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">下面是几个小例子，简要比较了三种计时方法的用法与精度</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>//Timer1.cpp </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">使用了</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">RDTSC</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">指令的</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Timer</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">类</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">//KTimer</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">类的定义可以参见《</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">图形编程》</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">P15<br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">编译行：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CL Timer1.cpp /link USER32.lib<br>#include &lt;stdio.h&gt;<br>#include "KTimer.h"<br>main()<br>{<br>unsigned t;<br>KTimer timer;<br>timer.Start();<br>Sleep(1000);<br>t = timer.Stop();<br>printf("Lasting Time: %d\n",t);<br>} </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">//Timer2.cpp </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">使用了</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">timeGetTime</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">需包含</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">&lt;mmsys.h&gt;</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">，但由于</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">Windows</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">头文件错综复杂的关系</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">简单包含</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">&lt;windows.h&gt;</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">比较偷懒：）</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">编译行：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CL timer2.cpp /link winmm.lib <br>#include &lt;windows.h&gt;<br>#include &lt;stdio.h&gt; </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">main()<br>{<br>DWORD t1, t2;<br>t1 = timeGetTime();<br>Sleep(1000);<br>t2 = timeGetTime();<br>printf("Begin Time: %u\n", t1);<br>printf("End Time: %u\n", t2);<br>printf("Lasting Time: %u\n",(t2-t1));<br>} </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">//Timer3.cpp </span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">使用了</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">QueryPerformanceCounter</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">函数</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> <br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">编译行：</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">CL timer3.cpp /link KERNEl32.lib<br>#include &lt;windows.h&gt;<br>#include &lt;stdio.h&gt; </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">main()<br>{<br>LARGE_INTEGER t1, t2, tc;<br>QueryPerformanceFrequency(&amp;tc);<br>printf("Frequency: %u\n", tc.QuadPart);<br>QueryPerformanceCounter(&amp;t1);<br>Sleep(1000);<br>QueryPerformanceCounter(&amp;t2);<br>printf("Begin Time: %u\n", t1.QuadPart);<br>printf("End Time: %u\n", t2.QuadPart);<br>printf("Lasting Time: %u\n",( t2.QuadPart- t1.QuadPart));<br>} </span></p>
<p style="LINE-HEIGHT: 180%"><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">////////////////////////////////////////////////<br>//</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">以上三个示例程序都是测试</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">1</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%">秒钟休眠所耗费的时间</span><span style="FONT-SIZE: 9pt; COLOR: black; LINE-HEIGHT: 180%"> </span></p>
</div>
<img src ="http://www.cppblog.com/bilicon/aggbug/28807.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/bilicon/" target="_blank">bilicon</a> 2007-07-26 10:57 <a href="http://www.cppblog.com/bilicon/articles/28807.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>