1 /* 2 * telepathy 3 * https://github.com/rummik/telepathy 4 * 5 * Copyright (c) 2012 rummik 6 * Licensed under the MPL license. 7 */ 8 9 var CryptoJS = require('../crypto-js/hmac-sha512'), 10 BD = require('bigdecimal'), 11 BigInteger = BD.BigInteger; 12 13 /** 14 * Telepathy constructor 15 * @constructor 16 * @param {object|string} [options] 17 */ 18 function Telepathy(options) { 19 options = options || {}; 20 21 if (typeof options == 'string') 22 options = { secret: options }; 23 24 this.setSecret(options.secret); 25 delete options.secret; 26 27 this.user = options.user || ''; 28 this.alphabet = options.alphabet || Telepathy.alphabet.base62; 29 this.length = options.length || 10; 30 this.domain = options.domain || ''; 31 } 32 33 /** 34 * Set the private secret we'll be using in generating passwords 35 * @param {string} secret 36 */ 37 Telepathy.prototype.setSecret = function(secret) { 38 this.password = this.constructor.prototype._password.bind(this, secret || ''); 39 }; 40 41 /** 42 * Generate a password 43 * @param {object|string|number} [options] 44 * @returns {string} Generated password 45 * @example 46 * var telepathy = new Telepathy('secret'); 47 * console.log(telepathy.password('google.com')); 48 */ 49 Telepathy.prototype.password = function(options) {}; 50 51 /** @ignore */ 52 Telepathy.prototype._password = function(secret, options) { 53 options = options || {}; 54 55 if (typeof options == 'string') 56 options = { domain: options }; 57 58 if (typeof options == 'number') 59 options = { index: options }; 60 61 var length = parseInt(options.length || this.length || 10, 10), 62 index = Math.max(parseInt(options.index || 0, 10), 0), 63 user = options.user || this.user, 64 domain = options.domain || this.domain, 65 alphabet = options.alphabet || this.alphabet, 66 password = '', 67 base = new BigInteger(alphabet.length.toString()), 68 data = user + domain, 69 len = length * (index + 1), 70 result, hash, remainder; 71 72 secret = options.secret || secret; 73 74 while (password.length < len) { 75 // create hash, and make it a big integer 76 hash = new BigInteger(CryptoJS.HmacSHA512(data + password, secret).toString(), 16); 77 78 // convert hash to an arbitrary base 79 while (hash.compareTo(base) >= 0 && password.length < len) { 80 result = hash.divideAndRemainder(base); 81 hash = result[0]; 82 remainder = result[1]; 83 84 password = alphabet[remainder] + password; 85 86 if (hash.compareTo(base) == -1 && password.length < len) 87 password = alphabet[hash] + password; 88 } 89 } 90 91 return password.substr(0, length); 92 }; 93 94 /** 95 * Output alphabets used in password generation (larger alphabets are better) 96 * @readonly 97 * @namespace 98 * @property {string} base94 WARNING: this could break insecure login forms 99 * @property {string} base62 100 * @property {string} base36 101 * @property {string} base16 102 * @property {string} base10 103 * @property {string} base8 104 * @property {string} base5 105 * @property {string} base2 106 */ 107 Telepathy.alphabet = { 108 base94: '!"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~', 109 base62: '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz', 110 base36: '0123456789abcdefghijklmnopqrstuvwxyz', 111 base16: '0123456789abcdef', 112 base10: '0123456789', 113 base8: '01234567', 114 base5: '01234', 115 base2: '01' 116 }; 117 118 module.exports = Telepathy; 119