文章目录
1. 简介
SHA2:
- 于2002年8月发布于FIPS 180-2,取代SHA1 (FIPS 180-1),增加了SHA-256、SHA-512、SHA-384;
- 2004年2月,发布FIPS 180-2 (with Change Notice 1),增加SHA-224,以适应3DES密钥长度;
- 2015年8月,FIPS 180-4增加了SHA-512/224, SHA-512/256。
以下为SHA系列算法表格,长度单位bits:
Algorithm | Message Size | Block Size | Word Size | Message Digest Size | Steps |
---|---|---|---|---|---|
SHA-1 | < 2^64 | 512 | 32 | 160 | 80 |
SHA-224 | < 2^64 | 512 | 32 | 224 | 64 |
SHA-256 | < 2^64 | 512 | 32 | 256 | 64 |
SHA-384 | < 2^128 | 1024 | 64 | 384 | 80 |
SHA-512 | < 2^128 | 1024 | 64 | 512 | 80 |
SHA-512/224 | < 2^128 | 1024 | 64 | 224 | 80 |
SHA-512/256 | < 2^128 | 1024 | 64 | 256 | 80 |
2. 算法流程
SHA2家族的流程可以套用同一个模板。
本部分参考FIPS 180-4整理,标号与文档保持一致。
4.1 FUNCTIONS
公用部分:
#define CH(x,y,z) (((x) & (y)) ^ (~(x) & (z)))
#define MAJ(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
#define ROTR32(n, x) (((x)>>(n)) | ((x)<<(32-(n))))
#define ROTR64(n, x) (((x)>>(n)) | ((x)<<(64-(n))))
#define SHR(n,x) ((x)>>(n))
它们各有4个sigma函数,区别在于字的位数和移位位数。
4.1.2 SHA-224 and SHA-256 Functions
#define ROTR32(n, x) (((x)>>(n)) | ((x)<<(32-(n))))
#define SHR(n,x) ((x)>>(n))
#define SIGMA_0_256(x) (ROTR32(2,x) ^ ROTR32(13,x) ^ ROTR32(22,x))
#define SIGMA_1_256(x) (ROTR32(6,x) ^ ROTR32(11,x) ^ ROTR32(25,x))
#define sigma_0_256(x) (ROTR32(7,x) ^ ROTR32(18,x) ^ SHR(3,x))
#define sigma_1_256(x) (ROTR32(17,x) ^ ROTR32(19,x) ^ SHR(10,x))
4.1.3 SHA-384, SHA-512, SHA-512/224 and SHA-512/256 Functions
#define SIGMA_0_512(x) (ROTR64(28,x) ^ ROTR64(34,x) ^ ROTR64(39,x))
#define SIGMA_1_512(x) (ROTR64(14,x) ^ ROTR64(18,x) ^ ROTR64(41,x))
#define sigma_0_512(x) (ROTR64(1,x) ^ ROTR64(8,x) ^ SHR(7,x))
#define sigma_1_512(x) (ROTR64(19,x) ^ ROTR64(61,x) ^ SHR(6,x))
4.2 CONSTANTS
4.2.2 SHA-224 and SHA-256 Constants
使用64个4字节整型常量。这些常量表示前64个质数立方根小数部分的前32位,以提高随机性。
4.2.3 SHA-384, SHA-512, SHA-512/224 and SHA-512/256 Constants
使用80个8字节整型常量。这些常量表示前80个质数立方根小数部分的前64位。
如何生成这些常数序列,可参考我的另一篇文章:如何生成SHA2常数序列_CodeStarr的博客
5. PREPROCESSING
5.1 Padding the Message
SHA-224 和SHA-256的填充方法与SHA1完全一致。而SHA-512系列和它们的区别是位数需要加倍。
第一步,填充数据,使位长度比1024的倍数小128。
bitsLen == 896 mod 1024
bytesLen == 112 mod 128
填充方法:
- 一个1,后续用0;
- 至少填充1 bit,至多1024 bits (128 bytes)。
第二步,添加128 bits消息长度。
经过这一步,消息长度就是1024 bits (128 bytes == 32 words)的倍数了。
代码层面,SHA-512要使用unsigned long long[2]
类型。
5.2 Parsing the Message
SHA-224 和SHA-256的消息处理方法与SHA1完全一致,分块长512 bit。
而SHA-512系列分块长度加倍为1024 bits即可。
在代码层面,消息是以单字节数组形式存储的,在计算时,则是需要以大端序转换成4字节或8字节整型。
5.3 Setting the Initial Hash Value
SHA2的hash数组长度都是8,最后拼接为hash值,只不过需要区分字长。
#if WORD_SIZE 4
typedef uint32_t sha2_word_t;
#elif WORD_SIZE==8
typedef uint64_t sha2_word_t;
#endif
sha2_word_t h[8] = {
#ifdef SHA224
0xc1059ed8,
0x367cd507,
0x3070dd17,
0xf70e5939,
0xffc00b31,
0x68581511,
0x64f98fa7,
0xbefa4fa4
#elif SHA256
0x6a09e667,
0xbb67ae85,
0x3c6ef372,
0xa54ff53a,
0x510e527f,
0x9b05688c,
0x1f83d9ab,
0x5be0cd19
#elif SHA384
0xcbbb9d5dc1059ed8ULL,
0x629a292a367cd507ULL,
0x9159015a3070dd17ULL,
0x152fecd8f70e5939ULL,
0x67332667ffc00b31ULL,
0x8eb44a8768581511ULL,
0xdb0c2e0d64f98fa7ULL,
0x47b5481dbefa4fa4ULL
#elif SHA512
0x6a09e667f3bcc908ULL,
0xbb67ae8584caa73bULL,
0x3c6ef372fe94f82bULL,
0xa54ff53a5f1d36f1ULL,
0x510e527fade682d1ULL,
0x9b05688c2b3e6c1fULL,
0x1f83d9abfb41bd6bULL,
0x5be0cd19137e2179ULL
#elif SHA512_224
0x8C3D37C819544DA2ULL,
0x73E1996689DCD4D6ULL,
0x1DFAB7AE32FF9C82ULL,
0x679DD514582F9FCFULL,
0x0F6D2B697BD44DA8ULL,
0x77E36F7304C48942ULL,
0x3F9D85A86A1D36C8ULL,
0x1112E6AD91D692A1ULL
#elif SHA512_256
0x22312194FC2BF72CULL,
0x9F555FA3C84C64C2ULL,
0x2393B86B6F53B151ULL,
0x963877195940EABDULL,
0x96283EE2A88EFFE3ULL,
0xBE5E1E2553863992ULL,
0x2B0199FC2C85B8AAULL,
0x0EB72DDC81C52CA2ULL
#endif
};
其中,SHA-384的初始值是取第9到16个素数的平方根的小数部分获取的(与常数部分类似),生成脚本如下:
import gmpy2
from gmpy2 import mpfr, floor, next_prime
def convert_primes_root_fractional_part_to_hex_constant(prime, hex_chars=8):
cube_root = mpfr(prime)**(1/mpfr(2))
frac_part = cube_root - floor(cube_root)
strRet = "{0:1a}".format(floor(frac_part*(16**hex_chars)))
# 0xc.19bf174p+28
strRet = strRet[2:]
strRet = strRet.replace(".","")
# c19bf174p+28
strRet = strRet[:strRet.index("p+")]
# c19bf174
while(len(strRet) < 8):
strRet = "0" + strRet
return strRet;
def generate_n_primes(n=64):
p = 2
i = 0
while i < n:
yield p
p = next_prime(p)
i += 1
gmpy2.get_context().precision=100
listPrimes = list(enumerate(generate_n_primes(16), 8))
# print(listPrimes)
for i,p in listPrimes[8:]:
if i % 4 == 0:
print("")
print(convert_primes_root_fractional_part_to_hex_constant(p, hex_chars=16), end=" ")
而SHA-512/224和SHA-512/256的初始值是通过SHA-512/t IV Generation Function
计算出来的:
for i in range(8):
new_iv[i] = H_sha512[i] ^ 0xa5a5a5a5a5a5a5a5;
h_512_t[i] = sha512("SHA-512/t") # using new_iv[8] as IV[8]
生成代码: https://github.com/C0deStarr/CryptoImp/tree/main/Hash/sha/sha512.c sha512_t_iv_generator()
函数。
6.2.2 SHA-256 Hash Computation
Prepare the message schedule
#define SCHEDULE(t) (sigma_1_256(W[t-2]) \
+ W[t-7] \
+ sigma_0_256(W[t-15]) \
+ W[t-16])
Initialize the eight working variables
a = h[0];
b = h[1];
c = h[2];
d = h[3];
e = h[4];
f = h[5];
g = h[6];
h = h[7];
64 Steps
与SHA1类似,区别是SHA1有80轮,而SHA256有64轮。
uint32_t W[64] = {0};
if ( 0 <= t && t <= 15)
{
W[t] = M[t];
}
else if (16 <= t && t <= 63)
{
T1 = h + SIGMA_1_256(e) + CH(e,f,g) + K[t] + W[t];
T2 = SIGMA_0_256(a) + MAJ(a,b,c);
h = g
g = f
f = e
e = d + T1
d = c
c = b
b = a
a = T1 + T2
}
需要强调,W是uint32_t类型数组,M则是单字节数组,SHA算法是按照大端序存储整型的。
另外对于t>=16的情况,其实可以优化。忽略掉变量置换以及临时变量T1、T2,只关注加法,其实可以看做如下逻辑:
#define CYCLE(a,b,c,d,e,f,g,h,t) \
h += SIGMA_1_256(e) + CH(e,f,g) + K[t] + W[t]; \
d += h; \
h += SIGMA_0_256(a) + MAJ(a,b,c);
而所有64步,只需要不停变换参数即可实现置换的效果:
CYCLE(a,b,c,d,e,f,g,h, 0);
CYCLE(h,a,b,c,d,e,f,g, 1);
//...
CYCLE(b,c,d,e,f,g,h,a, 63);
常量的个数与计算步骤数是一致的,每一次计算都会取用K[t]
。在SHA-512中,有80个常量,所以有80轮计算。
Compute the intermediate hash value
h[0] += a;
h[1] += b;
h[2] += c;
h[3] += d;
h[4] += e;
h[5] += f;
h[6] += g;
h[7] += h;
3. 实现
SHA-224可以嵌套调用SHA-256,SHA-384, SHA-512/224, SHA-512/256也可以嵌套调用SHA-512。
https://github.com/C0deStarr/CryptoImp/tree/main/Hash/sha
参考资料
https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf
https://csrc.nist.gov/publications/detail/fips/180/2/archive/2002-08-01
https://csrc.nist.gov/publications/detail/fips/180/2/archive/2004-02-25
https://csrc.nist.gov/CSRC/media/Publications/fips/180/2/archive/2002-08-01/documents/fips180-2.pdf