redis源码分析之sha1算法分析

mac2022-06-30  206

===================================================== redis源码学习系列文章:

redis源码分析之sha1算法分析 redis源码分析之字典源码分析 redis源码分析之内存编码分析intset, ziplist编码分析 redis源码分析之跳跃表 redis源码分析之内存淘汰策略的原理分析 redis源码分析之对象系统源码分析string, list链表,hash哈希,set集合,zset有序集合

=====================================================

在我的github上会持续更新Redis代码的中文分析,地址送出https://github.com/chensongpoixs/credis_source,共同学习进步

前言

在密码学分为三大类分别是

不可逆算法 (md5, sha家族, HmacSHa家族)对称加密的算法 (AES, DES, RC4, Rabbit, TripleDes)公钥和私钥 (RSA)

我在redis源码中hash表的因子是使用sha1算法生成的。

正文

1, sha1算法原理分析

了解sha1介绍

SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

sha1 用于2的64次方的数据进行加密生成 160位的(20字节)散列值。

sha1 是一块一块的加密的每块有512位(64字节)进行加密

最后使用80个int类型临时数组存放加密的数据

1怎么把我们的数据转换要转换一块一块数据是什么格式呢?

举例:

对 “abc” 加密 我们要先把abc转换为ASCII码表对应的二进制

01100001 01100010 01100011 ‘a’=97 ‘b’=98 ‘c’=99

字符串的长度就是24(二进制)

补位:

消息必须进行补位,以使其长度在对512取模以后的余数是448。也就是说,(补位后的消息长度)Q2 = 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了,我们可以进行下一步操作。

补长度

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

61626380 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000018

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

使用的常量

一系列的常量字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).

需要使用的函数

在SHA1中我们需要一系列的函数。每个函数ft (0 <= t <= 79)都操作32位字B,C,D并且产生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).

计算消息摘要

必须使用进行了补位和补长度后的消息来计算消息摘要。计算需要两个缓冲区,每个都由5个32位的字组成,还需要一个80个32位字的缓冲区。第一个5个字的缓冲区被标识为A,B,C,D,E。第二个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。你也可以用相似的办法来计算消息摘要。对消息进行补位的算法完全是一样的。

2, sha1算法的实现

#include <stdio.h> #include <stdlib.h> #include <string.h> static const int parm[5] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; static int chararraytoint(unsigned char* ptr, unsigned int index) { return ((ptr[index] & 0xff) << 24) | ((ptr[index + 1] & 0xff) << 16) | ((ptr[index + 2] & 0xff) << 8) | (ptr[index + 3] & 0xff); } static int s(unsigned int x, unsigned int i) { return (x << i) | x >> (32 - i); } static int f1(int x, int y, int z) { return (x&y) | (~x&z); } static int f2(int x, int y, int z) { return x^y^z; } static int f3(int x, int y, int z) { return (x&y) | (x&z) | (y&z); } static int f4(int x, int y, int z) { return x^y^z; } unsigned char * get_src_hex(char *p, unsigned int len, unsigned long long *size) { if (!p || len <= 0) { return NULL; } unsigned int fill = 0; //填充'0' //unsigned long long size = *_size; //数据的大小 unsigned int m_size = len % 64; //有几个数据块 512位为一块 if (m_size < 56) // 最后的16位是数据块的长度保存 { fill = 55 - m_size; *size = len - m_size + 64; //数据的大小//size = strlen(p) - src_lenght + 64;//data_size的开始位置 } else if (m_size == 56) { fill = 63; *size = len + 8 + 64; } else { fill = 63 - m_size + 56; *size = (len + 64) - m_size + 64; } unsigned char * ptr = (unsigned char *)malloc(sizeof(unsigned char) * (*size)); if (!ptr) { return NULL; } memcpy(ptr, p, len); // 结束标志位 0x80 ptr[len] = 0x80; // 补'0'操作 for (int i = 0; i < fill; ++i) { ptr[len + i + 1] = (unsigned char)0x00; } //保存到最后的十六位中 unsigned long long m_hex_size = len * 8; //memcpy(ptr[len + 2 + fill ], (unsigned char)m_hex_size, 8); ptr[strlen(p) + fill + 1] = (unsigned char)(m_hex_size >> 56); ptr[strlen(p) + fill + 2] = (unsigned char)((m_hex_size>> 48) & 0xFF); ptr[strlen(p) + fill + 3] = (unsigned char)((m_hex_size>> 40) & 0xFF); ptr[strlen(p) + fill + 4] = (unsigned char)((m_hex_size>> 32) & 0xFF); ptr[strlen(p) + fill + 5] = (unsigned char)((m_hex_size>> 24) & 0xFF); ptr[strlen(p) + fill + 6] = (unsigned char)((m_hex_size>> 16) & 0xFF); ptr[strlen(p) + fill + 7] = (unsigned char)((m_hex_size>> 8) & 0xFF); ptr[strlen(p) + fill + 8] = (unsigned char)(m_hex_size & 0xFF); return ptr; } int * get_sha1_update(unsigned char *ptr, unsigned long long size) { if (!ptr || size == 0) { return NULL; } //计算有多少块 int m_count = size / 64; int * temp = malloc(sizeof(int) * 80); if (!temp) { return NULL; } int * tempabcde = malloc(sizeof(int) * 5); if (!tempabcde) { return NULL; } int * h = malloc(sizeof(int) * 5); if (!h) { return NULL; } //memcpy(hex_enc, parm, 5); for (int i = 0; i < 5; ++i) { h[i] = parm[i]; } for (int pos = 0; pos < m_count; pos++) { printf("pos = %d\n", pos); for (int i = 0; i < 16; i++) { temp[i] = chararraytoint(ptr, (pos * 64) + (i * 4)); printf("[---> temp[%d] = %d]\n", i, temp[i]); } for (int t = 16; t <= 79; t++) { temp[t] = s(temp[t - 3] ^ temp[t - 8] ^ temp[t - 14] ^ temp[t - 16], 1); printf("m[%d]=%d\n", t, temp[t]); } for (int i = 0; i < 5; ++i) { tempabcde[i] = h[i]; } for (int i = 0; i <= 19; i++) { int temp_1 = s(tempabcde[0], 5) + f1(tempabcde[1], tempabcde[2], tempabcde[3]) + tempabcde[4] + temp[i] + 0x5A827999; tempabcde[4] = tempabcde[3]; tempabcde[3] = tempabcde[2]; tempabcde[2] = s(tempabcde[1], 30); tempabcde[1] = tempabcde[0]; tempabcde[0] = temp_1; } for (int i = 20; i <= 39; i++) { int temp_1 = s(tempabcde[0], 5) + f2(tempabcde[1], tempabcde[2], tempabcde[3]) + tempabcde[4] + temp[i] + 0x6ED9EBA1; tempabcde[4] = tempabcde[3]; tempabcde[3] = tempabcde[2]; tempabcde[2] = s(tempabcde[1], 30); tempabcde[1] = tempabcde[0]; tempabcde[0] = temp_1; } for (int i = 40; i <= 59; i++) { int temp_1 = s(tempabcde[0], 5) + f3(tempabcde[1], tempabcde[2], tempabcde[3]) + tempabcde[4] + temp[i] + 0x8F1BBCDC; tempabcde[4] = tempabcde[3]; tempabcde[3] = tempabcde[2]; tempabcde[2] = s(tempabcde[1], 30); tempabcde[1] = tempabcde[0]; tempabcde[0] = temp_1; } for (int i = 60; i <= 79; i++) { int temp_1 = s(tempabcde[0], 5) + f4(tempabcde[1], tempabcde[2], tempabcde[3]) + tempabcde[4] + temp[i] + 0xCA62C1D6; tempabcde[4] = tempabcde[3]; tempabcde[3] = tempabcde[2]; tempabcde[2] = s(tempabcde[1], 30); tempabcde[1] = tempabcde[0]; tempabcde[0] = temp_1; } //5.令 H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. for (int i = 0; i < 5; i++) { h[i] = h[i] + tempabcde[i]; printf("[h[%d] = %d]\n", i, h[i]); } //完成了一次操作 //清除之前的内容,开始下一个块的计算 for (int i = 0; i < 80; i++) { temp[i] = 0; } } free(tempabcde); free(temp); return h; } int main(int argc, char *argv[]) { char * p = "abc";//616263 a=> 61, b => 62, c => 63 unsigned long long size = 0; unsigned char * ptr = get_src_hex(p, strlen(p), &size); if (!ptr) { printf("[errro]\n"); //system("pause"); return -1; } printf("["); //61626380000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000018 for (size_t i = 0; i < size; i++) { printf("x", ptr[i]);// 616263 } printf("]\n"); printf("strlen(ptr) = %zd\n", strlen(ptr)); printf("----------------\n["); unsigned char hash[20]; int * h = get_sha1_update(ptr, size); if (!h) { printf("[error]\n"); //system("pause"); return -1; } for (int i = 0; i < 5; ++i) { hash[(i * 4)] =(char)(( h [i]>> 24) & 0xff); hash[(i * 4) + 1] = (char)((h[i] >> 16) & 0xff); hash[(i * 4) + 2] = (char)((h[i] >> 8) & 0xff); hash[(i * 4) + 3] = (char)((h[i] & 0xff)); } //memcpy(hash, h, 20); for (int i = 0; i < 20; ++i) { printf("x", hash[i]);// 616263 } printf("]\n"); //memcpy(ptr + strlen(p), 0x80, 1); //memcpy() free(h); free(ptr); //system("pause"); return EXIT_SUCCESS; }

3, redis中sha1源码分析

/* from valgrind tests */ /* ================ sha1.c ================ */ /* SHA-1 in C By Steve Reid <steve@edmweb.com> 100% Public Domain Test Vectors (from FIPS PUB 180-1) "abc" A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 A million repetitions of "a" 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F */ /* #define LITTLE_ENDIAN * This should be #define'd already, if true. */ /* #define SHA1HANDSOFF * Copies data before messing with it. */ #define SHA1HANDSOFF #include <stdio.h> #include <string.h> #include <stdint.h> #include "solarisfixes.h" #include "sha1.h" #include "config.h" #define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) /* blk0() and blk() perform the initial expand. */ /* I got the idea of expanding during the round function from SSLeay */ #if BYTE_ORDER == LITTLE_ENDIAN #define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \ |(rol(block->l[i],8)&0x00FF00FF)) #elif BYTE_ORDER == BIG_ENDIAN #define blk0(i) block->l[i] #else #error "Endianness not defined!" #endif #define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \ ^block->l[(i+2)&15]^block->l[i&15],1)) /* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ #define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30); #define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30); #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); /* Hash a single 512-bit block. This is the core of the algorithm. */ void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]) { uint32_t a, b, c, d, e; typedef union { unsigned char c[64]; uint32_t l[16]; } CHAR64LONG16; #ifdef SHA1HANDSOFF CHAR64LONG16 block[1]; /* use array to appear as a pointer */ memcpy(block, buffer, 64); #else /* The following had better never be used because it causes the * pointer-to-const buffer to be cast into a pointer to non-const. * And the result is written through. I threw a "const" in, hoping * this will cause a diagnostic. */ CHAR64LONG16* block = (const CHAR64LONG16*)buffer; #endif /* Copy context->state[] to working vars */ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; /* 4 rounds of 20 operations each. Loop unrolled. */ R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); /* Add the working vars back into context.state[] */ state[0] += a; state[1] += b; state[2] += c; state[3] += d; state[4] += e; /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF memset(block, '\0', sizeof(block)); #endif } /* SHA1Init - Initialize new context */ void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; context->state[1] = 0xEFCDAB89; context->state[2] = 0x98BADCFE; context->state[3] = 0x10325476; context->state[4] = 0xC3D2E1F0; context->count[0] = context->count[1] = 0; } /* Run your data through this. */ void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len) { uint32_t i, j; j = context->count[0]; // 0000 0000 0000 0000 0000 0000 0000 0000 // 0001 1111 1111 1111 1111 1111 1111 1111 => 536870911 * 4 = 2147483644 ==> 0111 1111 1111 1111 1111 1111 1111 1100 // ====> [例如: 3个字节 3 << 3 = 24位]得到数据的字节大小 [很巧妙方法位操作 << ] if ((context->count[0] += len << 3) < j) { // 这种情况 == // 0010 0000 0000 0000 0000 0000 0000 0000 ===转换位 ==>1000 0000 0000 0000 0000 0000 0000 0000 context->count[1]++; //无论如何都是内存溢出了都需要加放到count[1]++的 } context->count[1] += (len>>29); j = (j >> 3) & 63; // 字节长度===>>这个是因为sha1 一块内存加密需要512位 if ((j + len) > 63)//是否满足一块的加密的内存的大小 { memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); } j = 0; } else { i = 0; } memcpy(&context->buffer[j], &data[i], len - i); } /* Add padding and return the message digest. */ // 不足一块数据补'0'操作 void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { unsigned i; unsigned char finalcount[8]; unsigned char c; #if 0 /* untested "improvement" by DHR */ /* Convert context->count to a sequence of bytes * in finalcount. Second element first, but * big-endian order within element. * But we do it all backwards. */ unsigned char *fcp = &finalcount[8]; for (i = 0; i < 2; i++) { uint32_t t = context->count[i]; int j; for (j = 0; j < 4; t >>= 8, j++) *--fcp = (unsigned char) t; } #else for (i = 0; i < 8; i++) { finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)] >> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ } #endif c = 0200; // 结束标志位0x80 SHA1Update(context, &c, 1); while ((context->count[0] & 504) != 448) { c = 0000; //补'0'操作 SHA1Update(context, &c, 1); } // 数据的长度记录 64位-> 8字节 SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */ for (i = 0; i < 20; i++) { digest[i] = (unsigned char) ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255); } /* Wipe variables */ memset(context, '\0', sizeof(*context)); memset(&finalcount, '\0', sizeof(finalcount)); } /* ================ end of sha1.c ================ */ #ifdef REDIS_TEST #define BUFSIZE 4096 #define UNUSED(x) (void)(x) int sha1Test(int argc, char **argv) { SHA1_CTX ctx; unsigned char hash[20], buf[BUFSIZE]; int i; UNUSED(argc); UNUSED(argv); for (i = 0; i < BUFSIZE; i++) { buf[i] = i; } SHA1Init(&ctx); for (i = 0; i < 1000; i++) { SHA1Update(&ctx, buf, BUFSIZE); } SHA1Final(hash, &ctx); printf("SHA1="); for (i = 0; i < 20; i++) { } printf("\n"); return 0; } #endif

源码地址

结语

个人博客地址


最新回复(0)