rainbow-dragon/Sources/ForgeOffload/SHA1.swift

99 lines
3.2 KiB
Swift
Raw Normal View History

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()
}
}