rainbow-dragon/Sources/ForgeOffload/MD5.swift

109 lines
3.4 KiB
Swift
Raw Permalink Normal View History

import Foundation
/// MD5 (RFC 1321) pure Swift. Legacy-post-workflow compatibility only;
/// integrity verification uses xxHash64.
public struct MD5 {
private var a0: UInt32 = 0x67452301
private var b0: UInt32 = 0xefcdab89
private var c0: UInt32 = 0x98badcfe
private var d0: UInt32 = 0x10325476
private var buffer = [UInt8]()
private var totalLength: UInt64 = 0
private static let s: [UInt32] = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
]
private static let k: [UInt32] = (0..<64).map {
UInt32(truncatingIfNeeded: Int64(abs(sin(Double($0 + 1))) * 4294967296.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)
}
}
private mutating func processBlock(_ block: [UInt8]) {
var m = [UInt32](repeating: 0, count: 16)
for i in 0..<16 {
m[i] = UInt32(block[i * 4])
| (UInt32(block[i * 4 + 1]) << 8)
| (UInt32(block[i * 4 + 2]) << 16)
| (UInt32(block[i * 4 + 3]) << 24)
}
var a = a0, b = b0, c = c0, d = d0
for i in 0..<64 {
var f: UInt32
var g: Int
switch i {
case 0..<16:
f = (b & c) | (~b & d)
g = i
case 16..<32:
f = (d & b) | (~d & c)
g = (5 * i + 1) % 16
case 32..<48:
f = b ^ c ^ d
g = (3 * i + 5) % 16
default:
f = c ^ (b | ~d)
g = (7 * i) % 16
}
f = f &+ a &+ Self.k[i] &+ m[g]
a = d
d = c
c = b
b = b &+ ((f << Self.s[i]) | (f >> (32 - Self.s[i])))
}
a0 = a0 &+ a
b0 = b0 &+ b
c0 = c0 &+ c
d0 = d0 &+ d
}
public mutating func finalize() -> Data {
let bitLength = totalLength * 8
// Padding: 0x80 then zeros to 56 mod 64, then length LE64.
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))
var lenLE = bitLength.littleEndian
withUnsafeBytes(of: &lenLE) { padding.append(contentsOf: $0) }
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 [a0, b0, c0, d0] {
var le = v.littleEndian
withUnsafeBytes(of: &le) { out.append(contentsOf: $0) }
}
return out
}
public static func hexString(_ data: Data) -> String {
var md5 = MD5()
md5.update(data)
return md5.finalize().map { String(format: "%02x", $0) }.joined()
}
}