1. SHA1算法简介
SHA-1(Secure Hash Algorithm 1)是一种密码散列函数,美国国家安全局设计,并由美国国家标准技术研究所(NIST)发布为联邦数据处理标准(FIPS)。SHA-1可以生成一个被称为消息摘要的160位(20字节)散列值,散列值通常的呈现形式为40个十六进制数。
SHA1有如下特性:
- 不可以从消息摘要中复原信息;
- 两个不同的消息不会产生同样的消息摘要。
注: 2017年2月23日,Google公司公告宣称他们与CWI Amsterdam合作共同创建了两个有着相同的SHA-1值但内容不同的PDF文件,这代表SHA-1算法已被正式攻破。
2. 术语和概念
位(bit),字节(byte),双字(dword)
SHA1始终把消息当成一个位字符串来处理。正常情况下,一个“双字”是32位,而一个“字节”是8位。
比如,字符串“abc”可以被转换成一个位字符串:01100001 01100010 01100011。
它也可以被表示成16进制字符串: 0x616263。
3. SHA1算法描述
由于SHA1算法只接受位作为输入。因此在SHA1算法中,我们必须把原始消息(字符串,文件等)转换成位字符串。
假设我们对字符串“sha1”产生消息摘要。首先,我们将它转换成位字符串如下:
01110011 01110011 01100001 00110001
这个位字符串的长度为len = 4 * 8 = 32。下面我们需要5个步骤来计算SHA1。
定义如下变量:
byte *data; // 原始数据
dowrd len_lower; // 原始数据长度低32位
dowrd len_higher; // 原始数据长度高32位
qword len = len_lower | len_higher << 32;
dword n; // 补位后数据长度
3.1 补位
SHA1按照64byte间隔进行数据分批处理,且需要填充标志位和数据长度,因此消息必须进行补位。
补位规则:
根据Sha1,原始数据末尾需要先追加一个字节填充(0x80);
若此时数据长度不满足len % 512 = 448
, 填充(0x00)至满足条件。
以“sha1”为例,补位是这样进行的:
原始信息: 01110011 01110011 01100001 00110001
补位第一步:01110011 01110011 01100001 00110001
1 //补充1
补位第二步:01110011 01110011 01100001 00110001
10000000 00000000 ...... //补充415个00
转换为16进制如下显示:
73686131 80000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000
3.2 补长度
将len补到已经进行了补位操作的消息后面。
通常用一个64位的数据来表示原始消息的长度。
在补长度以后,整个消息如下显示:
73686131 80000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000020
3.3 使用的常量
常量K(0), K(1), … , K(79),用于不同轮次的计算。
/* const hash value for step */
#define K_00_19 0x5a827999UL // step:00~19
#define K_20_39 0x6ed9eba1UL // step:20~39
#define K_40_59 0x8f1bbcdcUL // step:40~59
#define K_60_79 0xca62c1d6UL // step:60~79
3.4 需要使用的函数
与常量对应,用于不同轮次输出双字的预定函数。
f
t
(
B
,
C
,
D
)
=
{
(
(
c
ˆ
d
)
&
b
)
ˆ
(
d
)
(
0
<
=
t
<
=
19
)
(
b
ˆ
c
ˆ
d
)
(
20
<
=
t
<
=
39
)
(
(
b
&
c
)
∣
(
(
b
∣
c
)
&
d
)
)
(
40
<
=
t
<
=
59
)
(
b
ˆ
c
ˆ
d
)
(
60
<
=
t
<
=
79
)
f_t(B,C,D) = {\begin{cases} ((c \medspace\^{\enspace}\medspace d) \&\medspace b) \^{\enspace}(d) & ( 0 <= t <= 19) \\ (b \medspace\^{\enspace}\medspace c \medspace\^{\enspace}\medspace d) & (20 <= t <= 39) \\ ((b \medspace\&\medspace c) | ((b \medspace|\medspace c) \& d)) & ( 40 <= t <= 59) \\ (b \medspace\^{\enspace}\medspace c \medspace\^{\enspace}\medspace d) & ( 60 <= t <= 79) \\ \end{cases}}
ft(B,C,D)=⎩
⎨
⎧((cˆd)&b)ˆ(d)(bˆcˆd)((b&c)∣((b∣c)&d))(bˆcˆd)(0<=t<=19)(20<=t<=39)(40<=t<=59)(60<=t<=79)
3.5 计算消息摘要
必须使用进行了补位和补长度后的消息来计算消息摘要。
计算需要两个160位的缓冲区,每个都由5个32位的字组成;还需要一个512位的数据缓冲区。
第一个5个字的缓冲区被标识为A,B,C,D,E。
第二个5个字的缓冲区被标识为H0, H1, H2, H3, H4
16个双字的缓冲区被标识为W0, W1,…, W15
另外还需要一个双字的TEMP缓冲区。
为了产生消息摘要,对数据块按照512位分割为M1, M2,…, Mn;
对每个数据块Mi 进行包含80个步骤的计算。
在处理每个数据块之前,初始化缓冲区
#define INIT_DATA_h0 0x67452301UL
#define INIT_DATA_h1 0xefcdab89UL
#define INIT_DATA_h2 0x98badcfeUL
#define INIT_DATA_h3 0x10325476UL
#define INIT_DATA_h4 0xc3d2e1f0UL
ctx_.h0 = INIT_DATA_h0;
ctx_.h1 = INIT_DATA_h1;
ctx_.h2 = INIT_DATA_h2;
ctx_.h3 = INIT_DATA_h3;
ctx_.h4 = INIT_DATA_h4;
现在开始处理M1, M2, … , Mn。为了处理 Mi,需要进行下面的步骤
(1). 将 Mi 分成 16 个字 W0, W1, … , W15
(2). 初始化
A = ctx_.h0;
B = ctx_.h1;
C = ctx_.h2;
D = ctx_.h3;
E = ctx_.h4;
(3). 对于 t = 16 到 79 令
Wt = S^1(W(t-3) ^ W(t-8) ^ W(t-14) ^ W(t-16))
(4) 对于 t = 0 到 79,执行下面的循环
T = S^5(A) + F((B), (C), (D)) + (E) + W(t) + K(t);
E = D;
D = C;
C = S^30(B);
B = A;
A = T;
(5). Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.
ctx_.h0 = (ctx_.h0 + A) & 0xffffffffL;
ctx_.h1 = (ctx_.h1 + B) & 0xffffffffL;
ctx_.h2 = (ctx_.h2 + C) & 0xffffffffL;
ctx_.h3 = (ctx_.h3 + D) & 0xffffffffL;
ctx_.h4 = (ctx_.h4 + E) & 0xffffffffL;
(6). 在处理完所有的 Mi后,将消息摘要以大端序写入缓冲区
4. 参考实现
4.1 宏
#define SHA_LBLOCK 16
#define SHA_CBLOCK (SHA_LBLOCK * 4)
#define SHA_LAST_BLOCK (SHA_CBLOCK - 8)
#define SHA_DIGEST_LENGTH 20
#define X(i) XX##i
/* default hash value */
#define INIT_DATA_h0 0x67452301UL
#define INIT_DATA_h1 0xefcdab89UL
#define INIT_DATA_h2 0x98badcfeUL
#define INIT_DATA_h3 0x10325476UL
#define INIT_DATA_h4 0xc3d2e1f0UL
/* const hash value for step */
#define K_00_19 0x5a827999UL // step:00~19
#define K_20_39 0x6ed9eba1UL // step:20~39
#define K_40_59 0x8f1bbcdcUL // step:40~59
#define K_60_79 0xca62c1d6UL // step:60~79
/* hash function for step */
#define F_00_19(b, c, d) ((((c) ^ (d)) & (b)) ^ (d)) // step:00~19
#define F_20_39(b, c, d) ((b) ^ (c) ^ (d)) // step:20~39
#define F_40_59(b, c, d) (((b) & (c)) | (((b) | (c)) & (d))) // step:40~59
#define F_60_79(b, c, d) F_20_39(b, c, d) // step:60~79
/* swap left and right by n */
#define ROTATE(a, n) (((a) << (n)) | (((a)&0xffffffff) >> (32 - (n))))
/*
* SHA1:
* W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)).
*/
#define Xupdate(a, ix, ia, ib, ic, id) \
((a) = (ia ^ ib ^ ic ^ id), ix = (a) = ROTATE((a), 1))
#define BODY_00_15(i, a, b, c, d, e, f, xi) \
(f) = ROTATE((a), 5) + F_00_19((b), (c), (d)) + (e) + xi + K_00_19; \
(b) = ROTATE((b), 30);
#define BODY_16_19(i, a, b, c, d, e, f, xi, xa, xb, xc, xd) \
Xupdate(f, xi, xa, xb, xc, xd); \
(f) += (e) + K_00_19 + ROTATE((a), 5) + F_00_19((b), (c), (d)); \
(b) = ROTATE((b), 30);
#define BODY_20_31(i, a, b, c, d, e, f, xi, xa, xb, xc, xd) \
Xupdate(f, xi, xa, xb, xc, xd); \
(f) += (e) + K_20_39 + ROTATE((a), 5) + F_20_39((b), (c), (d)); \
(b) = ROTATE((b), 30);
#define BODY_32_39(i, a, b, c, d, e, f, xa, xb, xc, xd) \
Xupdate(f, xa, xa, xb, xc, xd); \
(f) += (e) + K_20_39 + ROTATE((a), 5) + F_20_39((b), (c), (d)); \
(b) = ROTATE((b), 30);
#define BODY_40_59(i, a, b, c, d, e, f, xa, xb, xc, xd) \
Xupdate(f, xa, xa, xb, xc, xd); \
(f) += (e) + K_40_59 + ROTATE((a), 5) + F_40_59((b), (c), (d)); \
(b) = ROTATE((b), 30);
#define BODY_60_79(i, a, b, c, d, e, f, xa, xb, xc, xd) \
Xupdate(f, xa, xa, xb, xc, xd); \
(f) = xa + (e) + K_60_79 + ROTATE((a), 5) + F_60_79((b), (c), (d)); \
(b) = ROTATE((b), 30);
/* swap little-endian to big-endian, assign to l */
/* address increase dword size */
#define HOST_c2l(c, l) \
(l = (((unsigned long)(*((c)++))) << 24), \
l |= (((unsigned long)(*((c)++))) << 16), \
l |= (((unsigned long)(*((c)++))) << 8), \
l |= (((unsigned long)(*((c)++)))))
/* swap little-endian to big-endian, assign to c */
/* address increase dword size */
#define HOST_l2c(l, c) \
(*((c)++) = (unsigned char)(((l) >> 24) & 0xff), \
*((c)++) = (unsigned char)(((l) >> 16) & 0xff), \
*((c)++) = (unsigned char)(((l) >> 8) & 0xff), \
*((c)++) = (unsigned char)(((l)) & 0xff), l)
4.2 函数声明
typedef unsigned char byte;
typedef unsigned int dword;
struct SHA_CTX {
// default hash value, and storage the final hashed value
dword h0, h1, h2, h3, h4;
// num_lower storage the lower 32 bits of data's length, by bits.
// num_higher storage the higher 32 bits of data's length, by bits.
dword num_lower, num_higher;
// buff storage origin data, complement data, length aligned data
dword data[SHA_LBLOCK];
// record the length been used in buff
dword num_used;
};
class Sha1Context : public IDigestInterface {
public:
virtual dword Context_Init();
virtual dword Context_Update(const byte *data, const size_t len);
virtual dword Context_Final(byte *md);
// when sha only once, use this
virtual byte *Digest(const byte *data, const size_t len, byte *md);
private:
void SHA1_Block_Data_Order(const void *p, size_t num_block);
private:
SHA_CTX ctx_;
};
4.2 实现
dword Sha1Context::Context_Update(const byte *data, const size_t len) {
dword lower = 0;
dword offset = 0;
dword num_block = 0;
size_t length = 0;
byte *p = nullptr;
if (len == 0) {
return 1;
}
// convert length by byte to length by bit
lower = (ctx_.num_lower + (((dword)len) << 3)) & 0xffffffffUL;
// when happen overflow
if (lower < ctx_.num_lower) {
++ctx_.num_higher;
}
// update length of data
ctx_.num_higher += (dword)(len >> 29);
ctx_.num_lower = lower;
offset = ctx_.num_used;
length = len;
// when last time's data remains
if (0 < offset) {
p = (byte *)ctx_.data;
// complement buff by move data
if (length >= SHA_CBLOCK || length + offset >= SHA_CBLOCK) {
memcpy(p + offset, data, SHA_CBLOCK - offset);
SHA1_Block_Data_Order(p, 1);
offset = SHA_CBLOCK - offset;
data += offset;
length -= offset;
ctx_.num_used = 0;
memset(p, 0, SHA_CBLOCK);
} else {
memcpy(p + offset, data, length);
ctx_.num_used += (dword)length;
return 1;
}
}
num_block = length / SHA_CBLOCK;
// when length bigger than SHA_CBLOCK
if (num_block > 0) {
SHA1_Block_Data_Order(data, num_block);
offset = num_block * SHA_CBLOCK;
data += offset;
length -= offset;
}
// when remains length smaller than SHA_CBLOCK
if (length != 0) {
p = (byte *)ctx_.data;
memcpy(p, data, length);
ctx_.num_used = (dword)length;
}
return 1;
}
dword Sha1Context::Context_Final(byte *md) {
byte *p = (byte *)ctx_.data;
size_t n = ctx_.num_used;
/* complement 1000 0000 */
p[n] = 0x80;
n++;
/* if n > 56byte(448bit) */
if (n > (SHA_CBLOCK - 8)) {
memset(p + n, 0, SHA_CBLOCK - n);
n = 0;
SHA1_Block_Data_Order(p, 1);
}
/* complement 0000 0000 .... .... */
memset(p + n, 0, SHA_CBLOCK - 8 - n);
/* append data size to end */
p += SHA_CBLOCK - 8;
(void)HOST_l2c(ctx_.num_higher, p);
(void)HOST_l2c(ctx_.num_lower, p);
/* the end digest */
p -= SHA_CBLOCK;
SHA1_Block_Data_Order(p, 1);
/* clean */
ctx_.num_used = 0;
memset(p, 0x00, SHA_CBLOCK);
/* genarate digest messgae */
HASH_MAKE_STRING(&ctx_, md);
return 1;
}
byte *Sha1Context::Digest(const byte *data, const size_t len, byte *md) {
static byte m[SHA_DIGEST_LENGTH];
if (md == nullptr) {
md = m;
}
Context_Init();
Context_Update(data, len);
Context_Final(md);
return md;
}
void Sha1Context::SHA1_Block_Data_Order(const void *p, size_t num_block) {
const byte *data = (const byte *)p;
register dword A, B, C, D, E, T, l;
dword XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7, XX8, XX9, XX10, XX11, XX12,
XX13, XX14, XX15;
/*
* SHA1 Document:
* The words of the first 5-word buffer are labeled A,B,C,D,E.
* The words of the second 5-word buffer are labeled H0, H1, H2, H3, H4.
* Let A = H0, B = H1, C = H2, D = H3, E = H4.
*/
A = ctx_.h0;
B = ctx_.h1;
C = ctx_.h2;
D = ctx_.h3;
E = ctx_.h4;
for (;;) {
// for check endianness
const union {
long one;
char little;
} is_endian = {1};
/*
* SHA1 Document:
* The words of the 80-word sequence are labeled W(0), W(1),..., W(79).
* Divide M(i) into 16 words W(0), W(1), ... , W(15), where W(0) is the
left-most word.
* For t = 0 to 15 do:
T = S^5(A) + F((B), (C), (D)) + (E) + W(t) + K(t);
E = D;
D = C;
C = S^30(B);
B = A;
A = T;
* fact do:
T = S^5(A) + F((B), (C), (D)) + (E) + W(t) + K(t);
B = S^30(B);
* and change the significance when next call
*/
if (!is_endian.little && sizeof(dword) == 4 && ((size_t)p % 4) == 0) {
const dword *W = (const dword *)data;
X(0) = W[0];
BODY_00_15(0, A, B, C, D, E, T, X(0));
X(1) = W[1];
BODY_00_15(1, T, A, B, C, D, E, X(1));
X(2) = W[2];
BODY_00_15(2, E, T, A, B, C, D, X(2));
X(3) = W[3];
BODY_00_15(3, D, E, T, A, B, C, X(3));
X(4) = W[4];
BODY_00_15(4, C, D, E, T, A, B, X(4));
X(5) = W[5];
BODY_00_15(5, B, C, D, E, T, A, X(5));
X(6) = W[6];
BODY_00_15(6, A, B, C, D, E, T, X(6));
X(7) = W[7];
BODY_00_15(7, T, A, B, C, D, E, X(7));
X(8) = W[8];
BODY_00_15(8, E, T, A, B, C, D, X(8));
X(9) = W[9];
BODY_00_15(9, D, E, T, A, B, C, X(9));
X(10) = W[10];
BODY_00_15(10, C, D, E, T, A, B, X(10));
X(11) = W[11];
BODY_00_15(11, B, C, D, E, T, A, X(11));
X(12) = W[12];
BODY_00_15(12, A, B, C, D, E, T, X(12));
X(13) = W[13];
BODY_00_15(13, T, A, B, C, D, E, X(13));
X(14) = W[14];
BODY_00_15(14, E, T, A, B, C, D, X(14));
X(15) = W[15];
BODY_00_15(15, D, E, T, A, B, C, X(15));
data += SHA_CBLOCK;
} else {
(void)HOST_c2l(data, l);
X(0) = l;
(void)HOST_c2l(data, l);
X(1) = l;
BODY_00_15(0, A, B, C, D, E, T, X(0));
(void)HOST_c2l(data, l);
X(2) = l;
BODY_00_15(1, T, A, B, C, D, E, X(1));
(void)HOST_c2l(data, l);
X(3) = l;
BODY_00_15(2, E, T, A, B, C, D, X(2));
(void)HOST_c2l(data, l);
X(4) = l;
BODY_00_15(3, D, E, T, A, B, C, X(3));
(void)HOST_c2l(data, l);
X(5) = l;
BODY_00_15(4, C, D, E, T, A, B, X(4));
(void)HOST_c2l(data, l);
X(6) = l;
BODY_00_15(5, B, C, D, E, T, A, X(5));
(void)HOST_c2l(data, l);
X(7) = l;
BODY_00_15(6, A, B, C, D, E, T, X(6));
(void)HOST_c2l(data, l);
X(8) = l;
BODY_00_15(7, T, A, B, C, D, E, X(7));
(void)HOST_c2l(data, l);
X(9) = l;
BODY_00_15(8, E, T, A, B, C, D, X(8));
(void)HOST_c2l(data, l);
X(10) = l;
BODY_00_15(9, D, E, T, A, B, C, X(9));
(void)HOST_c2l(data, l);
X(11) = l;
BODY_00_15(10, C, D, E, T, A, B, X(10));
(void)HOST_c2l(data, l);
X(12) = l;
BODY_00_15(11, B, C, D, E, T, A, X(11));
(void)HOST_c2l(data, l);
X(13) = l;
BODY_00_15(12, A, B, C, D, E, T, X(12));
(void)HOST_c2l(data, l);
X(14) = l;
BODY_00_15(13, T, A, B, C, D, E, X(13));
(void)HOST_c2l(data, l);
X(15) = l;
BODY_00_15(14, E, T, A, B, C, D, X(14));
BODY_00_15(15, D, E, T, A, B, C, X(15));
}
/*
* SHA1 Document:
* For t = 16 to 79 let:
W(t) = S^1(W(t-3) XOR W(t-8) XOR W(t-14) XOR W(t-16)).
T = S^5(A) + F((B), (C), (D)) + (E) + W(t) + K(t);
E = D;
D = C;
C = S^30(B);
B = A;
A = T;
* description
when step 0-15 end, register val as follows:
{ C = T; B = E; A = D; T = C; E = B; D = A; }
so next call as follows:
(A,C,B,D,E,...) = {C,D,E,T,A,...}
*/
BODY_16_19(16, C, D, E, T, A, B, X(0), X(0), X(2), X(8), X(13));
BODY_16_19(17, B, C, D, E, T, A, X(1), X(1), X(3), X(9), X(14));
BODY_16_19(18, A, B, C, D, E, T, X(2), X(2), X(4), X(10), X(15));
BODY_16_19(19, T, A, B, C, D, E, X(3), X(3), X(5), X(11), X(0));
BODY_20_31(20, E, T, A, B, C, D, X(4), X(4), X(6), X(12), X(1));
BODY_20_31(21, D, E, T, A, B, C, X(5), X(5), X(7), X(13), X(2));
BODY_20_31(22, C, D, E, T, A, B, X(6), X(6), X(8), X(14), X(3));
BODY_20_31(23, B, C, D, E, T, A, X(7), X(7), X(9), X(15), X(4));
BODY_20_31(24, A, B, C, D, E, T, X(8), X(8), X(10), X(0), X(5));
BODY_20_31(25, T, A, B, C, D, E, X(9), X(9), X(11), X(1), X(6));
BODY_20_31(26, E, T, A, B, C, D, X(10), X(10), X(12), X(2), X(7));
BODY_20_31(27, D, E, T, A, B, C, X(11), X(11), X(13), X(3), X(8));
BODY_20_31(28, C, D, E, T, A, B, X(12), X(12), X(14), X(4), X(9));
BODY_20_31(29, B, C, D, E, T, A, X(13), X(13), X(15), X(5), X(10));
BODY_20_31(30, A, B, C, D, E, T, X(14), X(14), X(0), X(6), X(11));
BODY_20_31(31, T, A, B, C, D, E, X(15), X(15), X(1), X(7), X(12));
BODY_32_39(32, E, T, A, B, C, D, X(0), X(2), X(8), X(13));
BODY_32_39(33, D, E, T, A, B, C, X(1), X(3), X(9), X(14));
BODY_32_39(34, C, D, E, T, A, B, X(2), X(4), X(10), X(15));
BODY_32_39(35, B, C, D, E, T, A, X(3), X(5), X(11), X(0));
BODY_32_39(36, A, B, C, D, E, T, X(4), X(6), X(12), X(1));
BODY_32_39(37, T, A, B, C, D, E, X(5), X(7), X(13), X(2));
BODY_32_39(38, E, T, A, B, C, D, X(6), X(8), X(14), X(3));
BODY_32_39(39, D, E, T, A, B, C, X(7), X(9), X(15), X(4));
BODY_40_59(40, C, D, E, T, A, B, X(8), X(10), X(0), X(5));
BODY_40_59(41, B, C, D, E, T, A, X(9), X(11), X(1), X(6));
BODY_40_59(42, A, B, C, D, E, T, X(10), X(12), X(2), X(7));
BODY_40_59(43, T, A, B, C, D, E, X(11), X(13), X(3), X(8));
BODY_40_59(44, E, T, A, B, C, D, X(12), X(14), X(4), X(9));
BODY_40_59(45, D, E, T, A, B, C, X(13), X(15), X(5), X(10));
BODY_40_59(46, C, D, E, T, A, B, X(14), X(0), X(6), X(11));
BODY_40_59(47, B, C, D, E, T, A, X(15), X(1), X(7), X(12));
BODY_40_59(48, A, B, C, D, E, T, X(0), X(2), X(8), X(13));
BODY_40_59(49, T, A, B, C, D, E, X(1), X(3), X(9), X(14));
BODY_40_59(50, E, T, A, B, C, D, X(2), X(4), X(10), X(15));
BODY_40_59(51, D, E, T, A, B, C, X(3), X(5), X(11), X(0));
BODY_40_59(52, C, D, E, T, A, B, X(4), X(6), X(12), X(1));
BODY_40_59(53, B, C, D, E, T, A, X(5), X(7), X(13), X(2));
BODY_40_59(54, A, B, C, D, E, T, X(6), X(8), X(14), X(3));
BODY_40_59(55, T, A, B, C, D, E, X(7), X(9), X(15), X(4));
BODY_40_59(56, E, T, A, B, C, D, X(8), X(10), X(0), X(5));
BODY_40_59(57, D, E, T, A, B, C, X(9), X(11), X(1), X(6));
BODY_40_59(58, C, D, E, T, A, B, X(10), X(12), X(2), X(7));
BODY_40_59(59, B, C, D, E, T, A, X(11), X(13), X(3), X(8));
BODY_60_79(60, A, B, C, D, E, T, X(12), X(14), X(4), X(9));
BODY_60_79(61, T, A, B, C, D, E, X(13), X(15), X(5), X(10));
BODY_60_79(62, E, T, A, B, C, D, X(14), X(0), X(6), X(11));
BODY_60_79(63, D, E, T, A, B, C, X(15), X(1), X(7), X(12));
BODY_60_79(64, C, D, E, T, A, B, X(0), X(2), X(8), X(13));
BODY_60_79(65, B, C, D, E, T, A, X(1), X(3), X(9), X(14));
BODY_60_79(66, A, B, C, D, E, T, X(2), X(4), X(10), X(15));
BODY_60_79(67, T, A, B, C, D, E, X(3), X(5), X(11), X(0));
BODY_60_79(68, E, T, A, B, C, D, X(4), X(6), X(12), X(1));
BODY_60_79(69, D, E, T, A, B, C, X(5), X(7), X(13), X(2));
BODY_60_79(70, C, D, E, T, A, B, X(6), X(8), X(14), X(3));
BODY_60_79(71, B, C, D, E, T, A, X(7), X(9), X(15), X(4));
BODY_60_79(72, A, B, C, D, E, T, X(8), X(10), X(0), X(5));
BODY_60_79(73, T, A, B, C, D, E, X(9), X(11), X(1), X(6));
BODY_60_79(74, E, T, A, B, C, D, X(10), X(12), X(2), X(7));
BODY_60_79(75, D, E, T, A, B, C, X(11), X(13), X(3), X(8));
BODY_60_79(76, C, D, E, T, A, B, X(12), X(14), X(4), X(9));
BODY_60_79(77, B, C, D, E, T, A, X(13), X(15), X(5), X(10));
BODY_60_79(78, A, B, C, D, E, T, X(14), X(0), X(6), X(11));
BODY_60_79(79, T, A, B, C, D, E, X(15), X(1), X(7), X(12));
/*
* SHA1 Document:
* Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E.
*/
ctx_.h0 = (ctx_.h0 + E) & 0xffffffffL;
ctx_.h1 = (ctx_.h1 + T) & 0xffffffffL;
ctx_.h2 = (ctx_.h2 + A) & 0xffffffffL;
ctx_.h3 = (ctx_.h3 + B) & 0xffffffffL;
ctx_.h4 = (ctx_.h4 + C) & 0xffffffffL;
if (--num_block == 0) break;
/*
* SHA1 Document:
* Let A = H0, B = H1, C = H2, D = H3, E = H4.
*/
A = ctx_.h0;
B = ctx_.h1;
C = ctx_.h2;
D = ctx_.h3;
E = ctx_.h4;
}
}