99 lines
3.2 KiB
Swift
99 lines
3.2 KiB
Swift
|
|
import Foundation
|
||
|
|
|
||
|
|
/// SHA-1 (RFC 3174) — pure Swift streaming. Legacy post-house compatibility.
|
||
|
|
public struct SHA1 {
|
||
|
|
private var h0: UInt32 = 0x67452301
|
||
|
|
private var h1: UInt32 = 0xEFCDAB89
|
||
|
|
private var h2: UInt32 = 0x98BADCFE
|
||
|
|
private var h3: UInt32 = 0x10325476
|
||
|
|
private var h4: UInt32 = 0xC3D2E1F0
|
||
|
|
private var buffer = [UInt8]()
|
||
|
|
private var totalLength: UInt64 = 0
|
||
|
|
|
||
|
|
public init() {}
|
||
|
|
|
||
|
|
public mutating func update(_ data: Data) {
|
||
|
|
totalLength += UInt64(data.count)
|
||
|
|
buffer.append(contentsOf: data)
|
||
|
|
var offset = 0
|
||
|
|
while buffer.count - offset >= 64 {
|
||
|
|
processBlock(Array(buffer[offset..<offset + 64]))
|
||
|
|
offset += 64
|
||
|
|
}
|
||
|
|
if offset > 0 {
|
||
|
|
buffer.removeFirst(offset)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
@inline(__always)
|
||
|
|
private static func rotl(_ x: UInt32, _ n: UInt32) -> UInt32 {
|
||
|
|
(x << n) | (x >> (32 - n))
|
||
|
|
}
|
||
|
|
|
||
|
|
private mutating func processBlock(_ block: [UInt8]) {
|
||
|
|
var w = [UInt32](repeating: 0, count: 80)
|
||
|
|
for i in 0..<16 {
|
||
|
|
w[i] = UInt32(block[i * 4]) << 24 | UInt32(block[i * 4 + 1]) << 16
|
||
|
|
| UInt32(block[i * 4 + 2]) << 8 | UInt32(block[i * 4 + 3])
|
||
|
|
}
|
||
|
|
for i in 16..<80 {
|
||
|
|
w[i] = Self.rotl(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1)
|
||
|
|
}
|
||
|
|
var a = h0, b = h1, c = h2, d = h3, e = h4
|
||
|
|
for i in 0..<80 {
|
||
|
|
let (f, k): (UInt32, UInt32)
|
||
|
|
switch i {
|
||
|
|
case 0..<20: (f, k) = ((b & c) | (~b & d), 0x5A827999)
|
||
|
|
case 20..<40: (f, k) = (b ^ c ^ d, 0x6ED9EBA1)
|
||
|
|
case 40..<60: (f, k) = ((b & c) | (b & d) | (c & d), 0x8F1BBCDC)
|
||
|
|
default: (f, k) = (b ^ c ^ d, 0xCA62C1D6)
|
||
|
|
}
|
||
|
|
let temp = Self.rotl(a, 5) &+ f &+ e &+ k &+ w[i]
|
||
|
|
e = d
|
||
|
|
d = c
|
||
|
|
c = Self.rotl(b, 30)
|
||
|
|
b = a
|
||
|
|
a = temp
|
||
|
|
}
|
||
|
|
h0 = h0 &+ a
|
||
|
|
h1 = h1 &+ b
|
||
|
|
h2 = h2 &+ c
|
||
|
|
h3 = h3 &+ d
|
||
|
|
h4 = h4 &+ e
|
||
|
|
}
|
||
|
|
|
||
|
|
public mutating func finalize() -> Data {
|
||
|
|
let bitLength = totalLength * 8
|
||
|
|
var padding: [UInt8] = [0x80]
|
||
|
|
let remainder = (buffer.count + 1) % 64
|
||
|
|
let zeros = remainder <= 56 ? 56 - remainder : 120 - remainder
|
||
|
|
padding.append(contentsOf: [UInt8](repeating: 0, count: zeros))
|
||
|
|
// Length big-endian.
|
||
|
|
for shift in stride(from: 56, through: 0, by: -8) {
|
||
|
|
padding.append(UInt8((bitLength >> UInt64(shift)) & 0xFF))
|
||
|
|
}
|
||
|
|
buffer.append(contentsOf: padding)
|
||
|
|
var offset = 0
|
||
|
|
while buffer.count - offset >= 64 {
|
||
|
|
processBlock(Array(buffer[offset..<offset + 64]))
|
||
|
|
offset += 64
|
||
|
|
}
|
||
|
|
buffer.removeAll()
|
||
|
|
|
||
|
|
var out = Data()
|
||
|
|
for v in [h0, h1, h2, h3, h4] {
|
||
|
|
out.append(UInt8((v >> 24) & 0xFF))
|
||
|
|
out.append(UInt8((v >> 16) & 0xFF))
|
||
|
|
out.append(UInt8((v >> 8) & 0xFF))
|
||
|
|
out.append(UInt8(v & 0xFF))
|
||
|
|
}
|
||
|
|
return out
|
||
|
|
}
|
||
|
|
|
||
|
|
public static func hexString(_ data: Data) -> String {
|
||
|
|
var sha = SHA1()
|
||
|
|
sha.update(data)
|
||
|
|
return sha.finalize().map { String(format: "%02x", $0) }.joined()
|
||
|
|
}
|
||
|
|
}
|