=====================================================
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。也就是說,(補位後的消息長度)%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了,我們可以進行下一步操作。
補長度
所謂的補長度是將原始數據的長度補到已經進行了補位操作的消息後面。通常用一個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("%02x", 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("%02x", 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 <[email protected]>
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