-
-
Save Kreijstal/db65b1d39e99ce7640a5af359be2c83a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Bitwise rotate a 32-bit number to the left. | |
*/ | |
function ROTL(num, cnt) | |
{ | |
return (num << cnt) | (num >>> (32 - cnt)); | |
} | |
/* | |
* These functions implement the basic operation for each round of the | |
* algorithm. | |
*/ | |
function md4_cmn(q, a, b, x, s, t) | |
{ | |
return safeAdd32(ROTL(safeAdd32(safeAdd32(a, q), safeAdd32(x, t)), s), b); | |
} | |
function md4_ff(a, b, c, d, x, s) | |
{ | |
return md4_cmn((b & c) | ((~b) & d), a, 0, x, s, 0); | |
} | |
function md4_gg(a, b, c, d, x, s) | |
{ | |
return md4_cmn((b & c) | (b & d) | (c & d), a, 0, x, s, 1518500249); | |
} | |
function md4_hh(a, b, c, d, x, s) | |
{ | |
return md4_cmn(b ^ c ^ d, a, 0, x, s, 1859775393); | |
} | |
function core_md4(x, len) | |
{ | |
/* append padding */ | |
x[len >> 5] |= 0x80 << (len % 32); | |
x[(((len + 64) >>> 9) << 4) + 14] = len; | |
var a = 1732584193; | |
var b = -271733879; | |
var c = -1732584194; | |
var d = 271733878; | |
for(var i = 0; i < x.length; i += 16) | |
{ | |
var olda = a; | |
var oldb = b; | |
var oldc = c; | |
var oldd = d; | |
a = md4_ff(a, b, c, d, x[i+ 0], 3 ); | |
d = md4_ff(d, a, b, c, x[i+ 1], 7 ); | |
c = md4_ff(c, d, a, b, x[i+ 2], 11); | |
b = md4_ff(b, c, d, a, x[i+ 3], 19); | |
a = md4_ff(a, b, c, d, x[i+ 4], 3 ); | |
d = md4_ff(d, a, b, c, x[i+ 5], 7 ); | |
c = md4_ff(c, d, a, b, x[i+ 6], 11); | |
b = md4_ff(b, c, d, a, x[i+ 7], 19); | |
a = md4_ff(a, b, c, d, x[i+ 8], 3 ); | |
d = md4_ff(d, a, b, c, x[i+ 9], 7 ); | |
c = md4_ff(c, d, a, b, x[i+10], 11); | |
b = md4_ff(b, c, d, a, x[i+11], 19); | |
a = md4_ff(a, b, c, d, x[i+12], 3 ); | |
d = md4_ff(d, a, b, c, x[i+13], 7 ); | |
c = md4_ff(c, d, a, b, x[i+14], 11); | |
b = md4_ff(b, c, d, a, x[i+15], 19); | |
a = md4_gg(a, b, c, d, x[i+ 0], 3 ); | |
d = md4_gg(d, a, b, c, x[i+ 4], 5 ); | |
c = md4_gg(c, d, a, b, x[i+ 8], 9 ); | |
b = md4_gg(b, c, d, a, x[i+12], 13); | |
a = md4_gg(a, b, c, d, x[i+ 1], 3 ); | |
d = md4_gg(d, a, b, c, x[i+ 5], 5 ); | |
c = md4_gg(c, d, a, b, x[i+ 9], 9 ); | |
b = md4_gg(b, c, d, a, x[i+13], 13); | |
a = md4_gg(a, b, c, d, x[i+ 2], 3 ); | |
d = md4_gg(d, a, b, c, x[i+ 6], 5 ); | |
c = md4_gg(c, d, a, b, x[i+10], 9 ); | |
b = md4_gg(b, c, d, a, x[i+14], 13); | |
a = md4_gg(a, b, c, d, x[i+ 3], 3 ); | |
d = md4_gg(d, a, b, c, x[i+ 7], 5 ); | |
c = md4_gg(c, d, a, b, x[i+11], 9 ); | |
b = md4_gg(b, c, d, a, x[i+15], 13); | |
a = md4_hh(a, b, c, d, x[i+ 0], 3 ); | |
d = md4_hh(d, a, b, c, x[i+ 8], 9 ); | |
c = md4_hh(c, d, a, b, x[i+ 4], 11); | |
b = md4_hh(b, c, d, a, x[i+12], 15); | |
a = md4_hh(a, b, c, d, x[i+ 2], 3 ); | |
d = md4_hh(d, a, b, c, x[i+10], 9 ); | |
c = md4_hh(c, d, a, b, x[i+ 6], 11); | |
b = md4_hh(b, c, d, a, x[i+14], 15); | |
a = md4_hh(a, b, c, d, x[i+ 1], 3 ); | |
d = md4_hh(d, a, b, c, x[i+ 9], 9 ); | |
c = md4_hh(c, d, a, b, x[i+ 5], 11); | |
b = md4_hh(b, c, d, a, x[i+13], 15); | |
a = md4_hh(a, b, c, d, x[i+ 3], 3 ); | |
d = md4_hh(d, a, b, c, x[i+11], 9 ); | |
c = md4_hh(c, d, a, b, x[i+ 7], 11); | |
b = md4_hh(b, c, d, a, x[i+15], 15); | |
a = safeAdd32(a, olda); | |
b = safeAdd32(b, oldb); | |
c = safeAdd32(c, oldc); | |
d = safeAdd32(d, oldd); | |
} | |
return Array(a, b, c, d); | |
} | |
function md4(input) { | |
// Handle string input by converting to Uint8Array | |
let data = input; | |
if (typeof input === 'string') { | |
data = new TextEncoder().encode(input); | |
} | |
// Calculate input length in bits | |
const bitLength = data.length * 8; | |
// Calculate padding length (ensure room for length at end) | |
// Need at least 1 byte for 0x80 and 8 bytes for length | |
// Total length must be multiple of 64 bits (8 bytes) | |
const paddingLength = (56 - (data.length + 1) % 64 + 64) % 64; | |
// Create padded array | |
const paddedLength = data.length + 1 + paddingLength + 8; | |
const paddedData = new Uint8Array(paddedLength); | |
// Copy original data | |
paddedData.set(data); | |
// Add padding byte 0x80 | |
paddedData[data.length] = 0x80; | |
// Add original length in bits as little-endian 64-bit integer | |
const view = new DataView(paddedData.buffer); | |
view.setUint32(paddedLength - 8, bitLength, true); | |
view.setUint32(paddedLength - 4, 0, true); | |
// Convert to 32-bit array for core_md4 | |
const words = new Uint32Array(paddedData.buffer); | |
// Process with core_md4 | |
const result = core_md4(Array.from(words), bitLength); | |
// Convert result to hex string | |
return [...new Uint8Array(new Uint32Array(result).buffer)] | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
} | |
function safeAdd32(a, b) { | |
return (a + b) & 0xFFFFFFFF; | |
} | |
function ROTR(n, x) { | |
return (x >>> n) | (x << (32 - n)); | |
} | |
function md5cycle(x, k) { | |
let a = x[0], b = x[1], c = x[2], d = x[3]; | |
// Round 1 | |
a = ff(a, b, c, d, k[0], 7, -680876936); | |
d = ff(d, a, b, c, k[1], 12, -389564586); | |
c = ff(c, d, a, b, k[2], 17, 606105819); | |
b = ff(b, c, d, a, k[3], 22, -1044525330); | |
a = ff(a, b, c, d, k[4], 7, -176418897); | |
d = ff(d, a, b, c, k[5], 12, 1200080426); | |
c = ff(c, d, a, b, k[6], 17, -1473231341); | |
b = ff(b, c, d, a, k[7], 22, -45705983); | |
a = ff(a, b, c, d, k[8], 7, 1770035416); | |
d = ff(d, a, b, c, k[9], 12, -1958414417); | |
c = ff(c, d, a, b, k[10], 17, -42063); | |
b = ff(b, c, d, a, k[11], 22, -1990404162); | |
a = ff(a, b, c, d, k[12], 7, 1804603682); | |
d = ff(d, a, b, c, k[13], 12, -40341101); | |
c = ff(c, d, a, b, k[14], 17, -1502002290); | |
b = ff(b, c, d, a, k[15], 22, 1236535329); | |
// Round 2 | |
a = gg(a, b, c, d, k[1], 5, -165796510); | |
d = gg(d, a, b, c, k[6], 9, -1069501632); | |
c = gg(c, d, a, b, k[11], 14, 643717713); | |
b = gg(b, c, d, a, k[0], 20, -373897302); | |
a = gg(a, b, c, d, k[5], 5, -701558691); | |
d = gg(d, a, b, c, k[10], 9, 38016083); | |
c = gg(c, d, a, b, k[15], 14, -660478335); | |
b = gg(b, c, d, a, k[4], 20, -405537848); | |
a = gg(a, b, c, d, k[9], 5, 568446438); | |
d = gg(d, a, b, c, k[14], 9, -1019803690); | |
c = gg(c, d, a, b, k[3], 14, -187363961); | |
b = gg(b, c, d, a, k[8], 20, 1163531501); | |
a = gg(a, b, c, d, k[13], 5, -1444681467); | |
d = gg(d, a, b, c, k[2], 9, -51403784); | |
c = gg(c, d, a, b, k[7], 14, 1735328473); | |
b = gg(b, c, d, a, k[12], 20, -1926607734); | |
// Round 3 | |
a = hh(a, b, c, d, k[5], 4, -378558); | |
d = hh(d, a, b, c, k[8], 11, -2022574463); | |
c = hh(c, d, a, b, k[11], 16, 1839030562); | |
b = hh(b, c, d, a, k[14], 23, -35309556); | |
a = hh(a, b, c, d, k[1], 4, -1530992060); | |
d = hh(d, a, b, c, k[4], 11, 1272893353); | |
c = hh(c, d, a, b, k[7], 16, -155497632); | |
b = hh(b, c, d, a, k[10], 23, -1094730640); | |
a = hh(a, b, c, d, k[13], 4, 681279174); | |
d = hh(d, a, b, c, k[0], 11, -358537222); | |
c = hh(c, d, a, b, k[3], 16, -722521979); | |
b = hh(b, c, d, a, k[6], 23, 76029189); | |
a = hh(a, b, c, d, k[9], 4, -640364487); | |
d = hh(d, a, b, c, k[12], 11, -421815835); | |
c = hh(c, d, a, b, k[15], 16, 530742520); | |
b = hh(b, c, d, a, k[2], 23, -995338651); | |
// Round 4 | |
a = ii(a, b, c, d, k[0], 6, -198630844); | |
d = ii(d, a, b, c, k[7], 10, 1126891415); | |
c = ii(c, d, a, b, k[14], 15, -1416354905); | |
b = ii(b, c, d, a, k[5], 21, -57434055); | |
a = ii(a, b, c, d, k[12], 6, 1700485571); | |
d = ii(d, a, b, c, k[3], 10, -1894986606); | |
c = ii(c, d, a, b, k[10], 15, -1051523); | |
b = ii(b, c, d, a, k[1], 21, -2054922799); | |
a = ii(a, b, c, d, k[8], 6, 1873313359); | |
d = ii(d, a, b, c, k[15], 10, -30611744); | |
c = ii(c, d, a, b, k[6], 15, -1560198380); | |
b = ii(b, c, d, a, k[13], 21, 1309151649); | |
a = ii(a, b, c, d, k[4], 6, -145523070); | |
d = ii(d, a, b, c, k[11], 10, -1120210379); | |
c = ii(c, d, a, b, k[2], 15, 718787259); | |
b = ii(b, c, d, a, k[9], 21, -343485551); | |
x[0] = safeAdd32(a, x[0]); | |
x[1] = safeAdd32(b, x[1]); | |
x[2] = safeAdd32(c, x[2]); | |
x[3] = safeAdd32(d, x[3]); | |
} | |
function cmn(q, a, b, x, s, t) { | |
a = safeAdd32(safeAdd32(a, q), safeAdd32(x, t)); | |
return safeAdd32(ROTR(32 - s, a), b); | |
} | |
function ff(a, b, c, d, x, s, t) { | |
return cmn((b & c) | ((~b) & d), a, b, x, s, t); | |
} | |
function gg(a, b, c, d, x, s, t) { | |
return cmn((b & d) | (c & (~d)), a, b, x, s, t); | |
} | |
function hh(a, b, c, d, x, s, t) { | |
return cmn(b ^ c ^ d, a, b, x, s, t); | |
} | |
function ii(a, b, c, d, x, s, t) { | |
return cmn(c ^ (b | (~d)), a, b, x, s, t); | |
} | |
function processBlock(state, input, offset) { | |
const chunk = new Int32Array(16); | |
const dv = new DataView(input.buffer, input.byteOffset); | |
for (let j = 0; j < 16; j++) { | |
chunk[j] = dv.getUint32(offset + (j * 4), true); | |
} | |
md5cycle(state, chunk); | |
} | |
function md5(input) { | |
// Convert string to Uint8Array if needed | |
let data; | |
if (typeof input === 'string') { | |
data = new TextEncoder().encode(input); | |
} else if (input instanceof Uint8Array) { | |
data = input; | |
} else { | |
throw new Error('Input must be a string or Uint8Array'); | |
} | |
// Initialize hash values | |
const state = new Int32Array([ | |
1732584193, // A | |
-271733879, // B | |
-1732584194, // C | |
271733878 // D | |
]); | |
// Pre-processing: pad the input | |
const originalLength = data.length; | |
const paddedLength = Math.ceil((originalLength + 9) / 64) * 64; | |
const padded = new Uint8Array(paddedLength); | |
// Copy original data | |
padded.set(data); | |
// Append padding bits | |
padded[originalLength] = 0x80; | |
// Append original length in bits as little-endian 64-bit integer | |
const bitLength = originalLength * 8; | |
const dv = new DataView(padded.buffer); | |
dv.setUint32(paddedLength - 8, bitLength, true); | |
dv.setUint32(paddedLength - 4, 0, true); | |
// Process each 64-byte chunk | |
for (let i = 0; i < paddedLength; i += 64) { | |
processBlock(state, padded, i); | |
} | |
// Convert state to bytes | |
const result = new Uint8Array(16); | |
const resultView = new DataView(result.buffer); | |
for (let i = 0; i < 4; i++) { | |
resultView.setInt32(i * 4, state[i], true); | |
} | |
// Convert to hex string | |
return Array.from(result) | |
.map(b => b.toString(16).padStart(2, '0')) | |
.join(''); | |
} | |
function sha1(message, rounds = 80) { | |
let bytes; | |
if (typeof message === 'string') { | |
const encoder = new TextEncoder(); | |
bytes = encoder.encode(message); | |
} else if (message instanceof Uint8Array) { | |
bytes = message; | |
} else { | |
throw new Error("Invalid input type. Expected Uint8Array or string."); | |
} | |
let h0 = 0x67452301; | |
let h1 = 0xEFCDAB89; | |
let h2 = 0x98BADCFE; | |
let h3 = 0x10325476; | |
let h4 = 0xC3D2E1F0; | |
let ml = bytes.length * 8; | |
// Pre-processing | |
let paddedBytes = new Uint8Array([...bytes, 0x80]); | |
while ((paddedBytes.length * 8) % 512 !== 448) { | |
paddedBytes = new Uint8Array([...paddedBytes, 0x00]); | |
} | |
const mlBytes = new Uint8Array(8); | |
for (let i = 7; i >= 0; i--) { | |
mlBytes[i] = ml & 0xFF; | |
ml = ml >>> 8; | |
} | |
paddedBytes = new Uint8Array([...paddedBytes, ...mlBytes]); | |
// Process the message in successive 512-bit chunks | |
for (let i = 0; i < paddedBytes.length; i += 64) { | |
const chunk = paddedBytes.slice(i, i + 64); | |
let w = new Array(rounds).fill(0); | |
for (let j = 0; j < 16; j++) { | |
w[j] = (chunk[j * 4] << 24) | (chunk[j * 4 + 1] << 16) | (chunk[j * 4 + 2] << 8) | chunk[j * 4 + 3]; | |
} | |
for (let j = 16; j < rounds; j++) { | |
w[j] = ROTL(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); | |
} | |
let a = h0; | |
let b = h1; | |
let c = h2; | |
let d = h3; | |
let e = h4; | |
for (let j = 0; j < rounds; j++) { | |
let f, k; | |
if (0 <= j && j <= 19) { | |
f = (b & c) | ((~b) & d); | |
k = 0x5A827999; | |
} else if (20 <= j && j <= 39) { | |
f = b ^ c ^ d; | |
k = 0x6ED9EBA1; | |
} else if (40 <= j && j <= 59) { | |
f = (b & c) | (b & d) | (c & d); | |
k = 0x8F1BBCDC; | |
} else if (60 <= j && j <= 79) { | |
f = b ^ c ^ d; | |
k = 0xCA62C1D6; | |
} else { | |
f = b ^ c ^ d; | |
k = 0xCA62C1D6; | |
} | |
const temp = safeAdd32(ROTL(a, 5), safeAdd32(safeAdd32(safeAdd32(f, e), k), w[j])); | |
e = d; | |
d = c; | |
c = ROTL(b, 30); | |
b = a; | |
a = temp; | |
} | |
h0 = safeAdd32(h0, a); | |
h1 = safeAdd32(h1, b); | |
h2 = safeAdd32(h2, c); | |
h3 = safeAdd32(h3, d); | |
h4 = safeAdd32(h4, e); | |
} | |
const hh = (h0 >>> 0).toString(16).padStart(8, '0') + | |
(h1 >>> 0).toString(16).padStart(8, '0') + | |
(h2 >>> 0).toString(16).padStart(8, '0') + | |
(h3 >>> 0).toString(16).padStart(8, '0') + | |
(h4 >>> 0).toString(16).padStart(8, '0'); | |
return hh; | |
} | |
function sha256(msgBytes, rounds = 64) { | |
const 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 | |
]; | |
const H = [ | |
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 | |
]; | |
const paddedLength = Math.ceil((msgBytes.length + 9) / 64) * 64; | |
const paddedBytes = new Uint8Array(paddedLength); | |
paddedBytes.set(msgBytes); | |
paddedBytes[msgBytes.length] = 0x80; | |
const msgLengthBits = msgBytes.length * 8; | |
const dv = new DataView(paddedBytes.buffer); | |
dv.setUint32(paddedLength - 8, Math.floor(msgLengthBits / Math.pow(2, 32)), false); | |
dv.setUint32(paddedLength - 4, msgLengthBits & 0xffffffff, false); | |
const N = paddedLength / 64; | |
const M = new Array(N); | |
for (let i = 0; i < N; i++) { | |
M[i] = new Array(16); | |
for (let j = 0; j < 16; j++) { | |
M[i][j] = (paddedBytes[i * 64 + j * 4] << 24) | | |
(paddedBytes[i * 64 + j * 4 + 1] << 16) | | |
(paddedBytes[i * 64 + j * 4 + 2] << 8) | | |
(paddedBytes[i * 64 + j * 4 + 3]); | |
} | |
} | |
const W = new Array(64); | |
let a, b, c, d, e, f, g, h; | |
for (let i = 0; i < N; i++) { | |
for (let t = 0; t < 16; t++) W[t] = M[i][t]; | |
for (let t = 16; t < 64; t++) W[t] = (σ1(W[t - 2]) + W[t - 7] + σ0(W[t - 15]) + W[t - 16]) >>> 0; | |
a = H[0]; b = H[1]; c = H[2]; d = H[3]; | |
e = H[4]; f = H[5]; g = H[6]; h = H[7]; | |
for (let t = 0; t < rounds; t++) { | |
const T1 = (h + Σ1(e) + Ch(e, f, g) + K[t] + W[t]) >>> 0; | |
const T2 = (Σ0(a) + Maj(a, b, c)) >>> 0; | |
h = g; | |
g = f; | |
f = e; | |
e = (d + T1) >>> 0; | |
d = c; | |
c = b; | |
b = a; | |
a = (T1 + T2) >>> 0; | |
} | |
H[0] = (H[0] + a) >>> 0; | |
H[1] = (H[1] + b) >>> 0; | |
H[2] = (H[2] + c) >>> 0; | |
H[3] = (H[3] + d) >>> 0; | |
H[4] = (H[4] + e) >>> 0; | |
H[5] = (H[5] + f) >>> 0; | |
H[6] = (H[6] + g) >>> 0; | |
H[7] = (H[7] + h) >>> 0; | |
} | |
return H.map(n => n.toString(16).padStart(8, '0')).join(''); | |
function Σ0(x) { return ROTR(2, x) ^ ROTR(13, x) ^ ROTR(22, x); } | |
function Σ1(x) { return ROTR(6, x) ^ ROTR(11, x) ^ ROTR(25, x); } | |
function σ0(x) { return ROTR(7, x) ^ ROTR(18, x) ^ (x >>> 3); } | |
function σ1(x) { return ROTR(17, x) ^ ROTR(19, x) ^ (x >>> 10); } | |
function Ch(x, y, z) { return (x & y) ^ (~x & z); } | |
function Maj(x, y, z) { return (x & y) ^ (x & z) ^ (y & z); } | |
} | |
function hmac(key, message, hash, blockSize = 64) { | |
function hexToBytes(hex) { | |
const bytes = new Uint8Array(hex.length / 2); | |
for (let i = 0; i < hex.length; i += 2) { | |
bytes[i / 2] = parseInt(hex.slice(i, i + 2), 16); | |
} | |
return bytes; | |
} | |
function computeBlockSizedKey(key, hash, blockSize) { | |
let keyBytes = typeof key === 'string' ? new TextEncoder().encode(key) : key; | |
if (keyBytes.length > blockSize) { | |
const hashedKey = hash(keyBytes); | |
keyBytes = hexToBytes(hashedKey); | |
} | |
if (keyBytes.length < blockSize) { | |
const paddedKey = new Uint8Array(blockSize); | |
paddedKey.set(keyBytes); | |
return paddedKey; | |
} | |
return keyBytes; | |
} | |
const blockSizedKey = computeBlockSizedKey(key, hash, blockSize); | |
const oKeyPad = new Uint8Array(blockSize); | |
const iKeyPad = new Uint8Array(blockSize); | |
for (let i = 0; i < blockSize; i++) { | |
oKeyPad[i] = blockSizedKey[i] ^ 0x5c; | |
iKeyPad[i] = blockSizedKey[i] ^ 0x36; | |
} | |
const messageBytes = typeof message === 'string' | |
? new TextEncoder().encode(message) | |
: message; | |
const innerHashHex = hash(new Uint8Array([...iKeyPad, ...messageBytes])); | |
const innerHashBytes = hexToBytes(innerHashHex); | |
const hmacValue = hash(new Uint8Array([...oKeyPad, ...innerHashBytes])); | |
return hmacValue; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment