Last active
November 24, 2020 06:02
-
-
Save cababunga/cb62c32fe2b82be014d08f731a793b54 to your computer and use it in GitHub Desktop.
Bookmark for calculating MFA codes. Replace "SECRET" with your actual secret.
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
jvascript:((secret, element) => { | |
"use strict"; | |
const otp = secret => { | |
const tick = Date.now() / 30000 | 0; | |
const msg = new Uint8Array(8); | |
new DataView(msg.buffer).setInt32(4, tick); | |
const key = b32decode(secret); | |
const mac = hmac(msg, key, sha1); | |
const offset = mac[mac.length - 1] & 0x0f; | |
const buf = mac.slice(offset, offset + 4); | |
const num = new DataView(buf.buffer).getUint32(0) & 0x7fffffff; | |
return num.toString().padStart(6, "0").slice(-6); | |
}; | |
const b32alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; | |
const lookup = Object.fromEntries(b32alphabet.split("").map((x, i) => [x, i])); | |
const b32decode = str => { | |
str = str.replace(/=+$/, "").toUpperCase(); | |
let bits = 0; | |
let index = 0; | |
let value = 0; | |
const buf = new Uint8Array(str.length * 5 / 8 | 0); | |
for (const x of str) { | |
if (!(x in lookup)) | |
throw new Error(`Base32: Invalid character '${x}'`); | |
value = value << 5 | lookup[x]; | |
bits += 5; | |
if (bits >= 8) { | |
bits -= 8; | |
buf[index++] = value >>> bits & 0xff; | |
} | |
} | |
return buf; | |
}; | |
const hmac = (msg, key, hash) => { | |
if (key.length > 64) | |
key = hash(key); | |
if (key.length < 64) { | |
const buf = new Uint8Array(64); | |
buf.set(key); | |
key = buf; | |
} | |
const oKeyPad = key.map(x => x ^ 0x5C); | |
const iKeyPad = key.map(x => x ^ 0x36); | |
const concat = (a, b) => { | |
const c = new Uint8Array(a.length + b.length); | |
c.set(a); | |
c.set(b, a.length); | |
return c; | |
}; | |
const iPadRes = hash(concat(iKeyPad, msg)); | |
return hash(concat(oKeyPad, iPadRes)); | |
}; | |
const sha1 = msg => { | |
const rol = (value, bits) => value << bits | value >>> 32 - bits; | |
const state = [0x67452301,0xefcdab89,0x98badcfe,0x10325476,0xc3d2e1f0]; | |
const msgLen = msg.length; | |
const words = new Uint32Array((msgLen + 72 & ~63) / 4); | |
const data = new DataView(msg.buffer); | |
let i = 0; | |
for (; i < (msgLen / 4 | 0); ++i) | |
words[i] = data.getUint32(i * 4); | |
const tail = new Uint8Array(4); | |
const tailLen = msgLen % 4; | |
tail.set(msg.slice(msgLen - tailLen)); | |
words[i] = new DataView(tail.buffer).getUint32(0) | 0x80 << (3 - tailLen) * 8 >>> 0; | |
words[words.length - 2] = msgLen >>> 29; | |
words[words.length - 1] = msgLen << 3; | |
const w = new Array(80); | |
for (let block = 0; block < words.length; block += 16) { | |
for (let i = 0; i < 16; ++i) | |
w[i] = words[block + i]; | |
for (let i = 16; i < 80; ++i) | |
w[i] = rol(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1); | |
let [a, b, c, d, e] = state; | |
for (let i = 0; i < 20; ++i) { | |
const t = rol(a, 5) + (b & c | ~b & d) + e + w[i] + 0x5a827999; | |
[a, b, c, d, e] = [t, a, rol(b, 30), c, d]; | |
} | |
for (let i = 20; i < 40; ++i) { | |
const t = rol(a, 5) + (b ^ c ^ d) + e + w[i] + 0x6ed9eba1; | |
[a, b, c, d, e] = [t, a, rol(b, 30), c, d]; | |
} | |
for (let i = 40; i < 60; ++i) { | |
const t = rol(a, 5) + (b & c | b & d | c & d) + e + w[i] + 0x8f1bbcdc; | |
[a, b, c, d, e] = [t, a, rol(b, 30), c, d]; | |
} | |
for (let i = 60; i < 80; ++i) { | |
const t = rol(a, 5) + (b ^ c ^ d) + e + w[i] + 0xca62c1d6; | |
[a, b, c, d, e] = [t, a, rol(b, 30), c, d]; | |
} | |
state[0] += a; | |
state[1] += b; | |
state[2] += c; | |
state[3] += d; | |
state[4] += e; | |
} | |
const output = new Uint8Array(20); | |
const view = new DataView(output.buffer); | |
for (const [i, x] of state.entries()) | |
view.setUint32(i * 4, x); | |
return output; | |
}; | |
const code = otp(secret); | |
console.log(code); | |
element.value = code; | |
})("SECRET", document.activeElement) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is to be used as a bookmark in your browser, it generates same codes as the Google Authenticator does.
The code will be placed into the focused text input field. If input field is easy to locate by navigating DOM, you can replace
document.activeElement
with something likedocument.getElementById("mfa-key")
or whatever finds you that element.