Category talk:Wren-crypto

From Rosetta Code

Source code

/* Module "crypto.wren" */

/*
    Bits contains bit manipulation routines which are useful for cryptograpgic purposes.
    To make these as fast as possible no checks are performed on the inputs.
*/
class Bits {
    // Rotates the bits in a 32-bit unsigned integer 'x' to the left by 'c' places between 1 and 31.
    static leftRotate (x, c) { (x << c) | (x >> (32 - c)) }

    // Rotates the bits in a 32-bit unsigned integer 'x' to the right by 'c' places between 1 and 31.
    static rightRotate(x, c) { (x >> c) | (x << (32 - c)) }
}

/*
    Bytes contains byte manipulation routines which are useful for cryptograpgic purposes.
    To make these as fast as possible no checks are performed on the inputs.
*/
class Bytes {
    // Converts a 32-bit unsigned integer 'x' to a list of 4 bytes in big-endian format.
    static fromIntBE(x) {
        var bytes = List.filled(4, 0)
        bytes[3] = x         & 255
        bytes[2] = (x >> 8)  & 255
        bytes[1] = (x >> 16) & 255
        bytes[0] = (x >> 24) & 255
        return bytes
    }

    // Converts a 32-bit unsigned integer 'x' to a list of 4 bytes in little-endian format.
    static fromIntLE(x) {
        var bytes = List.filled(4, 0)
        bytes[0] = x         & 255
        bytes[1] = (x >> 8)  & 255
        bytes[2] = (x >> 16) & 255
        bytes[3] = (x >> 24) & 255
        return bytes
    }

    // Converts a list of 4 bytes in big-endian format to a 32-bit unsigned integer.
    static toIntBE(bytes) { bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3] }

    // Converts a list of 4 bytes in little-endian format to a 32-bit unsigned integer.
    static toIntLE(bytes) { bytes[0] | bytes[1] << 8 | bytes[2] << 16 | bytes[3] << 24 }

    // Converts a list of an even number of bytes or the 4 bytes of a 32-bit unsigned integer
    // in big-endian format into a lower case hexadecimal string.
    static toHexString(bytes) {
       if (bytes is Num) bytes = fromIntBE(bytes)
       var digits = "0123456789abcdef"
       var res = []
       for (byte in bytes) {
            var d = (byte != 0) ? [] : [0]
            while (byte > 0) {
                d.add(digits[byte % 16])
                byte = (byte/16).floor
            }
            res.add((d.count == 2) ? d[1] : 0)
            res.add(d[0])
       }
       return res.join()
    }

    // Converts a lower case hexadecimal string into a byte list.
    static fromHexString(hs) {
        var digits = "0123456789abcdef"
        var bytes = List.filled(hs.count/2, 0)
        var i = 0
        while (i < hs.count-1) {
            bytes[i/2] = digits.indexOf(hs[i]) * 16 + digits.indexOf(hs[i+1])
            i = i + 2
        }
        return bytes
    }
}

/* Md5 implements the MD5 hashing algorithm. */
class Md5 {
    static init_() {
        __k = [
            0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,
            0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
            0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
            0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
            0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,
            0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
            0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,
            0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
            0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
            0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
            0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,
            0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
            0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,
            0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
            0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
            0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391
        ]

        __r = [
            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
        ]
    }

    // Computes the MD5 message digest of a byte sequence or string.
    static digest(initBytes) {
        if (!__k) init_()
        var h0 = 0x67452301
        var h1 = 0xefcdab89
        var h2 = 0x98badcfe
        var h3 = 0x10325476
        if (initBytes is String) initBytes = initBytes.bytes
        var initLen = initBytes.count
        var newLen = initLen + 1
        while (newLen % 64 != 56) newLen = newLen + 1
        var msg = List.filled(newLen + 8, 0)
        for (i in 0...initLen) msg[i] = initBytes[i]
        msg[initLen] = 0x80 // remaining bytes already 0
        var lenBits = Bytes.fromIntLE(initLen * 8)
        for (i in newLen...newLen+4) msg[i] = lenBits[i-newLen]
        var extraBits = Bytes.fromIntLE(initLen >> 29)
        for (i in newLen+4...newLen+8) msg[i] = extraBits[i-newLen-4]
        var offset = 0
        var w = List.filled(16, 0)
        var mask = 0xffffffff
        while (offset < newLen) {
            for (i in 0...16) w[i] = Bytes.toIntLE(msg[offset+i*4...offset + i*4 + 4])
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var f
            var g
            for (i in 0...64) {
                if (i < 16) {
                    f = (b & c) | ((~b) & d)
                    g = i
                } else if (i < 32) {
                    f = (d & b) | ((~d) & c)
                    g = (5*i + 1) % 16
                } else if (i < 48) {
                    f = b ^ c ^ d
                    g = (3*i + 5) % 16
                } else {
                    f = c ^ (b | (~d))
                    g = (7*i) % 16
                }
                var temp = d
                d = c
                c = b
                b = b + Bits.leftRotate((a + f + __k[i] + w[g]), __r[i])
                a = temp
            }
            h0 = (h0 + a) & mask
            h1 = (h1 + b) & mask
            h2 = (h2 + c) & mask
            h3 = (h3 + d) & mask
            offset = offset + 64
        }

        h0 = Bytes.toHexString(Bytes.fromIntLE(h0))
        h1 = Bytes.toHexString(Bytes.fromIntLE(h1))
        h2 = Bytes.toHexString(Bytes.fromIntLE(h2))
        h3 = Bytes.toHexString(Bytes.fromIntLE(h3))
        return h0 + h1 + h2 + h3
    }
}

/* Sha256 implements the SHA-256 hashing algorithm. */
class Sha256 {
    static init_() {
        __k = [
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
        ]
    }

    // Computes the SHA-256 message digest of a byte sequence or string.
    static digest(initBytes) {
        if (!__k) init_()
        var h0 = 0x6a09e667
        var h1 = 0xbb67ae85
        var h2 = 0x3c6ef372
        var h3 = 0xa54ff53a
        var h4 = 0x510e527f
        var h5 = 0x9b05688c
        var h6 = 0x1f83d9ab
        var h7 = 0x5be0cd19
        if (initBytes is String) initBytes = initBytes.bytes
        var initLen = initBytes.count
        var newLen = initLen + 1
        while (newLen % 64 != 56) newLen = newLen + 1
        var msg = List.filled(newLen + 8, 0)
        for (i in 0...initLen) msg[i] = initBytes[i]
        msg[initLen] = 0x80 // remaining bytes already 0
        var initBits = initLen * 8
        var p = 2.pow(32)
        var u = (initBits/p).floor
        var l = initBits % p
        var bytesU = Bytes.fromIntBE(u)
        var bytesL = Bytes.fromIntBE(l)
        for (i in 0..3) msg[newLen+i]   = bytesU[i]
        for (i in 0..3) msg[newLen+i+4] = bytesL[i]
        var offset = 0
        var w = List.filled(64, 0)
        var mask = 0xffffffff
        while (offset < newLen) {
            for (i in 0..15) w[i] = Bytes.toIntBE(msg[offset+i*4...offset + i*4 + 4])
            for (i in 16..63) {
                var s0 = Bits.rightRotate(w[i-15],  7) ^ Bits.rightRotate(w[i-15], 18) ^ (w[i-15] >>  3)
                var s1 = Bits.rightRotate(w[i- 2], 17) ^ Bits.rightRotate(w[i- 2], 19) ^ (w[i- 2] >> 10)
                w[i] = w[i-16] + s0 + w[i-7] + s1
            }
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var e = h4
            var f = h5
            var g = h6
            var h = h7
            for (i in 0..63) {
                var s1 = Bits.rightRotate(e, 6) ^ Bits.rightRotate(e, 11) ^ Bits.rightRotate(e, 25)
                var ch = (e & f) ^ ((~e) & g)
                var temp1 = h + s1 + ch + __k[i] + w[i]
                var s0 = Bits.rightRotate(a, 2) ^ Bits.rightRotate(a, 13) ^ Bits.rightRotate(a, 22)
                var maj = (a & b) ^ (a & c) ^ (b & c)
                var temp2 = s0 + maj
                h = g
                g = f
                f = e
                e = d + temp1
                d = c
                c = b
                b = a
                a = temp1 + temp2
            }
            h0 = (h0 + a) & mask
            h1 = (h1 + b) & mask
            h2 = (h2 + c) & mask
            h3 = (h3 + d) & mask
            h4 = (h4 + e) & mask
            h5 = (h5 + f) & mask
            h6 = (h6 + g) & mask
            h7 = (h7 + h) & mask
            offset = offset + 64
        }
        h0 = Bytes.toHexString(h0)
        h1 = Bytes.toHexString(h1)
        h2 = Bytes.toHexString(h2)
        h3 = Bytes.toHexString(h3)
        h4 = Bytes.toHexString(h4)
        h5 = Bytes.toHexString(h5)
        h6 = Bytes.toHexString(h6)
        h7 = Bytes.toHexString(h7)
        return h0 + h1 + h2 + h3 + h4 + h5 + h6 + h7
    }
}

/* Sha224 implements the SHA-224 hashing algorithm. */
class Sha224 {
    static init_() {
        __k = [
            0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
            0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
            0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
            0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
            0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
            0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
            0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
            0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
        ]
    }

    // Computes the SHA-224 message digest of a byte sequence or string.
    static digest(initBytes) {
        if (!__k) init_()
        var h0 = 0xc1059ed8
        var h1 = 0x367cd507
        var h2 = 0x3070dd17
        var h3 = 0xf70e5939
        var h4 = 0xffc00b31
        var h5 = 0x68581511
        var h6 = 0x64f98fa7
        var h7 = 0xbefa4fa4
        if (initBytes is String) initBytes = initBytes.bytes
        var initLen = initBytes.count
        var newLen = initLen + 1
        while (newLen % 64 != 56) newLen = newLen + 1
        var msg = List.filled(newLen + 8, 0)
        for (i in 0...initLen) msg[i] = initBytes[i]
        msg[initLen] = 0x80 // remaining bytes already 0
        var initBits = initLen * 8
        var p = 2.pow(32)
        var u = (initBits/p).floor
        var l = initBits % p
        var bytesU = Bytes.fromIntBE(u)
        var bytesL = Bytes.fromIntBE(l)
        for (i in 0..3) msg[newLen+i]   = bytesU[i]
        for (i in 0..3) msg[newLen+i+4] = bytesL[i]
        var offset = 0
        var w = List.filled(64, 0)
        var mask = 0xffffffff
        while (offset < newLen) {
            for (i in 0..15) w[i] = Bytes.toIntBE(msg[offset+i*4...offset + i*4 + 4])
            for (i in 16..63) {
                var s0 = Bits.rightRotate(w[i-15],  7) ^ Bits.rightRotate(w[i-15], 18) ^ (w[i-15] >>  3)
                var s1 = Bits.rightRotate(w[i- 2], 17) ^ Bits.rightRotate(w[i- 2], 19) ^ (w[i- 2] >> 10)
                w[i] = w[i-16] + s0 + w[i-7] + s1
            }
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var e = h4
            var f = h5
            var g = h6
            var h = h7
            for (i in 0..63) {
                var s1 = Bits.rightRotate(e, 6) ^ Bits.rightRotate(e, 11) ^ Bits.rightRotate(e, 25)
                var ch = (e & f) ^ ((~e) & g)
                var temp1 = h + s1 + ch + __k[i] + w[i]
                var s0 = Bits.rightRotate(a, 2) ^ Bits.rightRotate(a, 13) ^ Bits.rightRotate(a, 22)
                var maj = (a & b) ^ (a & c) ^ (b & c)
                var temp2 = s0 + maj
                h = g
                g = f
                f = e
                e = d + temp1
                d = c
                c = b
                b = a
                a = temp1 + temp2
            }
            h0 = (h0 + a) & mask
            h1 = (h1 + b) & mask
            h2 = (h2 + c) & mask
            h3 = (h3 + d) & mask
            h4 = (h4 + e) & mask
            h5 = (h5 + f) & mask
            h6 = (h6 + g) & mask
            h7 = (h7 + h) & mask
            offset = offset + 64
        }
        h0 = Bytes.toHexString(h0)
        h1 = Bytes.toHexString(h1)
        h2 = Bytes.toHexString(h2)
        h3 = Bytes.toHexString(h3)
        h4 = Bytes.toHexString(h4)
        h5 = Bytes.toHexString(h5)
        h6 = Bytes.toHexString(h6)
        return h0 + h1 + h2 + h3 + h4 + h5 + h6
    }
}

/* Sha1 implements the SHA-1 hashing algorithm. */
class Sha1 {
    // Computes the SHA-1 message digest of a byte sequence or string.
    static digest(initBytes) {
        var h0 = 0x67452301
        var h1 = 0xefcdab89
        var h2 = 0x98badcfe
        var h3 = 0x10325476
        var h4 = 0xc3d2e1f0
        if (initBytes is String) initBytes = initBytes.bytes
        var initLen = initBytes.count
        var newLen = initLen + 1
        while (newLen % 64 != 56) newLen = newLen + 1
        var msg = List.filled(newLen + 8, 0)
        for (i in 0...initLen) msg[i] = initBytes[i]
        msg[initLen] = 0x80 // remaining bytes already 0
        var initBits = initLen * 8
        var p = 2.pow(32)
        var u = (initBits/p).floor
        var l = initBits % p
        var bytesU = Bytes.fromIntBE(u)
        var bytesL = Bytes.fromIntBE(l)
        for (i in 0..3) msg[newLen+i]   = bytesU[i]
        for (i in 0..3) msg[newLen+i+4] = bytesL[i]
        var offset = 0
        var w = List.filled(80, 0)
        var mask = 0xffffffff
        while (offset < newLen) {
            for (i in 0..15) w[i] = Bytes.toIntBE(msg[offset+i*4...offset + i*4 + 4])
            for (i in 16..79) {
                w[i] = Bits.leftRotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16], 1)
            }
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var e = h4
            var f
            var k
            for (i in 0..79) {
                if (i <= 19) {
                    f = (b & c) | ((~b) & d)
                    k = 0x5a827999
                } else if (i <= 39) {
                    f = b ^ c ^ d
                    k = 0x6ed9eba1
                } else if (i <= 59) {
                    f = (b & c) | (b & d) | (c & d)
                    k = 0x8f1bbcdc
                } else {
                    f = b ^ c ^ d
                    k = 0xca62c1d6
                }
                var temp = Bits.leftRotate(a, 5) + f + e + k + w[i]
                e = d
                d = c
                c = Bits.leftRotate(b, 30)
                b = a
                a = temp
            }
            h0 = (h0 + a) & mask
            h1 = (h1 + b) & mask
            h2 = (h2 + c) & mask
            h3 = (h3 + d) & mask
            h4 = (h4 + e) & mask
            offset = offset + 64
        }
        h0 = Bytes.toHexString(h0)
        h1 = Bytes.toHexString(h1)
        h2 = Bytes.toHexString(h2)
        h3 = Bytes.toHexString(h3)
        h4 = Bytes.toHexString(h4)
        return h0 + h1 + h2 + h3 + h4
    }
}

/* Ripemd160 implements the RIPEMD-160 hashing algorithm. */
class Ripemd160 {
    static init_() {
        __f = [
            Fn.new { |x, y, z| x ^ y ^ z },
            Fn.new { |x, y, z| (x & y) | ((~x) & z) },
            Fn.new { |x, y, z| (x | (~y)) ^ z },
            Fn.new { |x, y, z| (x & z) | (y & (~z)) },
            Fn.new { |x, y, z| x ^ (y | (~z)) }
        ]

        __k  = [ 0x00000000, 0x5a827999, 0x6ed9eba1, 0x8f1bbcdc, 0xa953fd4e ]
        __kk = [ 0x50a28be6, 0x5c4dd124, 0x6d703ef3, 0x7a6d76e9, 0x00000000 ]

        __r = [
            0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15,
            7,  4, 13,  1, 10,  6, 15,  3, 12,  0,  9,  5,  2, 14, 11,  8,
            3, 10, 14,  4,  9, 15,  8,  1,  2,  7,  0,  6, 13, 11,  5, 12,
            1,  9, 11, 10,  0,  8, 12,  4, 13,  3,  7, 15, 14,  5,  6,  2,
            4,  0,  5,  9,  7, 12,  2, 10, 14,  1,  3,  8, 11,  6, 15, 13
        ]

        __rr = [
             5, 14,  7,  0,  9,  2, 11,  4, 13,  6, 15,  8,  1, 10,  3, 12,
             6, 11,  3,  7,  0, 13,  5, 10, 14, 15,  8, 12,  4,  9,  1,  2,
            15,  5,  1,  3,  7, 14,  6,  9, 11,  8, 12,  2, 10,  0,  4, 13,
             8,  6,  4,  1,  3, 11, 15,  0,  5, 12,  2, 13,  9,  7, 10, 14,
            12, 15, 10,  4,  1,  5,  8,  7,  6,  2, 13, 14,  0,  3,  9, 11
        ]

        __s = [
            11, 14, 15, 12,  5,  8,  7,  9, 11, 13, 14, 15,  6,  7,  9,  8,
             7,  6,  8, 13, 11,  9,  7, 15,  7, 12, 15,  9, 11,  7, 13, 12,
            11, 13,  6,  7, 14,  9, 13, 15, 14,  8, 13,  6,  5, 12,  7,  5,
            11, 12, 14, 15, 14, 15,  9,  8,  9, 14,  5,  6,  8,  6,  5, 12,
             9, 15,  5, 11,  6,  8, 13, 12,  5, 12, 13, 14, 11,  8,  5,  6
        ]

        __ss = [
             8,  9,  9, 11, 13, 15, 15,  5,  7,  7,  8, 11, 14, 14, 12,  6,
             9, 13, 15,  7, 12,  8,  9, 11,  7,  7, 12,  7,  6, 15, 13, 11,
             9,  7, 15, 11,  8,  6,  6, 14, 12, 13,  5, 14, 13, 13,  7,  5,
            15,  5,  8, 11, 14, 14,  6, 14,  6,  9, 12,  9, 12,  5, 15,  8,
             8,  5, 12,  9, 12,  5, 14,  6,  8, 13,  6,  5, 15, 13, 11, 11
        ]
    }

    // Computes the RIPEMD-160 message digest of a byte sequence or string.
    static digest(initBytes) {
        if (!__f) init_()
        var h0 = 0x67452301
        var h1 = 0xefcdab89
        var h2 = 0x98badcfe
        var h3 = 0x10325476
        var h4 = 0xc3d2e1f0
        if (initBytes is String) initBytes = initBytes.bytes
        var initLen = initBytes.count
        var newLen = initLen + 1
        while (newLen % 64 != 56) newLen = newLen + 1
        var msg = List.filled(newLen + 8, 0)
        for (i in 0...initLen) msg[i] = initBytes[i]
        msg[initLen] = 0x80 // remaining bytes already 0
        var lenBits = Bytes.fromIntLE(initLen * 8)
        for (i in newLen...newLen+4) msg[i] = lenBits[i-newLen]
        var extraBits = Bytes.fromIntLE(initLen >> 29)
        for (i in newLen+4...newLen+8) msg[i] = extraBits[i-newLen-4]
        var offset = 0
        var x = List.filled(16, 0)
        var mask = 0xffffffff
        while (offset < newLen) {
            for (i in 0...16) x[i] = Bytes.toIntLE(msg[offset+i*4...offset + i*4 + 4])
            var a = h0
            var b = h1
            var c = h2
            var d = h3
            var e = h4
            var aa = h0
            var bb = h1
            var cc = h2
            var dd = h3
            var ee = h4
            for (j in 0..79) {
                var i = (j/16).floor
                var t = a + __f[i].call(b, c, d) + x[__r[j]] + __k[i]
                t = (Bits.leftRotate(t & mask, __s[j]) + e) & mask
                a = e
                e = d
                d = Bits.leftRotate(c, 10)
                c = b
                b = t
                t = aa + __f[4-i].call(bb, cc, dd) + x[__rr[j]] + __kk[i]
                t = (Bits.leftRotate(t & mask, __ss[j]) + ee) & mask
                aa = ee
                ee = dd
                dd = Bits.leftRotate(cc, 10)
                cc = bb
                bb = t
            }
            var temp = (h1 + c + dd) & mask
            h1 = (h2 + d + ee) & mask
            h2 = (h3 + e + aa) & mask
            h3 = (h4 + a + bb) & mask
            h4 = (h0 + b + cc) & mask
            h0 = temp
            offset = offset + 64
        }
        h0 = Bytes.toHexString(Bytes.fromIntLE(h0))
        h1 = Bytes.toHexString(Bytes.fromIntLE(h1))
        h2 = Bytes.toHexString(Bytes.fromIntLE(h2))
        h3 = Bytes.toHexString(Bytes.fromIntLE(h3))
        h4 = Bytes.toHexString(Bytes.fromIntLE(h4))
        return h0 + h1 + h2 + h3 + h4
    }
}

/* Hmac implements the HMAC ('keyed-hash message authentication code') algorithm. */
class Hmac {
    // Computes the HMAC message digest of a byte sequence or string for a given key and class
    // of cryptographic hashing algorithm with a specified block-size in bytes provided such class
    // has a static digest(initBytes) method.
    static digest(key, message, hashClass, blockSize) {
        if (key is String) key = key.bytes.toList
        if (message is String) message = message.bytes.toList
        if (key.count > blockSize) key = Bytes.fromHexString(hashClass.digest(key))
        if (key.count < blockSize) key = key + [0] * (blockSize - key.count)
        var outerKeyPad = key.map { |b|  b ^ 0x5c }.toList
        var innerKeyPad = key.map { |b|  b ^ 0x36 }.toList
        var innerHash = Bytes.fromHexString(hashClass.digest(innerKeyPad + message))
        return hashClass.digest(outerKeyPad + innerHash)
    }

    // Convenience version of the above method which assumes a block-size of 64 bytes
    // and is true for all cryptographic hashing algorithms supported by this module.
    static digest(key, message, hashClass) { digest(key, message, hashClass, 64) }
}