Skip to content

Instantly share code, notes, and snippets.

@Kreijstal
Forked from rumkin/sha-256.js
Last active November 15, 2024 12:28
Show Gist options
  • Save Kreijstal/db65b1d39e99ce7640a5af359be2c83a to your computer and use it in GitHub Desktop.
Save Kreijstal/db65b1d39e99ce7640a5af359be2c83a to your computer and use it in GitHub Desktop.
/*
* 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