const base64url = require('base64-url');
const crypto = require('crypto-js');
const lengthLegacy = 8;
const lengthDefault = 16;
/**
* Function to parse label DSL for length.
*
* @private
*
* @param {string} labelParam: the label
* @param {string} lengthParam: the length, defaults to 16
*
* @returns {array} array of label and length
*/
function getLabelAndLength(labelParam, lengthParam) {
let label = labelParam;
let length = lengthParam;
if (length) {
return [label, length];
} else {
length = lengthDefault;
if (label.match(/^([0-9]*)?\*/)) {
let rest;
[length, ...rest] = label.split('*');
label = rest.join('*');
length = parseInt(length, 10) || lengthLegacy;
}
return [label, length];
}
}
/**
* @function oplop
*
* @see {@link https://github.com/brettcannon/oplop}
* @see {@link https://github.com/thedod/loplop}
* @see {@link https://github.com/bkaney/oplop}
* @description
*
* Create a password given a label/mnemonic and master password and optional
* length.
*
* NOTE Unless length is explicitly passed as the third parameter, this library
* uses the "loplop" variation where the label can use an optional DSL:
*
* * if the label begins with a `<digit>*`, the <digit> should be the length of
* the password,
* * if the label begins with a `*`, the length of the password is assumed the
* classic length of 8,
* * otherwise the password will be 16 characters long.
*
* @example
*
* // 16-character password using `label` and `master`
* let password = require('loplop') ('label', 'master');
*
* // 8-character password using `label` and `master`
* let password = require('loplop') ('*label', 'master');
* let password = require('loplop') ('label', 'master', 8);
*
* // 10-character password using `label` and `master`
* let password = require('loplop') ('10*label', 'master')
* let password = require('loplop') ('label', 'master', 10)
*
* @param {string} labelParam - label/mnemonic
* @param {string} masterParam - master password
* @param {string} [lengthParam] - optional length of generated password
*
* @returns {string} the generated password
*/
module.exports = function (labelParam, masterParam, lengthParam) {
const [label, length] = getLabelAndLength(labelParam, lengthParam);
let password = crypto.MD5(`${masterParam}${label}`);
password = base64url.escape(password.toString(crypto.enc.Base64));
const digitRegex = /\d+/;
const digitPos = password.search(digitRegex);
if (digitPos < 0) {
password = `1${password}`;
} else if (digitPos >= length) {
const digit = password.match(digitRegex);
password = `${digit}${password}`;
}
return password.substring(0, length);
};