Files
ewpass/ewpass.js

238 lines
7.0 KiB
JavaScript

var ewpass = {};
(function(ns) {
const SEED_PREFIX = 'E. W. Password Generator Seed/';
const DERIVE_PREFIX = 'Domain Password/';
const Storage = window.localStorage;
const BASIC_MAP = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const EXTENDED_MAP = BASIC_MAP + '`~!@#$%^&*()-_=+[{]}\\|;:\'",<.>/?';
var settings = {
'checksum': undefined,
'DEFAULT': [ 16, 1, true ],
'domains': {}
};
var currentDomain = undefined;
var seedValid = false;
var checkSeedChecksum = () => {
const valid = $('#checksum').text() === settings.checksum;
$('#chkok').html(valid ? '✓' : '✗');
$('#chkset').prop('disabled', valid ? 'disabled' : '');
seedValid = valid;
};
var setDomains = () => {
$domainlist = $("#domainlist")
$domainlist.html('');
Object.keys(settings.domains).sort().forEach(domain =>
$domainlist.append($('<option>').attr('value', domain).text(domain))
);
};
var storeDomains = () => {
Storage.setItem('domains', JSON.stringify(settings.domains));
};
var calcRounds = (l, allowBias) => {
var m = 0xffffffff;
var n = 0;
while (m > 0) {
m = Math.floor(m / l);
n++;
}
return n;
};
ns.resetSeed = () => {
$('#seed').val('');
$('#seed').prop('disabled', '');
$('#checksum').text('-');
$('#chkok').text('');
$('#chkset').prop('disabled', 'disabled');
seedValid = false;
$('#seed').focus();
};
ns.calculateSeed = () => {
sha256(SEED_PREFIX + $('#seed').val()).then(hash => {
$('#seed').val(hash);
$('#seed').prop('disabled', 'disabled');
$('#checksum').text(hash.substring(0, 2));
checkSeedChecksum();
});
};
ns.storeSeedChecksum = () => {
settings.checksum = $('#checksum').text();
Storage.setItem('checksum', settings.checksum);
checkSeedChecksum();
};
ns.checkDomainInputMatchesToOption = () => {
var domain = $('#domain').val();
if (domain in settings.domains) {
ns.loadSettingsForDomain(domain);
} else {
ns.loadSettingsForDomain('');
}
};
ns.loadSettingsForDomain = (domain) => {
if (domain === undefined) {
ns.loadSettingsForDomain($('#domain').val());
return;
}
var _settings;
if (domain === '') {
$('#length').prop('disabled', 'disabled');
$('#iteration').prop('disabled', 'disabled');
$('#nextiter').prop('disabled', 'disabled');
$('#extended').prop('disabled', 'disabled');
$('#storedomain').prop('disabled', 'disabled');
$('#docopy').prop('disabled', 'disabled');
_settings = settings.DEFAULT;
} else if (domain in settings.domains) {
_settings = settings.domains[domain];
} else if (!confirm('Add new domain "' + domain + '" ?')) {
$('#domain').val('');
ns.loadSettingsForDomain('');
return;
} else {
settings.domains[domain] = settings.DEFAULT.slice();
setDomains();
_settings = settings.domains[domain];
Storage.setItem('domains', JSON.stringify(settings.domains));
}
$('#length').val(_settings[0]);
$('#iteration').val(_settings[1]);
$('#extended').prop('checked', _settings[2] ? 'checked' : '');
if (_settings !== settings.DEFAULT) {
$('#length').prop('disabled', '');
$('#iteration').prop('disabled', '');
$('#nextiter').prop('disabled', '');
$('#extended').prop('disabled', '');
$('#storedomain').prop('disabled', '');
$('#docopy').prop('disabled', '');
currentDomain = domain;
}
};
ns.storeSettingsForDomain = () => {
_settings = [
parseInt($('#length').val()),
parseInt($('#iteration').val()),
$('#extended').prop('checked')
];
settings.domains[currentDomain] = _settings;
storeDomains();
};
ns.nextIteration = () => {
$('#iteration').val(parseInt($('#iteration').val()) + 1);
ns.storeSettingsForDomain();
};
ns.deriveAndCopyToClipboard = () => {
if (!seedValid) {
alert('Enter master password first!');
ns.resetSeed();
return;
}
$docopy = $('#docopy');
$docopy.prop('disabled', 'disabled');
sha256(DERIVE_PREFIX + $('#seed').val() + '/' + settings.domains[currentDomain][1] + '/' + currentDomain).then(hash => {
const map = settings.domains[currentDomain][2] ? EXTENDED_MAP : BASIC_MAP;
const rounds = calcRounds(map.length);
var pw = '';
for (var i = 0; i < 8; i++) {
var x = parseInt(hash.substring(i * 8, (i+1) * 8), 16);
for (var j = 0; j < rounds; j++) {
pw += map.substring(x % map.length, x % map.length + 1);
x = Math.floor(x / map.length);
}
}
pw = pw.substring(0, settings.domains[currentDomain][0]);
navigator.clipboard.writeText(pw).then(() => {
$docopy.val('[ COPIED ]');
setTimeout(() => {
$docopy.val('Copy password');
$docopy.prop('disabled', '');
}, 1500);
});
});
};
ns.deleteDomain = () => {
if (Object.keys(settings.domains).indexOf($('#domain').val()) >= 0) {
delete settings.domains[$('#domain').val()];
storeDomains();
$('#domain').val('');
ns.loadSettingsForDomain();
}
};
ns.setup = () => {
if (Storage.getItem('checksum') != null) {
settings.checksum = Storage.getItem('checksum');
}
if (Storage.getItem('domains') != null) {
settings.domains = JSON.parse(Storage.getItem('domains'));
console.debug('Loaded domains:', settings.domains);
setDomains();
}
var setupReturnEvent = (o, handler) => {
o.keypress((event, value) => {
if (event.which === 13) {
handler(o.val());
}
});
};
setupReturnEvent($('#seed'), ns.calculateSeed);
setupReturnEvent($('#domain'), ns.loadSettingsForDomain);
ns.resetSeed();
ns.loadSettingsForDomain();
};
ns.configuration = {};
ns.configuration.dump = () =>
console.log(JSON.stringify(settings.domains));
ns.configuration.load = domainSettings => {
for (let k in domainSettings)
settings.domains[k] = domainSettings[k];
Storage.setItem('domains', JSON.stringify(settings.domains));
setDomains();
ns.loadSettingsForDomain();
};
})(ewpass);