# Category talk:Wren-crypto

### 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) {
byte = (byte/16).floor
}
res.add((d.count == 2) ? d[1] : 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) }
}
```