信息摘要算法之三:SHA256算法分析与实现

前面一篇中我们分析了SHA的原理,并且以SHA1为例实现了相关的算法,在这一片中我们将进一步分析SHA2并实现之。

1、SHA简述

前面的篇章中我们已经说明过,SHA实际包括有一系列算法,分别是SHA-1、SHA-224、SHA-256、SHA-384以及SHA-512。而我们所说的SHA2实际是对后面4中的统称。各种SHA算法的数据比较如下表,其中的长度单位均为位:

类别

SHA-1

SHA-224

SHA-256

SHA-384

SHA-512

消息摘要长度

160

224

256

384

512

消息长度

小于 位

小于 位

小于 位

小于 位

小于 位

分组长度

512

512

512

1024

1024

计算字长度

32

32

32

64

64

计算步骤数

80

64

64

80

80

从上表中我们不难发现,SHA-224和SHA-256、SHA-384和SHA-512在消息长度、分组长度、计算字长以及计算步骤各方面分别都是一致的。事实上通常认为SHA-224是SHA-256的缩减版,而SHA-384是SHA-512的缩减版。所以在接下来的讨论中,我们把SHA-224和SHA-256作为一组,而把SHA-384和SHA-512作为另一组来讨论。在这一篇我们先来分析和实现SHA-224和SHA-256算法。

2、消息的填充与解析

在这里我们讨论的散列函数用于在计算机中将根据作为输入消息或者数据文件生成其对应的信息摘要。消息或数据文件通常被作为是位字符串。消息的长度是消息中的比特数(空消息的长度为0)。如果消息中的比特数是8的倍数,那么我们就可以用十六进制来表示消息的紧凑性。消息填充的目的是为了在消息填充后,在SHA-224和SHA-256中消息的长度正好是512位的整数倍。

接下来我们说明消息或者数据文件将如何实现填充。总的来说就是,先添加一个“1”,再后跟多个“0”,然后再追加一个64位的消息长度信息,使得填充完成后的消息长度正好是512位的整数倍。追加的64位的消息长度信息是原始消息的位长,填充完成的消息会被分成512位的消息分组。

对于SHA-224和SHA-256来说消息的最大长度L<264,在对消息进行散列运算之前需要对消息做相应的填充处理。

首先,在原始信息之后填充一个“1”,例如:如果原始信息是"01010000",完成这一填充之后就是"010100001"。

接下来,在完成上一步填充后,在其后面需天充一定数量的“0”,数量记为K,则K的取值必须是满足下述表达式的最小非负整数值。

( L + 1 + K ) mod 512 =448

最后,在填充完必的消息后,追加64位的原始消息长度,因为消息的长度不会超过264位,所以其长度数据的值不会超过64位。填充完毕后,所有的消息分组都将是一个512位。

3、迭代函数与常数

SHA算法这类散列算法的计算过程都需要用到逻辑函数和计算常量。但由于具体算法的不同所使用的具体的函数和常数略有差别。我们在前面的篇章中说过MD5和SHA1,它们都有4个逻辑函数,而在SHA2的一系列算法中都采用了6个逻辑函数。接下来将说明SHA-224和SHA-256的逻辑函数和常量。

SHA-224和SHA-256采用6个逻辑函数,每个函数均基于32位字运算,这些输入的32位字我们记为x、y、z,同样的这些函数的计算结果也是一个32位字。这些逻辑函数表示如下:

        CH( x, y, z) = (x AND y)XOR ( (NOT x) AND z)

        MAJ( x, y, z) = (x AND y)XOR (x AND z) XOR (y AND z)

        BSIG0(x) = ROTR^2(x) XORROTR^13(x) XOR ROTR^22(x)

        BSIG1(x) = ROTR^6(x) XORROTR^11(x) XOR ROTR^25(x)

        SSIG0(x) = ROTR^7(x) XORROTR^18(x) XOR SHR^3(x)

        SSIG1(x) = ROTR^17(x) XORROTR^19(x) XOR SHR^10(x)

SHA-224和SHA-256采用相同的,64个32位的常数序列。通常记为:K0、K1、……、K63,这些常数的取值是前64个质数的立方根的小数部分的前32位。这些数以16进制表示如下:

        428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5

        d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174

        e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da

        983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967

        27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85

        a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070

        19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3

        748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2

4、计算过程

前面,我们已经介绍了消息的预处理及散列逻辑函数,接下来我们将说明摘要的计算过程。

每个安全散列函数的输出,在应用到一个分为N个分组的消息后,结果记为散列量H(N)。对于SHA-224和SHA-256,H(i)可以被认为是8个32位的字,记为:H(i)0、H(i)1、…、H(i)7。

散列字被初始化为一个特定的值,并在处理完每一个消息分组后对它进行更新,并在处理最后一个块后将其连接起来以产生输出。对于SHA-256,所有的H(N)变量都是串联的,而SHA-224散列值是通过最后连接时,省略一些而产生的。

接下来我们说明一下SHA-224和SHA-256的计算过程。首先初始化链接变量。对于SHA-224来说,初始散列值H(0)由以下8个32位的十六进制数组成:

        H(0)0 = c1059ed8

        H(0)1 = 367cd507

        H(0)2 = 3070dd17

        H(0)3 = f70e5939

        H(0)4 = ffc00b31

        H(0)5 = 68581511

        H(0)6 = 64f98fa7

        H(0)7 = befa4fa4

而对于SHA-256来说,初始散列值H(0)由以下8个32位的十六进制数组成。这些字由前8个质数的平方根的小数部分的钱32位组成。

        H(0)0 = 6a09e667

        H(0)1 = bb67ae85

        H(0)2 = 3c6ef372

        H(0)3 = a54ff53a

        H(0)4 = 510e527f

        H(0)5 = 9b05688c

        H(0)6 = 1f83d9ab

        H(0)7 = 5be0cd19

接下来我们描述一下摘要计算,SHA-224和SHA-256在消息分组执行相同的处理,只在初始化H(0)和如何生成最终输出的过程中有所不同。SHA-224和SHA-256可以用来散列处理长度为L位的消息,其中0 < L< =264。算法使用一个64个32位字的消息列表, 8个工作变量32位以及8个32位字的散列值。

消息列表每32位分为一个子分组,被标记为W0、W1、…、W63。8个工作变量分别为a、b、c、d、e、f、g和h,8个散列值被标记为h(i)0、h(i)1、…、H(i)7,并保留初始散列值H(0),替换为每一个连续的中间散列值(在处理完每个消息分组后) H(i),并以最终的散列值H(N)结束,在处理完所有N块后。它们还使用两个临时变量T1和T2。

从前面我们知道,填充完了之后消息被分为了512位的消息分组。每个分组被分为16个32位的子分组,记为:M(i)0、M(i)1、...、M(i)15。将对N个消息分组进行如下操作。

a、64个消息列表的生成

        从 t = 0 到 15 —— Wt = M(i)t

        从t = 16 t到 63 —— Wt =SSIG1(W(t-2)) + W(t-7) + SSIG0(w(t-15)) + W(t-16)

b、初始化工作变量

        a = H(i-1)0

        b = H(i-1)1

        c = H(i-1)2

        d = H(i-1)3

        e = H(i-1)4

        f = H(i-1)5

        g = H(i-1)6

        h = H(i-1)7

c、执行散列计算

        从 t = 0 到 63 :

                T1 = h +BSIG1(e) + CH(e,f,g) + Kt + Wt

                T2 = BSIG0(a)+ MAJ(a,b,c)

                h = g

                g = f

                f = e

                e = d + T1

                d = c

                c = b

                b = a

                a = T1 + T2

d、计算中间散列值

        H(i)0 = a + H(i-1)0

        H(i)1 = b + H(i-1)1

        H(i)2 = c + H(i-1)2

        H(i)3 = d + H(i-1)3

        H(i)4 = e + H(i-1)4

        H(i)5 = f + H(i-1)5

        H(i)6 = g + H(i-1)6

        H(i)7 = h + H(i-1)7

在对所有消息分组完成上述计算之后,计算最终输出。对于SHA-256,是所有H(N)0、H(N)1到H(N)7的串联。对于SHA-224,则是H(N)0、H(N)1直到H(N)6的串联。

5、代码实现

前面我们已经说明了SHA-256(SHA-224)的计算过程,接下来我们将这一过程代码化。同样的首先定义一个上下文的结构。

/** 定义SHA-256哈希操作的内容信息结构体 */
typedef struct SHA256Context {uint32_tIntermediate_Hash[SHA256HashSize/4]; /* 信息摘要 */uint32_t Length_High;                         /* 按位计算的信息长度高字 */uint32_t Length_Low;                          /* 按位计算的信息长度低字 */int_least16_tMessage_Block_Index;            /* 信息分组数组的索引 */uint8_tMessage_Block[SHA256_Message_Block_Size];/* 512位信息分组 */int Computed;                                 /* 摘要计算标识 */int Corrupted;                                /* 信息摘要损坏标识 */
} SHA256Context;

接下来实现SHA256Context结构的初始化,为后续的计算过程做准备。

static SHAStatusCode SHA224_256Reset(SHA256Context *context, uint32_t *H0)
{if (!context) return shaNull;context->Length_High =context->Length_Low = 0;context->Message_Block_Index = 0;context->Intermediate_Hash[0] = H0[0];context->Intermediate_Hash[1] = H0[1];context->Intermediate_Hash[2] = H0[2];context->Intermediate_Hash[3] = H0[3];context->Intermediate_Hash[4] = H0[4];context->Intermediate_Hash[5] = H0[5];context->Intermediate_Hash[6] = H0[6];context->Intermediate_Hash[7] = H0[7];context->Computed = 0;context->Corrupted = shaSuccess;return shaSuccess;
}

接下来实现信息分组的输入,这个函数接受一个字节数组作为下一个消息分组以便进行处理。

SHAStatusCode SHA256Input(SHA256Context *context, const uint8_t *message_array,unsigned intlength)
{if (!context) return shaNull;if (!length) return shaSuccess;if (!message_array) return shaNull;if (context->Computed) returncontext->Corrupted = shaStateError;if (context->Corrupted) returncontext->Corrupted;while (length--){context->Message_Block[context->Message_Block_Index++]=*message_array;if ((SHA224_256AddLength(context, 8) ==shaSuccess) &&(context->Message_Block_Index ==SHA256_Message_Block_Size))SHA224_256ProcessMessageBlock(context);message_array++;}return context->Corrupted;
}

当然还需要一个消息处理及最终摘要输出的函数,这个函数将返回一个224位或者256位的信息摘要到调用者给定的Message_Digest数组。返回的信息摘要,第一个元素索引为0,最后一个元素索引为27(SHA-2244)或者31(SHA-256)。

static SHAStatusCode SHA224_256ResultN(SHA256Context *context,uint8_t Message_Digest[], int HashSize)
{int i;if (!context) return shaNull;if (!Message_Digest) return shaNull;if (context->Corrupted) returncontext->Corrupted;if (!context->Computed)SHA224_256Finalize(context, 0x80);for (i = 0; i < HashSize; ++i)Message_Digest[i] =(uint8_t)(context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i& 0x03 ) ));return shaSuccess;
}

至此我们就完成了SHA-256(SHA-224)的编码,在后续我们将对这一编码进行验证。

6、结论

上一节我们实现了SHA-256(SHA-224)的编码,接下来我们来对这一实现进行验证。我们输入明文“abcd”并输出结果:

同时我们对比一下其他工具生成的“abcd”的SHA-256的信息摘要结果如下:

对比上述两个结果,我们发现是完全一致的,说明我们的编码是没有问题的。

欢迎关注:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/499485.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

focal loss的几种实现版本(Keras/Tensorflow)

起源于在工作中使用focal loss遇到的一个bug&#xff0c;我仔细的学习多个靠谱的focal loss讲解及实现版本 通过测试&#xff0c;我发现了这样一个奇怪的现象&#xff0c;几乎每个版本的focal loss实现对同样的输入计算出的loss都是不同的。 通过仔细的比对和思考&#xff0c…

基于ARM Cortex-M和Eclipse的SWO单总线输出

最近在MCU on Eclipse网站上看到Erich Styger所写的一篇有关通过SWD的跟踪接口SWO获取ARM Cortex-M相关信息的文章&#xff0c;文章结构明晰&#xff0c;讲解透彻&#xff0c;本人深受启发&#xff0c;特意将其翻译过来供各位同仁参考。当然限于个人水平&#xff0c;有不当之处…

PID控制器开发笔记之九:基于前馈补偿的PID控制器的实现

对于一般的时滞系统来说&#xff0c;设定值的变动会产生较大的滞后才能反映在被控变量上&#xff0c;从而产生合理的调节。而前馈控制系统是根据扰动或给定值的变化按补偿原理来工作的控制系统&#xff0c;其特点是当扰动产生后&#xff0c;被控变量还未变化以前&#xff0c;根…

借助百度识图爬取数据集

背景 一个能够实际应用的深度学习模型&#xff0c;背后的数据集往往都花费了大量的人力财力&#xff0c;通过聘用标注团队对真实场景数据进行标注生产出来&#xff0c;大多数情况不太可能使用网络来源的图片。但在项目初期的demo阶段&#xff0c;或者某些特定的场合下&#xf…

通过printf从目标板到调试器的输出

最近在SEGGER的博客上看到Johannes Lask写的一篇关于在调试时使用printf函数从目标MCU输出信息到调试器的文章&#xff0c;自我感觉很有启发&#xff0c;特此翻译此文并推荐给各位同仁。当然限于个人水平&#xff0c;有不当之处恳请指正。原文网址&#xff1a;https://blog.seg…

小心使用tf.image.resize_images,填坑经验分享给你

上上周&#xff0c;我在一个项目上线前对模型进行测试时出现了问题&#xff0c;这个问题困扰了我近两周&#xff0c;终于找到了问题根源&#xff0c;做个简短总结分享给你&#xff0c;希望对大家有帮助。 问题描述&#xff1a; 线上线下测试结果不一致&#xff0c;且差异很大…

PID控制器开发笔记之十:步进式PID控制器的实现

对于一般的PID控制系统来说&#xff0c;当设定值发生较大的突变时&#xff0c;很容易产生超调而使系统不稳定。为了解决这种阶跃变化造成的不利影响&#xff0c;人们发明了步进式PID控制算法。 1、步进式PID的基本思想 所谓步进式PID算法&#xff0c;实际就是在设定值发生阶跃…

AutoML 与 Bayesian Optimization 概述

1. AutoML 概述 AutoML是指对于一个超参数优化任务&#xff08;比如规定计算资源内&#xff0c;调整网络结构找到准确率最高的网络&#xff09;&#xff0c;尽量减少人为干预&#xff0c;使用某种学习机制&#xff0c;来调节这些超参数&#xff0c;使得目标问题达到最优。 这…

使用Eclipse进行Makefile项目

最近在MCU on Eclipse网站上看到Erich Styger所写的一篇有关在Eclipse中使用Makefile创建项目的文章&#xff0c;文章讲解清晰明了非常不错&#xff0c;所以呢没人将其翻译过来供各位同仁参考。当然限于个人水平&#xff0c;有不当之处恳请指正。原文网址&#xff1a;https://m…

C语言学习及应用笔记之一:C运算符优先级及使用问题

C语言中的运算符绝对是C语言学习和使用的一个难点&#xff0c;因为在2011版的标准中&#xff0c;C语言的运算符的数量超过40个&#xff0c;甚至比关键字的数量还要多。这些运算符有单目运算符、双目运算符以及三目运算符&#xff0c;又涉及到左结合和右结合的问题&#xff0c;真…

使用FreeRTOS进行性能和运行时分析

在MCU on Eclipse网站上看到Erich Styger在2月25日发的博文&#xff0c;一篇关于使用FreeRTOS进行性能和运行分析的文章&#xff0c;本人觉得很有启发&#xff0c;特将其翻译过来以备参考。当然限于个人水平&#xff0c;有描述不当之处恳请指正。原文网址&#xff1a;https://m…

生成微信公众号对应二维码的两种简单方法

方法1 在浏览器中打开下面的链接 https://open.weixin.qq.com/qr/code?usernameName 其中Name替换为对应公众号的微信号 例如&#xff0c;我们打算生成公众号 AI算法联盟 的二维码 只需首先关注这个公众号 在其详细信息中&#xff0c;查找到微信号信息&#xff1a;AIReport…

在Amazon FreeRTOS V10中使用运行时统计信息

在MCU on Eclipse网站上看到Erich Styger在8月2日发的博文&#xff0c;一篇关于在Amazon FreeRTOS V10中使用运行时统计信息的文章&#xff0c;本人觉得很有启发&#xff0c;特将其翻译过来以备参考。原文网址&#xff1a;https://mcuoneclipse.com/2018/08/02/tutorial-using-…

github无法加载图片的解决办法

最近发现我的github上面项目README里面的图片全裂了&#xff0c;一直以为是github最近服务器不稳定。今天通过简单的查询&#xff0c;发现原来这个问题可以解决&#xff0c;但是不能永久有效&#xff0c;之后还会用到&#xff0c;因此记录在这里&#xff0c; 也分享给大家。 解…

C语言学习及应用笔记之二:C语言static关键字及其使用

C语言有很多关键字&#xff0c;大多关键字使用起来是很明确的&#xff0c;但有一些关键字却要相对复杂一些。我们这里要说明的static关键字就是如此&#xff0c;它的功能很强大&#xff0c;相应的使用也就更复杂。 一般来说static关键字的常见用法有三种&#xff1a;一是用作局…

μCUnit,微控制器的单元测试框架

在MCU on Eclipse网站上看到Erich Styger在8月26日发布的博文&#xff0c;一篇关于微控制器单元测试的文章&#xff0c;有很高的参考价值&#xff0c;特将其翻译过来以备学习。原文网址&#xff1a;https://mcuoneclipse.com/2018/08/26/tutorial-%CE%BCcunit-a-unit-test-fram…

PID控制器开发笔记之十一:专家PID控制器的实现

前面我们讨论了经典的数字PID控制算法及其常见的改进与补偿算法&#xff0c;基本已经覆盖了无模型和简单模型PID控制经典算法的大部。再接下来的我们将讨论智能PID控制&#xff0c;智能PID控制不同于常规意义下的智能控制&#xff0c;是智能算法与PID控制算法的结合&#xff0c…

Modbus协议栈开发笔记之七:Modbus ASCII Slave开发

与Modbus RTU在串行链路上分为Slave和Master一样&#xff0c;Modbus ASCII也分为Slave和Master&#xff0c;这一节我们就来开发Slave。对于Modbus ASCII从站来说&#xff0c;需要实现的功能其实与Modbus RTU的Slave是一样的。其操作过程也是一样的。首先接收到主站的访问命令&a…

Modbus协议栈开发笔记之八:Modbus ASCII Master开发

这一节我们来封装Modbus ASCII Master应用&#xff0c;Modbus ASCII主站的开发与RTU主站的开发是一致的。同样的我们也不是做具体的应用&#xff0c;而是实现ASCII主站的基本功能。我们将ASCII主站的功能封装为函数&#xff0c;以便在开发具体应用时调用。 对于ASCII主站我们主…

在Eclipse中使用Git

本文原文出自MCU on Eclipse网站&#xff0c;作者为Erich Styger&#xff0c;原文网址&#xff1a;https://mcuoneclipse.com/2018/09/30/tutorial-git-with-eclipse/。本人翻译了此篇文章&#xff0c;有些短语难以找到准确表达的中文词语&#xff0c;所以保持了原文。限于个人…