全新的 SHA-3 加密标准 —— Keccak

本文转载自开源中国,原文链接:https://www.oschina.net/translate/keccak-the-new-sha-3-encryption-standard。SHA3算法是以太坊的基础加密算法,建议读者通过此文补充SHA3加密算法的基础知识。


经过多年的测试和分析,美国政府选择了Keccak算法作为SHA - 3的加密标准,现在我们分析一下它的工作原理以及使用方法。

2012年10月,美国NIST选择了Keccak算法作为SHA - 3的标准算法,Keccak拥有良好的加密性能以及抗解密能力。在本篇文章中,我将简单的阐述Keccak的工作原理,我将分析它的核心代码并告诉你它是如何对一段文本进行哈希处理的。我将其与SHA-1 和 SHA-2进行比较测试。

读者应该有C和Objective-C的编程经验,然后对加密算法基本认知。


SHA-1 和 SHA-2 的限制

不容忽视的是,SHA-1和SHA-2使用了相同的处理引擎。Merkle-Damgard称,在处理消息文本时,对SHA-1的成功攻击行为会影响到SHA-2的安全。

在SHA-1上举个例子:理论上进行暴力破解至少需要2的80次方(哈希循环的一个周期)才能碰撞破解。但是在2005年2月,王晓云(音译)以及他的同事使用差分路径攻击,只用了2的69次方次就完成了SHA-1的循环碰撞周期,2008年8月,这一方法被Martin Cochran证实。

到了2012年。Mark Stevens使用了一系列的云服务器在执行SHA-1的差分路径攻击,在2的58.5次方次就完成了一次攻击碰撞,他估计,在经过2的61次方次循环后,就可以形成一个完整的哈希碰撞密码库。

至于SHA-2, 对有限轮回的SHA-2散列的攻击是唯一成功的一类。对46轮回的SHA-2(512位的变体)和41轮回的SHA-2(512位的变体)的攻击是最有效的。破解256位的变体要花费 2253.6次循环,破解512位的变体要花费2511.5次循环。

事实上,即使还没有宣布一个全轮回的SHA-2被成功攻破, 但毫无疑问,攻击机制正私下的发展。这也是NIST赞助SHA-3竞赛的一个原因,也引起Keccak的发展和近来的采纳。


SHA-3的筛选

筛选SHA-3标准,候选人散列函数必须满足四个由NIST设置的条件。如果一个候选算法未能满足这些条件,它将被淘汰:

候选散列函数必须好实现。它应该消耗最少的资源即使散列大量的消息文本。许多候选算法实际上是无法达到这个要求。
候选算法必须保守安全。它应该抵御已知的攻击,同时保持一个大的安全系数。它应该同SHA-2相同的四个散列大小(224bit、256bit、384bit或512bit),但如果需要能够支持更长的散列位宽。
候选算法必须接受密码分析。源代码和分析结果公开为感兴趣的第三方审查和评论。在分析过程中发现的任何缺陷都需要解决,通过调整或通过重新设计。
候选算法必须使代码多样性。它不能使用Merkle-Damgard引擎产生消息散列。
这个SHA-3竞争看到51个候选算法进入第一轮评估。这当中14个晋级第二轮。第三轮候选算法只剩下5个。并从这5个中Keccak被宣布为获胜者。


介绍 Keccak

Keccak算法(读作为“ket-chak”)是Guido Bertoni, Joan Daemen, Michael Peters, and Giles Van Assche的工作。 SHA-3的候选人在2008年10月提交。

Keccak采用了创新的的“海绵引擎”散列消息文本。它是快速的,在英特尔酷睿2处理器下的平均速度为12.5周期每字节。它设计简单,方便硬件实现。

Keccak已可以抵御最小的复杂度为2n的攻击,其中N为散列的大小。它具有广泛的安全边际。至目前为止,第三方密码分析已经显示出Keccak没有严重的弱点。尽管如此,Keccak的创建者已经启动Crunchy加密比赛,挑起人们发现和报告成功且可核查的攻击Keccak的兴趣。


深入Keccak

在写作的时候, NIST还没有发布SHA-3 (FIPS 180-5)的官方文档。 因此, 下面的资料收集于Keccak的参考文档和来源于第三方。

这里有Keccak算法的三个部分 (图1).
图1: Keccak散列函数的基本模块

Hash()函数作为入口函数。它需要4个输入参数:散列位大小 (n),消息文本(M)和它的位大小 (N),和一个哈希变量 (H). 这个哈希变量必须有下面的malloc()函数创建:

HashReturn E;
char *H;
n = 224;
H = (char *)malloc(sizeof(char) * n / 8);
//...
E = Hash(n, M, N, H);

Init()函数为指定的哈希大小准备内部状态(S)。Update()函数开始压缩或吸收相。在这里根据内部状态组合消息文本, 然后置换. Final()函数开始提取或压缩相。这是位从内部状态提取和组装形成的散列值 。第一个n位组装的散列然后作为消息散列 。四个函数都返回一个错误的结果,返回0说明函数没有出错地执行结束。


图2显示了内部状态的数据结构。命名为spongeState的这个C结构包含8个域,其中两个是固定数组(以红色标出)。state数组域存储真正的状态字节,而dataQueue数组域存储着将被组合和变换的消息字节。

图2:保存Keccak哈希函数的内部状态的结构

capacity域是散列容量(用c表示)。它的值设置为散列大小的两倍(2*n)。rate域是每个循环处理的消息位长度。它设置为1600-c这个值。bitInQueue域是保留在dataQueue数组域中的消息位长度。fixedOutputlength域是期望的散列大小(用n表示)。squeezing域是模式标志。当设置为0时,keccak处在压缩模式。当设置为1时,keccak是处在解压缩模式。另外 filedbitsAvailableForSqueezing保存的是组合为最终消息散列的状态位长度。


Keccak使用了24个变换循环来缩减消息文本为散列值。每个循环连续调用了五个模块,如图3所示。

图3:每个变换循环调用的模块

θ模块把内部状态转化为5x5的每个元素为64位的数组。它计算每列中同位的部分,然后使用异或(XOR)操作符对它们进行组合。然后它按照下面的方式把获得的同位结果和每个状态位异或:

S[i][j][k] ^= parity(S[0...4][j-1][k]) 
    ^ parity(S[0...4][j+1][k-1])

where i = 0...4; j = 0...4; k = 0...63

ρ模块按照三角数的排列对每个64位元素循环移动。不过循环移动把元素S[0][0]排除在外。φ模块变换64位元素。变换遵循下面所示的固定赋值模式:

S[j][2*i + 3*j] = S[i][j]

χ模块给变换循环增加了非线性特性。它仅仅使用了三个逐位操作符:与(AND),非(NOT)和异或(XOR)来组合行元素。然后按照下面的方式把结果写入状态数组里:

S[i][j][k] ^= ~S[i][j + 1][k] & S[i][j + 2][k]

ι模块打破了由其他模块所产生的任何对称。它是这样做的,把数组中的一个元素与一循环常量异或。这个模块有24个循环常量供选择。这些常量由Keccak内部定义。不过,由于 Keccak的C源代码太大不能全部罗列在这儿了。


SHA-3评估

那么SHA-3与它的前任SHA-1和SHA-2比较又如何呢?为了回答这个问题,我对这三个哈希函数进行四个独立的测试:

  • 第一个测试是冲突测试。这儿,我准备了一些消息文本,并且让这些哈希函数处理每个文本。一个良好设计的哈希函数应当对每个文本生成一个唯一的散列值,甚至在两个文本仅仅有一个字符的差别时。如果冲突发生了,那么冲突应该是非常罕见的,并且复杂度至少是2n。
  • 第二个测试是运行位测试。这个测试揭示了位怎样分布在哈希值上才是良好的。理想情况下,一个哈希值应当具有相互平衡数目的1和0。如果我们把哈希划分为两相等的部分,并计算每一半中1的数目,我们应当得到相等的数量。如果有一半比另一半多出1的数量,这意味着有一个潜在的不对称,它可能转换为一个可能的冲突。
  • 下一个测试是雪崩或者瀑布测试。这个测试用于测量哈希函数对文本信息变化的反应能力。理想情况下,一个消息字节的变化应该能引起约半个散列字节的变化;并且这个变化应当顺着哈希值一致地发生。太大或者太小的变化就可能说明有冲突。
  • 最后一个测试是耗时测试。这个用来测量哈希函数处理消息文本的速度。一个良好的哈希函数处理一个大信息文本耗时不超过半秒。更长的哈希耗时是不期望的,除非这个函数做为高流量服务的一部分。

表1显示了SHA-3与前两任相比较的情况。测试的机器是苹果的MacBook,带有2G赫兹的Intel 酷睿2 Duo处理器和2GB的DDR2内存。操作系统是MacOS X 10.6.7。测试的消息是来自威廉.莎士比亚的戏剧哈姆雷特的摘要-“生存还是死亡”独白,由33行文本总共260个单词组成。由于SHA-1仅仅生成160位散列值,而SHA-2和SHA-3的输出限制在224位,所以它们生成大小相比较最接近。
表1:比较SHA-3与其前任

与SHA-1和SHA-2相比较,SHA-3有一个更小点的联动率。对每个消息字节的更改它仅仅更改三个哈希字节。SHA-3显示轻微的位倾斜。它的消息散列的右半部分比左半部分多出12个1位。SHA-3比SHA-2快些,哈希测试文本少花费了2.80纳秒。


表2显示了当生成四个不同大小的散列的时候SHA-3的执行情况。使用同样的机器和同样的消息文本获得的测试结果。
表2:生成四个不同大小的散列时SHA-3的执行情况

这儿,对所有四个大小的散列SHA-3仍然显示了小的联动率。这似乎是Keccak和它的海绵引擎的特性。SHA-3也显示了轻微的倾斜。消息散列的三个都是右半部分比左半部分有更多的1位。然而,512位散列值的左半部分比右半部分有更多的1位。SHA-3仍然快些,产生512位消息散列花费了2.010纳秒。这比256位的SHA-2快2.003纳秒。

这个表没有显示任何冲突测试结果,因为测试本身没有发现冲突。测试的消息可能太短或者太多变化而不能引起冲突。不过,更大点的文本,尤其是具有微小变化的更大文本,对这些哈希函数的一个可能引起至少一个冲突。Xcode项目FooSHA3可用做参考。


部署SHA-3

SHA-3仍然没有进入大多数主流平台。某些平台(如MacOS X和IOS)仍然依赖libcrypto库,这个库只支持SHA-1和SHA-2。把SHA-3部署到你所期望的平台的一种方法是编译这个哈希算法为静态库。然后把新的库安装到/urs/lib或者/usr/share目录下,在这儿假设你对这些目录具有写的权限。不过还有另一种方法去部署SHA-3,这种方法可以工作在Mac OS X和IOS上,即以框架捆绑包的形式部署。

框架捆绑包是一个层次性的文件集合,这些文件一起才形成一个动态库。不像大多数捆绑包,框架捆绑包是透明的;它的内容像其他普通目录的内容一样可以随意浏览。框架捆绑包可以位于常见的目录(比如~/Libray/Framworks),或者把框架捆绑包拷贝到应用捆绑包里,然后把它当作私有资源来用。对框架捆绑包的访问是通过NSBunble类或者通过核心基础的捆绑服务来实现的。


图4显示了SHA3框架捆绑包的结构。它的最顶部是四个目录连接和一个名字为Versions的目录。Versions目录里是另一个目录连接和子目录A。

图4:以框架捆绑包的形式安装的SHA-3


清单1显示了如何给NSString类增加SHA-3支持。这儿,类SHA3String 声明了两个方法原型(行8-9)。类方法sha3AsCstring使用实例方法cStringUsingEncoding:(行25)提取原始的消息字节。它声明标准的ASCII做为所期望的文本编码。这个方法计算了消息字节数,并且创建哈希变量tHash(行29-30)。 然后传递字节数,消息文本和哈希变量给入口函数Hash()(行33)。Hash()函数返回512位的消息散列值,接着sha3AsCstring 重新强制转换这个散列值为一个常量字符串(行36)。顺便说明一下,BitSequence是Keccak对char类型的自定义。

// -- File:SHA3Category.h
//
#import <Cocoa/Cocoa.h>
#import <string.h>
#import "KeccakNISTInterface.h"

@interface NSString (SHA3String)
- (const char *)sha3AsCstring;
- (NSData *)sha3AsData;
//...
@end

// -- File: SHA3Category.m
//
@implementation NSString (SHA3String)
// Return the SHA-3 hash as a C-string
- (const char *)sha3AsCstring
{
    const char *tTemp;
    const BitSequence *tData;
    BitSequence *tHash;
    NSUInteger tLen;

    // extract the message data
    tTemp = [self cStringUsingEncoding:NSASCIIStringEncoding];
    tData = (const BitSequence *)tTemp;

    // prepare the hash variable
    tLen = strlen(tTemp);
    tHash = malloc(sizeof(BitSequence) * 32);

    // hash the message data
    Hash(512, tData, tLen, tHash);

    // return the hash result
    tTemp = (const char *)tHash;
    return (tTemp);
}

// Return the SHA-3 hash as an NSData object
- (NSData *)sha3AsData
{
    const char *tHash;
    NSUInteger tLen;
    NSData *tData;

    // generate the SHA-3 hash
    tHash = [self sha3AsCstring];

    // prepare the hash object
    tLen = strlen(tHash);
    tData = [NSData dataWithBytes:tHash 
        length:tLen];

    // return the hash object
    return (tData);
}

//...
@end

类方法sha3AsData返回NSData对象形式的消息散列值。它依靠sha3AsCstring来处理消息字节(行48)。然后使用工厂方法dataWithBytes:length: 创建这个对象(行52-53)。


清单2显示出SHA-3是一个单例模式的类. 单例模式提供一个公共的全局访问点来访问SHA-3算法。它自己操纵自己的内存管理,所以,不需要显式的处理。

// -- File:SHA3Single.h
//
#import <Cocoa/Cocoa.h>
#import "KeccakNISTInterface.h"

// Define the enum constants
typedef enum _SHA3_SIZE
    {SIZE_224 = 224, SIZE_256 = 256,
        SIZE_384 = 384, SIZE_512 = 512}
    SHA3_SIZE;


// Declare the class interface
@interface SHA3Single : NSObject
{
    @private
        NSUInteger pSize;
}
@property (readwrite) NSUInteger hashSize;

+ (id)defaultGenerator;
+ (id)generatorForSize:(NSUInteger)aSize;

- (void)hashMessage:(const char *)aMesg
    into:(BitSequence *)aHash;
- (void)hashObject:(id)aMesg
    into:(BitSequence *)aHash;
@end

// -- File: SHA3Single.m
//
// Declare the static global
static SHA3Single *gHash;

// Define the singleton class
@implementation SHA3Single
@synthesize hashSize = pSize;

// Create the default SHA3 generator
+ (id)defaultGenerator
{
    @synchronized(self)
    {
        // check the static global
        if (gHash == nil)
        {
            // create the instance
            gHash = [[super alloc] init];

            // set the output hash size
            gHash.hashSize = SIZE_512;
        }
    }

    // return the singleton instance
    return (gHash);
}

// Create a "custom" SHA3 generator
+ (id)generatorForSize:(NSUInteger)aSize
{
    @synchronized(self)
    {
        // check the static global
        if (gHash == nil)
        {
            // create the instance
            gHash = [[super alloc] init];

            // set the output hash size
            gHash.hashSize = aSize;
        }
    }

    // return the singleton instance
    return (gHash);
}

- (void)hashMessage:(const char *)aMesg
    into:(BitSequence *)aHash
{
    NSUInteger tLen;

    // generate the SHA-3 hash
    tLen = strlen(aMesg);
    Hash(self.hashSize,
        (const BitSequence *)aMesg, tLen
        , aHash);
}

- (void)hashObject:(id)aMesg
    into:(BitSequence *)aHash
{
    const char *tData;
    NSUInteger tLen;

    // parametre check
    if ((aMesg != nil) && (aHash != nil))
    {
        // identify the message object
        if ([aMesg
            isKindOfClass:[NSString class]])
            // -- object:NSString
            tData = [aMesg cStringUsingEncoding:NSASCIIStringEncoding];
        else if ([aMesg
            isKindOfClass:[NSData class]])
        {
            // -- object:NSData
            tLen = [aMesg length];
            tData = malloc(tLen * sizeof(char));

            [aMesg getBytes:(void *)tData];
        }
        else
        {
            // handle other Cocoa objects
            // -- RESERVED
        }

        // hash the raw message bytes
        tLen = strlen(tData);
        Hash(self.hashSize,
            (const BitSequence *)tData, tLen
            , aHash);
    }
}

@end

单例类SHA3 首先声明一个私有的属性pSize(17行)。这个属性设置希望的散列位数大小。这个类也声明了属性变量hashSize和四个方法原型,其中两个是工厂方法(19-27行)。

接下来,该类声明了静态全局的变量gHash来引用单例实例(33行)。 然后定义了hashSize变量,两个工厂方法和两个实例方法。工厂方法defaultGenerator检测全局变量gHash (45行) 且生成SHA3单例的一个实例(48行).还设置pSize属性默认散列值大小为512位(51行).


工厂方法generatorForSize: 把哈希大小做为输入。同时它检查全局变量gHash,并且创建SHA3Single 实例。而且它更新pSize属性为指定的大小(行71)。

实例方法hashMessage:into: 把消息文本(aMesg)和哈希变量(aHash)做为输入。它计算了消息字节数,并且把消息文本 字节数和哈希变量传递给入口函数Hash()(行85-88)。然后Hash()函数把消息散列值存储到变量aHash里。

实例方法hashObject:into: 把通用的Cocoa对象(aMesg)做为其输入之一。它使用实例方法isKingdOfClass:(行101-102,105-106)来识别这个对象。一旦识别到这个对象的类型,hashObject:into:使用正确的代码提取原始消息文本(行104,112)。这时它也计算消息字节数,并且使用Hash()生成消息散列值(行121-124)。接着把得到的散列值存储到aHash里。

为了使用SHA-3单例模型,必须使用适当的工厂方法创建实例。下面的代码片段创建了SHA-3的512位消息散列。

SHA3Single *tSHA3;
tSHA3 = [SHA3Single defaultGenerator];

然后使用适当的实例方法处理这个消息。例如,接下来的代码片段使用了方法hashMessage:into:处理NSString对象。

NSString *tMesg;
const char *tHash;
//...
tMesg = [NSString stringWithString:@"foobar"];
[tSHA3 hashMessage:tMesg into:(BitSequence *)tHash];

Xcode的SHA3框架项目可用作参考。


结论

总体来说,Keccak 是 SHA-3 标准的一个不错的选择。它迅速,且位分布均匀,抗碰撞性好。但是,数字军备竞赛远未结束,也许数年之后,Keccak 亦会遭受狙击。当此发生之际,NIST 将寻求新的哈希标准。但那些,将是另一个时间讨论的话题。

  • 4
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值