1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-10-10 06:08:34 +02:00
bitwarden-browser/src/app/services/passwordService.js

137 lines
4.8 KiB
JavaScript

angular
.module('bit.services')
.factory('passwordService', function () {
var _service = {};
_service.generatePassword = function (options) {
var defaults = {
length: 10,
ambiguous: false,
number: true,
minNumber: 1,
uppercase: true,
minUppercase: 1,
lowercase: true,
minLowercase: 1,
special: false,
minSpecial: 1
};
// overload defaults with given options
var o = angular.extend({}, defaults, options);
// sanitize
if (o.uppercase && o.minUppercase < 0) o.minUppercase = 1;
if (o.lowercase && o.minLowercase < 0) o.minLowercase = 1;
if (o.number && o.minNumber < 0) o.minNumber = 1;
if (o.special && o.minSpecial < 0) o.minSpecial = 1;
if (!o.length || o.length < 1) o.length = 10;
var minLength = o.minUppercase + o.minLowercase + o.minNumber + o.minSpecial;
if (o.length < minLength) o.length = minLength;
var positions = [];
if (o.lowercase && o.minLowercase > 0) {
for (var i = 0; i < o.minLowercase; i++) {
positions.push('l');
}
}
if (o.uppercase && o.minUppercase > 0) {
for (var j = 0; j < o.minUppercase; j++) {
positions.push('u');
}
}
if (o.number && o.minNumber > 0) {
for (var k = 0; k < o.minNumber; k++) {
positions.push('n');
}
}
if (o.special && o.minSpecial > 0) {
for (var l = 0; l < o.minSpecial; l++) {
positions.push('s');
}
}
while (positions.length < o.length) {
positions.push('a');
}
// shuffle
positions.sort(function () {
return randomInt(0, 1) * 2 - 1;
});
// build out the char sets
var allCharSet = '';
var lowercaseCharSet = 'abcdefghijkmnopqrstuvwxyz';
if (o.ambiguous) lowercaseCharSet += 'l';
if (o.lowercase) allCharSet += lowercaseCharSet;
var uppercaseCharSet = 'ABCDEFGHIJKLMNPQRSTUVWXYZ';
if (o.ambiguous) uppercaseCharSet += 'O';
if (o.uppercase) allCharSet += uppercaseCharSet;
var numberCharSet = '23456789';
if (o.ambiguous) numberCharSet += '01';
if (o.number) allCharSet += numberCharSet;
var specialCharSet = '!@#$%^&*';
if (o.special) allCharSet += specialCharSet;
var password = '';
for (var m = 0; m < o.length; m++) {
var positionChars;
switch (positions[m]) {
case 'l': positionChars = lowercaseCharSet; break;
case 'u': positionChars = uppercaseCharSet; break;
case 'n': positionChars = numberCharSet; break;
case 's': positionChars = specialCharSet; break;
case 'a': positionChars = allCharSet; break;
}
var randomCharIndex = randomInt(0, positionChars.length - 1);
password += positionChars.charAt(randomCharIndex);
}
return password;
};
// EFForg/OpenWireless
// ref https://github.com/EFForg/OpenWireless/blob/master/app/js/diceware.js
function randomInt(min, max) {
var rval = 0;
var range = max - min;
var bits_needed = Math.ceil(Math.log2(range));
if (bits_needed > 53) {
throw new Exception("We cannot generate numbers larger than 53 bits.");
}
var bytes_needed = Math.ceil(bits_needed / 8);
var mask = Math.pow(2, bits_needed) - 1;
// 7776 -> (2^13 = 8192) -1 == 8191 or 0x00001111 11111111
// Create byte array and fill with N random numbers
var byteArray = new Uint8Array(bytes_needed);
window.crypto.getRandomValues(byteArray);
var p = (bytes_needed - 1) * 8;
for (var i = 0; i < bytes_needed; i++) {
rval += byteArray[i] * Math.pow(2, p);
p -= 8;
}
// Use & to apply the mask and reduce the number of recursive lookups
rval = rval & mask;
if (rval >= range) {
// Integer out of acceptable range
return randomInt(min, max);
}
// Return an integer that falls within the range
return min + rval;
}
return _service;
});