SHA1算法实现及详解

1 SHA1算法简介

安全哈希算法(Secure Hash Algorithm)主要适用于数字签名标准(Digital Signature Standard DSS)里面定义的数字签名算法(Digital Signature Algorithm DSA)。对于长度小于2^64位的消息,SHA1会产生一个160位的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。

SHA1有如下特性:不可以从消息摘要中复原信息;两个不同的消息不会产生同样的消息摘要。


术语和概念

2.1(Bit),字节(Byte)和字(Word

SHA1始终把消息当成一个位(bit)字符串来处理。本文中,一个“字”(Word)是32位,而一个“字节”(Byte)是8位。比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。它也可以被表示成16进制字符串: 0x616263.

2.2 运算符和符号

下面的逻辑运算符都被运用于“字”(Word

X^Y    = X Y逻辑与

X \/ Y   = X Y逻辑或

X XOR Y= X Y逻辑异或

~X     =   X逻辑取反

X+Y定义如下:

 X  Y 代表两个整数 x y, 其中 0 <= x < 2^32  0 <= y < 2^32. 令整数z = (x + y) mod 2^32. 这时候 0 <= z < 2^32. z转换成字Z, 那么就是 Z = X + Y.

循环左移位操作符Sn(X)X是一个字,n是一个整数,0<=n<=32Sn(X) = (X<<n)OR(X>>32-n)

X<<n定义如下:抛弃最左边的n位数字,将各个位依次向左移动n位,然后用0填补右边的n位(最后结果还是32位)。X>>n是抛弃右边的n位,将各个位依次向右移动n位,然后在左边的n位填0。因此可以叫Sn(X)位循环移位运算


3 SHA1算法描述

SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。SHA1算法只接受位作为输入。假设我们对字符串“abc”产生消息摘要。首先,我们将它转换成位字符串如下:

01100001 01100010 01100011

―――――――――――――

‘a’=97   ‘b’=98   ‘c’=99

这个位字符串的长度为24。下面我们需要5个步骤来计算MD5

3.1 补位

       消息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)%512 = 448。即使长度已经满足对512取模后余数是448,补位也必须要进行。

       补位是这样进行的:先补一个1,然后再补0,直到长度满足对512取模后余数是448。总而言之,补位是至少补一位,最多补512位。还是以前面的“abc”为例显示补位的过程。

原始信息: 01100001 01100010 01100011

补位第一步:01100001 01100010 01100011 1

                                                                首先补一个“1

补位第二步:01100001 01100010 01100011 10…..0

                                                                然后补423个“0

我们可以把最后补位完成后的数据用16进制写成下面的样子

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000

现在,数据的长度是448了,我们可以进行下一步操作。

3.2 补长度

       所谓的补长度是将原始数据的长度补到已经进行了补位操作的消息后面。通常用一个64位的数据来表示原始消息的长度。如果消息长度不大于2^64,那么第一个字就是0。在进行了补长度的操作以后,整个消息就变成下面这样了(16进制格式)

61626380 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000000

00000000 00000000 00000000 00000018

如果原始的消息长度超过了512,我们需要将它补成512的倍数。然后我们把整个消息分成一个一个512位的数据块,分别处理每一个数据块,从而得到消息摘要。

3.3 使用的常量

一系列的常量字K(0), K(1), ... , K(79),如果以16进制给出。它们如下:

Kt = 0x5A827999  (0 <= t <= 19)

Kt = 0x6ED9EBA1 (20 <= t <= 39)

Kt = 0x8F1BBCDC (40 <= t <= 59)

Kt = 0xCA62C1D6 (60 <= t <= 79).

3.4 需要使用的函数

SHA1中我们需要一系列的函数。每个函数ft (0 <= t <= 79)都操作32位字BCD并且产生32位字作为输出。ft(B,C,D)可以如下定义

ft(B,C,D) = (B AND C) OR ((NOT B) AND D) ( 0 <= t <= 19)

ft(B,C,D) = B XOR C XOR D              (20 <= t <= 39)

ft(B,C,D) = (B AND C) OR (B AND D) OR (C AND D) (40 <= t <= 59)

ft(B,C,D) = B XOR C XOR D                     (60 <= t <= 79).

3.5 计算消息摘要

必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个缓冲区,每个都由532位的字组成,还需要一个8032位字的缓冲区。第一个5个字的缓冲区被标识为ABCDE。第一个5个字的缓冲区被标识为H0, H1, H2, H3, H4

80个字的缓冲区被标识为W0, W1,..., W79


另外还需要一个一个字的TEMP缓冲区。

为了产生消息摘要,在第4部分中定义的16个字的数据块M1, M2,..., Mn

会依次进行处理,处理每个数据块Mi 包含80个步骤。

在处理每个数据块之前,缓冲区{Hi被初始化为下面的值(16进制)

H0 = 0x67452301

H1 = 0xEFCDAB89

H2 = 0x98BADCFE

H3 = 0x10325476

H4 = 0xC3D2E1F0. 
现在开始处理M1, M2, ... , Mn为了处理 Mi,需要进行下面的步骤

(1).  Mi 分成 16 个字 W0, W1, ... , W15,  W0 是最左边的字

(2). 对于 t = 16  79  Wt = S1(Wt-3 XOR Wt-8 XOR Wt- 14 XOR Wt-16).

(3).  A = H0, B = H1, C = H2, D = H3, E = H4.

(4) 对于 t = 0  79,执行下面的循环

TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt;

E = D; D = C; C = S30(B); B = A; A = TEMP;

(5).  H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. 
在处理完所有的 Mn后,消息摘要是一个160位的字符串,以下面的顺序标识

H0 H1 H2 H3 H4.

对于SHA256,SHA384,SHA512。你也可以用相似的办法来计算消息摘要。对消息进行补位的算法完全是一样的。


实现代码:

#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>//字节序的小头和大头的问题
#define ZEN_LITTLE_ENDIAN  0x0123
#define ZEN_BIG_ENDIAN     0x3210//目前所有的代码都是为了小头党服务的,不知道有生之年这套代码是否还会为大头党服务一次?
#ifndef ZEN_BYTES_ORDER
#define ZEN_BYTES_ORDER    ZEN_LITTLE_ENDIAN
#endif#ifndef ZEN_SWAP_UINT16
#define ZEN_SWAP_UINT16(x)  ((((x) & 0xff00) >>  8) | (((x) & 0x00ff) <<  8))
#endif
#ifndef ZEN_SWAP_UINT32
#define ZEN_SWAP_UINT32(x)  ((((x) & 0xff000000) >> 24) | (((x) & 0x00ff0000) >>  8) | \(((x) & 0x0000ff00) <<  8) | (((x) & 0x000000ff) << 24))
#endif
#ifndef ZEN_SWAP_UINT64
#define ZEN_SWAP_UINT64(x)  ((((x) & 0xff00000000000000) >> 56) | (((x) & 0x00ff000000000000) >>  40) | \(((x) & 0x0000ff0000000000) >> 24) | (((x) & 0x000000ff00000000) >>  8) | \(((x) & 0x00000000ff000000) << 8 ) | (((x) & 0x0000000000ff0000) <<  24) | \(((x) & 0x000000000000ff00) << 40 ) | (((x) & 0x00000000000000ff) <<  56))
#endif//将一个(字符串)数组,拷贝到另外一个uint32_t数组,同时每个uint32_t反字节序
void *swap_uint32_memcpy(void *to, const void *from, size_t length)
{memcpy(to, from, length);size_t remain_len =  (4 - (length & 3)) & 3;//数据不是4字节的倍数,补充0if (remain_len){for (size_t i = 0; i < remain_len; ++i){*((char *)(to) + length + i) = 0;}//调整成4的倍数length += remain_len;}//所有的数据反转for (size_t i = 0; i < length / 4; ++i){((uint32_t *)to)[i] = ZEN_SWAP_UINT32(((uint32_t *)to)[i]);}return to;
}///MD5的结果数据长度
static const size_t ZEN_MD5_HASH_SIZE   = 16;
///SHA1的结果数据长度
static const size_t ZEN_SHA1_HASH_SIZE  = 20;namespace ZEN_LIB
{/*!
@brief      求某个内存块的MD5,
@return     unsigned char* 返回的的结果,
@param[in]  buf    求MD5的内存BUFFER指针
@param[in]  size   BUFFER长度
@param[out] result 结果
*/
unsigned char *md5(const unsigned char *buf,size_t size,unsigned char result[ZEN_MD5_HASH_SIZE]);/*!
@brief      求内存块BUFFER的SHA1值
@return     unsigned char* 返回的的结果
@param[in]  buf    求SHA1的内存BUFFER指针
@param[in]  size   BUFFER长度
@param[out] result 结果
*/
unsigned char *sha1(const unsigned char *buf,size_t size,unsigned char result[ZEN_SHA1_HASH_SIZE]);
};//================================================================================================
//MD5的算法//每次处理的BLOCK的大小
static const size_t ZEN_MD5_BLOCK_SIZE = 64;//md5算法的上下文,保存一些状态,中间数据,结果
typedef struct md5_ctx
{//处理的数据的长度uint64_t length_;//还没有处理的数据长度uint64_t unprocessed_;//取得的HASH结果(中间数据)uint32_t  hash_[4];
} md5_ctx;#define ROTL32(dword, n) ((dword) << (n) ^ ((dword) >> (32 - (n))))
#define ROTR32(dword, n) ((dword) >> (n) ^ ((dword) << (32 - (n))))
#define ROTL64(qword, n) ((qword) << (n) ^ ((qword) >> (64 - (n))))
#define ROTR64(qword, n) ((qword) >> (n) ^ ((qword) << (64 - (n))))/*!
@brief      内部函数,初始化MD5的context,内容
@param      ctx
*/
static void zen_md5_init(md5_ctx *ctx)
{ctx->length_ = 0;ctx->unprocessed_ = 0;/* initialize state */ctx->hash_[0] = 0x67452301;ctx->hash_[1] = 0xefcdab89;ctx->hash_[2] = 0x98badcfe;ctx->hash_[3] = 0x10325476;
}/* First, define four auxiliary functions that each take as input* three 32-bit words and returns a 32-bit word.*//* F(x,y,z) = ((y XOR z) AND x) XOR z - is faster then original version */
#define MD5_F(x, y, z) ((((y) ^ (z)) & (x)) ^ (z))
#define MD5_G(x, y, z) (((x) & (z)) | ((y) & (~z)))
#define MD5_H(x, y, z) ((x) ^ (y) ^ (z))
#define MD5_I(x, y, z) ((y) ^ ((x) | (~z)))/* transformations for rounds 1, 2, 3, and 4. */
#define MD5_ROUND1(a, b, c, d, x, s, ac) { \(a) += MD5_F((b), (c), (d)) + (x) + (ac); \(a) = ROTL32((a), (s)); \(a) += (b); \}
#define MD5_ROUND2(a, b, c, d, x, s, ac) { \(a) += MD5_G((b), (c), (d)) + (x) + (ac); \(a) = ROTL32((a), (s)); \(a) += (b); \}
#define MD5_ROUND3(a, b, c, d, x, s, ac) { \(a) += MD5_H((b), (c), (d)) + (x) + (ac); \(a) = ROTL32((a), (s)); \(a) += (b); \}
#define MD5_ROUND4(a, b, c, d, x, s, ac) { \(a) += MD5_I((b), (c), (d)) + (x) + (ac); \(a) = ROTL32((a), (s)); \(a) += (b); \}/*!
@brief      内部函数,将64个字节,16个uint32_t的数组进行摘要(杂凑)处理,处理的数据自己序是小头数据
@param      state 存放处理的hash数据结果
@param      block 要处理的block,64个字节,16个uint32_t的数组
*/
static void zen_md5_process_block(uint32_t state[4], const uint32_t block[ZEN_MD5_BLOCK_SIZE / 4])
{register unsigned a, b, c, d;a = state[0];b = state[1];c = state[2];d = state[3];const uint32_t *x = NULL;//MD5里面计算的数据都是小头数据.大头党的数据要处理
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIANx = block;
#elseuint32_t swap_block[ZEN_MD5_BLOCK_SIZE / 4];swap_uint32_memcpy(swap_block, block, 64);x = swap_block;
#endifMD5_ROUND1(a, b, c, d, x[ 0],  7, 0xd76aa478);MD5_ROUND1(d, a, b, c, x[ 1], 12, 0xe8c7b756);MD5_ROUND1(c, d, a, b, x[ 2], 17, 0x242070db);MD5_ROUND1(b, c, d, a, x[ 3], 22, 0xc1bdceee);MD5_ROUND1(a, b, c, d, x[ 4],  7, 0xf57c0faf);MD5_ROUND1(d, a, b, c, x[ 5], 12, 0x4787c62a);MD5_ROUND1(c, d, a, b, x[ 6], 17, 0xa8304613);MD5_ROUND1(b, c, d, a, x[ 7], 22, 0xfd469501);MD5_ROUND1(a, b, c, d, x[ 8],  7, 0x698098d8);MD5_ROUND1(d, a, b, c, x[ 9], 12, 0x8b44f7af);MD5_ROUND1(c, d, a, b, x[10], 17, 0xffff5bb1);MD5_ROUND1(b, c, d, a, x[11], 22, 0x895cd7be);MD5_ROUND1(a, b, c, d, x[12],  7, 0x6b901122);MD5_ROUND1(d, a, b, c, x[13], 12, 0xfd987193);MD5_ROUND1(c, d, a, b, x[14], 17, 0xa679438e);MD5_ROUND1(b, c, d, a, x[15], 22, 0x49b40821);MD5_ROUND2(a, b, c, d, x[ 1],  5, 0xf61e2562);MD5_ROUND2(d, a, b, c, x[ 6],  9, 0xc040b340);MD5_ROUND2(c, d, a, b, x[11], 14, 0x265e5a51);MD5_ROUND2(b, c, d, a, x[ 0], 20, 0xe9b6c7aa);MD5_ROUND2(a, b, c, d, x[ 5],  5, 0xd62f105d);MD5_ROUND2(d, a, b, c, x[10],  9,  0x2441453);MD5_ROUND2(c, d, a, b, x[15], 14, 0xd8a1e681);MD5_ROUND2(b, c, d, a, x[ 4], 20, 0xe7d3fbc8);MD5_ROUND2(a, b, c, d, x[ 9],  5, 0x21e1cde6);MD5_ROUND2(d, a, b, c, x[14],  9, 0xc33707d6);MD5_ROUND2(c, d, a, b, x[ 3], 14, 0xf4d50d87);MD5_ROUND2(b, c, d, a, x[ 8], 20, 0x455a14ed);MD5_ROUND2(a, b, c, d, x[13],  5, 0xa9e3e905);MD5_ROUND2(d, a, b, c, x[ 2],  9, 0xfcefa3f8);MD5_ROUND2(c, d, a, b, x[ 7], 14, 0x676f02d9);MD5_ROUND2(b, c, d, a, x[12], 20, 0x8d2a4c8a);MD5_ROUND3(a, b, c, d, x[ 5],  4, 0xfffa3942);MD5_ROUND3(d, a, b, c, x[ 8], 11, 0x8771f681);MD5_ROUND3(c, d, a, b, x[11], 16, 0x6d9d6122);MD5_ROUND3(b, c, d, a, x[14], 23, 0xfde5380c);MD5_ROUND3(a, b, c, d, x[ 1],  4, 0xa4beea44);MD5_ROUND3(d, a, b, c, x[ 4], 11, 0x4bdecfa9);MD5_ROUND3(c, d, a, b, x[ 7], 16, 0xf6bb4b60);MD5_ROUND3(b, c, d, a, x[10], 23, 0xbebfbc70);MD5_ROUND3(a, b, c, d, x[13],  4, 0x289b7ec6);MD5_ROUND3(d, a, b, c, x[ 0], 11, 0xeaa127fa);MD5_ROUND3(c, d, a, b, x[ 3], 16, 0xd4ef3085);MD5_ROUND3(b, c, d, a, x[ 6], 23,  0x4881d05);MD5_ROUND3(a, b, c, d, x[ 9],  4, 0xd9d4d039);MD5_ROUND3(d, a, b, c, x[12], 11, 0xe6db99e5);MD5_ROUND3(c, d, a, b, x[15], 16, 0x1fa27cf8);MD5_ROUND3(b, c, d, a, x[ 2], 23, 0xc4ac5665);MD5_ROUND4(a, b, c, d, x[ 0],  6, 0xf4292244);MD5_ROUND4(d, a, b, c, x[ 7], 10, 0x432aff97);MD5_ROUND4(c, d, a, b, x[14], 15, 0xab9423a7);MD5_ROUND4(b, c, d, a, x[ 5], 21, 0xfc93a039);MD5_ROUND4(a, b, c, d, x[12],  6, 0x655b59c3);MD5_ROUND4(d, a, b, c, x[ 3], 10, 0x8f0ccc92);MD5_ROUND4(c, d, a, b, x[10], 15, 0xffeff47d);MD5_ROUND4(b, c, d, a, x[ 1], 21, 0x85845dd1);MD5_ROUND4(a, b, c, d, x[ 8],  6, 0x6fa87e4f);MD5_ROUND4(d, a, b, c, x[15], 10, 0xfe2ce6e0);MD5_ROUND4(c, d, a, b, x[ 6], 15, 0xa3014314);MD5_ROUND4(b, c, d, a, x[13], 21, 0x4e0811a1);MD5_ROUND4(a, b, c, d, x[ 4],  6, 0xf7537e82);MD5_ROUND4(d, a, b, c, x[11], 10, 0xbd3af235);MD5_ROUND4(c, d, a, b, x[ 2], 15, 0x2ad7d2bb);MD5_ROUND4(b, c, d, a, x[ 9], 21, 0xeb86d391);state[0] += a;state[1] += b;state[2] += c;state[3] += d;
}/*!
@brief      内部函数,处理数据的前面部分(>64字节的部分),每次组成一个64字节的block就进行杂凑处理
@param[out] ctx  算法的context,用于记录一些处理的上下文和结果
@param[in]  buf  处理的数据,
@param[in]  size 处理的数据长度
*/
static void zen_md5_update(md5_ctx *ctx, const unsigned char *buf, size_t size)
{//为什么不是=,因为在某些环境下,可以多次调用zen_md5_update,但这种情况,必须保证前面的调用,每次都没有unprocessed_ctx->length_ += size;//每个处理的块都是64字节while (size >= ZEN_MD5_BLOCK_SIZE){zen_md5_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf));buf  += ZEN_MD5_BLOCK_SIZE;size -= ZEN_MD5_BLOCK_SIZE;}ctx->unprocessed_ = size;
}/*!
@brief      内部函数,处理数据的末尾部分,我们要拼出最后1个(或者两个)要处理的BLOCK,加上0x80,加上长度进行处理
@param[in]  ctx    算法的context,用于记录一些处理的上下文和结果
@param[in]  buf    处理的数据
@param[in]  size   处理buffer的长度
@param[out] result 返回的结果,
*/
static void zen_md5_final(md5_ctx *ctx, const unsigned char *buf, size_t size, unsigned char *result)
{uint32_t message[ZEN_MD5_BLOCK_SIZE / 4];//保存剩余的数据,我们要拼出最后1个(或者两个)要处理的块,前面的算法保证了,最后一个块肯定小于64个字节if (ctx->unprocessed_){memcpy(message, buf + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_));}//得到0x80要添加在的位置(在uint32_t 数组中),uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2;uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8;//添加0x80进去,并且把余下的空间补充0message[index]   &= ~(0xFFFFFFFF << shift);message[index++] ^= 0x80 << shift;//如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个blockif (index > 14){while (index < 16){message[index++] = 0;}zen_md5_process_block(ctx->hash_, message);index = 0;}//补0while (index < 14){message[index++] = 0;}//保存长度,注意是bit位的长度,这个问题让我看着郁闷了半天,uint64_t data_len = (ctx->length_) << 3;//注意MD5算法要求的64bit的长度是小头LITTLE-ENDIAN编码,注意下面的比较是!=
#if ZEN_BYTES_ORDER != ZEN_LITTLE_ENDIANdata_len = ZEN_SWAP_UINT64(data_len);
#endifmessage[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF);message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32);zen_md5_process_block(ctx->hash_, message);//注意结果是小头党的,在大头的世界要进行转换
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIANmemcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE);
#elseswap_uint32_memcpy(result, &ctx->hash_, ZEN_MD5_HASH_SIZE);
#endif}//计算一个内存数据的MD5值
unsigned char *ZEN_LIB::md5(const unsigned char *buf,size_t size,unsigned char result[ZEN_MD5_HASH_SIZE])
{assert(result != NULL);md5_ctx ctx;zen_md5_init(&ctx);zen_md5_update(&ctx, buf, size);zen_md5_final(&ctx, buf, size, result);return result;
}//================================================================================================
//SHA1的算法//每次处理的BLOCK的大小
static const size_t ZEN_SHA1_BLOCK_SIZE = 64;//SHA1算法的上下文,保存一些状态,中间数据,结果
typedef struct sha1_ctx
{//处理的数据的长度uint64_t length_;//还没有处理的数据长度uint64_t unprocessed_;/* 160-bit algorithm internal hashing state */uint32_t hash_[5];
} sha1_ctx;//内部函数,SHA1算法的上下文的初始化
static void zen_sha1_init(sha1_ctx *ctx)
{ctx->length_ = 0;ctx->unprocessed_ = 0;// 初始化算法的几个常量,魔术数ctx->hash_[0] = 0x67452301;ctx->hash_[1] = 0xefcdab89;ctx->hash_[2] = 0x98badcfe;ctx->hash_[3] = 0x10325476;ctx->hash_[4] = 0xc3d2e1f0;
}/*!
@brief      内部函数,对一个64bit内存块进行摘要(杂凑)处理,
@param      hash  存放计算hash结果的的数组
@param      block 要计算的处理得内存块
*/
static void zen_sha1_process_block(uint32_t hash[5],const uint32_t block[ZEN_SHA1_BLOCK_SIZE / 4])
{size_t        t;uint32_t      wblock[80];register uint32_t      a, b, c, d, e, temp;//SHA1算法处理的内部数据要求是大头党的,在小头的环境转换
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIANswap_uint32_memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
#else::memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
#endif//处理for (t = 16; t < 80; t++){wblock[t] = ROTL32(wblock[t - 3] ^ wblock[t - 8] ^ wblock[t - 14] ^ wblock[t - 16], 1);}a = hash[0];b = hash[1];c = hash[2];d = hash[3];e = hash[4];for (t = 0; t < 20; t++){/* the following is faster than ((B & C) | ((~B) & D)) */temp =  ROTL32(a, 5) + (((c ^ d) & b) ^ d)+ e + wblock[t] + 0x5A827999;e = d;d = c;c = ROTL32(b, 30);b = a;a = temp;}for (t = 20; t < 40; t++){temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0x6ED9EBA1;e = d;d = c;c = ROTL32(b, 30);b = a;a = temp;}for (t = 40; t < 60; t++){temp = ROTL32(a, 5) + ((b & c) | (b & d) | (c & d))+ e + wblock[t] + 0x8F1BBCDC;e = d;d = c;c = ROTL32(b, 30);b = a;a = temp;}for (t = 60; t < 80; t++){temp = ROTL32(a, 5) + (b ^ c ^ d) + e + wblock[t] + 0xCA62C1D6;e = d;d = c;c = ROTL32(b, 30);b = a;a = temp;}hash[0] += a;hash[1] += b;hash[2] += c;hash[3] += d;hash[4] += e;
}/*!
@brief      内部函数,处理数据的前面部分(>64字节的部分),每次组成一个64字节的block就进行杂凑处理
@param      ctx  算法的上下文,记录中间数据,结果等
@param      msg  要进行计算的数据buffer
@param      size 长度
*/
static void zen_sha1_update(sha1_ctx *ctx,const unsigned char *buf, size_t size)
{//为了让zen_sha1_update可以多次进入,长度可以累计ctx->length_ += size;//每个处理的块都是64字节while (size >= ZEN_SHA1_BLOCK_SIZE){zen_sha1_process_block(ctx->hash_, reinterpret_cast<const uint32_t *>(buf));buf  += ZEN_SHA1_BLOCK_SIZE;size -= ZEN_SHA1_BLOCK_SIZE;}ctx->unprocessed_ = size;
}/*!
@brief      内部函数,处理数据的最后部分,添加0x80,补0,增加长度信息
@param      ctx    算法的上下文,记录中间数据,结果等
@param      msg    要进行计算的数据buffer
@param      result 返回的结果
*/
static void zen_sha1_final(sha1_ctx *ctx, const unsigned char *msg,size_t size, unsigned char *result)
{uint32_t message[ZEN_SHA1_BLOCK_SIZE / 4];//保存剩余的数据,我们要拼出最后1个(或者两个)要处理的块,前面的算法保证了,最后一个块肯定小于64个字节if (ctx->unprocessed_){memcpy(message, msg + size - ctx->unprocessed_, static_cast<size_t>( ctx->unprocessed_));}//得到0x80要添加在的位置(在uint32_t 数组中),uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2;uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8;//添加0x80进去,并且把余下的空间补充0message[index]   &= ~(0xFFFFFFFF << shift);message[index++] ^= 0x80 << shift;//如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个blockif (index > 14){while (index < 16){message[index++] = 0;}zen_sha1_process_block(ctx->hash_, message);index = 0;}//补0while (index < 14){message[index++] = 0;}//保存长度,注意是bit位的长度,这个问题让我看着郁闷了半天,uint64_t data_len = (ctx->length_) << 3;//注意SHA1算法要求的64bit的长度是大头BIG-ENDIAN,在小头的世界要进行转换
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIANdata_len = ZEN_SWAP_UINT64(data_len);
#endifmessage[14] = (uint32_t) (data_len & 0x00000000FFFFFFFF);message[15] = (uint32_t) ((data_len & 0xFFFFFFFF00000000ULL) >> 32);zen_sha1_process_block(ctx->hash_, message);//注意结果是大头党的,在小头的世界要进行转换
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIANswap_uint32_memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
#elsememcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
#endif
}//计算一个内存数据的SHA1值
unsigned char *ZEN_LIB::sha1(const unsigned char *msg,size_t size,unsigned char result[ZEN_SHA1_HASH_SIZE])
{assert(result != NULL);sha1_ctx ctx;zen_sha1_init(&ctx);zen_sha1_update(&ctx, msg, size);zen_sha1_final(&ctx, msg, size, result);return result;
}int main(int /*argc*/, char * /*argv*/[])
{int ret = 0;static unsigned char test_buf[7][81] ={{ "" }, { "a" },{ "abc" },{ "message digest" },{ "abcdefghijklmnopqrstuvwxyz" },{ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" },{ "12345678901234567890123456789012345678901234567890123456789012345678901234567890" }};static const size_t test_buflen[7] ={0, 1, 3, 14, 26, 62, 80};static const unsigned char md5_test_sum[7][16] ={{ 0xD4, 0x1D, 0x8C, 0xD9, 0x8F, 0x00, 0xB2, 0x04,  0xE9, 0x80, 0x09, 0x98, 0xEC, 0xF8, 0x42, 0x7E },{ 0x0C, 0xC1, 0x75, 0xB9, 0xC0, 0xF1, 0xB6, 0xA8,  0x31, 0xC3, 0x99, 0xE2, 0x69, 0x77, 0x26, 0x61 },{ 0x90, 0x01, 0x50, 0x98, 0x3C, 0xD2, 0x4F, 0xB0,  0xD6, 0x96, 0x3F, 0x7D, 0x28, 0xE1, 0x7F, 0x72 },{ 0xF9, 0x6B, 0x69, 0x7D, 0x7C, 0xB7, 0x93, 0x8D,  0x52, 0x5A, 0x2F, 0x31, 0xAA, 0xF1, 0x61, 0xD0 },{ 0xC3, 0xFC, 0xD3, 0xD7, 0x61, 0x92, 0xE4, 0x00,  0x7D, 0xFB, 0x49, 0x6C, 0xCA, 0x67, 0xE1, 0x3B },{ 0xD1, 0x74, 0xAB, 0x98, 0xD2, 0x77, 0xD9, 0xF5,  0xA5, 0x61, 0x1C, 0x2C, 0x9F, 0x41, 0x9D, 0x9F },{ 0x57, 0xED, 0xF4, 0xA2, 0x2B, 0xE3, 0xC9, 0x55,  0xAC, 0x49, 0xDA, 0x2E, 0x21, 0x07, 0xB6, 0x7A }};unsigned char result[32] ={0};for(size_t i=0;i<7;++i){ZEN_LIB::md5(test_buf[i],test_buflen[i],result);ret = memcmp(result,md5_test_sum[i],16);if (ret != 0){assert(false);}}static const unsigned char sha1_test_sum[7][20] ={{ 0xda,0x39,0xa3,0xee,0x5e,0x6b,0x4b,0x0d,0x32,0x55,0xbf,0xef,0x95,0x60,0x18,0x90,0xaf,0xd8,0x07,0x09 },{ 0x86,0xf7,0xe4,0x37,0xfa,0xa5,0xa7,0xfc,0xe1,0x5d,0x1d,0xdc,0xb9,0xea,0xea,0xea,0x37,0x76,0x67,0xb8 },{ 0xa9,0x99,0x3e,0x36,0x47,0x06,0x81,0x6a,0xba,0x3e,0x25,0x71,0x78,0x50,0xc2,0x6c,0x9c,0xd0,0xd8,0x9d },{ 0xc1,0x22,0x52,0xce,0xda,0x8b,0xe8,0x99,0x4d,0x5f,0xa0,0x29,0x0a,0x47,0x23,0x1c,0x1d,0x16,0xaa,0xe3 },{ 0x32,0xd1,0x0c,0x7b,0x8c,0xf9,0x65,0x70,0xca,0x04,0xce,0x37,0xf2,0xa1,0x9d,0x84,0x24,0x0d,0x3a,0x89 },{ 0x76,0x1c,0x45,0x7b,0xf7,0x3b,0x14,0xd2,0x7e,0x9e,0x92,0x65,0xc4,0x6f,0x4b,0x4d,0xda,0x11,0xf9,0x40 },{ 0x50,0xab,0xf5,0x70,0x6a,0x15,0x09,0x90,0xa0,0x8b,0x2c,0x5e,0xa4,0x0f,0xa0,0xe5,0x85,0x55,0x47,0x32 },};for(size_t i=0;i<7;++i){ZEN_LIB::sha1(test_buf[i],test_buflen[i],result);ret = memcmp(result,sha1_test_sum[i],20);if (ret != 0){assert(false);}}return 0;
}


总结:

通过魔术数和四次二十次迭代逻辑运算,可以比较容易的确定其相应算法。


代码转载来自:

https://www.cnblogs.com/fullsail/archive/2013/02/22/2921505.html

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

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

相关文章

CRC32算法实现

CRC32 检错能力极强&#xff0c;开销小&#xff0c;易于用编码器及检测电路实现。从其检错能力来看&#xff0c;它所不能发现的错误的几率仅为0.0047%以下。从性能上和开销上考虑&#xff0c;均远远优于奇偶校验及算术和校验等方式。因而&#xff0c;在数据存储和数据通讯领域&…

AES算法重点详解和实现

可以看到&#xff0c;在原始数据长度为16的整数倍时&#xff0c;假如原始数据长度等于16*n&#xff0c;则使用NoPadding时加密后数据长度等于16*n&#xff0c;其它情况下加密数据长度等于16*(n1)。在不足16的整数倍的情况下&#xff0c;假如原始数据长度等于16*nm[其中m小于16]…

protobuf流的反解析Message

0x01 protobuf的基本概念 protobuf通过定义".proto"文件来描述数据的结构。.proto文件中用"Message"所表示所需要序列化的数据的格式。Message由field组成&#xff0c;Field类似JAVA或者C中成员变量&#xff0c;通常一个field的定义包含修饰符、类型、名称…

勒索病毒傀儡进程脱壳

样本是&#xff1a;wallet勒索病毒 环境&#xff1a;虚拟机VMWARE win7 32位 工具&#xff1a;OD&#xff0c;winhex 初次拿到样本&#xff0c;先用火绒剑工具监控下病毒样本的流程&#xff0c;可以看到有一个自创建进程的行为。 我们等找到OEP后&#xff0c;在CreateProcessA下…

arm64动态链接库通过函数名获取函数偏移

基本思路是分析elf文件, 首先遍历节区头部Elf32_Shdr查看sh_type属性值&#xff0c;得到属性值为SHT_DYNSYM的节区。 其次通过名字遍历节区结点&#xff0c;找到类型为STT_FUNC并且名字与其相同的结点。 代码如下&#xff1a; static void * xmalloc(size_t size) {void *p…

arm32和arm64常用指令B BL BLX机器码计算

现在大部分手机cpu架构是ARM v7-A和ARMV8-A,&#xff0c;在ARM-v7A中常使用32位ARM指令集并且支持thumb指令集与arm的切换&#xff0c;而在ARMV8中使用的是64位ARM指令集且不再有thumb指令集状态的切换了。在调用函数时&#xff0c;会有常用的调用方式&#xff1a;BL和B&#x…

arm shellcode 编写详析1

在编写arm shell code 之前&#xff0c;先介绍下arm中r0-r15寄存器的主要用途&#xff1a; Register Alt. Name Usage r0 a1 First function argument Integer function result Scratch register r1 a2 Second function …

arm shellcode 编写详析2

前一篇中介绍了arm shellcode基本用法&#xff0c;现在涉及到arm和thumb状态 在前一篇中默认为arm32模式&#xff1a; text:00008074 ; Segment type: Pure code .text:00008074 AREA .text, CODE .text:00008074 ; ORG 0x8074 .text:0000807…

openssl c++实现bouncycastle中AES加解密

0x01 为什么要用bouncycastle 先说说JCE&#xff08;Java Cryptography Extension&#xff09;是一组包&#xff0c;它们提供用于加密、密钥生成和协商以及 Message Authentication Code&#xff08;MAC&#xff09;算法的框架和实现。 它提供对对称、不对称、块和流密码的加密…

zlib数据格式及解压缩实现

0x01 zlib和其他压缩的魔术头 一般来说压缩文件都有个魔术头&#xff0c;用于区分不同的压缩文件对应不同的解压缩算法。 7z文件: 00000000 37 7A BC AF 27 1C 00 03 CD F7 CC 2E 66 6A 33 00 7z集 枉?fj3 tar.xz文件 00000000 FD 37 7A 58 5A 00 00 04 E6 D6 B4 …

python3 Crypto环境

前言 最开始想尝试在windows下面安装python3.6&#xff0c;虽然python安装成功&#xff0c;但在安装Cryto模块用pip3 install pycrypto老是会报错。老夫搞了半天&#xff0c;最终决定在linux下面去做。 以下流程限于linux系统&#xff1a; 0x00 安装python apt-get install p…

win10用Eclipse+OpenJTag对S3C2440开发板进行动态调试

0 背景在S3C2400开发板裸板调试程序中&#xff0c;常用调试手段有三种&#xff1a;点灯法&#xff0c;串口打印&#xff0c;OpenOCD。OpenOCD又分命令行和图形界面(Eclipse)。点灯发和串口打印调试效率都很低&#xff0c;若能掌握第三种调试方法&#xff0c;会让开发过程变得高…

无源码情况下动态调试混淆的java程序

逆向工程JAVA通常是非常简单的&#xff0c;因为优秀的JAVA二进制反编译器已经存在多年。类似于jd-gui工具和恢复java二进制文件源代码功能也做的非常出色的。在这种情况下我们需要动态调试java反编译java程序的情况下&#xff0c;可以从反编译导出然后导入java IDE如Eclipse作为…

mdb access2000 中文密码破解

access数据库破解工具很多&#xff0c;密码能不用费多大功夫就能破解出来&#xff0c;但是对于包含特殊字符包括中文字符的密码&#xff0c;就算破解出来后想通过数据库工具查看&#xff0c;复制粘贴到密码输入框实际都起不了作用 已迁移到&#xff1a;分享最前沿的安全信息-a…

OpenJTAG调试S3C2440裸板程序

0x00 懵逼当你写好的初始化代码head.S和链接脚本uart.lds共同编译出来的*.bin&#xff0c;烧录到NandFlash中的时候&#xff0c;发现串口输出一片空白&#xff0c;这时你的想法是什么&#xff0c;砸电脑还是干点其他有用的事&#xff1f;还是老实的搭建调试环境吧&#xff0c;上…

APK逆向之静态分析篇

0x00 APK包结构0x01 APK反编译-apktool啰嗦一句&#xff0c;反编译之前配置好java环境&#xff0c;具体JDK安装过程&#xff0c;请参照之前的文章。下载最新版本的apktool.jar&#xff0c;并在当前目录下编辑脚本apktool.bat&#xff0c;内容如下&#xff1a; echo off set PAT…

S3C2440 lds链接脚本解析

1. SECTIONS到底意味着什么在一个裸版程序里面含有*.lds文件&#xff0c;而lds文件意味着如果你的程序烧录在nandflash&#xff0c;那在nandflash的内存将根据lds文件指定偏移来分布&#xff0c;下面从不同场景来解释SECTIONS的内容。2. 小于4K程序若程序小于4K&#xff0c;那…

安装qt5.9.5 windows环境

下载&#xff1a;用国外链接下载慢&#xff0c;还是乖乖用国内链接地址吧&#xff0c;我这里5.9.5http://mirrors.ustc.edu.cn/qtproject/archive/qt/5.9/5.9.5/qt-opensource-windows-x86-5.9.5.exe。安装&#xff1a; 在安装的时候需要创建qt账号&#xff0c;然后根据你的vis…

qt在visual studio 2015下的使用

创建工程&#xff1a; 打开visual studio&#xff0c;按上一篇文章的方式创建新工程QtGuiApplication1&#xff0c;默认我们可以看到里面会出现QtGuiApplication1这个类是继承于QMainWindow这个类的。在创建过程中注意下图选项&#xff1a;有三个对象分别是QMainwindow&#xf…

qt 收缩窗体

效果图&#xff1a;功能拆分图&#xff1a;代码&#xff1a; QtStubOption.cpp QtSubOption::QtSubOption(QWidget *parent): QLabel(parent) {ui.setupUi(this);m_GuiShow SHOWGUI;setMouseTracking(true);m_PicStatus[SHOWGUI] ":/QtGuiApplication3/tile";m_Pic…