diff --git a/src/base32.js b/src/base32.js index 944862c..10663ac 100644 --- a/src/base32.js +++ b/src/base32.js @@ -4,35 +4,35 @@ * @author : Gin (gin.lance.inside@hotmail.com) */ -const nibbler = require("./nibbler/nibbler"); +const nibbler = require('./nibbler/nibbler'); export class Base32 { - /** + /* * * Base32 decode function * * @param {secret} - * @type {String} + * @type {String} * @desc input string * * @return {String} */ - static decode(secret) { - return nibbler.b32decode(secret); - } + static decode(secret) { + return nibbler.b32decode(secret); + } - /** + /* * * Base32 generate random b32 encoded string function * * @param {length} - * @type {int} + * @type {int} * @desc the length of random b32 encoded string * * @return {String} */ - static random_gen(length=16) { - let random_str = Math.random().toString(36); - random_str = nibbler.b32encode(random_str); + static random_gen(length = 16) { + let random_str = Math.random().toString(36); + random_str = nibbler.b32encode(random_str); - return random_str.substring(0, length); - } -} \ No newline at end of file + return random_str.substring(0, length); + } +} diff --git a/src/hotp.js b/src/hotp.js index e36b3c2..86be771 100644 --- a/src/hotp.js +++ b/src/hotp.js @@ -6,11 +6,11 @@ import { OTP } from './otp'; export class HOTP extends OTP { - /** + /* * * Generate the OTP with the given count * * @param {count} - * @type {int} + * @type {int} * @desc the OTP HMAC counter * * @return {OTP} @@ -21,20 +21,20 @@ export class HOTP extends OTP { * hotp.at(0); // => 432143 * ``` */ - at(count) { - let digit = super.generate_otp(count); - return digit; - } + at(count) { + const digit = super.generate_otp(count); + return digit; + } - /** + /* * * Verifies the OTP passed in against the current counter. * * @param {otp} - * @type {String} + * @type {String} * @desc the OTP waiting for checking * * @param {counter} - * @type {int} + * @type {int} * @desc the OTP HMAC counter * * @return {Boolean} @@ -47,26 +47,25 @@ export class HOTP extends OTP { * hotp.verify(432143, 1); // => false * ``` */ - verify(otp, counter) { - let otp_count = this.at(counter); + verify(otp, counter) { + const otp_count = this.at(counter); - if (otp === otp_count) { - return true; - } else { - return false; - } + if (otp === otp_count) { + return true; } + return false; + } - /** + /* * * Generate a url with HOTP instance. * * @param {issuer} - * @type {String} + * @type {String} * @desc maybe it is the Service name * * @return {String} */ - url_gen(issuer="") { - return super.url_gen(issuer, "hotp"); - } -} \ No newline at end of file + url_gen(issuer = '') { + return super.url_gen(issuer, 'hotp'); + } +} diff --git a/src/jsotp.js b/src/jsotp.js index 3064314..0d3c5d5 100644 --- a/src/jsotp.js +++ b/src/jsotp.js @@ -10,42 +10,42 @@ import { HOTP } from './hotp'; import { Base32 } from './base32'; import { Util } from './util'; -/** +/* * * Generate and return HOTP object * * @param {secret} - * @type {String} + * @type {String} * @desc random base32-encoded key to generate OTP. * * @return {OTP} */ -function hotp_gen(secret, digits=6, digest="SHA-1") { - let hotp = new HOTP(secret, digits, digest); - return hotp; +function hotp_gen(secret, digits = 6, digest = 'SHA-1') { + const hotp = new HOTP(secret, digits, digest); + return hotp; } -/** +/* * * Generate and return TOTP object * * @param {secret} - * @type {String} + * @type {String} * @desc random base32-encoded key to generate OTP. * * @param {interval} - * @type {int} + * @type {int} * @desc the time interval in seconds for OTP. * This defaults to 30. * * @return {OTP} */ -function totp_gen(secret, interval=30) { - let totp = new TOTP(secret, interval); - return totp; +function totp_gen(secret, interval = 30) { + const totp = new TOTP(secret, interval); + return totp; } export { - hotp_gen as HOTP, - totp_gen as TOTP, - Base32, - Util -}; \ No newline at end of file + hotp_gen as HOTP, + totp_gen as TOTP, + Base32, + Util, +}; diff --git a/src/nibbler/nibbler.js b/src/nibbler/nibbler.js index 862f100..8731e5a 100644 --- a/src/nibbler/nibbler.js +++ b/src/nibbler/nibbler.js @@ -3,174 +3,188 @@ Adapted for Node.js by Matt Robenolt Reference: http://www.tumuski.com/2010/04/nibbler/ */ -var Nibbler = function (options) { - var construct, - - // options - pad, dataBits, codeBits, keyString, arrayData, - - // private instance variables - mask, group, max, - - // private methods - gcd, translate, - - // public methods - encode, decode; - +const Nibbler = function (options) { + let construct, + + // options + pad, + dataBits, + codeBits, + keyString, + arrayData, + + // private instance variables + mask, + group, + max, + + // private methods + gcd, + translate, + + // public methods + encode, + decode; + // pseudo-constructor - construct = function () { - var i, mag, prev; + construct = function () { + let i, + mag, + prev; - // options - pad = options.pad || ''; - dataBits = options.dataBits; - codeBits = options.codeBits; - keyString = options.keyString; - arrayData = options.arrayData; - - // bitmasks - mag = Math.max(dataBits, codeBits); - prev = 0; - mask = []; - for (i = 0; i < mag; i += 1) { - mask.push(prev); - prev += prev + 1; - } - max = prev; - - // ouput code characters in multiples of this number - group = dataBits / gcd(dataBits, codeBits); + // options + pad = options.pad || ''; + dataBits = options.dataBits; + codeBits = options.codeBits; + keyString = options.keyString; + arrayData = options.arrayData; + + // bitmasks + mag = Math.max(dataBits, codeBits); + prev = 0; + mask = []; + for (i = 0; i < mag; i += 1) { + mask.push(prev); + prev += prev + 1; + } + max = prev; + + // ouput code characters in multiples of this number + group = dataBits / gcd(dataBits, codeBits); + }; + + // greatest common divisor + gcd = function (a, b) { + let t; + while (b !== 0) { + t = b; + b = a % b; + a = t; + } + return a; + }; + + // the re-coder + translate = function (input, bitsIn, bitsOut, decoding) { + let i, + len, + chr, + byteIn, + buffer, + size, + output, + write; + + // append a byte to the output + write = function (n) { + if (!decoding) { + output.push(keyString.charAt(n)); + } else if (arrayData) { + output.push(n); + } else { + output.push(String.fromCharCode(n)); + } }; - // greatest common divisor - gcd = function (a, b) { - var t; - while (b !== 0) { - t = b; - b = a % b; - a = t; + buffer = 0; + size = 0; + output = []; + + len = input.length; + for (i = 0; i < len; i += 1) { + // the new size the buffer will be after adding these bits + size += bitsIn; + + // read a character + if (decoding) { + // decode it + chr = input.charAt(i); + byteIn = keyString.indexOf(chr); + if (chr === pad) { + break; + } else if (byteIn < 0) { + throw `the character "${chr}" is not a member of ${keyString}`; } - return a; - }; - - // the re-coder - translate = function (input, bitsIn, bitsOut, decoding) { - var i, len, chr, byteIn, - buffer, size, output, - write; - - // append a byte to the output - write = function (n) { - if (!decoding) { - output.push(keyString.charAt(n)); - } else if (arrayData) { - output.push(n); - } else { - output.push(String.fromCharCode(n)); - } - }; - - buffer = 0; - size = 0; - output = []; - - len = input.length; - for (i = 0; i < len; i += 1) { - // the new size the buffer will be after adding these bits - size += bitsIn; - - // read a character - if (decoding) { - // decode it - chr = input.charAt(i); - byteIn = keyString.indexOf(chr); - if (chr === pad) { - break; - } else if (byteIn < 0) { - throw 'the character "' + chr + '" is not a member of ' + keyString; - } - } else { - if (arrayData) { - byteIn = input[i]; - } else { - byteIn = input.charCodeAt(i); - } - if ((byteIn | max) !== max) { - throw byteIn + " is outside the range 0-" + max; - } - } - - // shift the buffer to the left and add the new bits - buffer = (buffer << bitsIn) | byteIn; - - // as long as there's enough in the buffer for another output... - while (size >= bitsOut) { - // the new size the buffer will be after an output - size -= bitsOut; - - // output the part that lies to the left of that number of bits - // by shifting the them to the right - write(buffer >> size); - - // remove the bits we wrote from the buffer - // by applying a mask with the new size - buffer &= mask[size]; - } + } else { + if (arrayData) { + byteIn = input[i]; + } else { + byteIn = input.charCodeAt(i); } - - // If we're encoding and there's input left over, pad the output. - // Otherwise, leave the extra bits off, 'cause they themselves are padding - if (!decoding && size > 0) { - - // flush the buffer - write(buffer << (bitsOut - size)); - - // add padding keyString for the remainder of the group - len = output.length % group; - for (i = 0; i < len; i += 1) { - output.push(pad); - } + if ((byteIn | max) !== max) { + throw `${byteIn} is outside the range 0-${max}`; } + } - // string! - return (arrayData && decoding) ? output : output.join(''); - }; - - /** + // shift the buffer to the left and add the new bits + buffer = (buffer << bitsIn) | byteIn; + + // as long as there's enough in the buffer for another output... + while (size >= bitsOut) { + // the new size the buffer will be after an output + size -= bitsOut; + + // output the part that lies to the left of that number of bits + // by shifting the them to the right + write(buffer >> size); + + // remove the bits we wrote from the buffer + // by applying a mask with the new size + buffer &= mask[size]; + } + } + + // If we're encoding and there's input left over, pad the output. + // Otherwise, leave the extra bits off, 'cause they themselves are padding + if (!decoding && size > 0) { + // flush the buffer + write(buffer << (bitsOut - size)); + + // add padding keyString for the remainder of the group + len = output.length % group; + for (i = 0; i < len; i += 1) { + output.push(pad); + } + } + + // string! + return (arrayData && decoding) ? output : output.join(''); + }; + + /** * Encode. Input and output are strings. */ - encode = function (input) { - return translate(input, dataBits, codeBits, false); - }; - - /** + encode = function (input) { + return translate(input, dataBits, codeBits, false); + }; + + /** * Decode. Input and output are strings. */ - decode = function (input) { - return translate(input, codeBits, dataBits, true); - }; - - this.encode = encode; - this.decode = decode; - construct(); + decode = function (input) { + return translate(input, codeBits, dataBits, true); + }; + + this.encode = encode; + this.decode = decode; + construct(); }; -var Base32 = new Nibbler({ - dataBits: 8, - codeBits: 5, - keyString: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', - pad: '=' +const Base32 = new Nibbler({ + dataBits: 8, + codeBits: 5, + keyString: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567', + pad: '=', }); -var Base64 = new Nibbler({ - dataBits: 8, - codeBits: 6, - keyString: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', - pad: '=' +const Base64 = new Nibbler({ + dataBits: 8, + codeBits: 6, + keyString: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', + pad: '=', }); exports.Nibbler = Nibbler; exports.b32encode = Base32.encode; exports.b32decode = Base32.decode; exports.b64encode = Base64.encode; -exports.b64decode = Base64.decode; \ No newline at end of file +exports.b64decode = Base64.decode; diff --git a/src/otp.js b/src/otp.js index 15a6d30..8d04a11 100644 --- a/src/otp.js +++ b/src/otp.js @@ -2,96 +2,97 @@ * @module : OTP module to generate the password * @author : Gin (gin.lance.inside@hotmail.com) */ -const jsSHA = require("jssha"); +const jsSHA = require('jssha'); + import { Base32 } from './base32'; import { Util } from './util'; -export class OTP { - /** +export class OTP { + /* * * This constructor will create OTP instance. * * @param {secret} * @type {String} - * @desc random base32-encoded key, it is the + * @desc random base32-encoded key, it is the * key that be used to verify. * * @param {digits} - * @type {int} + * @type {int} * @desc the length of the one-time password, default to be 6 * * @param {digest} * @type {String} - * @desc the key that be used to do HMAC encoding, dedault and + * @desc the key that be used to do HMAC encoding, dedault and * only to be "sha1" * */ - constructor(secret, digits=6, digest="SHA-1") { - this.secret = secret; - this.digits = digits; - this.digest = digest; - } + constructor(secret, digits = 6, digest = 'SHA-1') { + this.secret = secret; + this.digits = digits; + this.digest = digest; + } - /** + /* * * When class HOTP or TOTP pass the input params to this * function, it will generate the OTP object with params, - * the params may be counter or time. + * the params may be counter or time. * * @param {input} - * @type {int} + * @type {int} * @desc input params to generate OTP object, maybe * counter or time. * * @return {OTP} */ - generate_otp(input) { - // generate HMAC object with SHA-1 digest - let hmacObj = new jsSHA(this.digest, "BYTES"); - // set hmac token - hmacObj.setHMACKey(Util.byte_secret(this.secret), "BYTES"); - // hamc encode the input param - hmacObj.update(Util.int_to_bytestring(input)); + generate_otp(input) { + // generate HMAC object with SHA-1 digest + const hmacObj = new jsSHA(this.digest, 'BYTES'); + // set hmac token + hmacObj.setHMACKey(Util.byte_secret(this.secret), 'BYTES'); + // hamc encode the input param + hmacObj.update(Util.int_to_bytestring(input)); - // get HMAC ans - let hmac = hmacObj.getHMAC("BYTES"); + // get HMAC ans + const hmac = hmacObj.getHMAC('BYTES'); - // transfer hmac to Array - let hmac_a = hmac.split(""); + // transfer hmac to Array + const hmac_a = hmac.split(''); - // calculate the init offset - let offset = hmac_a[hmac_a.length - 1].charCodeAt() & 0xf; + // calculate the init offset + const offset = hmac_a[hmac_a.length - 1].charCodeAt() & 0xf; - // calculate the code - let code = ( - (hmac_a[offset].charCodeAt() & 0x7f) << 24 | + // calculate the code + const code = ( + (hmac_a[offset].charCodeAt() & 0x7f) << 24 | (hmac_a[offset + 1].charCodeAt() & 0xff) << 16 | - (hmac_a[offset + 2].charCodeAt() & 0xff) << 8 | + (hmac_a[offset + 2].charCodeAt() & 0xff) << 8 | (hmac_a[offset + 3].charCodeAt() & 0xff) - ); + ); - // get the init str code - let str_code = (code % 10 ** this.digits).toString(); + // get the init str code + let str_code = (code % 10 ** this.digits).toString(); - // rjust format - str_code = Util.rjust(str_code, this.digits); + // rjust format + str_code = Util.rjust(str_code, this.digits); - return str_code; - } + return str_code; + } - /** + /* * * Generate a url with TOTP or HOTP instance. * * @param {issuer} - * @type {String} + * @type {String} * @desc maybe it is the Service name * * @param {type} - * @type {String} + * @type {String} * @desc type of OTP instance * * @return {String} */ - url_gen(issuer, type) { - return `otpauth://${type}/SK?secret=${this.secret}&issuer=${issuer}`; - } - } + url_gen(issuer, type) { + return `otpauth://${type}/SK?secret=${this.secret}&issuer=${issuer}`; + } +} diff --git a/src/totp.js b/src/totp.js index 3c93f0a..cbe753a 100644 --- a/src/totp.js +++ b/src/totp.js @@ -7,24 +7,24 @@ import { OTP } from './otp'; import { Util } from './util'; export class TOTP extends OTP { - /** + /* * * @param {secret} - * @type {String} + * @type {String} * @desc random base32-encoded key to generate OTP. * * @param {interval} - * @type {int} + * @type {int} * @desc the time interval in seconds for OTP. * This defaults to 30. * * @return {OTP} */ - constructor(secret, interval=30) { - super(secret); - this.interval = interval; - } + constructor(secret, interval = 30) { + super(secret); + this.interval = interval; + } - /** + /* * * Generate the OTP with current time. * * @return {OTP} @@ -35,24 +35,24 @@ export class TOTP extends OTP { * totp.now(); // => 432143 * ``` */ - now() { - // get now time string - let now = Util.timecode(new Date(), this.interval); + now() { + // get now time string + const now = Util.timecode(new Date(), this.interval); - // generate the one-time password - let digit = super.generate_otp(now); - return digit; - } + // generate the one-time password + const digit = super.generate_otp(now); + return digit; + } - /** + /* * * Verifies the OTP passed in against the current time OTP. * * @param {otp} - * @type {String} + * @type {String} * @desc the OTP waiting for checking * * @param {time} - * @type {int or datetime} + * @type {int or datetime} * @desc Time to check OTP at (defaults to now) * * @return {Boolean} @@ -67,31 +67,30 @@ export class TOTP extends OTP { * totp.verify(432143); // => false * ``` */ - verify(otp, time=null) { - let otp_time; + verify(otp, time = null) { + let otp_time; - if (null == time) { - time = new Date(); - otp_time = this.now() - } - - if (otp === otp_time) { - return true; - } else { - return false; - } + if (time == null) { + time = new Date(); + otp_time = this.now(); } - /** + if (otp === otp_time) { + return true; + } + return false; + } + + /* * * Generate a url with TOTP instance. * * @param {issuer} - * @type {String} + * @type {String} * @desc maybe it is the Service name * * @return {String} */ - url_gen(issuer="") { - return super.url_gen(issuer, "totp"); - } - } \ No newline at end of file + url_gen(issuer = '') { + return super.url_gen(issuer, 'totp'); + } +} diff --git a/src/util.js b/src/util.js index 162af91..c766d89 100644 --- a/src/util.js +++ b/src/util.js @@ -6,113 +6,111 @@ import { Base32 } from './base32'; export class Util { - /** + /* * * Util rjust number with 0 * * @param {num} - * @type {int} + * @type {int} * @desc input number * * @param {n} - * @type {int} + * @type {int} * @desc wanted length * * @return {String} */ - static rjust(num, n) { - let len = num.toString().length; + static rjust(num, n) { + let len = num.toString().length; - while (len < n) { - num = "0" + num; - len++; - } - - return num; + while (len < n) { + num = `0${num}`; + len++; } - /** + return num; + } + + /* * * Util rjust array with "" * * @param {arr} - * @type {Array} + * @type {Array} * @desc input array * * @param {n} - * @type {int} + * @type {int} * @desc wanted length * * @return {BYTES} */ - static arr_rjust(arr, n) { - if (n <= arr.length) { - arr = arr.splice(arr.length - 1 - n); - return arr; - } else { - let diff = n - arr.length; - for (let i = 0; i < diff; i++) { - arr.unshift(String.fromCharCode(0)); - } - return arr; - } + static arr_rjust(arr, n) { + if (n <= arr.length) { + arr = arr.splice(arr.length - 1 - n); + return arr; } + const diff = n - arr.length; + for (let i = 0; i < diff; i++) { + arr.unshift(String.fromCharCode(0)); + } + return arr; + } - /** + /* * * Base32 decode the init secret * * @param {secret} - * @type {String} + * @type {String} * @desc input param, the init secret * * @return {String} */ - static byte_secret(secret) { - return Base32.decode(secret.toUpperCase()); - } + static byte_secret(secret) { + return Base32.decode(secret.toUpperCase()); + } - /** + /* * * transfer the int type to BYTES type * * @param {input} - * @type {int} + * @type {int} * @desc input param, maybe counter or time * * @return {BYTES} */ - static int_to_bytestring(input, padding=8) { - let result = []; + static int_to_bytestring(input, padding = 8) { + let result = []; - while (0 != input) { - result.push(String.fromCharCode(input & 0xFF)); - input >>= 8; - } - - result = result.reverse(); - result = Util.arr_rjust(result, padding).join(""); - - return result; + while (input != 0) { + result.push(String.fromCharCode(input & 0xFF)); + input >>= 8; } - /** + result = result.reverse(); + result = Util.arr_rjust(result, padding).join(''); + + return result; + } + + /* * * format the time string to int * * @param {time} - * @type {Date} + * @type {Date} * @desc the time need to be format * * @param {interval} - * @type {Int} + * @type {Int} * @desc interval means the one-time password's life, * default to be 30. * * @return {Int} */ - static timecode(time, interval) { - let time_str = Date.parse(time).toString(); + static timecode(time, interval) { + const time_str = Date.parse(time).toString(); - // fotmat the time, the ms is not needed. - let format_time = time_str.substring(0, time_str.length-3); + // fotmat the time, the ms is not needed. + const format_time = time_str.substring(0, time_str.length - 3); - return parseInt(parseInt(format_time) / interval); - } - -} \ No newline at end of file + return parseInt(parseInt(format_time) / interval); + } +}