Skip to content

Instantly share code, notes, and snippets.

@cababunga
Last active November 24, 2020 06:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cababunga/cb62c32fe2b82be014d08f731a793b54 to your computer and use it in GitHub Desktop.
Save cababunga/cb62c32fe2b82be014d08f731a793b54 to your computer and use it in GitHub Desktop.
Bookmark for calculating MFA codes. Replace "SECRET" with your actual secret.
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)
@cababunga
Copy link
Author

cababunga commented Nov 24, 2020

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 like document.getElementById("mfa-key") or whatever finds you that element.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment