diff --git a/authenticator.js b/authenticator.js new file mode 100644 index 0000000..e60fc53 --- /dev/null +++ b/authenticator.js @@ -0,0 +1,86 @@ +(function (exports) { +'use strict'; + +var Authenticator = exports.Authenticator || exports; +var Unibabel = window.Unibabel || require('unibabel'); +console.log('window.totp', window.totp); +var totp = window.totp || require('notp').totp; + +if (!window.crypto) { + document.addEventListener('mousemove', function (event) { + var ev = event || window.event; + + window.forge.random.collectInt(ev.pageX, 16); + window.forge.random.collectInt(ev.pageY, 16); + }); +} + +// Generate a key +function generateOtpKey() { + // 20 cryptographically random binary bytes (160-bit key) + if (false && window.crypto) { + var key = window.crypto.getRandomValues(new Uint8Array(20)); + + return Promise.resolve(key); + } else { + return new Promise(function (resolve, reject) { + window.forge.random.getBytes(20, function (err, bytes) { + if (err) { + reject(err); + return; + } + + resolve(Unibabel.binaryStringToBuffer(bytes)); + }); + }); + } +} + +// Text-encode the key as base32 (in the style of Google Authenticator - same as Facebook, Microsoft, etc) +function encodeGoogleAuthKey(bin) { + // 32 ascii characters without trailing '='s + var base32 = Unibabel.bufferToBase32(bin).replace(/=/g, ''); + + // lowercase with a space every 4 characters + var key = base32.toLowerCase().replace(/(\w{4})/g, "$1 ").trim(); + + return key; +} + +function generateGoogleAuthKey() { + return generateOtpKey().then(encodeGoogleAuthKey); +} + +// Binary-decode the key from base32 (Google Authenticator, FB, M$, etc) +function decodeGoogleAuthKey(key) { + // decode base32 google auth key to binary + var unformatted = key.replace(/\W+/g, '').toUpperCase(); + var bin = Unibabel.base32ToBuffer(unformatted); + + return bin; +} + +// Generate a Google Auth Token +function generateGoogleAuthToken(key) { + var bin = decodeGoogleAuthKey(key); + + return totp.gen(bin); +} + +// Verify a Google Auth Token +function verifyGoogleAuthToken(key, token) { + var bin = decodeGoogleAuthKey(key); + + token = token.replace(/\W+/g, ''); + + // window is +/- 1 period of 30 seconds + return totp.verify(token, bin, { window: 1, time: 30 }); +} + +Authenticator.generateKey = generateGoogleAuthKey; +Authenticator.generateToken = generateGoogleAuthToken; +Authenticator.verifyToken = verifyGoogleAuthToken; + +}( + 'undefined' !== typeof window ? (window.Authenticator = {}) : module.exports +)); diff --git a/test.html b/test.html new file mode 100644 index 0000000..ebb3057 --- /dev/null +++ b/test.html @@ -0,0 +1,66 @@ + + +
+20-byte (160-bit) Base32 Key:
+