Skip to content

Instantly share code, notes, and snippets.

@ZaneHannanAU
Last active March 30, 2018 12:46
Show Gist options
  • Save ZaneHannanAU/60e1781524fada2cf8f7f0babd897043 to your computer and use it in GitHub Desktop.
Save ZaneHannanAU/60e1781524fada2cf8f7f0babd897043 to your computer and use it in GitHub Desktop.
integrated totp (nodejs)
const {fn: {b32_buf, buf_b32, getOTP}} = require('./totp')
const givea = fuck => console.log('%s %j', fuck.length.toString().padStart(4), fuck.toString())
const it = process.hrtime()
const rt = ([s,ns] = process.hrtime(it)) => console.log('%ss elapsed', (s+ns/1e9).toFixed(9).padStart(12))
rt()
const fucks = Buffer.from('Hi there fucks')
givea(fucks)
rt()
const b32fucks = buf_b32(fucks)
givea(b32fucks)
rt()
const ofucks = b32_buf(b32fucks)
givea(ofucks)
console.assert(ofucks.equals(fucks))
rt()
const ok = Buffer.from([0x3d])
givea(ok)
rt()
const bok = buf_b32(ok)
givea(bok)
rt()
const b32ok = b32_buf(bok)
givea(b32ok)
console.assert(b32ok.equals(ok))
rt()
const crap = getOTP(fucks)
givea(crap)
rt()
const shit = getOTP(fucks)
givea(shit)
setTimeout(()=>{rt(); givea(getOTP(fucks)); rt()}, 3e4)
0.000192332s elapsed
14 "Hi there fucks"
0.005373599s elapsed
24 "JBUSA5DIMVZGKIDGOVRWW4Y="
0.006357218s elapsed
14 "Hi there fucks"
0.007532192s elapsed
1 "="
0.008020879s elapsed
8 "HU======"
0.008177638s elapsed
1 "="
0.008350557s elapsed
6 "122610"
0.009628935s elapsed
6 "122610"
30.043018793s elapsed
6 "465683"
30.044405246s elapsed
const crypto = require('crypto')
const {b32c, b256} = Array.from('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567').reduce((o,c,i)=>{
o.b32c[c] = i;
o.b32c[c.codePointAt(0)] = i;
o.b256[i] = c.codePointAt(0);
return o;
}, {b32c: Object.create(null), b256: Buffer.alloc(33)})
// console.log(b32c, c32b)
const _31 = Math.pow(2,31)-1 // shorthand to fill crap
const b32_buf = b32 => {
let B32 = b32
let ec = B32.indexOf('=') > -1 ? B32.length - B32.indexOf('=') : 0
let v1, v2, v3, v4, v5, v6, v7, v8,
buf = Buffer.allocUnsafe(Math.abs(5 * Math.ceil(B32.length / 8) - ec)),
i = 0,
idx = 0,
len = b32.indexOf('='),
cnt = len >> 3 << 3,
rem = len - cnt
// 8 chars are 5 bytes
while (i < cnt) {
v1 = b32c[B32[i++]];
v2 = b32c[B32[i++]];
v3 = b32c[B32[i++]];
v4 = b32c[B32[i++]];
v5 = b32c[B32[i++]];
v6 = b32c[B32[i++]];
v7 = b32c[B32[i++]];
v8 = b32c[B32[i++]];
buf[idx++] = (v1 << 3 | v2 >>> 2) & 255;
buf[idx++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
buf[idx++] = (v4 << 4 | v5 >>> 1) & 255;
buf[idx++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
buf[idx++] = (v7 << 5 | v8) & 255;
};
switch (rem) {
case 2:
v1 = b32c[B32[i++]];
v2 = b32c[B32[i++]];
buf[idx++] = (v1 << 3 | v2 >>> 2) & 255;
break;
case 4:
v1 = b32c[B32[i++]];
v2 = b32c[B32[i++]];
v3 = b32c[B32[i++]];
v4 = b32c[B32[i++]];
buf[idx++] = (v1 << 3 | v2 >>> 2) & 255;
buf[idx++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
break;
case 5:
v1 = b32c[B32[i++]];
v2 = b32c[B32[i++]];
v3 = b32c[B32[i++]];
v4 = b32c[B32[i++]];
v5 = b32c[B32[i++]];
buf[idx++] = (v1 << 3 | v2 >>> 2) & 255;
buf[idx++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
buf[idx++] = (v4 << 4 | v5 >>> 1) & 255;
break;
case 7:
v1 = b32c[B32[i++]];
v2 = b32c[B32[i++]];
v3 = b32c[B32[i++]];
v4 = b32c[B32[i++]];
v5 = b32c[B32[i++]];
v6 = b32c[B32[i++]];
v7 = b32c[B32[i++]];
buf[idx++] = (v1 << 3 | v2 >>> 2) & 255;
buf[idx++] = (v2 << 6 | v3 << 1 | v4 >>> 4) & 255;
buf[idx++] = (v4 << 4 | v5 >>> 1) & 255;
buf[idx++] = (v5 << 7 | v6 << 2 | v7 >>> 3) & 255;
break;
};
return buf;
};
const buf_b32 = buf => {
let v1, v2, v3, v4, v5,
b32 = Buffer.allocUnsafe(8 * Math.ceil(buf.length / 5)),
i = 0,
idx = 0,
len = buf.length,
cnt = Math.floor(len / 5) * 5,
rem = len - cnt
while (i < cnt) {
v1 = buf[i++];
v2 = buf[i++];
v3 = buf[i++];
v4 = buf[i++];
v5 = buf[i++];
b32.writeInt32BE(
b256[v1 >>> 3] << 24
| b256[(v1 << 2 | v2 >>> 6) & 31] << 16
| b256[(v2 >>> 1) & 31] << 8
| b256[(v2 << 4 | v3 >>> 4) & 31],
4 * idx++
);
b32.writeInt32BE(
b256[(v3 << 1 | v4 >>> 7) & 31] << 24
| b256[(v4 >>> 2) & 31] << 16
| b256[(v4 << 3 | v5 >>> 5) & 31] << 8
| b256[v5 & 31],
4 * idx++
);
};
switch (rem) {
case 1:
v1 = buf[i++];
b32.writeInt32BE(b256[v1 >>> 3] << 24 | b256[(v1 << 2) & 31] << 16 | 0x3d3d, 4 * idx++);
b32.writeInt32BE(0x3d3d3d3d, 4 * idx++);
break;
case 2:
v1 = buf[i++];
v2 = buf[i++];
b32.writeInt32BE(
b256[v1 >>> 3] << 24
| b256[(v1 << 2 | v2 >>> 6) & 31] << 16
| b256[(v2 >>> 1) & 31] << 8
| b256[(v2 << 4) & 31],
4 * idx++
);
b32.writeInt32BE(0x3d3d3d3d, 4 * idx++);
break;
case 3:
v1 = buf[i++];
v2 = buf[i++];
v3 = buf[i++];
b32.writeInt32BE(
b256[v1 >>> 3] << 24
| b256[(v1 << 2 | v2 >>> 6) & 31] << 16
| b256[(v2 >>> 1) & 31] << 8
| b256[(v2 << 4 | v3 >>> 4) & 31],
4 * idx++
);
b32.writeInt32BE(
b256[(v3 << 1) & 31] << 24 | 0x3d3d3d,
4 * idx++
);
break;
case 4:
v1 = buf[i++];
v2 = buf[i++];
v3 = buf[i++];
v4 = buf[i++];
b32.writeInt32BE(
b256[v1 >>> 3] << 24
| b256[(v1 << 2 | v2 >>> 6) & 31] << 16
| b256[(v2 >>> 1) & 31] << 8
| b256[(v2 << 4 | v3 >>> 4) & 31],
4 * idx++
);
b32.writeInt32BE(
b256[(v3 << 1 | v4 >>> 7) & 31] << 24
| b256[(v4 >>> 2) & 31] << 16
| b256[(v4 << 3) & 31] << 8
| 0x3d,
4 * idx++
);
break;
};
return b32;
};
const getOTP = (secret, len = 6, T0 = 0, TI = 30) => {
const TC = Math.floor((Math.round(Date.now() / 1e3) - T0) / TI)
.toString(16)
.padStart(16, '0');
const TOTP = crypto.createHmac('sha1', secret)
.update(TC, 'hex')
.digest()
.readInt32BE(16) & _31;
return String(TOTP % Math.pow(10, len)).padStart(6,'0');
};
const TOTP = {
constants: {b32c, b256, _31},
fn: {b32_buf, buf_b32, getOTP}
};
module.exports = TOTP;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment