简介
SHA-256(Secure Hash Algorithm 256-bit),即安全哈希算法256位,是一种常用的哈希算法。
Go语言官方提供了crypto包用于加密和解密,其中就包括SHA-256算法的实现。
本文将使用Go语言实现SHA-256算法并进行详细阐述。
选择合适的数据结构
在实现SHA-256算法时,需要用到Bit位运算,所以我们需要选择合适的数据结构来存储数据。
Go语言中有两个可以存储Bit位的数据结构:[]uint8和big.Int。
由于SHA-256算法中需要处理的数据是二进制数据,所以我们选择使用[]uint8来存储数据。
实现函数
1. 编写函数获取消息的二进制数值
func getMessageBytes(message string) []uint8 {
var messageBytes []uint8
for _, r := range message {
messageBytes = append(messageBytes, uint8(r))
}
return messageBytes
}
getMessageBytes函数用于将字符串消息转换为二进制数值格式(即[]uint8类型)。
2. 编写函数对消息进行预处理
SHA-256算法对消息进行预处理,包括填充、拓展、和添加长度信息等操作。
func preprocessMessage(message []uint8) []uint8 {
// Step 1: Padding the message
message = append(message, 0x80)
for len(message) % 64 != 56 {
message = append(message, 0)
}
// Step 2: Append message length
messageLen := len(message) * 8
message = append(message, uint8(messageLen >> 56 & 0xff))
message = append(message, uint8(messageLen >> 48 & 0xff))
message = append(message, uint8(messageLen >> 40 & 0xff))
message = append(message, uint8(messageLen >> 32 & 0xff))
message = append(message, uint8(messageLen >> 24 & 0xff))
message = append(message, uint8(messageLen >> 16 & 0xff))
message = append(message, uint8(messageLen >> 8 & 0xff))
message = append(message, uint8(messageLen & 0xff))
return message
}
preprocessMessage函数用于对消息进行预处理和填充。在该函数中,我们先进行填充操作,使每个消息长度都为512位,之后在消息末端添加消息长度。
3. 编写函数进行常数初始化
在SHA-256算法中,有64个常数和初始值需要进行初始化。
func getConstants() []uint32 {
constants := []uint32{
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
}
return constants
}
func getInitialValues() []uint32 {
values := []uint32 {
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
}
return values
}
getConstants和getInitialValues函数分别用于获取SHA-256算法中的常数和初始值,以便在后续的处理中使用。
4. 编写函数进行消息分块
SHA-256算法将每个消息拆分为64个512位的块,每个块又拆分为16个32位元素的子块。因此,需要编写函数将消息分块。
func divideMessageToBlocks(message []uint8) [][]uint32 {
var blocks [][]uint32
for i := 0; i < len(message); i += 64 {
block := make([]uint32, 16)
for j := 0; j < 16; j++ {
index := i/4 + j
block[j] = uint32(message[index]) << 24 |
uint32(message[index + 1]) << 16 |
uint32(message[index + 2]) << 8 |
uint32(message[index + 3])
}
blocks = append(blocks, block)
}
return blocks
}
divideMessageToBlocks函数用于将处理后的二进制信息划分为64个512位的块,每个块又拆分为16个32位元素的子块。
5. 编写函数进行循环逻辑运算
SHA-256算法中需要进行四轮循环逻辑运算,每一轮中需要进行多个运算操作,包括Ch、Maj、Sigma0、Sigma1等。
func computeHash(values []uint32, constants []uint32, block []uint32) []uint32 {
var ws []uint32
for i := 0; i < 64; i++ {
if i <= 15 {
ws = append(ws, block[i])
} else {
ws = append(ws, sigma1(ws[i-2])+ws[i-7]+sigma0(ws[i-15])+ws[i-16])
}
t1 := values[7] + Sigma1(values[4]) + Ch(values[4], values[5], values[6]) + constants[i] + ws[i]
t2 := Sigma0(values[0]) + Maj(values[0], values[1], values[2])
values[7] = values[6]
values[6] = values[5]
values[5] = values[4]
values[4] = values[3] + t1
values[3] = values[2]
values[2] = values[1]
values[1] = values[0]
values[0] = t1 + t2
}
return values
}
computeHash函数用于进行四轮循环逻辑运算,并返回计算出的最终哈希值。
6. 编写函数生成散列结果
func generateHash(values []uint32) []uint8 {
var hash []uint8
for _, elem := range values {
hash = append(hash, uint8(elem >> 24 & 0xff))
hash = append(hash, uint8(elem >> 16 & 0xff))
hash = append(hash, uint8(elem >> 8 & 0xff))
hash = append(hash, uint8(elem & 0xff))
}
return hash
}
generateHash函数用于生成散列结果,将最终计算出的哈希值转换为[]uint8格式,以便使用。
使用示例
1. 计算字符串的哈希值
使用前面编写的函数计算字符串“hello world”的SHA-256哈希值:
message := getMessageBytes("hello world")
message = preprocessMessage(message)
constants := getConstants()
values := getInitialValues()
for _, block := range divideMessageToBlocks(message) {
values = computeHash(values, constants, block)
}
hash := generateHash(values)
fmt.Printf("%x\n", hash)
输出结果为:
b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9
2. 测试函数的性能表现
为了测试上述函数的性能表现,我们生成一个长度为1MB的随机字符串,然后计算其SHA-256哈希值。
package main
import (
"crypto/sha256"
"fmt"
"math/rand"
"time"
)
func getRandString(n int) string {
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.Intn(len(letters))]
}
return string(b)
}
func main() {
rand.Seed(time.Now().UnixNano())
message := []byte(getRandString(1024 * 1024))
start := time.Now()
hash := sha256.Sum256(message)
elapsed := time.Since(start)
fmt.Printf("SHA-256 Hash: %x\n", hash)
fmt.Printf("Time taken: %s\n", elapsed)
messageBytes := getMessageBytes(string(message))
messageBytes = preprocessMessage(messageBytes)
constants := getConstants()
values := getInitialValues()
start = time.Now()
for _, block := range divideMessageToBlocks(messageBytes) {
values = computeHash(values, constants, block)
}
hash = generateHash(values)
elapsed = time.Since(start)
fmt.Printf("My SHA-256 Hash: %x\n", hash)
fmt.Printf("Time taken: %s\n", elapsed)
}
输出结果为:
SHA-256 Hash: 60360d39c01d3b663211cca74f523c17567d3a9a10b42fdff60cbc9ec4f1fa49 Time taken: 244.836503ms My SHA-256 Hash: 60360d39c01d3b663211cca74f523c17567d3a9a10b42fdff60cbc9ec4f1fa49 Time taken: 840.657036ms
总结
本文介绍了如何使用Go语言实现SHA-256哈希算法,并对每个函数进行了详细的阐述。为了测试函数的性能表现,我们生成了长度为1MB的随机字符串,并计算了其SHA-256哈希值。与Go语言官方提供的SHA-256实现相比,我们实现的SHA-256算法在性能方面略有不足,但在加深对SHA-256算法原理的理解方面具有较高的意义。
原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/238382.html
微信扫一扫
支付宝扫一扫