除空字符串外,自定义 SHA256 哈希计算失败
Custom SHA256 hash calculation fails on anything but an empty String
我正在尝试创建自己的散列 framework/library,但我偶然发现了一个问题。当我计算一个空字符串的 SHA256 哈希时,哈希计算成功,但是当我计算其他任何东西时,它失败了。有人可以帮我弄清楚为什么吗?
如 Wikipedia 所提供,在线执行并使用 python 时,此哈希匹配。
let h = SHA256(message: Data("".utf8))
let d = h.digest()
// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
print(d)
但是'Hello world'没有
let h = SHA256(message: Data("Hello world".utf8))
let d = h.digest()
// ce9f4c08f0688d09b8061ed6692c1d5af2516c8682fad2d9a5d72f96ba787a80
print(d)
// Expected:
// 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c
希望有人能帮助我。 SHA256 实现如下:
/*
First 32 bits of the fractional parts of the
square roots of the first 8 primes 2..19.
*/
fileprivate let kSHA256H0: UInt32 = 0x6a09e667
fileprivate let kSHA256H1: UInt32 = 0xbb67ae85
fileprivate let kSHA256H2: UInt32 = 0x3c6ef372
fileprivate let kSHA256H3: UInt32 = 0xa54ff53a
fileprivate let kSHA256H4: UInt32 = 0x510e527f
fileprivate let kSHA256H5: UInt32 = 0x9b05688c
fileprivate let kSHA256H6: UInt32 = 0x1f83d9ab
fileprivate let kSHA256H7: UInt32 = 0x5be0cd19
/*
First 32 bits of the fractional parts of the
cube roots of the first 64 primes 2..311.
*/
fileprivate let kSHA256K: [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
]
/// Shift the value of x n amount to the right.
/// - Parameters:
/// - x: The value to shift.
/// - n: The amount to shift by.
/// - Returns: The shifted value.
fileprivate func shiftRight(_ x: UInt32, _ n: UInt32) -> UInt32 { x >> n }
/// Rotate the value of x n amount of times.
/// - Parameters:
/// - x: The value to rotate.
/// - y: The amount to rotate by.
/// - Returns: The rotated value.
fileprivate func rotateRight(_ x: UInt32, _ y: UInt32) -> UInt32 { (x >> (y & 31)) | (x << (32 - (y & 31))) }
/// Split data into chunks of specified size.
/// - Note: This function will not pad or append data
/// to make sure all the chunks are equal in size.
/// - Parameters:
/// - data: The data to split.
/// - size: The size of a chunk.
/// - Returns: An array containing chunks of specified size (when able).
fileprivate func chunk(_ data: Data, toSize size: Int) -> [Data] {
stride(from: 0, to: data.count, by: size).map {
data.subdata(in: [=12=] ..< Swift.min([=12=] + size, data.count))
}
}
public class SHA256 {
/// The pre-processed data.
fileprivate let message: Data
fileprivate var hash = [
kSHA256H0, kSHA256H1, kSHA256H2, kSHA256H3,
kSHA256H4, kSHA256H5, kSHA256H6, kSHA256H7
]
public init(message: Data) {
self.message = Self.preProcess(message: message)
}
fileprivate static func preProcess(message: Data) -> Data {
let L = message.count * 8 // Original message length in bits.
var K = 0 // Required padding bits.
while (L + 1 + K + 64) % 512 != 0 {
K += 1
}
var padding = Data(repeating: 0, count: K / 8)
padding.insert(0x80, at: 0) // Insert 1000 0000 into the padding.
var length = UInt64(L).bigEndian
return message + padding + Data(bytes: &length, count: 8)
}
public func digest() -> Data {
let chunks = chunk(message, toSize: 64)
for chunk in chunks {
var w = [UInt32](repeating: 0, count: 64) // 64-entry message schedule array of 32-bit words.
// Copy the chunk into first 16 words w[0..15] of the schedule array.
for i in 0 ..< 16 {
let sub = chunk.subdata(in: i ..< i + 4)
w[i] = sub.withUnsafeBytes { [=12=].load(as: UInt32.self) }.bigEndian
}
// Extend the first 16 words into the remaining 48 words w[16..63] of the schedule array.
for i in 16 ..< 64 {
let s0 = rotateRight(w[i - 15], 7) ^ rotateRight(w[i - 15], 18) ^ shiftRight(w[i - 15], 3)
let s1 = rotateRight(w[i - 2], 17) ^ rotateRight(w[i - 2], 19) ^ shiftRight(w[i - 2], 10)
w[i] = s1 &+ w[i - 7] &+ s0 &+ w[i - 16]
}
// Create some working variables.
var a = hash[0]
var b = hash[1]
var c = hash[2]
var d = hash[3]
var e = hash[4]
var f = hash[5]
var g = hash[6]
var h = hash[7]
// Compress function main loop.
for i in 0 ..< 64 {
let S1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25)
let ch = (e & f) ^ (~e & g)
let T1 = h &+ S1 &+ ch &+ kSHA256K[i] &+ w[i]
let S0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22)
let maj = (a & b) ^ (a & c) ^ (b & c)
let T2 = S0 &+ maj
h = g
g = f
f = e
e = d &+ T1
d = c
c = b
b = a
a = T1 &+ T2
}
hash[0] &+= a
hash[1] &+= b
hash[2] &+= c
hash[3] &+= d
hash[4] &+= e
hash[5] &+= f
hash[6] &+= g
hash[7] &+= h
}
return hash.map {
var num = [=12=].bigEndian
return Data(bytes: &num, count: 4)
}.reduce(Data(), +)
}
}
事实证明,我创建了错误的子数据来构造我的 UInt32
从创建消息计划数组。 (.digest()
函数中的前几行)
旧的是
let sub = chunk.subdata(in: i ..< i + 4)
新的是
let sub = chunk.subdata(in: i * 4 ..< (i * 4) + 4)
这解决了问题
我正在尝试创建自己的散列 framework/library,但我偶然发现了一个问题。当我计算一个空字符串的 SHA256 哈希时,哈希计算成功,但是当我计算其他任何东西时,它失败了。有人可以帮我弄清楚为什么吗?
如 Wikipedia 所提供,在线执行并使用 python 时,此哈希匹配。
let h = SHA256(message: Data("".utf8))
let d = h.digest()
// e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
print(d)
但是'Hello world'没有
let h = SHA256(message: Data("Hello world".utf8))
let d = h.digest()
// ce9f4c08f0688d09b8061ed6692c1d5af2516c8682fad2d9a5d72f96ba787a80
print(d)
// Expected:
// 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c
希望有人能帮助我。 SHA256 实现如下:
/*
First 32 bits of the fractional parts of the
square roots of the first 8 primes 2..19.
*/
fileprivate let kSHA256H0: UInt32 = 0x6a09e667
fileprivate let kSHA256H1: UInt32 = 0xbb67ae85
fileprivate let kSHA256H2: UInt32 = 0x3c6ef372
fileprivate let kSHA256H3: UInt32 = 0xa54ff53a
fileprivate let kSHA256H4: UInt32 = 0x510e527f
fileprivate let kSHA256H5: UInt32 = 0x9b05688c
fileprivate let kSHA256H6: UInt32 = 0x1f83d9ab
fileprivate let kSHA256H7: UInt32 = 0x5be0cd19
/*
First 32 bits of the fractional parts of the
cube roots of the first 64 primes 2..311.
*/
fileprivate let kSHA256K: [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
]
/// Shift the value of x n amount to the right.
/// - Parameters:
/// - x: The value to shift.
/// - n: The amount to shift by.
/// - Returns: The shifted value.
fileprivate func shiftRight(_ x: UInt32, _ n: UInt32) -> UInt32 { x >> n }
/// Rotate the value of x n amount of times.
/// - Parameters:
/// - x: The value to rotate.
/// - y: The amount to rotate by.
/// - Returns: The rotated value.
fileprivate func rotateRight(_ x: UInt32, _ y: UInt32) -> UInt32 { (x >> (y & 31)) | (x << (32 - (y & 31))) }
/// Split data into chunks of specified size.
/// - Note: This function will not pad or append data
/// to make sure all the chunks are equal in size.
/// - Parameters:
/// - data: The data to split.
/// - size: The size of a chunk.
/// - Returns: An array containing chunks of specified size (when able).
fileprivate func chunk(_ data: Data, toSize size: Int) -> [Data] {
stride(from: 0, to: data.count, by: size).map {
data.subdata(in: [=12=] ..< Swift.min([=12=] + size, data.count))
}
}
public class SHA256 {
/// The pre-processed data.
fileprivate let message: Data
fileprivate var hash = [
kSHA256H0, kSHA256H1, kSHA256H2, kSHA256H3,
kSHA256H4, kSHA256H5, kSHA256H6, kSHA256H7
]
public init(message: Data) {
self.message = Self.preProcess(message: message)
}
fileprivate static func preProcess(message: Data) -> Data {
let L = message.count * 8 // Original message length in bits.
var K = 0 // Required padding bits.
while (L + 1 + K + 64) % 512 != 0 {
K += 1
}
var padding = Data(repeating: 0, count: K / 8)
padding.insert(0x80, at: 0) // Insert 1000 0000 into the padding.
var length = UInt64(L).bigEndian
return message + padding + Data(bytes: &length, count: 8)
}
public func digest() -> Data {
let chunks = chunk(message, toSize: 64)
for chunk in chunks {
var w = [UInt32](repeating: 0, count: 64) // 64-entry message schedule array of 32-bit words.
// Copy the chunk into first 16 words w[0..15] of the schedule array.
for i in 0 ..< 16 {
let sub = chunk.subdata(in: i ..< i + 4)
w[i] = sub.withUnsafeBytes { [=12=].load(as: UInt32.self) }.bigEndian
}
// Extend the first 16 words into the remaining 48 words w[16..63] of the schedule array.
for i in 16 ..< 64 {
let s0 = rotateRight(w[i - 15], 7) ^ rotateRight(w[i - 15], 18) ^ shiftRight(w[i - 15], 3)
let s1 = rotateRight(w[i - 2], 17) ^ rotateRight(w[i - 2], 19) ^ shiftRight(w[i - 2], 10)
w[i] = s1 &+ w[i - 7] &+ s0 &+ w[i - 16]
}
// Create some working variables.
var a = hash[0]
var b = hash[1]
var c = hash[2]
var d = hash[3]
var e = hash[4]
var f = hash[5]
var g = hash[6]
var h = hash[7]
// Compress function main loop.
for i in 0 ..< 64 {
let S1 = rotateRight(e, 6) ^ rotateRight(e, 11) ^ rotateRight(e, 25)
let ch = (e & f) ^ (~e & g)
let T1 = h &+ S1 &+ ch &+ kSHA256K[i] &+ w[i]
let S0 = rotateRight(a, 2) ^ rotateRight(a, 13) ^ rotateRight(a, 22)
let maj = (a & b) ^ (a & c) ^ (b & c)
let T2 = S0 &+ maj
h = g
g = f
f = e
e = d &+ T1
d = c
c = b
b = a
a = T1 &+ T2
}
hash[0] &+= a
hash[1] &+= b
hash[2] &+= c
hash[3] &+= d
hash[4] &+= e
hash[5] &+= f
hash[6] &+= g
hash[7] &+= h
}
return hash.map {
var num = [=12=].bigEndian
return Data(bytes: &num, count: 4)
}.reduce(Data(), +)
}
}
事实证明,我创建了错误的子数据来构造我的 UInt32
从创建消息计划数组。 (.digest()
函数中的前几行)
旧的是
let sub = chunk.subdata(in: i ..< i + 4)
新的是
let sub = chunk.subdata(in: i * 4 ..< (i * 4) + 4)
这解决了问题