SHA1 算法:
消息摘要算法,把消息按照 512 bits 进行分组,不断的对 5 个int型变量进行计算,直到所有消息都运算完毕。
最终得到 160 bit 即 20 字节的哈希值。流程图:
--------------------------------------------------------
C语言实现:
用到的数据结构:
//SHA1算法的上下文,保存一些状态,中间数据,结果
typedef struct sha1_ctx
{
//处理的数据的长度
uint64_t length_;
//还没有处理的数据长度
uint64_t unprocessed_;
/* 保存 160 位哈希 的内部状态 */
uint32_t hash_[5];
} sha1_ctx;
具体流程:
1.
一开始调用 SHA1_Init 初始化 5 个 int 型变量摘要(这是算法的特征,PEID的算法识别插件会识别):
//内部函数,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;
}
2.
接着就是把消息按照 512 bits 进行分组,轮流进行 SHA1_Update ,不断更新这 5个int型变量,
直到所有消息都参与了 SHA1_Update 计算。
SHA1_Update 内部调用 SHA1_Process_Block 把64字节的消息分成 16 个 int型变量,并把这 16 个变量通过
64 次循环 扩展到 80 个 int 型变量。再通过80次循环,分别用 循环移位,加法运算,异或运算, 把初始化好的
哈希值与消息进行杂凑处理。最后再加回原来的哈希值,得到 SHA1_Update 后的 哈希值。
/*!
@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_ENDIAN
swap_uint32_memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
#else
::memcpy(wblock, block, ZEN_SHA1_BLOCK_SIZE);
#endif
//处理,根据提供的 (512 bits = 32 bits * 16 组) 原始数据
//生成新的 80 组 * 32 bits 数据
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;
}
3.
补充: 最后一个消息块不足 512 bit 要进行扩展处理,用0 填充,并且最后 64 位用于存放整个消息的长度,然后
调用 SHA1_Update 更新哈希值。
/*!
@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( ctx->unprocessed_));
}
//得到0x80要添加在的位置(在uint32_t 数组中),
uint32_t index = ((uint32_t)ctx->length_ & 63) >> 2;
uint32_t shift = ((uint32_t)ctx->length_ & 3) * 8;
//添加0x80进去,并且把余下的空间补充0
message[index] &= ~(0xFFFFFFFF << shift);
message[index++] ^= 0x80 << shift;
//如果这个block还无法处理,其后面的长度无法容纳长度64bit,那么先处理这个block
if (index > 14)
{
while (index < 16)
{
message[index++] = 0;
}
zen_sha1_process_block(ctx->hash_, message);
index = 0;
}
//补0
while (index < 14)
{
message[index++] = 0;
}
//保存长度,注意是bit位的长度,这个问题让我看着郁闷了半天,
uint64_t data_len = (ctx->length_) << 3;
//注意SHA1算法要求的64bit的长度是大头BIG-ENDIAN,在小头的世界要进行转换
#if ZEN_BYTES_ORDER == ZEN_LITTLE_ENDIAN
data_len = ZEN_SWAP_UINT64(data_len);
#endif
message[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_ENDIAN
swap_uint32_memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
#else
memcpy(result, &ctx->hash_, ZEN_SHA1_HASH_SIZE);
#endif
}
4.
附上使用的方法:
//每次处理的BLOCK的大小
static const size_t ZEN_SHA1_BLOCK_SIZE = 64;
/*!
@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(buf));
buf += ZEN_SHA1_BLOCK_SIZE;
size -= ZEN_SHA1_BLOCK_SIZE;
}
ctx->unprocessed_ = size;
}
//计算一个内存数据的SHA1值
unsigned char *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;
}
// main 函数进行SHA1 的使用:
int main()
{
unsigned char buf[]="hunter";
unsigned char result[ZEN_SHA1_HASH_SIZE];
sha1(buf,6,result);
for(int i=0;i
参考:
《图解密码技术:结城浩》
代码出自:未知,不是本人写的。