//
//  RIPEMD160.swift
//
//  https://stackoverflow.com/questions/43091858/swift-hash-a-string-using-hash-hmac-with-ripemd160/43191938
//

import Foundation

public struct RIPEMD160 {
    
    private var MDbuf: (UInt32, UInt32, UInt32, UInt32, UInt32)
    private var buffer: Data
    private var count: Int64 // Total # of bytes processed.
    
    public init() {
        MDbuf = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0)
        buffer = Data()
        count = 0
    }
    
    private mutating func compress(_ X: UnsafePointer<UInt32>) {
        
        // *** Helper functions (originally macros in rmd160.h) ***
        
        /* ROL(x, n) cyclically rotates x over n bits to the left */
        /* x must be of an unsigned 32 bits type and 0 <= n < 32. */
        func ROL(_ x: UInt32, _ n: UInt32) -> UInt32 {
            return (x << n) | ( x >> (32 - n))
        }
        
        /* the five basic functions F(), G() and H() */
        
        func F(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return x ^ y ^ z
        }
        
        func G(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x & y) | (~x & z)
        }
        
        func H(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x | ~y) ^ z
        }
        
        func I(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return (x & z) | (y & ~z)
        }
        
        func J(_ x: UInt32, _ y: UInt32, _ z: UInt32) -> UInt32 {
            return x ^ (y | ~z)
        }
        
        /* the ten basic operations FF() through III() */
        
        func FF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ F(b, c, d) &+ x
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func GG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ G(b, c, d) &+ x &+ 0x5a827999
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func HH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ H(b, c, d) &+ x &+ 0x6ed9eba1
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func II(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ I(b, c, d) &+ x &+ 0x8f1bbcdc
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func JJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ J(b, c, d) &+ x &+ 0xa953fd4e
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func FFF(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ F(b, c, d) &+ x
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func GGG(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ G(b, c, d) &+ x &+ 0x7a6d76e9
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func HHH(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ H(b, c, d) &+ x &+ 0x6d703ef3
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func III(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ I(b, c, d) &+ x &+ 0x5c4dd124
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        func JJJ(_ a: inout UInt32, _ b: UInt32, _ c: inout UInt32, _ d: UInt32, _ e: UInt32, _ x: UInt32, _ s: UInt32) {
            a = a &+ J(b, c, d) &+ x &+ 0x50a28be6
            a = ROL(a, s) &+ e
            c = ROL(c, 10)
        }
        
        // *** The function starts here ***
        
        var (aa, bb, cc, dd, ee) = MDbuf
        var (aaa, bbb, ccc, ddd, eee) = MDbuf
        
        /* round 1 */
        FF(&aa, bb, &cc, dd, ee, X[ 0], 11)
        FF(&ee, aa, &bb, cc, dd, X[ 1], 14)
        FF(&dd, ee, &aa, bb, cc, X[ 2], 15)
        FF(&cc, dd, &ee, aa, bb, X[ 3], 12)
        FF(&bb, cc, &dd, ee, aa, X[ 4],  5)
        FF(&aa, bb, &cc, dd, ee, X[ 5],  8)
        FF(&ee, aa, &bb, cc, dd, X[ 6],  7)
        FF(&dd, ee, &aa, bb, cc, X[ 7],  9)
        FF(&cc, dd, &ee, aa, bb, X[ 8], 11)
        FF(&bb, cc, &dd, ee, aa, X[ 9], 13)
        FF(&aa, bb, &cc, dd, ee, X[10], 14)
        FF(&ee, aa, &bb, cc, dd, X[11], 15)
        FF(&dd, ee, &aa, bb, cc, X[12],  6)
        FF(&cc, dd, &ee, aa, bb, X[13],  7)
        FF(&bb, cc, &dd, ee, aa, X[14],  9)
        FF(&aa, bb, &cc, dd, ee, X[15],  8)
        
        /* round 2 */
        GG(&ee, aa, &bb, cc, dd, X[ 7],  7)
        GG(&dd, ee, &aa, bb, cc, X[ 4],  6)
        GG(&cc, dd, &ee, aa, bb, X[13],  8)
        GG(&bb, cc, &dd, ee, aa, X[ 1], 13)
        GG(&aa, bb, &cc, dd, ee, X[10], 11)
        GG(&ee, aa, &bb, cc, dd, X[ 6],  9)
        GG(&dd, ee, &aa, bb, cc, X[15],  7)
        GG(&cc, dd, &ee, aa, bb, X[ 3], 15)
        GG(&bb, cc, &dd, ee, aa, X[12],  7)
        GG(&aa, bb, &cc, dd, ee, X[ 0], 12)
        GG(&ee, aa, &bb, cc, dd, X[ 9], 15)
        GG(&dd, ee, &aa, bb, cc, X[ 5],  9)
        GG(&cc, dd, &ee, aa, bb, X[ 2], 11)
        GG(&bb, cc, &dd, ee, aa, X[14],  7)
        GG(&aa, bb, &cc, dd, ee, X[11], 13)
        GG(&ee, aa, &bb, cc, dd, X[ 8], 12)
        
        /* round 3 */
        HH(&dd, ee, &aa, bb, cc, X[ 3], 11)
        HH(&cc, dd, &ee, aa, bb, X[10], 13)
        HH(&bb, cc, &dd, ee, aa, X[14],  6)
        HH(&aa, bb, &cc, dd, ee, X[ 4],  7)
        HH(&ee, aa, &bb, cc, dd, X[ 9], 14)
        HH(&dd, ee, &aa, bb, cc, X[15],  9)
        HH(&cc, dd, &ee, aa, bb, X[ 8], 13)
        HH(&bb, cc, &dd, ee, aa, X[ 1], 15)
        HH(&aa, bb, &cc, dd, ee, X[ 2], 14)
        HH(&ee, aa, &bb, cc, dd, X[ 7],  8)
        HH(&dd, ee, &aa, bb, cc, X[ 0], 13)
        HH(&cc, dd, &ee, aa, bb, X[ 6],  6)
        HH(&bb, cc, &dd, ee, aa, X[13],  5)
        HH(&aa, bb, &cc, dd, ee, X[11], 12)
        HH(&ee, aa, &bb, cc, dd, X[ 5],  7)
        HH(&dd, ee, &aa, bb, cc, X[12],  5)
        
        /* round 4 */
        II(&cc, dd, &ee, aa, bb, X[ 1], 11)
        II(&bb, cc, &dd, ee, aa, X[ 9], 12)
        II(&aa, bb, &cc, dd, ee, X[11], 14)
        II(&ee, aa, &bb, cc, dd, X[10], 15)
        II(&dd, ee, &aa, bb, cc, X[ 0], 14)
        II(&cc, dd, &ee, aa, bb, X[ 8], 15)
        II(&bb, cc, &dd, ee, aa, X[12],  9)
        II(&aa, bb, &cc, dd, ee, X[ 4],  8)
        II(&ee, aa, &bb, cc, dd, X[13],  9)
        II(&dd, ee, &aa, bb, cc, X[ 3], 14)
        II(&cc, dd, &ee, aa, bb, X[ 7],  5)
        II(&bb, cc, &dd, ee, aa, X[15],  6)
        II(&aa, bb, &cc, dd, ee, X[14],  8)
        II(&ee, aa, &bb, cc, dd, X[ 5],  6)
        II(&dd, ee, &aa, bb, cc, X[ 6],  5)
        II(&cc, dd, &ee, aa, bb, X[ 2], 12)
        
        /* round 5 */
        JJ(&bb, cc, &dd, ee, aa, X[ 4],  9)
        JJ(&aa, bb, &cc, dd, ee, X[ 0], 15)
        JJ(&ee, aa, &bb, cc, dd, X[ 5],  5)
        JJ(&dd, ee, &aa, bb, cc, X[ 9], 11)
        JJ(&cc, dd, &ee, aa, bb, X[ 7],  6)
        JJ(&bb, cc, &dd, ee, aa, X[12],  8)
        JJ(&aa, bb, &cc, dd, ee, X[ 2], 13)
        JJ(&ee, aa, &bb, cc, dd, X[10], 12)
        JJ(&dd, ee, &aa, bb, cc, X[14],  5)
        JJ(&cc, dd, &ee, aa, bb, X[ 1], 12)
        JJ(&bb, cc, &dd, ee, aa, X[ 3], 13)
        JJ(&aa, bb, &cc, dd, ee, X[ 8], 14)
        JJ(&ee, aa, &bb, cc, dd, X[11], 11)
        JJ(&dd, ee, &aa, bb, cc, X[ 6],  8)
        JJ(&cc, dd, &ee, aa, bb, X[15],  5)
        JJ(&bb, cc, &dd, ee, aa, X[13],  6)
        
        /* parallel round 1 */
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 5],  8)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[14],  9)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 7],  9)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[ 0], 11)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 9], 13)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[ 2], 15)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[11], 15)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 4],  5)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[13],  7)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 6],  7)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[15],  8)
        JJJ(&eee, aaa, &bbb, ccc, ddd, X[ 8], 11)
        JJJ(&ddd, eee, &aaa, bbb, ccc, X[ 1], 14)
        JJJ(&ccc, ddd, &eee, aaa, bbb, X[10], 14)
        JJJ(&bbb, ccc, &ddd, eee, aaa, X[ 3], 12)
        JJJ(&aaa, bbb, &ccc, ddd, eee, X[12],  6)
        
        /* parallel round 2 */
        III(&eee, aaa, &bbb, ccc, ddd, X[ 6],  9)
        III(&ddd, eee, &aaa, bbb, ccc, X[11], 13)
        III(&ccc, ddd, &eee, aaa, bbb, X[ 3], 15)
        III(&bbb, ccc, &ddd, eee, aaa, X[ 7],  7)
        III(&aaa, bbb, &ccc, ddd, eee, X[ 0], 12)
        III(&eee, aaa, &bbb, ccc, ddd, X[13],  8)
        III(&ddd, eee, &aaa, bbb, ccc, X[ 5],  9)
        III(&ccc, ddd, &eee, aaa, bbb, X[10], 11)
        III(&bbb, ccc, &ddd, eee, aaa, X[14],  7)
        III(&aaa, bbb, &ccc, ddd, eee, X[15],  7)
        III(&eee, aaa, &bbb, ccc, ddd, X[ 8], 12)
        III(&ddd, eee, &aaa, bbb, ccc, X[12],  7)
        III(&ccc, ddd, &eee, aaa, bbb, X[ 4],  6)
        III(&bbb, ccc, &ddd, eee, aaa, X[ 9], 15)
        III(&aaa, bbb, &ccc, ddd, eee, X[ 1], 13)
        III(&eee, aaa, &bbb, ccc, ddd, X[ 2], 11)
        
        /* parallel round 3 */
        HHH(&ddd, eee, &aaa, bbb, ccc, X[15],  9)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 5],  7)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[ 1], 15)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[ 3], 11)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 7],  8)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[14],  6)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 6],  6)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[ 9], 14)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[11], 12)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 8], 13)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[12],  5)
        HHH(&ccc, ddd, &eee, aaa, bbb, X[ 2], 14)
        HHH(&bbb, ccc, &ddd, eee, aaa, X[10], 13)
        HHH(&aaa, bbb, &ccc, ddd, eee, X[ 0], 13)
        HHH(&eee, aaa, &bbb, ccc, ddd, X[ 4],  7)
        HHH(&ddd, eee, &aaa, bbb, ccc, X[13],  5)
        
        /* parallel round 4 */
        GGG(&ccc, ddd, &eee, aaa, bbb, X[ 8], 15)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[ 6],  5)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 4],  8)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 1], 11)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[ 3], 14)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[11], 14)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[15],  6)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 0], 14)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 5],  6)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[12],  9)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[ 2], 12)
        GGG(&bbb, ccc, &ddd, eee, aaa, X[13],  9)
        GGG(&aaa, bbb, &ccc, ddd, eee, X[ 9], 12)
        GGG(&eee, aaa, &bbb, ccc, ddd, X[ 7],  5)
        GGG(&ddd, eee, &aaa, bbb, ccc, X[10], 15)
        GGG(&ccc, ddd, &eee, aaa, bbb, X[14],  8)
        
        /* parallel round 5 */
        FFF(&bbb, ccc, &ddd, eee, aaa, X[12] ,  8)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[15] ,  5)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[10] , 12)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 4] ,  9)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 1] , 12)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[ 5] ,  5)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[ 8] , 14)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[ 7] ,  6)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 6] ,  8)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 2] , 13)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[13] ,  6)
        FFF(&aaa, bbb, &ccc, ddd, eee, X[14] ,  5)
        FFF(&eee, aaa, &bbb, ccc, ddd, X[ 0] , 15)
        FFF(&ddd, eee, &aaa, bbb, ccc, X[ 3] , 13)
        FFF(&ccc, ddd, &eee, aaa, bbb, X[ 9] , 11)
        FFF(&bbb, ccc, &ddd, eee, aaa, X[11] , 11)
        
        /* combine results */
        MDbuf = (MDbuf.1 &+ cc &+ ddd,
                 MDbuf.2 &+ dd &+ eee,
                 MDbuf.3 &+ ee &+ aaa,
                 MDbuf.4 &+ aa &+ bbb,
                 MDbuf.0 &+ bb &+ ccc)
    }
    
    public mutating func update(data: Data) {
        data.withUnsafeBytes { (ptr: UnsafePointer<UInt8>) in
            var ptr = ptr
            var length = data.count
            var X = [UInt32](repeating: 0, count: 16)
            
            // Process remaining bytes from last call:
            if buffer.count > 0 && buffer.count + length >= 64 {
                let amount = 64 - buffer.count
                buffer.append(ptr, count: amount)
                buffer.withUnsafeBytes { _ = memcpy(&X, $0, 64) }
                compress(X)
                ptr += amount
                length -= amount
            }
            // Process 64 byte chunks:
            while length >= 64 {
                memcpy(&X, ptr, 64)
                compress(X)
                ptr += 64
                length -= 64
            }
            // Save remaining unprocessed bytes:
            buffer = Data(bytes: ptr, count: length)
        }
        count += Int64(data.count)
    }
    
    public mutating func finalize() -> Data {
        var X = [UInt32](repeating: 0, count: 16)
        /* append the bit m_n == 1 */
        buffer.append(0x80)
        buffer.withUnsafeBytes { _ = memcpy(&X, $0, buffer.count) }
        
        if (count & 63) > 55 {
            /* length goes to next block */
            compress(X)
            X = [UInt32](repeating: 0, count: 16)
        }
        
        /* append length in bits */
        let lswlen = UInt32(truncatingIfNeeded: count)
        let mswlen = UInt32(UInt64(count) >> 32)
        X[14] = lswlen << 3
        X[15] = (lswlen >> 29) | (mswlen << 3)
        compress(X)
        
        var data = Data(count: 20)
        data.withUnsafeMutableBytes { (ptr: UnsafeMutablePointer<UInt32>) in
            ptr[0] = MDbuf.0
            ptr[1] = MDbuf.1
            ptr[2] = MDbuf.2
            ptr[3] = MDbuf.3
            ptr[4] = MDbuf.4
        }
        
        buffer = Data()
        
        return data
    }
}
public extension RIPEMD160 {
    
    static func hash(message: Data) -> Data {
        var md = RIPEMD160()
        md.update(data: message)
        return md.finalize()
    }
    
    static func hash(message: String) -> Data {
        return RIPEMD160.hash(message: message.data(using: .utf8)!)
    }
}

public extension RIPEMD160 {
    
    static func hmac(key: Data, message: Data) -> Data {
        
        var key = key
        key.count = 64 // Truncate to 64 bytes or fill-up with zeros.
        
        let outerKeyPad = Data(bytes: key.map { $0 ^ 0x5c })
        let innerKeyPad = Data(bytes: key.map { $0 ^ 0x36 })
        
        var innerMd = RIPEMD160()
        innerMd.update(data: innerKeyPad)
        innerMd.update(data: message)
        
        var outerMd = RIPEMD160()
        outerMd.update(data: outerKeyPad)
        outerMd.update(data: innerMd.finalize())
        
        return outerMd.finalize()
    }
    
    static func hmac(key: Data, message: String) -> Data {
        return RIPEMD160.hmac(key: key, message: message.data(using: .utf8)!)
    }
    
    static func hmac(key: String, message: String) -> Data {
        return RIPEMD160.hmac(key: key.data(using: .utf8)!, message: message)
    }
}
