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.. 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.. String { var md5 = MD5() md5.update(data) return md5.finalize().map { String(format: "%02x", $0) }.joined() } }